Merge branch 'wip-MDL-31836-m26' of git://github.com/samhemelryk/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 13 Aug 2013 06:57:23 +0000 (14:57 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 13 Aug 2013 06:57:23 +0000 (14:57 +0800)
210 files changed:
admin/cli/install.php
admin/purgecaches.php
admin/roles/allow.php
admin/roles/assign.php
admin/roles/define.php
admin/roles/manage.php
admin/roles/override.php
admin/settings/server.php
admin/tool/behat/tests/behat/data_generators.feature
admin/webservice/forms.php
backup/backup.php
backup/restore.php
badges/classes/observer.php [new file with mode: 0644]
badges/tests/badgeslib_test.php
blocks/calendar_upcoming/block_calendar_upcoming.php
blog/locallib.php
blog/tests/bloglib_test.php
cache/locallib.php
cache/testperformance.php
calendar/lib.php
completion/completion_completion.php
config-dist.php
course/lib.php
course/moodleform_mod.php
course/tests/courselib_test.php
course/yui/dragdrop/dragdrop.js
files/externallib.php
files/renderer.php
files/tests/externallib_test.php
grade/edit/tree/action.php
grade/edit/tree/lib.php
grade/lib.php
install.php
lang/en/blog.php
lang/en/cache.php
lang/en/completion.php
lang/en/form.php
lang/en/moodle.php
lang/en/role.php
lang/en/webservice.php
lib/accesslib.php
lib/badgeslib.php
lib/classes/component.php
lib/classes/event/assessable_submitted.php [new file with mode: 0644]
lib/classes/event/assessable_uploaded.php [new file with mode: 0644]
lib/classes/event/blog_entry_deleted.php [new file with mode: 0644]
lib/classes/event/course_completed.php [new file with mode: 0644]
lib/classes/event/course_module_completion_updated.php [new file with mode: 0644]
lib/classes/event/role_allow_assign_updated.php [new file with mode: 0644]
lib/classes/event/role_allow_override_updated.php [new file with mode: 0644]
lib/classes/event/role_allow_switch_updated.php [new file with mode: 0644]
lib/classes/event/role_assigned.php
lib/classes/event/role_capabilities_updated.php [new file with mode: 0644]
lib/classes/event/role_deleted.php [new file with mode: 0644]
lib/classes/event/role_unassigned.php
lib/classes/string_manager.php [new file with mode: 0644]
lib/classes/string_manager_install.php [new file with mode: 0644]
lib/classes/string_manager_standard.php [new file with mode: 0644]
lib/completionlib.php
lib/coursecatlib.php
lib/db/caches.php
lib/db/events.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/plugins/managefiles/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/pdw/readme_moodle.txt
lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin.js
lib/editor/tinymce/styles.css
lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-debug.js [deleted file]
lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-min.js [deleted file]
lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse.js [deleted file]
lib/editor/tinymce/yui/src/collapse/build.json [deleted file]
lib/editor/tinymce/yui/src/collapse/js/collapse.js [deleted file]
lib/editor/tinymce/yui/src/collapse/meta/collapse.json [deleted file]
lib/form/editor.php
lib/form/filemanager.js
lib/formslib.php
lib/grade/grade_category.php
lib/grade/grade_item.php
lib/grade/grade_object.php
lib/grade/tests/fixtures/lib.php
lib/grade/tests/grade_category_test.php
lib/grade/tests/grade_item_test.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpunit/classes/util.php
lib/portfoliolib.php
lib/setuplib.php
lib/testing/classes/util.php
lib/tests/accesslib_test.php
lib/tests/behat/behat_data_generators.php
lib/tests/behat/behat_hooks.php
lib/tests/completionlib_test.php
lib/tests/component_test.php
lib/tests/string_manager_standard_test.php [moved from lib/tests/string_manager_test.php with 95% similarity]
lib/upgrade.txt
lib/upgradelib.php
lib/weblib.php
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/build/moodle-core-chooserdialogue/moodle-core-chooserdialogue-debug.js
lib/yui/build/moodle-core-chooserdialogue/moodle-core-chooserdialogue-min.js
lib/yui/build/moodle-core-chooserdialogue/moodle-core-chooserdialogue.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue.js
lib/yui/dragdrop/dragdrop.js
lib/yui/src/blocks/js/blocks.js
lib/yui/src/chooserdialogue/js/chooserdialogue.js
lib/yui/src/notification/js/dialogue.js
mod/assign/adminlib.php
mod/assign/classes/event/assessable_submitted.php [new file with mode: 0644]
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/submission/file/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/assign/submission/file/lang/en/assignsubmission_file.php
mod/assign/submission/file/locallib.php
mod/assign/submission/file/tests/events_test.php [new file with mode: 0644]
mod/assign/submission/onlinetext/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/submission/onlinetext/tests/events_test.php [new file with mode: 0644]
mod/assignment/type/online/assignment.class.php
mod/assignment/type/online/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/assignment/type/online/lang/en/assignment_online.php
mod/assignment/type/upload/assignment.class.php
mod/assignment/type/upload/classes/event/assessable_submitted.php [new file with mode: 0644]
mod/assignment/type/upload/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/assignment/type/upload/lang/en/assignment_upload.php
mod/chat/gui_basic/index.php
mod/chat/lib.php
mod/data/field/file/field.class.php
mod/data/field/picture/field.class.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/module.js [new file with mode: 0644]
mod/data/templates.php
mod/data/tests/lib_test.php [new file with mode: 0644]
mod/data/view.php
mod/forum/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/tests/lib_test.php [new file with mode: 0644]
mod/lesson/essay.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/mod_form.php
mod/quiz/renderer.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/tests/fixtures/qstats00.csv [new file with mode: 0644]
mod/quiz/report/statistics/tests/fixtures/questions00.csv [new file with mode: 0644]
mod/quiz/report/statistics/tests/fixtures/results00.csv [new file with mode: 0644]
mod/quiz/report/statistics/tests/fixtures/steps00.csv [new file with mode: 0644]
mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php [new file with mode: 0644]
mod/quiz/settings.php
mod/quiz/styles.css
mod/quiz/tests/attempt_walkthrough_from_csv_test.php
mod/resource/tests/generator/lib.php [new file with mode: 0644]
mod/resource/tests/generator_test.php [new file with mode: 0644]
mod/scorm/datamodels/aicc.js.php
mod/scorm/datamodels/scorm_12.js.php
mod/scorm/datamodels/scorm_13.js.php
mod/workshop/classes/event/assessable_uploaded.php [new file with mode: 0644]
mod/workshop/lang/en/workshop.php
mod/workshop/locallib.php
mod/workshop/submission.php
question/behaviour/manualgraded/tests/walkthrough_test.php
question/engine/lib.php
question/engine/questionattempt.php
question/engine/tests/questionattempt_test.php
question/format.php
question/format/webct/TODO.txt [deleted file]
question/format/webct/format.php
question/format/webct/lang/en/qformat_webct.php
question/format/webct/tests/fixtures/sample_webct.txt [new file with mode: 0644]
question/format/webct/tests/webctformat_test.php [new file with mode: 0644]
question/format/webct/version.php
question/format/xhtml/format.php
question/format/xhtml/lib.php [new file with mode: 0644]
question/type/edit_question_form.php
question/type/essay/question.php
question/type/essay/tests/question_test.php
question/type/multichoice/questiontype.php
question/type/questionbase.php
question/type/upgrade.txt
repository/filepicker.js
theme/base/style/core.css
theme/base/style/filemanager.css
theme/base/version.php
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/responsive.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/version.php
theme/upgrade.txt
user/externallib.php
user/tests/externallib_test.php
version.php
webservice/externallib.php
webservice/tests/externallib_test.php
webservice/upgrade.txt [new file with mode: 0644]
webservice/upload.php

index 2d992b5..21617cf 100644 (file)
@@ -172,6 +172,9 @@ ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include
 
 require_once($CFG->libdir.'/classes/component.php');
 require_once($CFG->libdir.'/classes/text.php');
+require_once($CFG->libdir.'/classes/string_manager.php');
+require_once($CFG->libdir.'/classes/string_manager_install.php');
+require_once($CFG->libdir.'/classes/string_manager_standard.php');
 require_once($CFG->libdir.'/installlib.php');
 require_once($CFG->libdir.'/clilib.php');
 require_once($CFG->libdir.'/setuplib.php');
index 006bb0d..c2247dc 100644 (file)
@@ -27,37 +27,37 @@ require_once('../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 
 $confirm = optional_param('confirm', 0, PARAM_BOOL);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
 
-admin_externalpage_setup('purgecaches');
-
-require_login();
-require_capability('moodle/site:config', context_system::instance());
-
-if ($confirm) {
-    require_sesskey();
+// If we have got here as a confirmed aciton, do it.
+if ($confirm && isloggedin() && confirm_sesskey()) {
+    require_capability('moodle/site:config', context_system::instance());
 
-    // Valid request. Purge, and redisplay the form so it is easy to purge again
-    // in the near future.
+    // Valid request. Purge, and redirect the user back to where they came from.
     purge_all_caches();
-    redirect(new moodle_url('/admin/purgecaches.php'), get_string('purgecachesfinished', 'admin'));
-
-} else {
-    // Show a confirm form.
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('purgecaches', 'admin'));
-
-    $url = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
-    $button = new single_button($url, get_string('purgecaches','admin'), 'post');
-
-    // Cancel button takes them back to the page the were on, if possible,
-    // otherwise to the site home page.
-    $return = new moodle_url('/');
-    if (isset($_SERVER['HTTP_REFERER']) and !empty($_SERVER['HTTP_REFERER'])) {
-        if ($_SERVER['HTTP_REFERER'] !== "$CFG->wwwroot/$CFG->admin/purgecaches.php") {
-            $return = $_SERVER['HTTP_REFERER'];
-        }
+
+    if ($returnurl) {
+        $returnurl = $CFG->wwwroot . $returnurl;
+    } else {
+        $returnurl = new moodle_url('/admin/purgecaches.php');
     }
+    redirect($returnurl, get_string('purgecachesfinished', 'admin'));
+}
 
-    echo $OUTPUT->confirm(get_string('purgecachesconfirm', 'admin'), $button, $return);
-    echo $OUTPUT->footer();
+// Otherwise, show a button to actually purge the caches.
+admin_externalpage_setup('purgecaches');
+
+$actionurl = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
+if ($returnurl) {
+    $actionurl->param('returnurl', $returnurl);
 }
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('purgecaches', 'admin'));
+
+echo $OUTPUT->box_start('generalbox', 'notice');
+echo html_writer::tag('p', get_string('purgecachesconfirm', 'admin'));
+echo $OUTPUT->single_button($actionurl, get_string('purgecaches', 'admin'), 'post');
+echo $OUTPUT->box_end();
+
+echo $OUTPUT->footer();
index 9b7e709..8ffd54d 100644 (file)
@@ -46,7 +46,22 @@ $controller = new $classformode[$mode]();
 if (optional_param('submit', false, PARAM_BOOL) && data_submitted() && confirm_sesskey()) {
     $controller->process_submission();
     $syscontext->mark_dirty();
-    add_to_log(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl), '', '', $USER->id);
+    $event = null;
+    // Create event depending on mode.
+    switch ($mode) {
+        case 'assign':
+            $event = \core\event\role_allow_assign_updated::create(array('context' => $syscontext));
+            break;
+        case 'override':
+            $event = \core\event\role_allow_override_updated::create(array('context' => $syscontext));
+            break;
+        case 'switch':
+            $event = \core\event\role_allow_switch_updated::create(array('context' => $syscontext));
+            break;
+    }
+    if ($event) {
+        $event->trigger();
+    }
     redirect($baseurl);
 }
 
index 8346640..cce2334 100644 (file)
@@ -110,8 +110,6 @@ if ($roleid) {
             $potentialuserselector->invalidate_selected_users();
             $currentuserselector->invalidate_selected_users();
 
-            $rolename = $assignableroles[$roleid];
-            add_to_log($course->id, 'role', 'assign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
             // Counts have changed, so reload.
             list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true);
         }
@@ -130,8 +128,6 @@ if ($roleid) {
             $potentialuserselector->invalidate_selected_users();
             $currentuserselector->invalidate_selected_users();
 
-            $rolename = $assignableroles[$roleid];
-            add_to_log($course->id, 'role', 'unassign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
             // Counts have changed, so reload.
             list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true);
         }
index f588d1a..265b100 100644 (file)
@@ -196,8 +196,22 @@ if (optional_param('cancel', false, PARAM_BOOL)) {
 // Process submission in necessary.
 if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey() && $definitiontable->is_submission_valid()) {
     $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);
+    $tableroleid = $definitiontable->get_role_id();
+    // Trigger event.
+    $event = \core\event\role_capabilities_updated::create(
+        array(
+            'context' => $systemcontext,
+            'objectid' => $roleid,
+            'other' => array('name' => $definitiontable->get_role_name())
+        )
+    );
+    $event->set_legacy_logdata(array(SITEID, 'role', $action, 'admin/roles/define.php?action=view&roleid=' . $tableroleid,
+        $definitiontable->get_role_name(), '', $USER->id));
+    if (!empty($role)) {
+        $event->add_record_snapshot('role', $role);
+    }
+    $event->trigger();
+
     if ($action === 'add') {
         redirect(new moodle_url('/admin/roles/define.php', array('action'=>'view', 'roleid'=>$definitiontable->get_role_id())));
     } else {
index ec02517..e3e094a 100644 (file)
@@ -90,7 +90,6 @@ switch ($action) {
         }
         // Deleted a role sitewide...
         $systemcontext->mark_dirty();
-        add_to_log(SITEID, 'role', 'delete', 'admin/roles/manage.php', $roles[$roleid]->localname, '', $USER->id);
         redirect($baseurl);
         break;
 
index 7d27dc2..bc506f1 100644 (file)
@@ -125,7 +125,25 @@ $overridestable->read_submitted_permissions();
 if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
     $overridestable->save_changes();
     $rolename = $overridableroles[$roleid];
-    add_to_log($course->id, 'role', 'override', 'admin/roles/override.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id);
+    // Trigger event.
+    $event = \core\event\role_capabilities_updated::create(
+        array(
+            'context' => $context,
+            'objectid' => $roleid,
+            'courseid' => $courseid,
+            'other' => array('name' => $rolename)
+        )
+    );
+
+    $event->set_legacy_logdata(
+        array(
+            $course->id, 'role', 'override', 'admin/roles/override.php?contextid=' . $context->id . '&roleid=' . $roleid,
+            $rolename, '', $USER->id
+        )
+    );
+    $event->add_record_snapshot('role', $role);
+    $event->trigger();
+
     redirect($returnurl);
 }
 
index 85e3f2d..3a58d34 100644 (file)
@@ -175,16 +175,25 @@ $ADMIN->add('server', new admin_externalpage('phpinfo', new lang_string('phpinfo
 // "performance" settingpage
 $temp = new admin_settingpage('performance', new lang_string('performance', 'admin'));
 
+// Memory limit options for large administration tasks.
+$memoryoptions = array(
+    '64M' => '64M',
+    '128M' => '128M',
+    '256M' => '256M',
+    '512M' => '512M',
+    '1024M' => '1024M',
+    '2048M' => '2048M');
+
+// Allow larger memory usage for 64-bit sites only.
+if (PHP_INT_SIZE === 8) {
+    $memoryoptions['3072M'] = '3072M';
+    $memoryoptions['4096M'] = '4096M';
+}
+
 $temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
                                           new lang_string('configextramemorylimit', 'admin'), '512M',
-                                          // if this option is set to 0, default 128M will be used
-                                          array( '64M' => '64M',
-                                                 '128M' => '128M',
-                                                 '256M' => '256M',
-                                                 '512M' => '512M',
-                                                 '1024M' => '1024M',
-                                                 '2048M' => '2048M',
-                                             )));
+                                          $memoryoptions));
+
 $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
                                         new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
 
index e37f9bc..c0d3665 100644 (file)
@@ -65,6 +65,37 @@ Feature: Set up contextual data for tests
     And I should see "Grouping 1"
     And I should see "Grouping 2"
 
+  @javascript
+  Scenario: Role overrides
+    Given the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com |
+      | student1 | Student | 1 | student1@asd.com |
+    And the following "categories" exists:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+    And the following "courses" exists:
+      | fullname | shortname |
+      | Course 1 | C1 |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | student1 | C1 | student |
+      | teacher1 | C1 | editingteacher |
+    And the following "permission overrides" exists:
+      | capability | permission | role | contextlevel | reference |
+      | mod/forum:editanypost | Allow | student | Course | C1 |
+      | mod/forum:replynews | Prevent | editingteacher | Course | C1 |
+    When I log in as "admin"
+    And I follow "Course 1"
+    And I expand "Users" node
+    And I follow "Permissions"
+    And I select "Student (1)" from "Advanced role override"
+    Then the "mod/forum:editanypost" field should match "1" value
+    And I press "Cancel"
+    And I select "Teacher (1)" from "Advanced role override"
+    And the "mod/forum:replynews" field should match "-1" value
+    And I press "Cancel"
+
   Scenario: Add course enrolments
     Given the following "users" exists:
       | username | firstname | lastname | email |
index b1c09ae..3aef57c 100644 (file)
@@ -73,12 +73,17 @@ class external_service_form extends moodleform {
         $mform->addHelpButton('restrictedusers', 'restrictedusers', 'webservice');
         $mform->setType('restrictedusers', PARAM_BOOL);
 
-        //can users download files
+        // Can users download files?
         $mform->addElement('advcheckbox', 'downloadfiles', get_string('downloadfiles', 'webservice'));
         $mform->setAdvanced('downloadfiles');
         $mform->addHelpButton('downloadfiles', 'downloadfiles', 'webservice');
         $mform->setType('downloadfiles', PARAM_BOOL);
 
+        // Can users upload files?
+        $mform->addElement('advcheckbox', 'uploadfiles', get_string('uploadfiles', 'webservice'));
+        $mform->setAdvanced('uploadfiles');
+        $mform->addHelpButton('uploadfiles', 'uploadfiles', 'webservice');
+
         /// needed to select automatically the 'No required capability" option
         $currentcapabilityexist = false;
         if (empty($service->requiredcapability)) {
@@ -297,4 +302,4 @@ class web_service_token_form extends moodleform {
         return $errors;
     }
 
-}
\ No newline at end of file
+}
index 2847665..cfe16ef 100644 (file)
@@ -79,6 +79,10 @@ switch ($type) {
         print_error('unknownbackuptype');
 }
 
+// Backup of large courses requires extra memory. Use the amount configured
+// in admin settings.
+raise_memory_limit(MEMORY_EXTRA);
+
 if (!($bc = backup_ui::load_controller($backupid))) {
     $bc = new backup_controller($type, $id, backup::FORMAT_MOODLE,
                             backup::INTERACTIVE_YES, backup::MODE_GENERAL, $USER->id);
index 6091c09..cd51cae 100644 (file)
@@ -17,6 +17,10 @@ $PAGE->set_pagelayout('standard');
 require_login($course, null, $cm);
 require_capability('moodle/restore:restorecourse', $context);
 
+// Restore of large courses requires extra memory. Use the amount configured
+// in admin settings.
+raise_memory_limit(MEMORY_EXTRA);
+
 if ($stage & restore_ui::STAGE_CONFIRM + restore_ui::STAGE_DESTINATION) {
     $restore = restore_ui::engage_independent_stage($stage, $contextid);
 } else {
diff --git a/badges/classes/observer.php b/badges/classes/observer.php
new file mode 100644 (file)
index 0000000..6d7b054
--- /dev/null
@@ -0,0 +1,108 @@
+<?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/>.
+
+/**
+ * Local stuff for category enrolment plugin.
+ *
+ * @package    core_badges
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event observer for badges.
+ */
+class core_badges_observer {
+    /**
+     * Triggered when 'course_module_completion_updated' event is triggered.
+     *
+     * @param \core\event\course_module_completion_updated $event
+     */
+    public static function course_module_criteria_review(\core\event\course_module_completion_updated $event) {
+        global $DB, $CFG;
+
+        if (!empty($CFG->enablebadges)) {
+            require_once($CFG->dirroot.'/lib/badgeslib.php');
+
+            $eventdata = $event->get_record_snapshot('course_modules_completion', $event->objectid);
+            $userid = $event->other['relateduserid'];
+            $mod = $eventdata->coursemoduleid;
+
+            if ($eventdata->completionstate == COMPLETION_COMPLETE
+                || $eventdata->completionstate == COMPLETION_COMPLETE_PASS
+                || $eventdata->completionstate == COMPLETION_COMPLETE_FAIL) {
+                // Need to take into account that there can be more than one badge with the same activity in its criteria.
+                if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'module_' . $mod, 'value' => $mod))) {
+                    foreach ($rs as $r) {
+                        $bid = $DB->get_field('badge_criteria', 'badgeid', array('id' => $r->critid), MUST_EXIST);
+                        $badge = new badge($bid);
+                        if (!$badge->is_active() || $badge->is_issued($userid)) {
+                            continue;
+                        }
+
+                        if ($badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->review($userid)) {
+                            $badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->mark_complete($userid);
+
+                            if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) {
+                                $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid);
+                                $badge->issue($userid);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Triggered when 'course_completed' event is triggered.
+     *
+     * @param   \core\event\course_completed $event
+     */
+    public static function course_criteria_review(\core\event\course_completed $event) {
+        global $DB, $CFG;
+
+        if (!empty($CFG->enablebadges)) {
+            require_once($CFG->dirroot.'/lib/badgeslib.php');
+
+            $eventdata = $event->get_record_snapshot('course_completions', $event->objectid);
+            $userid = $event->other['relateduserid'];
+            $courseid = $event->courseid;
+
+            // Need to take into account that course can be a part of course_completion and courseset_completion criteria.
+            if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'course_' . $courseid, 'value' => $courseid))) {
+                foreach ($rs as $r) {
+                    $crit = $DB->get_record('badge_criteria', array('id' => $r->critid), 'badgeid, criteriatype', MUST_EXIST);
+                    $badge = new badge($crit->badgeid);
+                    if (!$badge->is_active() || $badge->is_issued($userid)) {
+                        continue;
+                    }
+
+                    if ($badge->criteria[$crit->criteriatype]->review($userid)) {
+                        $badge->criteria[$crit->criteriatype]->mark_complete($userid);
+
+                        if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) {
+                            $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid);
+                            $badge->issue($userid);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index 9ccafc1..fa7b7cb 100644 (file)
@@ -31,9 +31,13 @@ require_once($CFG->libdir . '/badgeslib.php');
 
 class core_badgeslib_testcase extends advanced_testcase {
     protected $badgeid;
+    protected $course;
+    protected $user;
+    protected $module;
+    protected $coursebadge;
 
     protected function setUp() {
-        global $DB;
+        global $DB, $CFG;
         $this->resetAfterTest(true);
 
         $user = $this->getDataGenerator()->create_user();
@@ -59,6 +63,29 @@ class core_badgeslib_testcase extends advanced_testcase {
         $fordb->status = BADGE_STATUS_INACTIVE;
 
         $this->badgeid = $DB->insert_record('badge', $fordb, true);
+
+        // Create a course with activity and auto completion tracking.
+        $this->course = $this->getDataGenerator()->create_course();
+        $this->user = $this->getDataGenerator()->create_user();
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->assertNotEmpty($studentrole);
+
+        // Get manual enrolment plugin and enrol user.
+        require_once($CFG->dirroot.'/enrol/manual/locallib.php');
+        $manplugin = enrol_get_plugin('manual');
+        $maninstance = $DB->get_record('enrol', array('courseid' => $this->course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
+        $manplugin->enrol_user($maninstance, $this->user->id, $studentrole->id);
+        $this->assertEquals(1, $DB->count_records('user_enrolments'));
+
+        $completionauto = array('completion' => COMPLETION_TRACKING_AUTOMATIC);
+        $this->module = $this->getDataGenerator()->create_module('forum', array('course' => $this->course->id), $completionauto);
+
+        // Build badge and criteria.
+        $fordb->type = BADGE_TYPE_COURSE;
+        $fordb->courseid = $this->course->id;
+        $fordb->status = BADGE_STATUS_ACTIVE;
+
+        $this->coursebadge = $DB->insert_record('badge', $fordb, true);
     }
 
     public function test_create_badge() {
@@ -183,4 +210,54 @@ class core_badgeslib_testcase extends advanced_testcase {
         $this->assertEquals(badge_message_from_template($message, $params), $result);
     }
 
+    /**
+     * Test badges observer when course module completion event id fired.
+     */
+    public function test_badges_observer_course_module_criteria_review() {
+        $badge = new badge($this->coursebadge);
+        $this->assertFalse($badge->is_issued($this->user->id));
+
+        $criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
+        $criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
+        $criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_ACTIVITY, 'badgeid' => $badge->id));
+        $criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'module_'.$this->module->id => $this->module->id));
+
+        // Set completion for forum activity.
+        $c = new completion_info($this->course);
+        $activities = $c->get_activities();
+        $this->assertEquals(1, count($activities));
+        $this->assertTrue(isset($activities[$this->module->cmid]));
+        $this->assertEquals($activities[$this->module->cmid]->name, $this->module->name);
+
+        $current = $c->get_data($activities[$this->module->cmid], false, $this->user->id);
+        $current->completionstate = COMPLETION_COMPLETE;
+        $current->timemodified = time();
+        $c->internal_set_data($activities[$this->module->cmid], $current);
+
+        // Check if badge is awarded.
+        $this->assertDebuggingCalled('Error baking badge image!');
+        $this->assertTrue($badge->is_issued($this->user->id));
+    }
+
+    /**
+     * Test badges observer when course_completed event is fired.
+     */
+    public function test_badges_observer_course_criteria_review() {
+        $badge = new badge($this->coursebadge);
+        $this->assertFalse($badge->is_issued($this->user->id));
+
+        $criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
+        $criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
+        $criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_COURSE, 'badgeid' => $badge->id));
+        $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'course_'.$this->course->id => $this->course->id));
+
+        $ccompletion = new completion_completion(array('course' => $this->course->id, 'userid' => $this->user->id));
+
+        // Mark course as complete.
+        $ccompletion->mark_complete();
+
+        // Check if badge is awarded.
+        $this->assertDebuggingCalled('Error baking badge image!');
+        $this->assertTrue($badge->is_issued($this->user->id));
+    }
 }
index 1655178..a387294 100644 (file)
@@ -60,7 +60,9 @@ class block_calendar_upcoming extends block_base {
         $events = calendar_get_upcoming($courses, $group, $user, $lookahead, $maxevents);
 
         if (!empty($this->instance)) {
-            $this->content->text = calendar_get_block_upcoming($events, 'view.php?view=day&amp;course='.$courseshown.'&amp;');
+            $link = 'view.php?view=day&amp;course='.$courseshown.'&amp;';
+            $showcourselink = ($this->page->course->id == SITEID);
+            $this->content->text = calendar_get_block_upcoming($events, $link, $showcourselink);
         }
 
         if (empty($this->content->text)) {
index 7038060..d743f70 100644 (file)
@@ -316,11 +316,18 @@ class blog_entry implements renderable {
         $this->delete_attachments();
         $this->remove_associations();
 
+        // Get record to pass onto the event.
+        $record = $DB->get_record('post', array('id' => $this->id));
         $DB->delete_records('post', array('id' => $this->id));
         tag_set('post', $this->id, array());
 
-        add_to_log(SITEID, 'blog', 'delete', 'index.php?userid='. $this->userid, 'deleted blog entry with entry id# '. $this->id);
-        events_trigger('blog_entry_deleted', $this);
+        $event = \core\event\blog_entry_deleted::create(array('objectid' => $this->id,
+                                                            'userid'   => $this->userid,
+                                                            'other'   => array("record" => (array)$record)
+                                                      ));
+        $event->add_record_snapshot("post", $record);
+        $event->set_custom_data($this);
+        $event->trigger();
     }
 
     /**
index 6ed0658..60e5858 100644 (file)
@@ -155,7 +155,7 @@ class core_bloglib_testcase extends advanced_testcase {
      * Test various blog related events.
      */
     public function test_blog_entry_events() {
-        global $USER;
+        global $USER, $DB;
 
         $this->setAdminUser();
         $this->resetAfterTest();
@@ -178,5 +178,21 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertEquals($blog->id, $event->objectid);
         $this->assertEquals($USER->id, $event->userid);
         $this->assertEquals("post", $event->objecttable);
+
+        // Delete a blog entry.
+        $record = $DB->get_record('post', array('id' => $blog->id));
+        $blog->delete();
+        $events = $sink->get_events();
+        $event = array_pop($events);
+
+        // Validate event data.
+        $this->assertInstanceOf('\core\event\blog_entry_deleted', $event);
+        $this->assertEquals(context_system::instance()->id, $event->contextid);
+        $this->assertEquals($blog->id, $event->objectid);
+        $this->assertEquals($USER->id, $event->userid);
+        $this->assertEquals("post", $event->objecttable);
+        $this->assertEquals($record, $event->get_record_snapshot("post", $blog->id));
+        $this->assertSame('blog_entry_deleted', $event->get_legacy_eventname());
+
     }
 }
index 71fce81..c72c52c 100644 (file)
@@ -149,7 +149,7 @@ class cache_config_writer extends cache_config {
         }
         $class = 'cachestore_'.$plugin;
         if (!class_exists($class)) {
-            $plugins = get_plugin_list_with_file('cachestore', 'lib.php');
+            $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php');
             if (!array_key_exists($plugin, $plugins)) {
                 throw new cache_exception('Invalid plugin name specified. The plugin does not exist or is not valid.');
             }
@@ -204,7 +204,7 @@ class cache_config_writer extends cache_config {
         }
         $class = 'cachelock_'.$plugin;
         if (!class_exists($class)) {
-            $plugins = get_plugin_list_with_file('cachelock', 'lib.php');
+            $plugins = core_component::get_plugin_list_with_file('cachelock', 'lib.php');
             if (!array_key_exists($plugin, $plugins)) {
                 throw new cache_exception('Invalid lock name specified. The plugin does not exist or is not valid.');
             }
@@ -315,7 +315,7 @@ class cache_config_writer extends cache_config {
         if (!array_key_exists($name, $this->configstores)) {
             throw new cache_exception('The requested instance does not exist.');
         }
-        $plugins = get_plugin_list_with_file('cachestore', 'lib.php');
+        $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php');
         if (!array_key_exists($plugin, $plugins)) {
             throw new cache_exception('Invalid plugin name specified. The plugin either does not exist or is not valid.');
         }
@@ -523,7 +523,7 @@ class cache_config_writer extends cache_config {
         if (!$coreonly) {
             $plugintypes = core_component::get_plugin_types();
             foreach ($plugintypes as $type => $location) {
-                $plugins = get_plugin_list_with_file($type, 'db/caches.php');
+                $plugins = core_component::get_plugin_list_with_file($type, 'db/caches.php');
                 foreach ($plugins as $plugin => $filepath) {
                     $component = clean_param($type.'_'.$plugin, PARAM_COMPONENT); // Standardised plugin name.
                     $files[$component] = $filepath;
@@ -738,7 +738,7 @@ abstract class cache_administration_helper extends cache_helper {
      */
     public static function get_store_plugin_summaries() {
         $return = array();
-        $plugins = get_plugin_list_with_file('cachestore', 'lib.php', true);
+        $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php', true);
         foreach ($plugins as $plugin => $path) {
             $class = 'cachestore_'.$plugin;
             $return[$plugin] = array(
index 301bee1..4592e41 100644 (file)
@@ -57,7 +57,7 @@ $struntestable = new lang_string('untestable', 'cache');
 $strtested = new lang_string('tested', 'cache');
 $strnotready = new lang_string('storenotready', 'cache');
 
-foreach (get_plugin_list_with_file('cachestore', 'lib.php', true) as $plugin => $path) {
+foreach (core_component::get_plugin_list_with_file('cachestore', 'lib.php', true) as $plugin => $path) {
 
     $class = 'cachestore_'.$plugin;
     $plugin = get_string('pluginname', 'cachestore_'.$plugin);
index 50ef3a6..86c265f 100644 (file)
@@ -607,6 +607,29 @@ function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxeve
     return $output;
 }
 
+
+/**
+ * Get a HTML link to a course.
+ *
+ * @param int $courseid the course id
+ * @return string a link to the course (as HTML); empty if the course id is invalid
+ */
+function calendar_get_courselink($courseid) {
+
+    if (!$courseid) {
+        return '';
+    }
+
+    calendar_get_course_cached($coursecache, $courseid);
+    $context = context_course::instance($courseid);
+    $fullname = format_string($coursecache[$courseid]->fullname, true, array('context' => $context));
+    $url = new moodle_url('/course/view.php', array('id' => $courseid));
+    $link = html_writer::link($url, $fullname);
+
+    return $link;
+}
+
+
 /**
  * Add calendar event metadata
  *
@@ -638,29 +661,21 @@ function calendar_add_event_metadata($event) {
         }
         $icon = $OUTPUT->pix_url('icon', $event->modulename) . '';
 
-        $context = context_course::instance($module->course);
-        $fullname = format_string($coursecache[$module->course]->fullname, true, array('context' => $context));
-
         $event->icon = '<img src="'.$icon.'" alt="'.$eventtype.'" title="'.$modulename.'" class="icon" />';
         $event->referer = '<a href="'.$CFG->wwwroot.'/mod/'.$event->modulename.'/view.php?id='.$module->id.'">'.$event->name.'</a>';
-        $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$module->course.'">'.$fullname.'</a>';
+        $event->courselink = calendar_get_courselink($module->course);
         $event->cmid = $module->id;
 
-
     } else if($event->courseid == SITEID) {                              // Site event
         $event->icon = '<img src="'.$OUTPUT->pix_url('i/siteevent') . '" alt="'.get_string('globalevent', 'calendar').'" class="icon" />';
         $event->cssclass = 'calendar_event_global';
     } else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) {          // Course event
-        calendar_get_course_cached($coursecache, $event->courseid);
-
-        $context = context_course::instance($event->courseid);
-        $fullname = format_string($coursecache[$event->courseid]->fullname, true, array('context' => $context));
-
         $event->icon = '<img src="'.$OUTPUT->pix_url('i/courseevent') . '" alt="'.get_string('courseevent', 'calendar').'" class="icon" />';
-        $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$event->courseid.'">'.$fullname.'</a>';
+        $event->courselink = calendar_get_courselink($event->courseid);
         $event->cssclass = 'calendar_event_course';
     } else if ($event->groupid) {                                    // Group event
         $event->icon = '<img src="'.$OUTPUT->pix_url('i/groupevent') . '" alt="'.get_string('groupevent', 'calendar').'" class="icon" />';
+        $event->courselink = calendar_get_courselink($event->courseid);
         $event->cssclass = 'calendar_event_group';
     } else if($event->userid) {                                      // User event
         $event->icon = '<img src="'.$OUTPUT->pix_url('i/userevent') . '" alt="'.get_string('userevent', 'calendar').'" class="icon" />';
@@ -1193,9 +1208,10 @@ function calendar_days_in_month($month, $year) {
  *
  * @param array $events list of events
  * @param moodle_url|string $linkhref link to event referer
+ * @param boolean $showcourselink whether links to courses should be shown
  * @return string|null $content html block content
  */
-function calendar_get_block_upcoming($events, $linkhref = NULL) {
+function calendar_get_block_upcoming($events, $linkhref = NULL, $showcourselink = false) {
     $content = '';
     $lines = count($events);
     if (!$lines) {
@@ -1223,6 +1239,9 @@ function calendar_get_block_upcoming($events, $linkhref = NULL) {
             }
         }
         $events[$i]->time = str_replace('&raquo;', '<br />&raquo;', $events[$i]->time);
+        if ($showcourselink && !empty($events[$i]->courselink)) {
+            $content .= html_writer::div($events[$i]->courselink, 'course');
+        }
         $content .= '<div class="date">'.$events[$i]->time.'</div></div>';
         if ($i < $lines - 1) $content .= '<hr />';
     }
index dab34d8..52619b2 100644 (file)
@@ -143,23 +143,35 @@ class completion_completion extends data_object {
      * @return void
      */
     public function mark_complete($timecomplete = null) {
+        global $USER;
 
-        // Never change a completion time
+        // Never change a completion time.
         if ($this->timecompleted) {
             return;
         }
 
-        // Use current time if nothing supplied
+        // Use current time if nothing supplied.
         if (!$timecomplete) {
             $timecomplete = time();
         }
 
-        // Set time complete
+        // Set time complete.
         $this->timecompleted = $timecomplete;
 
-        // Save record
+        // Save record.
         if ($result = $this->_save()) {
-            events_trigger('course_completed', $this->get_record_data());
+            $data = $this->get_record_data();
+            $event = \core\event\course_completed::create(
+                array(
+                    'objectid' => $data->id,
+                    'userid' => $USER->id,
+                    'context' => context_course::instance($data->course),
+                    'courseid' => $data->course,
+                    'other' => array('relateduserid' => $data->userid)
+                    )
+                );
+            $event->add_record_snapshot('course_completions', $data);
+            $event->trigger();
         }
 
         return $result;
index 2022649..8c3b733 100644 (file)
@@ -365,25 +365,6 @@ $CFG->admin = 'admin';
 // Locking resolves race conditions and is strongly recommended for production servers.
 //     $CFG->preventfilelocking = false;
 //
-// If $CFG->langstringcache is enabled (which should always be in production
-// environment), Moodle keeps aggregated strings in its own internal format
-// optimised for performance. By default, this on-disk cache is created in
-// $CFG->cachedir/lang. In cluster environment, you may wish to specify
-// an alternative location of this cache so that each web server in the cluster
-// uses its own local cache and does not need to access the shared dataroot.
-// Make sure that the web server process has write permission to this location
-// and that it has permission to remove the folder, too (so that the cache can
-// be pruned).
-//
-//     $CFG->langcacheroot = '/var/www/moodle/htdocs/altcache/lang';
-//
-// If $CFG->langcache is enabled (which should always be in production
-// environment), Moodle stores the list of available languages in a cache file.
-// By default, the file $CFG->dataroot/languages is used. You may wish to
-// specify an alternative location of this cache file.
-//
-//     $CFG->langmenucachefile = '/var/www/moodle/htdocs/altcache/languages';
-//
 // Site default language can be set via standard administration interface. If you
 // want to have initial error messages for eventual database connection problems
 // localized too, you have to set your language code here.
@@ -488,7 +469,7 @@ $CFG->admin = 'admin';
 // Prevent JS caching
 // $CFG->jsrev = -1; // NOT FOR PRODUCTION SERVERS!
 //
-// Prevent core_string_manager on-disk cache
+// Prevent core_string_manager application caching
 // $CFG->langstringcache = false; // NOT FOR PRODUCTION SERVERS!
 //
 // When working with production data on test servers, no emails or other messages
index 6092350..3aa7bd6 100644 (file)
@@ -1236,9 +1236,10 @@ function get_category_or_system_context($categoryid) {
 function make_categories_options() {
     global $CFG;
     require_once($CFG->libdir. '/coursecatlib.php');
-    $cats = coursecat::make_categories_list();
+    $cats = coursecat::make_categories_list('', 0, ' / ');
     foreach ($cats as $key => $value) {
-        $cats[$key] = str_repeat('&nbsp;', coursecat::get($key)->depth - 1). $value;
+        // Prefix the value with the number of spaces equal to category depth (number of separators in the value).
+        $cats[$key] = str_repeat('&nbsp;', substr_count($value, ' / ')). $value;
     }
     return $cats;
 }
@@ -1328,6 +1329,10 @@ function course_create_sections_if_missing($courseorid, $sections) {
  *
  * Updates both tables {course_sections} and {course_modules}
  *
+ * Note: This function does not use modinfo PROVIDED that the section you are
+ * adding the module to already exists. If the section does not exist, it will
+ * build modinfo if necessary and create the section.
+ *
  * @param int|stdClass $courseorid course id or course object
  * @param int $cmid id of the module already existing in course_modules table
  * @param int $sectionnum relative number of the section (field course_sections.section)
@@ -1347,9 +1352,16 @@ function course_add_cm_to_section($courseorid, $cmid, $sectionnum, $beforemod =
     } else {
         $courseid = $courseorid;
     }
-    course_create_sections_if_missing($courseorid, $sectionnum);
     // Do not try to use modinfo here, there is no guarantee it is valid!
-    $section = $DB->get_record('course_sections', array('course'=>$courseid, 'section'=>$sectionnum), '*', MUST_EXIST);
+    $section = $DB->get_record('course_sections',
+            array('course' => $courseid, 'section' => $sectionnum), '*', IGNORE_MISSING);
+    if (!$section) {
+        // This function call requires modinfo.
+        course_create_sections_if_missing($courseorid, $sectionnum);
+        $section = $DB->get_record('course_sections',
+                array('course' => $courseid, 'section' => $sectionnum), '*', MUST_EXIST);
+    }
+
     $modarray = explode(",", trim($section->sequence));
     if (empty($section->sequence)) {
         $newsequence = "$cmid";
@@ -2965,6 +2977,9 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
             'markedthistopic',
             'move',
             'movesection',
+            'movecontent',
+            'aftercontent',
+            'emptydragdropregion'
         ), 'moodle');
 
     // Include format-specific strings
index 5c9e91c..97724aa 100644 (file)
@@ -821,7 +821,7 @@ abstract class moodleform_mod extends moodleform {
         $label = is_null($customlabel) ? get_string('moduleintro') : $customlabel;
 
         $mform->addElement('editor', 'introeditor', $label, array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES,
-            'noclean' => true, 'context' => $this->context, 'collapsed' => true));
+            'noclean' => true, 'context' => $this->context));
         $mform->setType('introeditor', PARAM_RAW); // no XSS prevention here, users must be trusted
         if ($required) {
             $mform->addRule('introeditor', get_string('required'), 'required', null, 'client');
index 55db3b2..b805289 100644 (file)
@@ -639,6 +639,55 @@ class core_course_courselib_testcase extends advanced_testcase {
         $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
     }
 
+    public function test_course_add_cm_to_section() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create course with 1 section.
+        $course = $this->getDataGenerator()->create_course(
+                array('shortname' => 'GrowingCourse',
+                    'fullname' => 'Growing Course',
+                    'numsections' => 1),
+                array('createsections' => true));
+
+        // Trash modinfo.
+        rebuild_course_cache($course->id, true);
+
+        // Create some cms for testing.
+        $cmids = array();
+        for ($i=0; $i<4; $i++) {
+            $cmids[$i] = $DB->insert_record('course_modules', array('course' => $course->id));
+        }
+
+        // Add it to section that exists.
+        course_add_cm_to_section($course, $cmids[0], 1);
+
+        // Check it got added to sequence.
+        $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
+        $this->assertEquals($cmids[0], $sequence);
+
+        // Add a second, this time using courseid variant of parameters.
+        course_add_cm_to_section($course->id, $cmids[1], 1);
+        $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
+        $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
+
+        // Check modinfo was not rebuilt (important for performance if calling
+        // repeatedly).
+        $this->assertNull($DB->get_field('course', 'modinfo', array('id' => $course->id)));
+
+        // Add one to section that doesn't exist (this might rebuild modinfo).
+        course_add_cm_to_section($course, $cmids[2], 2);
+        $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
+        $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
+        $this->assertEquals($cmids[2], $sequence);
+
+        // Add using the 'before' option.
+        course_add_cm_to_section($course, $cmids[3], 2, $cmids[2]);
+        $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id)));
+        $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2));
+        $this->assertEquals($cmids[3] . ',' . $cmids[2], $sequence);
+    }
+
     public function test_reorder_sections() {
         global $DB;
         $this->resetAfterTest(true);
index 0145f0f..fc5f88d 100644 (file)
@@ -295,6 +295,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                     resources.addClass(CSS.SECTION);
                     sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after');
                 }
+                resources.setAttribute('data-draggroups', this.groups.join(' '));
                 // Define empty ul as droptarget, so that item could be moved to empty list
                 var tar = new Y.DD.Drop({
                     node: resources,
index f2b0ab6..e086c6e 100644 (file)
@@ -272,11 +272,24 @@ class core_files_external extends external_api {
             $filepath = '/';
         }
 
+        // Only allow uploads to draft or private areas (private is deprecated but still supported)
+        if (!($fileinfo['component'] == 'user' and in_array($fileinfo['filearea'], array('private', 'draft')))) {
+            throw new coding_exception('File can be uploaded to user private or draft areas only');
+        } else {
+            $component = 'user';
+            $filearea = $fileinfo['filearea'];
+        }
+
+        $itemid = 0;
         if (isset($fileinfo['itemid'])) {
+            $itemid = $fileinfo['itemid'];
+        }
+        if ($filearea == 'draft' && $itemid <= 0) {
+            // Generate a draft area for the files.
+            $itemid = file_get_unused_draft_itemid();
+        } else if ($filearea == 'private') {
             // TODO MDL-31116 in user private area, itemid is always 0.
             $itemid = 0;
-        } else {
-            throw new coding_exception('itemid cannot be empty');
         }
 
         // We need to preserve backword compatibility. Context id is no more a required.
@@ -287,13 +300,8 @@ class core_files_external extends external_api {
         // Get and validate context.
         $context = self::get_context_from_params($fileinfo);
         self::validate_context($context);
-
-        if (!($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) {
-            throw new coding_exception('File can be uploaded to user private area only');
-        } else {
-            // TODO MDL-31116 hard-coded to use user_private area.
-            $component = 'user';
-            $filearea = 'private';
+        if (($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) {
+            debugging('Uploading directly to user private files area is deprecated. Upload to a draft area and then move the files with core_user::add_user_private_files');
         }
 
         $browser = get_file_browser();
index a48187e..199d1eb 100644 (file)
@@ -107,14 +107,14 @@ class core_files_renderer extends plugin_renderer_base {
         $module = array(
             'name'=>'form_filemanager',
             'fullpath'=>'/lib/form/filemanager.js',
-            'requires' => array('core_filepicker', 'base', 'io-base', 'node', 'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
+            'requires' => array('moodle-core-notification-dialogue', 'core_filepicker', 'base', 'io-base', 'node', 'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
             'strings' => array(
                 array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
                 array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
                 array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
                 array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
                 array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
-                array('confirmrenamefile', 'repository'), array('newfolder', 'repository')
+                array('confirmrenamefile', 'repository'), array('newfolder', 'repository'), array('edit', 'moodle')
             )
         );
         if (empty($filemanagertemplateloaded)) {
@@ -527,7 +527,7 @@ class core_files_renderer extends plugin_renderer_base {
             <li class="{!}fp-repo"><a href="#"><img class="{!}fp-repo-icon" alt="'. get_string('repositoryicon', 'repository') .'" width="16" height="16" />&nbsp;<span class="{!}fp-repo-name"></span></a></li>
         </ul>
     </div>
-    <div class="fp-repo-items">
+    <div class="fp-repo-items" tabindex="0">
         <div class="fp-navbar">
             <div>
                 <div class="{!}fp-toolbar">
index 88fbb34..7f6de7d 100644 (file)
@@ -45,7 +45,7 @@ class core_files_externallib_testcase extends advanced_testcase {
         $context = context_user::instance($USER->id);
         $contextid = $context->id;
         $component = "user";
-        $filearea = "private";
+        $filearea = "draft";
         $itemid = 0;
         $filepath = "/";
         $filename = "Simple.txt";
@@ -59,15 +59,16 @@ class core_files_externallib_testcase extends advanced_testcase {
         $this->assertEmpty($file);
 
         // Call the api to create a file.
-        core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
+        $fileinfo = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
                 $filename, $filecontent, $contextlevel, $instanceid);
+        // Get the created draft item id.
+        $itemid = $fileinfo['itemid'];
 
         // Make sure the file was created.
         $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
         $this->assertNotEmpty($file);
 
         // Make sure no file exists.
-        $itemid = 2;
         $filename = "Simple2.txt";
         $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
         $this->assertEmpty($file);
@@ -75,12 +76,10 @@ class core_files_externallib_testcase extends advanced_testcase {
         // Call the api to create a file.
         $fileinfo = core_files_external::upload($contextid, $component, $filearea, $itemid,
                 $filepath, $filename, $filecontent, $contextlevel, $instanceid);
-
-        // Make sure itemid is always set to 0.
-        $this->assertEquals(0, $fileinfo['itemid']);
+        $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
+        $this->assertNotEmpty($file);
 
         // Let us try creating a file using contextlevel and instance id.
-        $itemid = 0;
         $filename = "Simple5.txt";
         $contextid = 0;
         $contextlevel = "user";
@@ -89,7 +88,8 @@ class core_files_externallib_testcase extends advanced_testcase {
         $this->assertEmpty($file);
         $fileinfo = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
                 $filename, $filecontent, $contextlevel, $instanceid);
-        $this->assertEmpty($file);
+        $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
+        $this->assertNotEmpty($file);
 
         // Make sure the same file cannot be created again.
         $this->setExpectedException("moodle_exception");
@@ -123,7 +123,7 @@ class core_files_externallib_testcase extends advanced_testcase {
     }
 
     /*
-     * Make sure only private area is allowed in  core_files_external::upload().
+     * Make sure only private or draft areas are allowed in  core_files_external::upload().
      */
     public function test_upload_param_area() {
         global $USER;
@@ -134,17 +134,18 @@ class core_files_externallib_testcase extends advanced_testcase {
         $contextid = $context->id;
         $component = "user";
         $filearea = "draft";
-        $itemid = 0;
+        $itemid = file_get_unused_draft_itemid();
         $filepath = "/";
         $filename = "Simple4.txt";
         $filecontent = base64_encode("Let us create a nice simple file");
         $contextlevel = null;
         $instanceid = null;
 
-        // Make sure exception is thrown.
-        $this->setExpectedException("coding_exception");
-        core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
-                $filename, $filecontent, $contextlevel, $instanceid);
+        // Make sure the file is created.
+        @core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent);
+        $browser = get_file_browser();
+        $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
+        $this->assertNotEmpty($file);
     }
 
     /*
@@ -164,10 +165,14 @@ class core_files_externallib_testcase extends advanced_testcase {
         $filename = "Simple4.txt";
         $filecontent = base64_encode("Let us create a nice simple file");
 
-        // Make sure the file is created.
         @core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent);
+
+        // Assert debugging called (deprecation warning).
+        $this->assertDebuggingCalled();
+
+        // Make sure the file is created.
         $browser = get_file_browser();
         $file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
         $this->assertNotEmpty($file);
     }
-}
\ No newline at end of file
+}
index 33c702e..f8d18f3 100644 (file)
@@ -62,6 +62,9 @@ switch ($action) {
             if ($type == 'grade' and empty($object->id)) {
                 $object->insert();
             }
+            if (!$object->can_control_visibility()) {
+                print_error('componentcontrolsvisibility', 'grades', $returnurl);
+            }
             $object->set_hidden(1, true);
         }
         break;
@@ -74,6 +77,9 @@ switch ($action) {
             if ($type == 'grade' and empty($object->id)) {
                 $object->insert();
             }
+            if (!$object->can_control_visibility()) {
+                print_error('componentcontrolsvisibility', 'grades', $returnurl);
+            }
             $object->set_hidden(0, true);
         }
         break;
index 6a71f1a..e782303 100644 (file)
@@ -26,7 +26,7 @@ class grade_edit_tree {
     public $columns = array();
 
     /**
-     * @var object $gtree          @see grade/lib.php
+     * @var grade_tree $gtree   @see grade/lib.php
      */
     public $gtree;
 
index 3603ac4..7639e08 100644 (file)
@@ -1541,6 +1541,10 @@ class grade_structure {
     public function get_hiding_icon($element, $gpr) {
         global $CFG, $OUTPUT;
 
+        if (!$element['object']->can_control_visibility()) {
+            return '';
+        }
+
         if (!has_capability('moodle/grade:manage', $this->context) and
             !has_capability('moodle/grade:hide', $this->context)) {
             return '';
index 32f8f2d..7b5fa23 100644 (file)
@@ -198,6 +198,9 @@ if (!empty($memlimit) and $memlimit != -1) {
 
 // Continue with lib loading
 require_once($CFG->libdir.'/classes/text.php');
+require_once($CFG->libdir.'/classes/string_manager.php');
+require_once($CFG->libdir.'/classes/string_manager_install.php');
+require_once($CFG->libdir.'/classes/string_manager_standard.php');
 require_once($CFG->libdir.'/weblib.php');
 require_once($CFG->libdir.'/outputlib.php');
 require_once($CFG->libdir.'/dmllib.php');
index edf9dab..bf1c97c 100644 (file)
@@ -88,6 +88,7 @@ $string['entrysaved'] = 'Your entry has been saved';
 $string['entrytitle'] = 'Entry title';
 $string['entryupdated'] = 'Blog entry updated';
 $string['evententryadded'] = 'Blog entry added';
+$string['evententrydeleted'] = 'Blog entry deleted';
 $string['externalblogcrontime'] = 'External blog cron schedule';
 $string['externalblogdeleteconfirm'] = 'Unregister this external blog?';
 $string['externalblogdeleted'] = 'External blog unregistered';
index be7bb40..a60b503 100644 (file)
@@ -46,6 +46,7 @@ $string['cachedef_databasemeta'] = 'Database meta information';
 $string['cachedef_eventinvalidation'] = 'Event invalidation';
 $string['cachedef_groupdata'] = 'Course group information';
 $string['cachedef_htmlpurifier'] = 'HTML Purifier - cleaned content';
+$string['cachedef_langmenu'] = 'List of available languages';
 $string['cachedef_locking'] = 'Locking';
 $string['cachedef_observers'] = 'Event observers';
 $string['cachedef_plugininfo_base'] = 'Plugin info - base';
index 3cc14c0..5fdeed1 100644 (file)
@@ -123,6 +123,8 @@ $string['err_noroles'] = 'There are no roles with the capability moodle/course:m
 $string['err_nousers'] = 'There are no students on this course or group for whom completion information is displayed. (By default, completion information is displayed only for students, so if there are no students, you will see this error. Administrators can alter this option via the admin screens.)';
 $string['err_settingslocked'] = 'One or more students have already completed a criteria so the settings have been locked. Unlocking the completion criteria settings will delete any existing user data and may cause confusion.';
 $string['err_system'] = 'An internal error occurred in the completion system. (System administrators can enable debugging information to see more detail.)';
+$string['eventcoursecompleted'] = 'Course completed';
+$string['eventcoursemodulecompletionupdated'] = 'Course module completion updated';
 $string['excelcsvdownload'] = 'Download in Excel-compatible format (.csv)';
 $string['fraction'] = 'Fraction';
 $string['graderequired'] = 'Required course grade';
index e1db4bc..b259c16 100644 (file)
@@ -41,7 +41,6 @@ $string['err_rangelength'] = 'You must enter between {$a->format[0]} and {$a->fo
 $string['err_required'] = 'You must supply a value here.';
 $string['general'] = 'General';
 $string['hideadvanced'] = 'Hide advanced';
-$string['hideeditortoolbar'] = 'Hide editing tools';
 $string['hour'] = 'Hour';
 $string['minute'] = 'Minute';
 $string['miscellaneoussettings'] = 'Miscellaneous settings';
@@ -60,7 +59,6 @@ $string['selected'] = 'Selected';
 $string['showadvanced'] = 'Show advanced';
 $string['showless'] = 'Show less...';
 $string['showmore'] = 'Show more...';
-$string['showeditortoolbar'] = 'Show editing tools';
 $string['somefieldsrequired'] = 'There are required fields in this form marked {$a}.';
 $string['time'] = 'Time';
 $string['timeunit'] = 'Time unit';
index e92ab1f..24e37ef 100644 (file)
@@ -115,6 +115,7 @@ $string['administratorsandteachers'] = 'Administrators and teachers';
 $string['advanced'] = 'Advanced';
 $string['advancedfilter'] = 'Advanced search';
 $string['advancedsettings'] = 'Advanced settings';
+$string['aftercontent'] = 'After {$a}';
 $string['again'] = 'again';
 $string['aimid'] = 'AIM ID';
 $string['ajaxuse'] = 'AJAX and Javascript';
@@ -638,6 +639,7 @@ $string['emailpasswordsent'] = 'Thank you for confirming the change of password.
 An email containing your new password has been sent to your address at<br /><b>{$a->email}</b>.<br />
 The new password was automatically generated - you might like to
 <a href="{$a->link}">change your password</a> to something easier to remember.';
+$string['emptydragdropregion'] = 'empty region';
 $string['enable'] = 'Enable';
 $string['encryptedcode'] = 'Encrypted code';
 $string['english'] = 'English';
@@ -1073,6 +1075,7 @@ $string['moreinformation'] = 'More information about this error';
 $string['moreprofileinfoneeded'] = 'Please tell us more about yourself';
 $string['mostrecently'] = 'most recently';
 $string['move'] = 'Move';
+$string['movecontent'] = 'Move {$a}';
 $string['movecategorycontentto'] = 'Move into';
 $string['movecategoryto'] = 'Move category to:';
 $string['movecontentstoanothercategory'] = 'Move contents to another category';
index 0ad7b6a..dcbb81b 100644 (file)
@@ -181,7 +181,12 @@ $string['errorbadrolename'] = 'Incorrect role name';
 $string['errorbadroleshortname'] = 'Incorrect role short name';
 $string['errorexistsrolename'] = 'Role name already exists';
 $string['errorexistsroleshortname'] = 'Role name already exists';
+$string['eventroleallowassignupdated'] = 'Allow role assignment';
+$string['eventroleallowoverrideupdated'] = 'Allow role override';
+$string['eventroleallowswitchupdated'] = 'Allow role switch';
 $string['eventroleassigned'] = 'Role assigned';
+$string['eventrolecapabilitiesupdated'] = 'Role capabilities updated';
+$string['eventroledeleted'] = 'Role deleted';
 $string['eventroleunassigned'] = 'Role unassigned';
 $string['existingadmins'] = 'Current site administrators';
 $string['existingusers'] = '{$a} existing users';
index 74eece2..83cdb58 100644 (file)
@@ -183,6 +183,8 @@ $string['tokencreator'] = 'Creator';
 $string['unknownoptionkey'] = 'Unknown option key ({$a})';
 $string['unnamedstringparam'] = 'A string parameter is unnamed.';
 $string['updateusersettings'] = 'Update';
+$string['uploadfiles'] = 'Can upload files';
+$string['uploadfiles_help'] = 'If enabled, any user can upload files with their security keys to their own private files area or a draft file area. Any user file quotas apply.';
 $string['userasclients'] = 'Users as clients with token';
 $string['userasclientsdescription'] = 'The following steps help you to set up the Moodle web service for users as clients. These steps also help to set up the recommended token (security keys) authentication method. In this use case, the user will generate his token from the security keys page via My profile settings.';
 $string['usermissingcaps'] = 'Missing capabilities: {$a}';
index 0d31a88..2927638 100644 (file)
@@ -1515,13 +1515,28 @@ function delete_role($roleid) {
     $DB->delete_records('role_names',          array('roleid'=>$roleid));
     $DB->delete_records('role_context_levels', array('roleid'=>$roleid));
 
-    // finally delete the role itself
-    // get this before the name is gone for logging
-    $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
+    // Get role record before it's deleted.
+    $role = $DB->get_record('role', array('id'=>$roleid));
 
+    // Finally delete the role itself.
     $DB->delete_records('role', array('id'=>$roleid));
 
-    add_to_log(SITEID, 'role', 'delete', 'admin/roles/action=delete&roleid='.$roleid, $rolename, '');
+    // Trigger event.
+    $event = \core\event\role_deleted::create(
+        array(
+            'context' => context_system::instance(),
+            'objectid' => $roleid,
+            'other' =>
+                array(
+                    'name' => $role->name,
+                    'shortname' => $role->shortname,
+                    'description' => $role->description,
+                    'archetype' => $role->archetype
+                )
+            )
+        );
+    $event->add_record_snapshot('role', $role);
+    $event->trigger();
 
     return true;
 }
index 1cff7fd..c8e18af 100644 (file)
@@ -931,84 +931,6 @@ function badges_add_course_navigation(navigation_node $coursenode, stdClass $cou
     }
 }
 
-/**
- * Triggered when 'course_completed' event happens.
- *
- * @param   object $eventdata
- * @return  boolean
- */
-function badges_award_handle_course_criteria_review(stdClass $eventdata) {
-    global $DB, $CFG;
-
-    if (!empty($CFG->enablebadges)) {
-        $userid = $eventdata->userid;
-        $courseid = $eventdata->course;
-
-        // Need to take into account that course can be a part of course_completion and courseset_completion criteria.
-        if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'course_' . $courseid, 'value' => $courseid))) {
-            foreach ($rs as $r) {
-                $crit = $DB->get_record('badge_criteria', array('id' => $r->critid), 'badgeid, criteriatype', MUST_EXIST);
-                $badge = new badge($crit->badgeid);
-                if (!$badge->is_active() || $badge->is_issued($userid)) {
-                    continue;
-                }
-
-                if ($badge->criteria[$crit->criteriatype]->review($userid)) {
-                    $badge->criteria[$crit->criteriatype]->mark_complete($userid);
-
-                    if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) {
-                        $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid);
-                        $badge->issue($userid);
-                    }
-                }
-            }
-        }
-    }
-
-    return true;
-}
-
-/**
- * Triggered when 'activity_completed' event happens.
- *
- * @param   object $eventdata
- * @return  boolean
- */
-function badges_award_handle_activity_criteria_review(stdClass $eventdata) {
-    global $DB, $CFG;
-
-    if (!empty($CFG->enablebadges)) {
-        $userid = $eventdata->userid;
-        $mod = $eventdata->coursemoduleid;
-
-        if ($eventdata->completionstate == COMPLETION_COMPLETE
-            || $eventdata->completionstate == COMPLETION_COMPLETE_PASS
-            || $eventdata->completionstate == COMPLETION_COMPLETE_FAIL) {
-            // Need to take into account that there can be more than one badge with the same activity in its criteria.
-            if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'module_' . $mod, 'value' => $mod))) {
-                foreach ($rs as $r) {
-                    $bid = $DB->get_field('badge_criteria', 'badgeid', array('id' => $r->critid), MUST_EXIST);
-                    $badge = new badge($bid);
-                    if (!$badge->is_active() || $badge->is_issued($userid)) {
-                        continue;
-                    }
-
-                    if ($badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->review($userid)) {
-                        $badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->mark_complete($userid);
-
-                        if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) {
-                            $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid);
-                            $badge->issue($userid);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    return true;
-}
-
 /**
  * Triggered when 'user_updated' event happens.
  *
index 09a9205..3f68221 100644 (file)
@@ -41,6 +41,10 @@ class core_component {
     protected static $subsystems = null;
     /** @var null list of all known classes that can be autoloaded */
     protected static $classmap = null;
+    /** @var null list of some known files that can be included. */
+    protected static $filemap = null;
+    /** @var array list of the files to map. */
+    protected static $filestomap = array('lib.php', 'settings.php');
 
     /**
      * Class loader for Frankenstyle named classes in standard locations.
@@ -105,6 +109,7 @@ class core_component {
                 self::$plugins     = $cache['plugins'];
                 self::$subsystems  = $cache['subsystems'];
                 self::$classmap    = $cache['classmap'];
+                self::$filemap     = $cache['filemap'];
                 return;
             }
 
@@ -138,6 +143,7 @@ class core_component {
                     self::$plugins     = $cache['plugins'];
                     self::$subsystems  = $cache['subsystems'];
                     self::$classmap    = $cache['classmap'];
+                    self::$filemap     = $cache['filemap'];
                     return;
                 }
                 // Note: we do not verify $CFG->admin here intentionally,
@@ -223,6 +229,7 @@ class core_component {
             'plugintypes' => self::$plugintypes,
             'plugins'     => self::$plugins,
             'classmap'    => self::$classmap,
+            'filemap'     => self::$filemap,
         );
 
         return '<?php
@@ -244,6 +251,7 @@ $cache = '.var_export($cache, true).';
         }
 
         self::fill_classmap_cache();
+        self::fill_filemap_cache();
     }
 
     /**
@@ -508,6 +516,35 @@ $cache = '.var_export($cache, true).';
         self::$classmap['collatorlib'] = "$CFG->dirroot/lib/classes/collator.php";
     }
 
+
+    /**
+     * Fills up the cache defining what plugins have certain files.
+     *
+     * @see self::get_plugin_list_with_file
+     * @return void
+     */
+    protected static function fill_filemap_cache() {
+        global $CFG;
+
+        self::$filemap = array();
+
+        foreach (self::$filestomap as $file) {
+            if (!isset(self::$filemap[$file])) {
+                self::$filemap[$file] = array();
+            }
+            foreach (self::$plugins as $plugintype => $plugins) {
+                if (!isset(self::$filemap[$file][$plugintype])) {
+                    self::$filemap[$file][$plugintype] = array();
+                }
+                foreach ($plugins as $pluginname => $fulldir) {
+                    if (file_exists("$fulldir/$file")) {
+                        self::$filemap[$file][$plugintype][$pluginname] = "$fulldir/$file";
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Find classes in directory and recurse to subdirs.
      * @param string $component
@@ -647,6 +684,45 @@ $cache = '.var_export($cache, true).';
         return $pluginclasses;
     }
 
+    /**
+     * Get a list of all the plugins of a given type that contain a particular file.
+     *
+     * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
+     * @param string $file the name of file that must be present in the plugin.
+     *                     (e.g. 'view.php', 'db/install.xml').
+     * @param bool $include if true (default false), the file will be include_once-ed if found.
+     * @return array with plugin name as keys (e.g. 'forum', 'courselist') and the path
+     *               to the file relative to dirroot as value (e.g. "$CFG->dirroot/mod/forum/view.php").
+     */
+    public static function get_plugin_list_with_file($plugintype, $file, $include = false) {
+        global $CFG; // Necessary in case it is referenced by included PHP scripts.
+        $pluginfiles = array();
+
+        if (isset(self::$filemap[$file])) {
+            // If the file was supposed to be mapped, then it should have been set in the array.
+            if (isset(self::$filemap[$file][$plugintype])) {
+                $pluginfiles = self::$filemap[$file][$plugintype];
+            }
+        } else {
+            // Old-style search for non-cached files.
+            $plugins = self::get_plugin_list($plugintype);
+            foreach ($plugins as $plugin => $fulldir) {
+                $path = $fulldir . '/' . $file;
+                if (file_exists($path)) {
+                    $pluginfiles[$plugin] = $path;
+                }
+            }
+        }
+
+        if ($include) {
+            foreach ($pluginfiles as $path) {
+                include_once($path);
+            }
+        }
+
+        return $pluginfiles;
+    }
+
     /**
      * Returns the exact absolute path to plugin directory.
      *
@@ -779,6 +855,58 @@ $cache = '.var_export($cache, true).';
         return $return;
     }
 
+    /**
+     * Returns hash of all versions including core and all plugins.
+     *
+     * This is relatively slow and not fully cached, use with care!
+     *
+     * @return string sha1 hash
+     */
+    public static function get_all_versions_hash() {
+        global $CFG;
+
+        self::init();
+
+        $versions = array();
+
+        // Main version first.
+        $version = null;
+        include($CFG->dirroot.'/version.php');
+        $versions['core'] = $version;
+
+        // The problem here is tha the component cache might be stable,
+        // we want this to work also on frontpage without resetting the component cache.
+        $usecache = false;
+        if (CACHE_DISABLE_ALL or (defined('IGNORE_COMPONENT_CACHE') and IGNORE_COMPONENT_CACHE)) {
+            $usecache = true;
+        }
+
+        // Now all plugins.
+        $plugintypes = core_component::get_plugin_types();
+        foreach ($plugintypes as $type => $typedir) {
+            if ($usecache) {
+                $plugs = core_component::get_plugin_list($type);
+            } else {
+                $plugs = self::fetch_plugins($type, $typedir);
+            }
+            foreach ($plugs as $plug => $fullplug) {
+                if ($type === 'mod') {
+                    $module = new stdClass();
+                    $module->version = null;
+                    include($fullplug.'/version.php');
+                    $versions[$plug] = $module->version;
+                } else {
+                    $plugin = new stdClass();
+                    $plugin->version = null;
+                    @include($fullplug.'/version.php');
+                    $versions[$plug] = $plugin->version;
+                }
+            }
+        }
+
+        return sha1(serialize($versions));
+    }
+
     /**
      * Invalidate opcode cache for given file, this is intended for
      * php files that are stored in dataroot.
diff --git a/lib/classes/event/assessable_submitted.php b/lib/classes/event/assessable_submitted.php
new file mode 100644 (file)
index 0000000..dbdc54b
--- /dev/null
@@ -0,0 +1,67 @@
+<?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/>.
+
+/**
+ * Abstract assessable submitted event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Abstract assessable submitted event class.
+ *
+ * This class has to be extended by any event which represent that some content,
+ * on which someone will be assessed, has been submitted and so made available
+ * for grading. See {@link \core\event\assessable_uploaded} for when the content
+ * has just been uploaded.
+ *
+ * Both events could be triggered in a row, first the uploaded, then the submitted.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class assessable_submitted extends \core\event\base {
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['level'] = 50;          // TODO MDL-37658.
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws coding_exception on error.
+     * @return void
+     */
+    protected function validate_data() {
+        if (!$this->context->contextlevel === CONTEXT_MODULE) {
+            throw new coding_exception('Content level must be CONTEXT_MODULE.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/assessable_uploaded.php b/lib/classes/event/assessable_uploaded.php
new file mode 100644 (file)
index 0000000..9d7389c
--- /dev/null
@@ -0,0 +1,71 @@
+<?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/>.
+
+/**
+ * Abstract assessable uploaded event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Abstract assessable uploaded event class.
+ *
+ * This class has to be extended by any event which represent that some content,
+ * on which someone will be assessed, has been uploaded. This is different
+ * than other events such as assessable_submitted, which means that the content
+ * has been submitted and made available for grading.
+ *
+ * Both events could be triggered in a row, first the uploaded, then the submitted.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class assessable_uploaded extends \core\event\base {
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'c';
+        $this->data['level'] = 50;          // TODO MDL-37658.
+    }
+
+    /**
+     * Validation that should be shared among child classes.
+     *
+     * @throws coding_exception when validation fails.
+     * @return void
+     */
+    protected function validate_data() {
+        if (!$this->context->contextlevel === CONTEXT_MODULE) {
+            throw new coding_exception('Content level must be CONTEXT_MODULE.');
+        } else if (!isset($this->other['pathnamehashes']) || !is_array($this->other['pathnamehashes'])) {
+            throw new coding_exception('pathnamehashes must be set in $other and must be an array.');
+        } else if (!isset($this->other['content']) || !is_string($this->other['content'])) {
+            throw new coding_exception('content must be set in $other and must be a string.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/blog_entry_deleted.php b/lib/classes/event/blog_entry_deleted.php
new file mode 100644 (file)
index 0000000..9de612d
--- /dev/null
@@ -0,0 +1,104 @@
+<?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/>.
+/**
+ * Event for when a new blog entry is deleted.
+ *
+ * @package    core_blog
+ * @copyright  2013 Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core\event;
+
+/**
+ * class blog_entry_deleted
+ *
+ * Event for when a new blog entry is deleted.
+ *
+ * @package    core_blog
+ * @copyright  2013 Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class blog_entry_deleted extends \core\event\base {
+
+    /** @var  \blog_entry A reference to the active blog_entry object. */
+    protected $customobject;
+
+    /**
+     * Set basic event properties.
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['objecttable'] = 'post';
+        $this->data['crud'] = 'd';
+        // TODO: MDL-37658 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string("evententrydeleted", "core_blog");
+    }
+
+    /**
+     * Set custom data of the event.
+     *
+     * @param \blog_entry $data A reference to the active blog_entry object.
+     */
+    public function set_custom_data($data) {
+        $this->customobject = $data;
+    }
+
+    /**
+     * Returns non-localised description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "Blog entry ".$this->other['record']['subject']." was deleted by user with id ".$this->userid;
+    }
+
+    /**
+     * Does this event replace legacy event?
+     *
+     * @return string legacy event name
+     */
+    public static function get_legacy_eventname() {
+        return 'blog_entry_deleted';
+    }
+
+    /**
+     * Legacy event data if get_legacy_eventname() is not empty.
+     *
+     * @return \blog_entry
+     */
+    protected function get_legacy_eventdata() {
+        return $this->customobject;
+    }
+
+    /**
+     * replace add_to_log() statement.
+     *
+     * @return array of parameters to be passed to legacy add_to_log() function.
+     */
+    protected function get_legacy_logdata() {
+        return array (SITEID, 'blog', 'delete', 'index.php?userid='.$this->userid, 'deleted blog entry with entry id# '. $this->objectid);
+    }
+}
diff --git a/lib/classes/event/course_completed.php b/lib/classes/event/course_completed.php
new file mode 100644 (file)
index 0000000..e57ddae
--- /dev/null
@@ -0,0 +1,83 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Event when course completed.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_completed extends base {
+
+    /**
+     * Initialise required event data properties.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'course_completions';
+        $this->data['crud'] = 'u';
+        // TODO: MDL-37658 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return new get_string('eventcoursecompleted', 'core_completion');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Course completed by user '.$this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new moodle_url('/report/completion/index.php', array('course' => $this->courseid));
+    }
+
+    /**
+     * Return name of the legacy event, which is replaced by this event.
+     *
+     * @return string legacy event name
+     */
+    public static function get_legacy_eventname() {
+        return 'course_completed';
+    }
+
+    /**
+     * Return course_completed legacy event data.
+     *
+     * @return \stdClass completion data.
+     */
+    protected function get_legacy_eventdata() {
+        return $this->get_record_snapshot('course_completions', $this->objectid);
+    }
+
+}
diff --git a/lib/classes/event/course_module_completion_updated.php b/lib/classes/event/course_module_completion_updated.php
new file mode 100644 (file)
index 0000000..c57f0c7
--- /dev/null
@@ -0,0 +1,83 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Event when course module completion is updated.
+ *
+ * @package    core
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_module_completion_updated extends base {
+
+    /**
+     * Initialise required event data properties.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'course_modules_completion';
+        $this->data['crud'] = 'u';
+        // TODO: MDL-37658 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return new get_string('eventcoursemodulecompletionupdated', 'core_completion');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Course module completion updated for user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new moodle_url('/report/completion/index.php', array('course' => $this->courseid));
+    }
+
+    /**
+     * Return name of the legacy event, which is replaced by this event.
+     *
+     * @return string legacy event name
+     */
+    public static function get_legacy_eventname() {
+        return 'activity_completion_changed';
+    }
+
+    /**
+     * Return course module completion legacy event data.
+     *
+     * @return \stdClass completion data.
+     */
+    protected function get_legacy_eventdata() {
+        return $this->get_record_snapshot('course_modules_completion', $this->objectid);
+    }
+
+}
diff --git a/lib/classes/event/role_allow_assign_updated.php b/lib/classes/event/role_allow_assign_updated.php
new file mode 100644 (file)
index 0000000..538e56c
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Event when role allow assignments is updated.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class role_allow_assign_updated extends base {
+    /**
+     * Initialise event parameters.
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        // TODO: MDL-41040 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventroleallowassignupdated', 'role');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Allow role assignments updated by user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/roles/allow.php', array('mode' => 'assign'));
+    }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        return array(SITEID, 'role', 'edit allow assign', 'admin/roles/allow.php?mode=assign');
+    }
+}
diff --git a/lib/classes/event/role_allow_override_updated.php b/lib/classes/event/role_allow_override_updated.php
new file mode 100644 (file)
index 0000000..2edee4b
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Event when role allow override is updated.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class role_allow_override_updated extends base {
+    /**
+     * Initialise event parameters.
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        // TODO: MDL-41040 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventroleallowoverrideupdated', 'role');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Allow role override updated by user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/roles/allow.php', array('mode' => 'override'));
+    }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        return array(SITEID, 'role', 'edit allow override', 'admin/roles/allow.php?mode=override');
+    }
+}
diff --git a/lib/classes/event/role_allow_switch_updated.php b/lib/classes/event/role_allow_switch_updated.php
new file mode 100644 (file)
index 0000000..04606e7
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Event when role allow switch is updated.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class role_allow_switch_updated extends base {
+    /**
+     * Initialise event parameters.
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        // TODO: MDL-41040 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventroleallowswitchupdated', 'role');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Allow role switch updated by user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/roles/allow.php', array('mode' => 'switch'));
+    }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        return array(SITEID, 'role', 'edit allow switch', 'admin/roles/allow.php?mode=switch');
+    }
+}
index ae670b9..9e74839 100644 (file)
@@ -75,4 +75,16 @@ class role_assigned extends base {
     protected function get_legacy_eventdata() {
         return $this->get_record_snapshot('role_assignments', $this->data['other']['id']);
     }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        $roles = get_all_roles();
+        $rolenames = role_fix_names($roles, $this->get_context(), ROLENAME_ORIGINAL, true);
+        return array($this->courseid, 'role', 'assign', 'admin/roles/assign.php?contextid='.$this->contextid.'&roleid='.$this->objectid,
+                $rolenames[$this->objectid], '', $this->userid);
+    }
 }
diff --git a/lib/classes/event/role_capabilities_updated.php b/lib/classes/event/role_capabilities_updated.php
new file mode 100644 (file)
index 0000000..6f836dc
--- /dev/null
@@ -0,0 +1,90 @@
+<?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/>.
+
+namespace core\event;
+
+/**
+ * Role updated event.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class role_capabilities_updated extends base {
+    /** @var array Legacy log data */
+    protected $legacylogdata = null;
+
+    /**
+     * Initialise event parameters.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'role';
+        $this->data['crud'] = 'u';
+        // TODO: MDL-41040 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventrolecapabilitiesupdated', 'role');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Capabilities for role ' . $this->objectid . ' are updated by user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->contextlevel === CONTEXT_SYSTEM) {
+            return new \moodle_url('admin/roles/define.php', array('action' => 'view', 'roleid' => $this->objectid));
+        } else {
+            return new \moodle_url('/admin/roles/override.php', array('contextid' => $this->contextid, 'roleid' => $this->objectid));
+        }
+    }
+
+    /**
+     * Sets legacy log data.
+     *
+     * @param array $legacylogdata
+     * @return void
+     */
+    public function set_legacy_logdata($legacylogdata) {
+        $this->legacylogdata = $legacylogdata;
+    }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return null|array
+     */
+    protected function get_legacy_logdata() {
+        return $this->legacylogdata;
+    }
+}
diff --git a/lib/classes/event/role_deleted.php b/lib/classes/event/role_deleted.php
new file mode 100644 (file)
index 0000000..11e15b9
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace core\event;
+
+/**
+ * Role assigned event.
+ *
+ * @package    core_event
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class role_deleted extends base {
+    /**
+     * Initialise event parameters.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'role';
+        $this->data['crud'] = 'd';
+        // TODO: MDL-41040 set level.
+        $this->data['level'] = 50;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventroledeleted', 'role');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Role ' . $this->objectid . ' is deleted by user ' . $this->userid;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/roles/manage.php');
+    }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        return array(SITEID, 'role', 'delete', 'admin/roles/manage.php?action=delete&roleid='.$this->objectid, $this->other['shortname'], '');
+    }
+}
index 8912dda..3bb4a10 100644 (file)
@@ -75,4 +75,16 @@ class role_unassigned extends base {
     protected function get_legacy_eventdata() {
         return $this->get_record_snapshot('role_assignments', $this->data['other']['id']);
     }
+
+    /**
+     * Returns array of parameters to be passed to legacy add_to_log() function.
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        $roles = get_all_roles();
+        $rolenames = role_fix_names($roles, $this->get_context(), ROLENAME_ORIGINAL, true);
+        return array($this->courseid, 'role', 'unassign', 'admin/roles/assign.php?contextid='.$this->contextid.'&roleid='.$this->objectid,
+                $rolenames[$this->objectid], '', $this->userid);
+    }
 }
diff --git a/lib/classes/string_manager.php b/lib/classes/string_manager.php
new file mode 100644 (file)
index 0000000..b0d617f
--- /dev/null
@@ -0,0 +1,129 @@
+<?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/>.
+
+/**
+ * String manager interface.
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Interface for string manager
+ *
+ * Interface describing class which is responsible for getting
+ * of localised strings from language packs.
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface core_string_manager {
+    /**
+     * Get String returns a requested string
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @param string|object|array $a An object, string or number that can be used
+     *      within translation strings
+     * @param string $lang moodle translation language, null means use current
+     * @return string The String !
+     */
+    public function get_string($identifier, $component = '', $a = null, $lang = null);
+
+    /**
+     * Does the string actually exist?
+     *
+     * get_string() is throwing debug warnings, sometimes we do not want them
+     * or we want to display better explanation of the problem.
+     *
+     * Use with care!
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @return bool true if exists
+     */
+    public function string_exists($identifier, $component);
+
+    /**
+     * Returns a localised list of all country names, sorted by country keys.
+     * @param bool $returnall return all or just enabled
+     * @param string $lang moodle translation language, null means use current
+     * @return array two-letter country code => translated name.
+     */
+    public function get_list_of_countries($returnall = false, $lang = null);
+
+    /**
+     * Returns a localised list of languages, sorted by code keys.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @param string $standard language list standard
+     *                     iso6392: three-letter language code (ISO 639-2/T) => translated name.
+     * @return array language code => translated name
+     */
+    public function get_list_of_languages($lang = null, $standard = 'iso6392');
+
+    /**
+     * Checks if the translation exists for the language
+     *
+     * @param string $lang moodle translation language code
+     * @param bool $includeall include also disabled translations
+     * @return bool true if exists
+     */
+    public function translation_exists($lang, $includeall = true);
+
+    /**
+     * Returns localised list of installed translations
+     * @param bool $returnall return all or just enabled
+     * @return array moodle translation code => localised translation name
+     */
+    public function get_list_of_translations($returnall = false);
+
+    /**
+     * Returns localised list of currencies.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @return array currency code => localised currency name
+     */
+    public function get_list_of_currencies($lang = null);
+
+    /**
+     * Load all strings for one component
+     * @param string $component The module the string is associated with
+     * @param string $lang
+     * @param bool $disablecache Do not use caches, force fetching the strings from sources
+     * @param bool $disablelocal Do not use customized strings in xx_local language packs
+     * @return array of all string for given component and lang
+     */
+    public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false);
+
+    /**
+     * Invalidates all caches, should the implementation use any
+     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
+     */
+    public function reset_caches($phpunitreset = false);
+
+    /**
+     * Returns string revision counter, this is incremented after any
+     * string cache reset.
+     * @return int lang string revision counter, -1 if unknown
+     */
+    public function get_revision();
+}
+
diff --git a/lib/classes/string_manager_install.php b/lib/classes/string_manager_install.php
new file mode 100644 (file)
index 0000000..353e507
--- /dev/null
@@ -0,0 +1,248 @@
+<?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/>.
+
+/**
+ * Installation time string manager.
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Fetches minimum strings for installation
+ *
+ * Minimalistic string fetching implementation
+ * that is used in installer before we fetch the wanted
+ * language pack from moodle.org lang download site.
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda (http://skodak.org)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_string_manager_install implements core_string_manager {
+    /** @var string location of pre-install packs for all langs */
+    protected $installroot;
+
+    /**
+     * Crate new instance of install string manager
+     */
+    public function __construct() {
+        global $CFG;
+        $this->installroot = "$CFG->dirroot/install/lang";
+    }
+
+    /**
+     * Load all strings for one component
+     * @param string $component The module the string is associated with
+     * @param string $lang
+     * @param bool $disablecache Do not use caches, force fetching the strings from sources
+     * @param bool $disablelocal Do not use customized strings in xx_local language packs
+     * @return array of all string for given component and lang
+     */
+    public function load_component_strings($component, $lang, $disablecache = false, $disablelocal = false) {
+        // Not needed in installer.
+        return array();
+    }
+
+    /**
+     * Does the string actually exist?
+     *
+     * get_string() is throwing debug warnings, sometimes we do not want them
+     * or we want to display better explanation of the problem.
+     *
+     * Use with care!
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @return boot true if exists
+     */
+    public function string_exists($identifier, $component) {
+        // Simple old style hack ;).
+        $str = get_string($identifier, $component);
+        return (strpos($str, '[[') === false);
+    }
+
+    /**
+     * Get String returns a requested string
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @param string|object|array $a An object, string or number that can be used
+     *      within translation strings
+     * @param string $lang moodle translation language, null means use current
+     * @return string The String !
+     */
+    public function get_string($identifier, $component = '', $a = null, $lang = null) {
+        if (!$component) {
+            $component = 'moodle';
+        }
+
+        if ($lang === null) {
+            $lang = current_language();
+        }
+
+        // Get parent lang.
+        $parent = '';
+        if ($lang !== 'en' and $identifier !== 'parentlanguage' and $component !== 'langconfig') {
+            if (file_exists("$this->installroot/$lang/langconfig.php")) {
+                $string = array();
+                include("$this->installroot/$lang/langconfig.php");
+                if (isset($string['parentlanguage'])) {
+                    $parent = $string['parentlanguage'];
+                }
+            }
+        }
+
+        // Include en string first.
+        if (!file_exists("$this->installroot/en/$component.php")) {
+            return "[[$identifier]]";
+        }
+        $string = array();
+        include("$this->installroot/en/$component.php");
+
+        // Now override en with parent if defined.
+        if ($parent and $parent !== 'en' and file_exists("$this->installroot/$parent/$component.php")) {
+            include("$this->installroot/$parent/$component.php");
+        }
+
+        // Finally override with requested language.
+        if ($lang !== 'en' and file_exists("$this->installroot/$lang/$component.php")) {
+            include("$this->installroot/$lang/$component.php");
+        }
+
+        if (!isset($string[$identifier])) {
+            return "[[$identifier]]";
+        }
+
+        $string = $string[$identifier];
+
+        if ($a !== null) {
+            if (is_object($a) or is_array($a)) {
+                $a = (array)$a;
+                $search = array();
+                $replace = array();
+                foreach ($a as $key => $value) {
+                    if (is_int($key)) {
+                        // We do not support numeric keys - sorry!
+                        continue;
+                    }
+                    $search[] = '{$a->' . $key . '}';
+                    $replace[] = (string)$value;
+                }
+                if ($search) {
+                    $string = str_replace($search, $replace, $string);
+                }
+            } else {
+                $string = str_replace('{$a}', (string)$a, $string);
+            }
+        }
+
+        return $string;
+    }
+
+    /**
+     * Returns a localised list of all country names, sorted by country keys.
+     *
+     * @param bool $returnall return all or just enabled
+     * @param string $lang moodle translation language, null means use current
+     * @return array two-letter country code => translated name.
+     */
+    public function get_list_of_countries($returnall = false, $lang = null) {
+        // Not used in installer.
+        return array();
+    }
+
+    /**
+     * Returns a localised list of languages, sorted by code keys.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @param string $standard language list standard
+     *                     iso6392: three-letter language code (ISO 639-2/T) => translated name.
+     * @return array language code => translated name
+     */
+    public function get_list_of_languages($lang = null, $standard = 'iso6392') {
+        // Not used in installer.
+        return array();
+    }
+
+    /**
+     * Checks if the translation exists for the language
+     *
+     * @param string $lang moodle translation language code
+     * @param bool $includeall include also disabled translations
+     * @return bool true if exists
+     */
+    public function translation_exists($lang, $includeall = true) {
+        return file_exists($this->installroot . '/' . $lang . '/langconfig.php');
+    }
+
+    /**
+     * Returns localised list of installed translations
+     * @param bool $returnall return all or just enabled
+     * @return array moodle translation code => localised translation name
+     */
+    public function get_list_of_translations($returnall = false) {
+        // Return all is ignored here - we need to know all langs in installer.
+        $languages = array();
+        // Get raw list of lang directories.
+        $langdirs = get_list_of_plugins('install/lang');
+        asort($langdirs);
+        // Get some info from each lang.
+        foreach ($langdirs as $lang) {
+            if (file_exists($this->installroot . '/' . $lang . '/langconfig.php')) {
+                $string = array();
+                include($this->installroot . '/' . $lang . '/langconfig.php');
+                if (!empty($string['thislanguage'])) {
+                    $languages[$lang] = $string['thislanguage'] . ' (' . $lang . ')';
+                }
+            }
+        }
+        // Return array.
+        return $languages;
+    }
+
+    /**
+     * Returns localised list of currencies.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @return array currency code => localised currency name
+     */
+    public function get_list_of_currencies($lang = null) {
+        // Not used in installer.
+        return array();
+    }
+
+    /**
+     * This implementation does not use any caches.
+     *
+     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
+     */
+    public function reset_caches($phpunitreset = false) {
+        // Nothing to do.
+    }
+
+    /**
+     * Returns string revision counter, this is incremented after any string cache reset.
+     * @return int lang string revision counter, -1 if unknown
+     */
+    public function get_revision() {
+        return -1;
+    }
+}
diff --git a/lib/classes/string_manager_standard.php b/lib/classes/string_manager_standard.php
new file mode 100644 (file)
index 0000000..7ec3708
--- /dev/null
@@ -0,0 +1,622 @@
+<?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/>.
+
+/**
+ * Standard string manager.
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Standard string_manager implementation
+ *
+ * Implements string_manager with getting and printing localised strings
+ *
+ * @package    core
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_string_manager_standard implements core_string_manager {
+    /** @var string location of all packs except 'en' */
+    protected $otherroot;
+    /** @var string location of all lang pack local modifications */
+    protected $localroot;
+    /** @var cache lang string cache - it will be optimised more later */
+    protected $cache;
+    /** @var int get_string() counter */
+    protected $countgetstring = 0;
+    /** @var bool use disk cache */
+    protected $translist;
+    /** @var cache stores list of available translations */
+    protected $menucache;
+
+    /**
+     * Create new instance of string manager
+     *
+     * @param string $otherroot location of downloaded lang packs - usually $CFG->dataroot/lang
+     * @param string $localroot usually the same as $otherroot
+     * @param array $translist limit list of visible translations
+     */
+    public function __construct($otherroot, $localroot, $translist) {
+        $this->otherroot    = $otherroot;
+        $this->localroot    = $localroot;
+        if ($translist) {
+            $this->translist = array_combine($translist, $translist);
+        } else {
+            $this->translist = array();
+        }
+
+        if ($this->get_revision() > 0) {
+            // We can use a proper cache, establish the cache using the 'String cache' definition.
+            $this->cache = cache::make('core', 'string');
+            $this->menucache = cache::make('core', 'langmenu');
+        } else {
+            // We only want a cache for the length of the request, create a static cache.
+            $options = array(
+                'simplekeys' => true,
+                'simpledata' => true
+            );
+            $this->cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core', 'string', array(), $options);
+            $this->menucache = cache::make_from_params(cache_store::MODE_REQUEST, 'core', 'langmenu', array(), $options);
+        }
+    }
+
+    /**
+     * Returns list of all explicit parent languages for the given language.
+     *
+     * English (en) is considered as the top implicit parent of all language packs
+     * and is not included in the returned list. The language itself is appended to the
+     * end of the list. The method is aware of circular dependency risk.
+     *
+     * @see self::populate_parent_languages()
+     * @param string $lang the code of the language
+     * @return array all explicit parent languages with the lang itself appended
+     */
+    public function get_language_dependencies($lang) {
+        return $this->populate_parent_languages($lang);
+    }
+
+    /**
+     * Load all strings for one component
+     *
+     * @param string $component The module the string is associated with
+     * @param string $lang
+     * @param bool $disablecache Do not use caches, force fetching the strings from sources
+     * @param bool $disablelocal Do not use customized strings in xx_local language packs
+     * @return array of all string for given component and lang
+     */
+    public function load_component_strings($component, $lang, $disablecache = false, $disablelocal = false) {
+        global $CFG;
+
+        list($plugintype, $pluginname) = core_component::normalize_component($component);
+        if ($plugintype === 'core' and is_null($pluginname)) {
+            $component = 'core';
+        } else {
+            $component = $plugintype . '_' . $pluginname;
+        }
+
+        $cachekey = $lang.'_'.$component.'_'.$this->get_key_suffix();
+
+        $cachedstring = $this->cache->get($cachekey);
+        if (!$disablecache and !$disablelocal) {
+            if ($cachedstring !== false) {
+                return $cachedstring;
+            }
+        }
+
+        // No cache found - let us merge all possible sources of the strings.
+        if ($plugintype === 'core') {
+            $file = $pluginname;
+            if ($file === null) {
+                $file = 'moodle';
+            }
+            $string = array();
+            // First load english pack.
+            if (!file_exists("$CFG->dirroot/lang/en/$file.php")) {
+                return array();
+            }
+            include("$CFG->dirroot/lang/en/$file.php");
+            $enstring = $string;
+
+            // And then corresponding local if present and allowed.
+            if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
+                include("$this->localroot/en_local/$file.php");
+            }
+            // Now loop through all langs in correct order.
+            $deps = $this->get_language_dependencies($lang);
+            foreach ($deps as $dep) {
+                // The main lang string location.
+                if (file_exists("$this->otherroot/$dep/$file.php")) {
+                    include("$this->otherroot/$dep/$file.php");
+                }
+                if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
+                    include("$this->localroot/{$dep}_local/$file.php");
+                }
+            }
+
+        } else {
+            if (!$location = core_component::get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) {
+                return array();
+            }
+            if ($plugintype === 'mod') {
+                // Bloody mod hack.
+                $file = $pluginname;
+            } else {
+                $file = $plugintype . '_' . $pluginname;
+            }
+            $string = array();
+            // First load English pack.
+            if (!file_exists("$location/lang/en/$file.php")) {
+                // English pack does not exist, so do not try to load anything else.
+                return array();
+            }
+            include("$location/lang/en/$file.php");
+            $enstring = $string;
+            // And then corresponding local english if present.
+            if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
+                include("$this->localroot/en_local/$file.php");
+            }
+
+            // Now loop through all langs in correct order.
+            $deps = $this->get_language_dependencies($lang);
+            foreach ($deps as $dep) {
+                // Legacy location - used by contrib only.
+                if (file_exists("$location/lang/$dep/$file.php")) {
+                    include("$location/lang/$dep/$file.php");
+                }
+                // The main lang string location.
+                if (file_exists("$this->otherroot/$dep/$file.php")) {
+                    include("$this->otherroot/$dep/$file.php");
+                }
+                // Local customisations.
+                if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
+                    include("$this->localroot/{$dep}_local/$file.php");
+                }
+            }
+        }
+
+        // We do not want any extra strings from other languages - everything must be in en lang pack.
+        $string = array_intersect_key($string, $enstring);
+
+        if (!$disablelocal) {
+            // Now we have a list of strings from all possible sources,
+            // cache it in MUC cache if not already there.
+            if ($cachedstring === false) {
+                $this->cache->set($cachekey, $string);
+            }
+        }
+        return $string;
+    }
+
+    /**
+     * Does the string actually exist?
+     *
+     * get_string() is throwing debug warnings, sometimes we do not want them
+     * or we want to display better explanation of the problem.
+     * Note: Use with care!
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @return boot true if exists
+     */
+    public function string_exists($identifier, $component) {
+        $lang = current_language();
+        $string = $this->load_component_strings($component, $lang);
+        return isset($string[$identifier]);
+    }
+
+    /**
+     * Get String returns a requested string
+     *
+     * @param string $identifier The identifier of the string to search for
+     * @param string $component The module the string is associated with
+     * @param string|object|array $a An object, string or number that can be used
+     *      within translation strings
+     * @param string $lang moodle translation language, null means use current
+     * @return string The String !
+     */
+    public function get_string($identifier, $component = '', $a = null, $lang = null) {
+        $this->countgetstring++;
+        // There are very many uses of these time formatting strings without the 'langconfig' component,
+        // it would not be reasonable to expect that all of them would be converted during 2.0 migration.
+        static $langconfigstrs = array(
+            'strftimedate' => 1,
+            'strftimedatefullshort' => 1,
+            'strftimedateshort' => 1,
+            'strftimedatetime' => 1,
+            'strftimedatetimeshort' => 1,
+            'strftimedaydate' => 1,
+            'strftimedaydatetime' => 1,
+            'strftimedayshort' => 1,
+            'strftimedaytime' => 1,
+            'strftimemonthyear' => 1,
+            'strftimerecent' => 1,
+            'strftimerecentfull' => 1,
+            'strftimetime' => 1);
+
+        if (empty($component)) {
+            if (isset($langconfigstrs[$identifier])) {
+                $component = 'langconfig';
+            } else {
+                $component = 'moodle';
+            }
+        }
+
+        if ($lang === null) {
+            $lang = current_language();
+        }
+
+        $string = $this->load_component_strings($component, $lang);
+
+        if (!isset($string[$identifier])) {
+            if ($component === 'pix' or $component === 'core_pix') {
+                // This component contains only alt tags for emoticons, not all of them are supposed to be defined.
+                return '';
+            }
+            if ($identifier === 'parentlanguage' and ($component === 'langconfig' or $component === 'core_langconfig')) {
+                // Identifier parentlanguage is a special string, undefined means use English if not defined.
+                return 'en';
+            }
+            // Do not rebuild caches here!
+            // Devs need to learn to purge all caches after any change or disable $CFG->langstringcache.
+            if (!isset($string[$identifier])) {
+                // The string is still missing - should be fixed by developer.
+                if (debugging('', DEBUG_DEVELOPER)) {
+                    list($plugintype, $pluginname) = core_component::normalize_component($component);
+                    if ($plugintype === 'core') {
+                        $file = "lang/en/{$component}.php";
+                    } else if ($plugintype == 'mod') {
+                        $file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
+                    } else {
+                        $path = core_component::get_plugin_directory($plugintype, $pluginname);
+                        $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
+                    }
+                    debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
+                    "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER);
+                }
+                return "[[$identifier]]";
+            }
+        }
+
+        $string = $string[$identifier];
+
+        if ($a !== null) {
+            // Process array's and objects (except lang_strings).
+            if (is_array($a) or (is_object($a) && !($a instanceof lang_string))) {
+                $a = (array)$a;
+                $search = array();
+                $replace = array();
+                foreach ($a as $key => $value) {
+                    if (is_int($key)) {
+                        // We do not support numeric keys - sorry!
+                        continue;
+                    }
+                    if (is_array($value) or (is_object($value) && !($value instanceof lang_string))) {
+                        // We support just string or lang_string as value.
+                        continue;
+                    }
+                    $search[]  = '{$a->'.$key.'}';
+                    $replace[] = (string)$value;
+                }
+                if ($search) {
+                    $string = str_replace($search, $replace, $string);
+                }
+            } else {
+                $string = str_replace('{$a}', (string)$a, $string);
+            }
+        }
+
+        return $string;
+    }
+
+    /**
+     * Returns information about the core_string_manager performance.
+     *
+     * @return array
+     */
+    public function get_performance_summary() {
+        return array(array(
+            'langcountgetstring' => $this->countgetstring,
+        ), array(
+            'langcountgetstring' => 'get_string calls',
+        ));
+    }
+
+    /**
+     * Returns a localised list of all country names, sorted by localised name.
+     *
+     * @param bool $returnall return all or just enabled
+     * @param string $lang moodle translation language, null means use current
+     * @return array two-letter country code => translated name.
+     */
+    public function get_list_of_countries($returnall = false, $lang = null) {
+        global $CFG;
+
+        if ($lang === null) {
+            $lang = current_language();
+        }
+
+        $countries = $this->load_component_strings('core_countries', $lang);
+        core_collator::asort($countries);
+        if (!$returnall and !empty($CFG->allcountrycodes)) {
+            $enabled = explode(',', $CFG->allcountrycodes);
+            $return = array();
+            foreach ($enabled as $c) {
+                if (isset($countries[$c])) {
+                    $return[$c] = $countries[$c];
+                }
+            }
+            return $return;
+        }
+
+        return $countries;
+    }
+
+    /**
+     * Returns a localised list of languages, sorted by code keys.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @param string $standard language list standard
+     *    - iso6392: three-letter language code (ISO 639-2/T) => translated name
+     *    - iso6391: two-letter language code (ISO 639-1) => translated name
+     * @return array language code => translated name
+     */
+    public function get_list_of_languages($lang = null, $standard = 'iso6391') {
+        if ($lang === null) {
+            $lang = current_language();
+        }
+
+        if ($standard === 'iso6392') {
+            $langs = $this->load_component_strings('core_iso6392', $lang);
+            ksort($langs);
+            return $langs;
+
+        } else if ($standard === 'iso6391') {
+            $langs2 = $this->load_component_strings('core_iso6392', $lang);
+            static $mapping = array('aar' => 'aa', 'abk' => 'ab', 'afr' => 'af', 'aka' => 'ak', 'sqi' => 'sq', 'amh' => 'am', 'ara' => 'ar', 'arg' => 'an', 'hye' => 'hy',
+                'asm' => 'as', 'ava' => 'av', 'ave' => 'ae', 'aym' => 'ay', 'aze' => 'az', 'bak' => 'ba', 'bam' => 'bm', 'eus' => 'eu', 'bel' => 'be', 'ben' => 'bn', 'bih' => 'bh',
+                'bis' => 'bi', 'bos' => 'bs', 'bre' => 'br', 'bul' => 'bg', 'mya' => 'my', 'cat' => 'ca', 'cha' => 'ch', 'che' => 'ce', 'zho' => 'zh', 'chu' => 'cu', 'chv' => 'cv',
+                'cor' => 'kw', 'cos' => 'co', 'cre' => 'cr', 'ces' => 'cs', 'dan' => 'da', 'div' => 'dv', 'nld' => 'nl', 'dzo' => 'dz', 'eng' => 'en', 'epo' => 'eo', 'est' => 'et',
+                'ewe' => 'ee', 'fao' => 'fo', 'fij' => 'fj', 'fin' => 'fi', 'fra' => 'fr', 'fry' => 'fy', 'ful' => 'ff', 'kat' => 'ka', 'deu' => 'de', 'gla' => 'gd', 'gle' => 'ga',
+                'glg' => 'gl', 'glv' => 'gv', 'ell' => 'el', 'grn' => 'gn', 'guj' => 'gu', 'hat' => 'ht', 'hau' => 'ha', 'heb' => 'he', 'her' => 'hz', 'hin' => 'hi', 'hmo' => 'ho',
+                'hrv' => 'hr', 'hun' => 'hu', 'ibo' => 'ig', 'isl' => 'is', 'ido' => 'io', 'iii' => 'ii', 'iku' => 'iu', 'ile' => 'ie', 'ina' => 'ia', 'ind' => 'id', 'ipk' => 'ik',
+                'ita' => 'it', 'jav' => 'jv', 'jpn' => 'ja', 'kal' => 'kl', 'kan' => 'kn', 'kas' => 'ks', 'kau' => 'kr', 'kaz' => 'kk', 'khm' => 'km', 'kik' => 'ki', 'kin' => 'rw',
+                'kir' => 'ky', 'kom' => 'kv', 'kon' => 'kg', 'kor' => 'ko', 'kua' => 'kj', 'kur' => 'ku', 'lao' => 'lo', 'lat' => 'la', 'lav' => 'lv', 'lim' => 'li', 'lin' => 'ln',
+                'lit' => 'lt', 'ltz' => 'lb', 'lub' => 'lu', 'lug' => 'lg', 'mkd' => 'mk', 'mah' => 'mh', 'mal' => 'ml', 'mri' => 'mi', 'mar' => 'mr', 'msa' => 'ms', 'mlg' => 'mg',
+                'mlt' => 'mt', 'mon' => 'mn', 'nau' => 'na', 'nav' => 'nv', 'nbl' => 'nr', 'nde' => 'nd', 'ndo' => 'ng', 'nep' => 'ne', 'nno' => 'nn', 'nob' => 'nb', 'nor' => 'no',
+                'nya' => 'ny', 'oci' => 'oc', 'oji' => 'oj', 'ori' => 'or', 'orm' => 'om', 'oss' => 'os', 'pan' => 'pa', 'fas' => 'fa', 'pli' => 'pi', 'pol' => 'pl', 'por' => 'pt',
+                'pus' => 'ps', 'que' => 'qu', 'roh' => 'rm', 'ron' => 'ro', 'run' => 'rn', 'rus' => 'ru', 'sag' => 'sg', 'san' => 'sa', 'sin' => 'si', 'slk' => 'sk', 'slv' => 'sl',
+                'sme' => 'se', 'smo' => 'sm', 'sna' => 'sn', 'snd' => 'sd', 'som' => 'so', 'sot' => 'st', 'spa' => 'es', 'srd' => 'sc', 'srp' => 'sr', 'ssw' => 'ss', 'sun' => 'su',
+                'swa' => 'sw', 'swe' => 'sv', 'tah' => 'ty', 'tam' => 'ta', 'tat' => 'tt', 'tel' => 'te', 'tgk' => 'tg', 'tgl' => 'tl', 'tha' => 'th', 'bod' => 'bo', 'tir' => 'ti',
+                'ton' => 'to', 'tsn' => 'tn', 'tso' => 'ts', 'tuk' => 'tk', 'tur' => 'tr', 'twi' => 'tw', 'uig' => 'ug', 'ukr' => 'uk', 'urd' => 'ur', 'uzb' => 'uz', 'ven' => 've',
+                'vie' => 'vi', 'vol' => 'vo', 'cym' => 'cy', 'wln' => 'wa', 'wol' => 'wo', 'xho' => 'xh', 'yid' => 'yi', 'yor' => 'yo', 'zha' => 'za', 'zul' => 'zu');
+            $langs1 = array();
+            foreach ($mapping as $c2 => $c1) {
+                $langs1[$c1] = $langs2[$c2];
+            }
+            ksort($langs1);
+            return $langs1;
+
+        } else {
+            debugging('Unsupported $standard parameter in get_list_of_languages() method: '.$standard);
+        }
+
+        return array();
+    }
+
+    /**
+     * Checks if the translation exists for the language
+     *
+     * @param string $lang moodle translation language code
+     * @param bool $includeall include also disabled translations
+     * @return bool true if exists
+     */
+    public function translation_exists($lang, $includeall = true) {
+        $translations = $this->get_list_of_translations($includeall);
+        return isset($translations[$lang]);
+    }
+
+    /**
+     * Returns localised list of installed translations
+     *
+     * @param bool $returnall return all or just enabled
+     * @return array moodle translation code => localised translation name
+     */
+    public function get_list_of_translations($returnall = false) {
+        global $CFG;
+
+        $languages = array();
+
+        $cachekey = 'list_'.$this->get_key_suffix();
+        $cachedlist = $this->menucache->get($cachekey);
+        if ($cachedlist !== false) {
+            // The cache content is invalid.
+            if ($returnall or empty($this->translist)) {
+                return $cachedlist;
+            }
+            // Return only enabled translations.
+            foreach ($cachedlist as $langcode => $langname) {
+                if (isset($this->translist[$langcode])) {
+                    $languages[$langcode] = $langname;
+                }
+            }
+            return $languages;
+        }
+
+        // Get all languages available in system.
+        $langdirs = get_list_of_plugins('', 'en', $this->otherroot);
+        $langdirs["$CFG->dirroot/lang/en"] = 'en';
+
+        // Loop through all langs and get info.
+        foreach ($langdirs as $lang) {
+            if (strrpos($lang, '_local') !== false) {
+                // Just a local tweak of some other lang pack.
+                continue;
+            }
+            if (strrpos($lang, '_utf8') !== false) {
+                // Legacy 1.x lang pack.
+                continue;
+            }
+            if ($lang !== clean_param($lang, PARAM_SAFEDIR)) {
+                // Invalid lang pack name!
+                continue;
+            }
+            $string = $this->load_component_strings('langconfig', $lang);
+            if (!empty($string['thislanguage'])) {
+                $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
+            }
+        }
+
+        core_collator::asort($languages);
+
+        // Cache the list so that it can be used next time.
+        $this->menucache->set($cachekey, $languages);
+
+        if ($returnall or empty($this->translist)) {
+            return $languages;
+        }
+
+        $cachedlist = $languages;
+
+        // Return only enabled translations.
+        $languages = array();
+        foreach ($cachedlist as $langcode => $langname) {
+            if (isset($this->translist[$langcode])) {
+                $languages[$langcode] = $langname;
+            }
+        }
+
+        return $languages;
+    }
+
+    /**
+     * Returns localised list of currencies.
+     *
+     * @param string $lang moodle translation language, null means use current
+     * @return array currency code => localised currency name
+     */
+    public function get_list_of_currencies($lang = null) {
+        if ($lang === null) {
+            $lang = current_language();
+        }
+
+        $currencies = $this->load_component_strings('core_currencies', $lang);
+        asort($currencies);
+
+        return $currencies;
+    }
+
+    /**
+     * Clears both in-memory and on-disk caches
+     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
+     */
+    public function reset_caches($phpunitreset = false) {
+        // Clear the on-disk disk with aggregated string files.
+        $this->cache->purge();
+        $this->menucache->purge();
+
+        if (!$phpunitreset) {
+            // Increment the revision counter.
+            $langrev = get_config('core', 'langrev');
+            $next = time();
+            if ($langrev !== false and $next <= $langrev and $langrev - $next < 60*60) {
+                // This resolves problems when reset is requested repeatedly within 1s,
+                // the < 1h condition prevents accidental switching to future dates
+                // because we might not recover from it.
+                $next = $langrev+1;
+            }
+            set_config('langrev', $next);
+        }
+
+        // Lang packs use PHP files in dataroot, it is better to invalidate opcode caches.
+        if (function_exists('opcache_reset')) {
+            opcache_reset();
+        }
+    }
+
+    /**
+     * Returns cache key suffix, this enables us to store string + lang menu
+     * caches in local caches on cluster nodes. We can not use prefix because
+     * it would cause problems when creating subdirs in cache file store.
+     * @return string
+     */
+    protected function get_key_suffix() {
+        $rev = $this->get_revision();
+        if ($rev < 0) {
+            // Simple keys do not like minus char.
+            $rev = 0;
+        }
+
+        return $rev;
+    }
+
+    /**
+     * Returns string revision counter, this is incremented after any string cache reset.
+     * @return int lang string revision counter, -1 if unknown
+     */
+    public function get_revision() {
+        global $CFG;
+        if (empty($CFG->langstringcache)) {
+            return -1;
+        }
+        if (isset($CFG->langrev)) {
+            return (int)$CFG->langrev;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Helper method that recursively loads all parents of the given language.
+     *
+     * @see self::get_language_dependencies()
+     * @param string $lang language code
+     * @param array $stack list of parent languages already populated in previous recursive calls
+     * @return array list of all parents of the given language with the $lang itself added as the last element
+     */
+    protected function populate_parent_languages($lang, array $stack = array()) {
+
+        // English does not have a parent language.
+        if ($lang === 'en') {
+            return $stack;
+        }
+
+        // Prevent circular dependency (and thence the infinitive recursion loop).
+        if (in_array($lang, $stack)) {
+            return $stack;
+        }
+
+        // Load language configuration and look for the explicit parent language.
+        if (!file_exists("$this->otherroot/$lang/langconfig.php")) {
+            return $stack;
+        }
+        $string = array();
+        include("$this->otherroot/$lang/langconfig.php");
+
+        if (empty($string['parentlanguage']) or $string['parentlanguage'] === 'en') {
+            return array_merge(array($lang), $stack);
+
+        }
+
+        $parentlang = $string['parentlanguage'];
+        return $this->populate_parent_languages($parentlang, array_merge(array($lang), $stack));
+    }
+}
index 78f9eb8..1b05837 100644 (file)
@@ -1036,7 +1036,20 @@ class completion_info {
         }
         $transaction->allow_commit();
 
-        events_trigger('activity_completion_changed', $data);
+        $cmcontext = context_module::instance($data->coursemoduleid, MUST_EXIST);
+        $coursecontext = $cmcontext->get_parent_context();
+
+        // Trigger an event for course module completion changed.
+        $event = \core\event\course_module_completion_updated::create(
+            array('objectid' => $data->id,
+                'userid' => $USER->id,
+                'context' => $cmcontext,
+                'courseid' => $coursecontext->instanceid,
+                'other' => array('relateduserid' => $data->userid)
+                )
+            );
+        $event->add_record_snapshot('course_modules_completion', $data);
+        $event->trigger();
 
         if ($data->userid == $USER->id) {
             $SESSION->completioncache[$cm->course][$cm->id] = $data;
index b33cc90..816c001 100644 (file)
@@ -48,7 +48,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         'descriptionformat' => null, // not cached
         'parent' => array('pa', 0),
         'sortorder' => array('so', 0),
-        'coursecount' => null, // not cached
+        'coursecount' => array('cc', 0),
         'visible' => array('vi', 1),
         'visibleold' => null, // not cached
         'timemodified' => null, // not cached
@@ -1896,6 +1896,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             foreach ($rs as $record) {
                 // If the category's parent is not visible to the user, it is not visible as well.
                 if (!$record->parent || isset($baselist[$record->parent])) {
+                    context_helper::preload_from_record($record);
                     $context = context_coursecat::instance($record->id);
                     if (!$record->visible && !has_capability('moodle/category:viewhiddencategories', $context)) {
                         // No cap to view category, added to neither $baselist nor $thislist
index 347c824..9f36f54 100644 (file)
@@ -29,8 +29,9 @@
 $definitions = array(
 
     // Used to store processed lang files.
-    // The keys used are the component of the string file.
+    // The keys used are the revision, lang and component of the string file.
     // The persistent max size has been based upon student access of the site.
+    // NOTE: this data may be safely stored in local caches on cluster nodes.
     'string' => array(
         'mode' => cache_store::MODE_APPLICATION,
         'simplekeys' => true,
@@ -39,6 +40,15 @@ $definitions = array(
         'persistentmaxsize' => 30
     ),
 
+    // Used to store cache of all available translations.
+    // NOTE: this data may be safely stored in local caches on cluster nodes.
+    'langmenu' => array(
+        'mode' => cache_store::MODE_APPLICATION,
+        'simplekeys' => true,
+        'simpledata' => true,
+        'persistent' => true,
+    ),
+
     // Used to store database meta information.
     // The database meta information includes information about tables and there columns.
     // Its keys are the table names.
index 1e354fc..b3ba54a 100644 (file)
@@ -37,18 +37,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $handlers = array(
 
-    'course_completed' => array (
-        'handlerfile'      => '/lib/badgeslib.php',
-        'handlerfunction'  => 'badges_award_handle_course_criteria_review',
-        'schedule'         => 'instant',
-        'internal'         => 1,
-    ),
-    'activity_completion_changed' => array (
-        'handlerfile'      => '/lib/badgeslib.php',
-        'handlerfunction'  => 'badges_award_handle_activity_criteria_review',
-        'schedule'         => 'instant',
-        'internal'         => 1,
-    ),
     'user_updated' => array (
         'handlerfile'      => '/lib/badgeslib.php',
         'handlerfunction'  => 'badges_award_handle_profile_criteria_review',
@@ -73,7 +61,18 @@ $handlers = array(
 /* no more here please, core should not consume any events!!!!!!! */
 );
 
+$observers = array(
 
+    array(
+        'eventname'   => '\core\event\course_module_completion_updated',
+        'callback'    => 'core_badges_observer::course_module_criteria_review',
+    ),
+    array(
+        'eventname'   => '\core\event\course_completed',
+        'callback'    => 'core_badges_observer::course_criteria_review',
+    )
+
+);
 
 
 /* List of events thrown from Moodle core
index 27e3b73..60e3b36 100644 (file)
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
         <FIELD NAME="shortname" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="a unique shortname"/>
         <FIELD NAME="downloadfiles" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 if the service allow people to download file from webservice/plugins.php - 0 if not"/>
+        <FIELD NAME="uploadfiles" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 if the service allow people to upload files to webservice/upload.php - 0 if not"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index 6b377d7..311c0dd 100644 (file)
@@ -883,6 +883,7 @@ $services = array(
         'enabled' => 0,
         'restrictedusers' => 0,
         'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE,
-        'downloadfiles' => 1
+        'downloadfiles' => 1,
+        'uploadfiles' => 1
     ),
 );
index ee19a96..cdc076b 100644 (file)
@@ -2313,5 +2313,19 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2013072600.01);
     }
 
+    if ($oldversion < 2013081200.00) {
+        // Define field uploadfiles to be added to external_services.
+        $table = new xmldb_table('external_services');
+        $field = new xmldb_field('uploadfiles', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'downloadfiles');
+
+        // Conditionally launch add field uploadfiles.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013081200.00);
+    }
+
     return true;
 }
index 796efaf..863480a 100644 (file)
@@ -4401,3 +4401,21 @@ function get_related_contexts_string(context $context) {
 function isediting() {
     throw new coding_exception('isediting() can not be used any more, please use $PAGE->user_is_editing() instead.');
 }
+
+/**
+ * Get a list of all the plugins of a given type that contain a particular file.
+ *
+ * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
+ * @param string $file the name of file that must be present in the plugin.
+ *      (e.g. 'view.php', 'db/install.xml').
+ * @param bool $include if true (default false), the file will be include_once-ed if found.
+ * @return array with plugin name as keys (e.g. 'forum', 'courselist') and the path
+ *      to the file relative to dirroot as value (e.g. "$CFG->dirroot/mod/forum/view.php").
+ * @deprecated since 2.6
+ * @see core_component::get_plugin_list_with_file()
+ */
+function get_plugin_list_with_file($plugintype, $file, $include = false) {
+    debugging('get_plugin_list_with_file() is deprecated, please use core_component::get_plugin_list_with_file() instead.',
+        DEBUG_DEVELOPER);
+    return core_component::get_plugin_list_with_file($plugintype, $file, $include);
+}
index ac912e7..eae4a60 100644 (file)
@@ -109,7 +109,6 @@ class tinymce_texteditor extends texteditor {
         if ($fpoptions) {
             $PAGE->requires->js_init_call('M.editor_tinymce.init_filepicker', array($elementid, $fpoptions), true);
         }
-        $this->initialise_collapse_js();
     }
 
     protected function get_init_params($elementid, array $options=null) {
@@ -276,22 +275,4 @@ class tinymce_texteditor extends texteditor {
         return new moodle_url("$CFG->httpswwwroot/lib/editor/tinymce/tiny_mce/$this->version/");
     }
 
-    /**
-     * Initialise javascript form elements
-     * @return void
-     */
-    public function initialise_collapse_js() {
-        global $PAGE;
-        // This method is called for every editor instance. Ensure it's only run once.
-        // Static is a clunky solution but the best we could find to keep everything simple and encapsulated.
-        static $isinitialised;
-        if ($isinitialised) {
-            return;
-        }
-
-        // Initialise language strings.
-        $PAGE->requires->strings_for_js(array('hideeditortoolbar', 'showeditortoolbar'), 'form');
-        $PAGE->requires->yui_module('moodle-editor_tinymce-collapse', 'M.editor_collapse.init');
-        $isinitialised = true;
-    }
 }
index 7e7b9cb..f3bb3e9 100644 (file)
@@ -56,7 +56,7 @@
                 };
                 ed.windowManager.onClose.add(onClose);
                 var vp = ed.dom.getViewPort(),
-                        width = 865 + parseInt(ed.getLang('advimage.delta_width', 0)),
+                        width = 900 + parseInt(ed.getLang('advimage.delta_width', 0)),
                         height = 600 + parseInt(ed.getLang('advimage.delta_height', 0)),
                         maximizedmode = (width >= vp.w - 2 || height >= vp.h - 2);
                 if (maximizedmode) {
index 9175492..10550cf 100644 (file)
@@ -12,6 +12,6 @@ Upgrade procedure:
 2/ bump up version.php
 3/ update lib/thirdpartylibs.xml
 4/ reimplement patch in MDL-23646
-5/ add in "DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe" (without quotes)
+5/ reimplement patch in MDL-40668
+6/ add in "DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe" (without quotes)
    after "DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe"
-5/ reminify the js manually (I used uglifyjs)
index 13a65a4..40b0f5f 100644 (file)
                _resizeIframe : function(ed, tb_id, dy) {\r
                    var ifr = ed.getContentAreaContainer().firstChild;\r
                    var ifrcon = ed.getContentAreaContainer();\r
-                       \r
-                   DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe\r
-                   DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe\r
-                       ed.theme.deltaHeight += dy; // For resize cookie\r
+            var textarea = DOM.get(ed.id);\r
+            var rows = textarea ? textarea.getAttribute('rows') : 3;\r
+\r
+            // For very small text areas - allow the editable region to be smaller than the size of the toolbars.\r
+            if (rows >= 3) {\r
+                DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe\r
+                DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe\r
+                ed.theme.deltaHeight += dy; // For resize cookie\r
+            }\r
                },\r
 \r
                /**\r
index b0708bb..9398912 100644 (file)
@@ -1,19 +1,3 @@
-.mform .felement.feditor .toggle_editor_toolbar {
-    background: #EEEEEE;
-    border-color: #BBBBBB;
-    border-radius: 4px 4px 0 0;
-    border-style: solid solid none;
-    border-width: 1px 1px 0;
-    display: inline-block;
-    font-size: 0.7em;
-    padding: 3px 6px;
-}
-.mform .felement.feditor .toggle_editor_toolbar:hover {
-    text-decoration: underline;
-    color: red;
-    cursor: pointer;
-}
-
 @media (max-width: 480px) {
     .mceToolbar td {
         float: left;
diff --git a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-debug.js b/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-debug.js
deleted file mode 100644 (file)
index 732008b..0000000
Binary files a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-debug.js and /dev/null differ
diff --git a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-min.js b/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-min.js
deleted file mode 100644 (file)
index 485e26e..0000000
Binary files a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse-min.js and /dev/null differ
diff --git a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse.js b/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse.js
deleted file mode 100644 (file)
index 732008b..0000000
Binary files a/lib/editor/tinymce/yui/build/moodle-editor_tinymce-collapse/moodle-editor_tinymce-collapse.js and /dev/null differ
diff --git a/lib/editor/tinymce/yui/src/collapse/build.json b/lib/editor/tinymce/yui/src/collapse/build.json
deleted file mode 100644 (file)
index 996558e..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": "moodle-editor_tinymce-collapse",
-  "builds": {
-    "moodle-editor_tinymce-collapse": {
-      "jsfiles": [
-        "collapse.js"
-      ]
-    }
-  }
-}
diff --git a/lib/editor/tinymce/yui/src/collapse/js/collapse.js b/lib/editor/tinymce/yui/src/collapse/js/collapse.js
deleted file mode 100644 (file)
index 28e77d1..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-var COLLAPSE = function() {
-    COLLAPSE.superclass.constructor.apply(this, arguments);
-};
-
-Y.extend(COLLAPSE, Y.Base, {
-    // A location to store the node toggling template so that we do not have to create it each time.
-    toggleNodeTemplate : null,
-
-    /**
-      * Set up basic values for static access.
-      */
-    init : function() {
-        this.initialise_toggles(10);
-    },
-
-    /**
-     * Has TinyMCE been loaded and the editors been initialised?
-     * Designed mainly for IE
-     * @return bool
-     */
-    editors_initialised : function() {
-        return typeof tinyMCE !== 'undefined';
-    },
-
-    initialise_toggles : function(refreshes) {
-        var editors_initialised = this.editors_initialised(), self = this, editor;
-        if (!editors_initialised && refreshes) {
-            setTimeout(function() {
-                    self.initialise_toggles(refreshes - 1);
-                }, 100);
-            return;
-        }
-
-        // Create the toggle template for use later
-        this.toggleNodeTemplate = Y.Node.create('<a class="toggle_editor_toolbar" />');
-        this.toggleNodeTemplate.setContent(M.util.get_string('showeditortoolbar', 'form'));
-
-        // Delegate clicks of the toggle_editor_toolbar
-        Y.one('body').delegate('click', this.toggle_collapse_from_event, 'a.toggle_editor_toolbar', this);
-
-        // Set up editors which have already been created
-        for (editor in tinyMCE.editors) {
-            this.setup_collapse(tinyMCE.editors[editor]);
-        }
-
-        // Set up for future editors.
-        // I haven't yet found a way of directly delegating the editor.onInit event. Instead we have to listen for the
-        // tinyMCE.onAddEditor event, and then add a further event listener to the editor's onInit event.
-        // onAddEditor is triggered before the editor has been created.
-        // We use Y.Bind to ensure that context is maintained.
-        tinyMCE.onAddEditor.add(Y.bind(this.add_setup_collapse_listener, this));
-
-    },
-
-    /**
-      * Setup a listener for a new editor which will actually set the editor up
-      * @param {Manager} mgr
-      * @param {Editor} ed
-      */
-    add_setup_collapse_listener : function (mgr, ed) {
-        // Bind the editor.onInit function to set this editor up. This ensures we maintain our context (this)
-        ed.onInit.add(Y.bind(this.setup_collapse, this));
-    },
-
-    /**
-      * Setup the toggle system for the provided editor
-      *
-      * @param {Editor} ed The TinyMCE editor instance
-      */
-    setup_collapse : function(ed) {
-        var textarea = Y.Node(ed.getElement()),
-            editortable = Y.Node(ed.getContainer()).one('> table'),
-            thisToggleNode;
-
-        // Does this text area support collapsing at all?
-        if (!textarea.hasClass('collapsible')) {
-            return;
-        }
-
-        // Did we find an appropriate table to work with
-        if (!editortable) {
-            return;
-        }
-
-        // Add toggle button.
-        thisToggleNode = this.toggleNodeTemplate.cloneNode(true);
-        editortable.get('parentNode').insert(thisToggleNode, editortable);
-
-        // Toggle the toolbars initially.
-        if (Y.Node(ed.getElement()).hasClass('collapsed')) {
-            this.toggle_collapse(thisToggleNode, editortable, 0);
-        } else {
-            this.toggle_collapse(thisToggleNode, editortable, 1);
-        }
-
-        // When TinyMCE initialises itself, it adds a height to the table.
-        // Unfortuately, the height it sets is too big for when the editor is collpsed.
-        // Fortunately, the hight is not necessary, so we can just remove it.
-        // (If you re-size the editor then it will remove this style attribute itself.)
-        editortable.setStyle('height', '');
-        editortable.setStyle('width', '');
-    },
-
-    /**
-      * Toggle the specified editor toolbars.
-      *
-      * @param {Node} button The toggle button which we have to change the text for
-      * @param {Node} editortable The table which the tinyMCE editor is in
-      * @param {Boolean} newstate The intended toggle state
-      */
-    toggle_collapse : function(button, editortable, newstate) {
-        var toolbar = editortable.one('td.mceToolbar').ancestor('tr'),
-            statusbar = editortable.one('.mceStatusbar').ancestor('tr');
-
-        // Check whether we have a state already.
-        if (typeof newstate === 'undefined') {
-            if (toolbar.getStyle('display') === 'none') {
-                newstate = 1;
-            } else {
-                newstate = 0;
-            }
-        }
-
-        // Toggle the various states and update the button text to suit
-        if (newstate === 0) {
-            toolbar.hide();
-            statusbar.hide();
-            button.setContent(M.util.get_string('showeditortoolbar', 'form'));
-        } else {
-            toolbar.show();
-            statusbar.show();
-            button.setContent(M.util.get_string('hideeditortoolbar', 'form'));
-        }
-    },
-
-    toggle_collapse_from_event : function(thisevent) {
-        var button = thisevent.target.ancestor('a', true),
-            editortable = thisevent.target.ancestor('span', true).one('table.mceLayout');
-        this.toggle_collapse(button, editortable);
-    }
-});
-
-M.editor_collapse = M.editor_collapse || {};
-M.editor_collapse.init = function(params) {
-    return new COLLAPSE(params);
-};
diff --git a/lib/editor/tinymce/yui/src/collapse/meta/collapse.json b/lib/editor/tinymce/yui/src/collapse/meta/collapse.json
deleted file mode 100644 (file)
index cd81377..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "moodle-editor_tinymce-collapse": {
-    "requires": [
-        "base",
-        "node",
-        "dom"
-    ]
-  }
-}
index f56eb03..22fbbf3 100644 (file)
@@ -53,7 +53,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
     /** @var array options provided to initalize filepicker */
     protected $_options = array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 0, 'changeformat' => 0,
             'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, 'context' => null, 'noclean' => 0, 'trusttext' => 0,
-            'return_types' => 7, 'collapsible' => 0, 'collapsed' => 0);
+            'return_types' => 7);
     // $_options['return_types'] = FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE
 
     /** @var array values for editor */
@@ -365,17 +365,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
         if (!is_null($this->getAttribute('onblur')) && !is_null($this->getAttribute('onchange'))) {
             $editorrules = ' onblur="'.htmlspecialchars($this->getAttribute('onblur')).'" onchange="'.htmlspecialchars($this->getAttribute('onchange')).'"';
         }
-        $str .= '<div><textarea id="'.$id.'" name="'.$elname.'[text]" rows="'.$rows.'" cols="'.$cols.'" spellcheck="true"';
-        $classes = array();
-        if (isset($this->_options['collapsed']) && $this->_options['collapsed']) {
-            $this->_options['collapsible'] = 1;
-            $classes[] = 'collapsed';
-        }
-        if (isset($this->_options['collapsible']) && $this->_options['collapsible']) {
-            $classes[] = 'collapsible';
-        }
-        $str .= ' class="' . implode(' ', $classes) . '"';
-        $str .= $editorrules.'>';
+        $str .= '<div><textarea id="'.$id.'" name="'.$elname.'[text]" rows="'.$rows.'" cols="'.$cols.'" spellcheck="true"'.$editorrules.'>';
         $str .= s($text);
         $str .= '</textarea></div>';
 
index a63119c..177a1f5 100644 (file)
@@ -29,7 +29,7 @@
  * this.areamaxbytes, the maximum size of the area
  * this.filemanager, contains reference to filemanager Node
  * this.selectnode, contains referenct to select-file Node
- * this.selectui, YUI Panel to select the file
+ * this.selectui, M.core.dialogue to select the file
  *
  * FileManager options:
  * =====
@@ -104,16 +104,20 @@ M.form_filemanager.init = function(Y, options) {
             }
             // initialize 'select file' panel
             this.selectnode = Y.Node.createWithFilesSkin(M.form_filemanager.templates.fileselectlayout);
+            this.selectnode.setAttribute('aria-live', 'assertive');
+            this.selectnode.setAttribute('role', 'dialog');
             this.selectnode.generateID();
-            this.selectui = new Y.Panel({
-                srcNode      : this.selectnode,
-                zIndex       : 7600,
+
+            var labelid = 'fm-dialog-label_'+ this.selectnode.get('id');
+            this.selectui = new M.core.dialogue({
+                draggable    : true,
+                headerContent: '<span id="' + labelid +'">' + M.str.moodle.edit + '</span>',
+                bodyContent  : this.selectnode,
                 centered     : true,
                 modal        : true,
-                close        : true,
-                render       : true
+                visible      : false
             });
-            this.selectui.plug(Y.Plugin.Drag,{handles:['#'+this.selectnode.get('id')+' .yui3-widget-hd']});
+            Y.one('#'+this.selectnode.get('id')).setAttribute('aria-labelledby', labelid);
             this.selectui.hide();
             this.setup_select_file();
             // setup buttons onclick events
@@ -237,15 +241,13 @@ M.form_filemanager.init = function(Y, options) {
                 this.msg_dlg_node = Y.Node.createWithFilesSkin(M.form_filemanager.templates.message);
                 var nodeid = this.msg_dlg_node.generateID();
 
-                this.msg_dlg = new Y.Panel({
-                    srcNode      : this.msg_dlg_node,
-                    zIndex       : 8000,
+                this.msg_dlg = new M.core.dialogue({
+                    draggable    : true,
+                    bodyContent  : this.msg_dlg_node,
                     centered     : true,
                     modal        : true,
                     visible      : false,
-                    render       : true
                 });
-                this.msg_dlg.plug(Y.Plugin.Drag,{handles:['#'+nodeid+' .yui3-widget-hd']});
                 this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {
                     e.preventDefault();
                     this.msg_dlg.hide();
@@ -317,15 +319,13 @@ M.form_filemanager.init = function(Y, options) {
                     };
                     if (!this.mkdir_dialog) {
                         var node = Y.Node.createWithFilesSkin(M.form_filemanager.templates.mkdir);
-                        this.mkdir_dialog = new Y.Panel({
-                            srcNode      : node,
-                            zIndex       : 8000,
+                        this.mkdir_dialog = new M.core.dialogue({
+                            draggable    : true,
+                            bodyContent  : node,
                             centered     : true,
                             modal        : true,
                             visible      : false,
-                            render       : true
                         });
-                        this.mkdir_dialog.plug(Y.Plugin.Drag,{handles:['.yui3-widget-hd']});
                         node.one('.fp-dlg-butcreate').set('id', 'fm-mkdir-butcreate-'+this.client_id).on('click',
                                 perform_action, this);
                         node.one('input').set('id', 'fm-newname-'+this.client_id).on('keydown', function(e) {
@@ -735,16 +735,14 @@ M.form_filemanager.init = function(Y, options) {
                 this.confirm_dlg_node = Y.Node.createWithFilesSkin(M.form_filemanager.templates.confirmdialog);
                 var node = this.confirm_dlg_node;
                 node.generateID();
-                this.confirm_dlg = new Y.Panel({
-                    srcNode      : node,
-                    zIndex       : 8000,
+                this.confirm_dlg = new M.core.dialogue({
+                    draggable    : true,
+                    bodyContent  : node,
                     centered     : true,
                     modal        : true,
                     visible      : false,
-                    render       : true,
                     buttons      : {}
                 });
-                this.confirm_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']});
                 var handle_confirm = function(ev) {
                     var dlgopt = this.confirm_dlg.dlgopt;
                     ev.preventDefault();
@@ -1011,8 +1009,17 @@ M.form_filemanager.init = function(Y, options) {
                     }
                 }, false);
             }
+            // update dialog header
+            var nodename = node.fullname;
+            // Limit the string length so it fits nicely on mobile devices
+            var namelength = 50;
+            if (nodename.length > namelength) {
+                nodename = nodename.substring(0, namelength) + '...';
+            }
+            Y.one('#fm-dialog-label_'+selectnode.get('id')).setContent(Y.Escape.html(M.str.moodle.edit+' '+nodename));
             // show panel
             this.selectui.show();
+            Y.one('#'+selectnode.get('id')).focus();
         },
         render: function() {
             this.print_path();
index 4a91911..b4c23ac 100644 (file)
@@ -2570,13 +2570,13 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         // switch next two lines for ol li containers for form items.
         //        $this->_elementTemplates=array('default'=>"\n\t\t".'<li class="fitem"><label>{label}{help}<!-- BEGIN required -->{req}<!-- END required --></label><div class="qfelement<!-- BEGIN error --> error<!-- END error --> {type}"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></li>');
         $this->_elementTemplates = array(
-        'default'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}" {aria-live}><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
+        'default'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}" {aria-live}><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} </label>{help}</div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
 
         'actionbuttons'=>"\n\t\t".'<div id="{id}" class="fitem fitem_actionbuttons fitem_{type}"><div class="felement {type}">{element}</div></div>',
 
-        'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
+        'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} </label>{help}</div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
 
-        'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}&nbsp;</div></div>',
+        'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} </label>{help}</div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}&nbsp;</div></div>',
 
         'warning'=>"\n\t\t".'<div class="fitem {advanced}">{element}</div>',
 
index 93d0185..190ab09 100644 (file)
@@ -1616,7 +1616,9 @@ class grade_category extends grade_object {
             if ($children = grade_item::fetch_all(array('categoryid'=>$this->id))) {
 
                 foreach ($children as $child) {
-                    $child->set_hidden($hidden, $cascade);
+                    if ($child->can_control_visibility()) {
+                        $child->set_hidden($hidden, $cascade);
+                    }
                 }
             }
 
index 9ca8c36..ce6713b 100644 (file)
@@ -2097,6 +2097,6 @@ class grade_item extends grade_object {
         if (core_component::get_plugin_directory($this->itemtype, $this->itemmodule)) {
             return !plugin_supports($this->itemtype, $this->itemmodule, FEATURE_CONTROLS_GRADE_VISIBILITY, false);
         }
-        return true;
+        return parent::can_control_visibility();
     }
 }
index 5c7e6ad..cea1c18 100644 (file)
@@ -438,4 +438,13 @@ abstract class grade_object {
         $this->hidden = $hidden;
         $this->update();
     }
+
+    /**
+     * Returns whether the grade object can control the visibility of the grades.
+     *
+     * @return bool
+     */
+    public function can_control_visibility() {
+        return true;
+    }
 }
index dcdb3c8..b7d90ef 100644 (file)
@@ -105,6 +105,9 @@ abstract class grade_base_testcase extends advanced_testcase {
 
         $this->activities[6] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id));
         $this->course_module[6] = get_coursemodule_from_instance('forum', $this->activities[6]->id);
+
+        $this->activities[7] = $this->getDataGenerator()->create_module('quiz', array('course'=>$this->course->id));
+        $this->course_module[7] = get_coursemodule_from_instance('quiz', $this->activities[7]->id);
     }
 
     private function load_scales() {
@@ -494,6 +497,29 @@ abstract class grade_base_testcase extends advanced_testcase {
 
         $grade_item->id = $DB->insert_record('grade_items', $grade_item);
         $this->grade_items[10] = $grade_item;
+
+        // Quiz grade_item (course_module = 7).
+        // id = 11
+        $grade_item = new stdClass();
+
+        $grade_item->courseid = $this->course->id;
+        $grade_item->categoryid = $course_category->id;
+        $grade_item->itemname = 'Quiz grade item';
+        $grade_item->itemtype = 'mod';
+        $grade_item->itemmodule = $this->course_module[7]->modname;
+        $grade_item->iteminstance = $this->course_module[7]->instance;
+        $grade_item->itemnumber = 0;
+        $grade_item->gradetype = GRADE_TYPE_VALUE;
+        $grade_item->grademin = 0;
+        $grade_item->grademax = 100;
+        $grade_item->locked = 0;
+        $grade_item->iteminfo = 'Quiz grade item used for unit testing';
+        $grade_item->timecreated = time();
+        $grade_item->timemodified = time();
+        $grade_item->sortorder = 11;
+
+        $grade_item->id = $DB->insert_record('grade_items', $grade_item);
+        $this->grade_items[11] = $grade_item;
     }
 
     /**
@@ -772,5 +798,3 @@ abstract class grade_base_testcase extends advanced_testcase {
         $this->grade_outcomes[] = $grade_outcome;
     }
 }
-
-
index 60ad6d2..bf15662 100644 (file)
@@ -60,6 +60,7 @@ class core_grade_category_testcase extends grade_base_testcase {
         $this->sub_test_grade_category_set_locked();
         $this->sub_test_grade_category_is_hidden();
         $this->sub_test_grade_category_set_hidden();
+        $this->sub_test_grade_category_can_control_visibility();
 
         //this won't work until MDL-11837 is complete
         //$this->sub_test_grade_category_generate_grades();
@@ -693,6 +694,11 @@ class core_grade_category_testcase extends grade_base_testcase {
         $this->assertEquals(true, $category->grade_item->is_hidden());
     }
 
+    protected function sub_test_grade_category_can_control_visibility() {
+        $category = new grade_category($this->grade_categories[0]);
+        $this->assertTrue($category->can_control_visibility());
+    }
+
     //beware: adding a duplicate course category messes up the data in a way that's hard to recover from
     protected function sub_test_grade_category_insert_course_category() {
         $grade_category = new grade_category();
index b884d8d..70ec325 100644 (file)
@@ -64,6 +64,7 @@ class core_grade_item_testcase extends grade_base_testcase {
         $this->sub_test_grade_item_get_calculation();
         $this->sub_test_grade_item_compute();
         $this->sub_test_update_final_grade();
+        $this->sub_test_grade_item_can_control_visibility();
     }
 
     protected function sub_test_grade_item_construct() {
@@ -99,7 +100,7 @@ class core_grade_item_testcase extends grade_base_testcase {
         $last_grade_item = end($this->grade_items);
 
         $this->assertEquals($grade_item->id, $last_grade_item->id + 1);
-        $this->assertEquals(11, $grade_item->sortorder);
+        $this->assertEquals(12, $grade_item->sortorder);
 
         //keep our reference collection the same as what is in the database
         $this->grade_items[] = $grade_item;
@@ -588,4 +589,14 @@ class core_grade_item_testcase extends grade_base_testcase {
         $this->assertEquals($min, $grade_grade->rawgrademin);
         $this->assertEquals($max, $grade_grade->rawgrademax);
     }
+
+    protected function sub_test_grade_item_can_control_visibility() {
+        // Grade item 0 == Course module 0 == Assignment.
+        $grade_item = new grade_item($this->grade_items[0], false);
+        $this->assertTrue($grade_item->can_control_visibility());
+
+        // Grade item  == Course module 7 == Quiz.
+        $grade_item = new grade_item($this->grade_items[11], false);
+        $this->assertFalse($grade_item->can_control_visibility());
+    }
 }
index e9ccf63..cfab5d8 100644 (file)
@@ -6616,7 +6616,7 @@ function get_parent_language($lang=null) {
  *
  * @category string
  * @param bool $forcereload shall the singleton be released and new instance created instead?
- * @return string_manager
+ * @return core_string_manager
  */
 function get_string_manager($forcereload=false) {
     global $CFG;
@@ -6635,977 +6635,16 @@ function get_string_manager($forcereload=false) {
                 $translist = explode(',', $CFG->langlist);
             }
 
-            if (empty($CFG->langmenucachefile)) {
-                $langmenucache = $CFG->cachedir . '/languages';
-            } else {
-                $langmenucache = $CFG->langmenucachefile;
-            }
-
-            $singleton = new core_string_manager($CFG->langotherroot, $CFG->langlocalroot,
-                                                 !empty($CFG->langstringcache), $translist, $langmenucache);
+            $singleton = new core_string_manager_standard($CFG->langotherroot, $CFG->langlocalroot, $translist);
 
         } else {
-            $singleton = new install_string_manager();
+            $singleton = new core_string_manager_install();
         }
     }
 
     return $singleton;
 }
 
-
-/**
- * Interface for string manager
- *
- * Interface describing class which is responsible for getting
- * of localised strings from language packs.
- *
- * @package    core
- * @copyright  2010 Petr Skoda (http://skodak.org)
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-interface string_manager {
-    /**
-     * Get String returns a requested string
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @param string|object|array $a An object, string or number that can be used
-     *      within translation strings
-     * @param string $lang moodle translation language, null means use current
-     * @return string The String !
-     */
-    public function get_string($identifier, $component = '', $a = null, $lang = null);
-
-    /**
-     * Does the string actually exist?
-     *
-     * get_string() is throwing debug warnings, sometimes we do not want them
-     * or we want to display better explanation of the problem.
-     *
-     * Use with care!
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @return boot true if exists
-     */
-    public function string_exists($identifier, $component);
-
-    /**
-     * Returns a localised list of all country names, sorted by country keys.
-     * @param bool $returnall return all or just enabled
-     * @param string $lang moodle translation language, null means use current
-     * @return array two-letter country code => translated name.
-     */
-    public function get_list_of_countries($returnall = false, $lang = null);
-
-    /**
-     * Returns a localised list of languages, sorted by code keys.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @param string $standard language list standard
-     *                     iso6392: three-letter language code (ISO 639-2/T) => translated name.
-     * @return array language code => translated name
-     */
-    public function get_list_of_languages($lang = null, $standard = 'iso6392');
-
-    /**
-     * Checks if the translation exists for the language
-     *
-     * @param string $lang moodle translation language code
-     * @param bool $includeall include also disabled translations
-     * @return bool true if exists
-     */
-    public function translation_exists($lang, $includeall = true);
-
-    /**
-     * Returns localised list of installed translations
-     * @param bool $returnall return all or just enabled
-     * @return array moodle translation code => localised translation name
-     */
-    public function get_list_of_translations($returnall = false);
-
-    /**
-     * Returns localised list of currencies.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @return array currency code => localised currency name
-     */
-    public function get_list_of_currencies($lang = null);
-
-    /**
-     * Load all strings for one component
-     * @param string $component The module the string is associated with
-     * @param string $lang
-     * @param bool $disablecache Do not use caches, force fetching the strings from sources
-     * @param bool $disablelocal Do not use customized strings in xx_local language packs
-     * @return array of all string for given component and lang
-     */
-    public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false);
-
-    /**
-     * Invalidates all caches, should the implementation use any
-     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
-     */
-    public function reset_caches($phpunitreset = false);
-
-    /**
-     * Returns string revision counter, this is incremented after any
-     * string cache reset.
-     * @return int lang string revision counter, -1 if unknown
-     */
-    public function get_revision();
-}
-
-
-/**
- * Standard string_manager implementation
- *
- * Implements string_manager with getting and printing localised strings
- *
- * @package    core
- * @category   string
- * @copyright  2010 Petr Skoda (http://skodak.org)
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class core_string_manager implements string_manager {
-    /** @var string location of all packs except 'en' */
-    protected $otherroot;
-    /** @var string location of all lang pack local modifications */
-    protected $localroot;
-    /** @var cache lang string cache - it will be optimised more later */
-    protected $cache;
-    /** @var int get_string() counter */
-    protected $countgetstring = 0;
-    /** @var bool use disk cache */
-    protected $usecache;
-    /** @var array limit list of translations */
-    protected $translist;
-    /** @var string location of a file that caches the list of available translations */
-    protected $menucache;
-
-    /**
-     * Create new instance of string manager
-     *
-     * @param string $otherroot location of downlaoded lang packs - usually $CFG->dataroot/lang
-     * @param string $localroot usually the same as $otherroot
-     * @param bool $usecache use disk cache
-     * @param array $translist limit list of visible translations
-     * @param string $menucache the location of a file that caches the list of available translations
-     */
-    public function __construct($otherroot, $localroot, $usecache, $translist, $menucache) {
-        $this->otherroot    = $otherroot;
-        $this->localroot    = $localroot;
-        $this->usecache     = $usecache;
-        $this->translist    = $translist;
-        $this->menucache    = $menucache;
-
-        if ($this->usecache) {
-            // We can use a proper cache, establish the cache using the 'String cache' definition.
-            $this->cache = cache::make('core', 'string');
-        } else {
-            // We only want a cache for the length of the request, create a static cache.
-            $options = array(
-                'simplekeys' => true,
-                'simpledata' => true
-            );
-            $this->cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core', 'string', array(), $options);
-        }
-    }
-
-    /**
-     * Returns list of all explicit parent languages for the given language.
-     *
-     * English (en) is considered as the top implicit parent of all language packs
-     * and is not included in the returned list. The language itself is appended to the
-     * end of the list. The method is aware of circular dependency risk.
-     *
-     * @see self::populate_parent_languages()
-     * @param string $lang the code of the language
-     * @return array all explicit parent languages with the lang itself appended
-     */
-    public function get_language_dependencies($lang) {
-        return $this->populate_parent_languages($lang);
-    }
-
-    /**
-     * Load all strings for one component
-     *
-     * @param string $component The module the string is associated with
-     * @param string $lang
-     * @param bool $disablecache Do not use caches, force fetching the strings from sources
-     * @param bool $disablelocal Do not use customized strings in xx_local language packs
-     * @return array of all string for given component and lang
-     */
-    public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) {
-        global $CFG;
-
-        list($plugintype, $pluginname) = core_component::normalize_component($component);
-        if ($plugintype == 'core' and is_null($pluginname)) {
-            $component = 'core';
-        } else {
-            $component = $plugintype . '_' . $pluginname;
-        }
-
-        $cachekey = $lang.'_'.$component;
-
-        if (!$disablecache and !$disablelocal) {
-            $string = $this->cache->get($cachekey);
-            if ($string) {
-                return $string;
-            }
-        }
-
-        // No cache found - let us merge all possible sources of the strings.
-        if ($plugintype === 'core') {
-            $file = $pluginname;
-            if ($file === null) {
-                $file = 'moodle';
-            }
-            $string = array();
-            // First load english pack.
-            if (!file_exists("$CFG->dirroot/lang/en/$file.php")) {
-                return array();
-            }
-            include("$CFG->dirroot/lang/en/$file.php");
-            $originalkeys = array_keys($string);
-            $originalkeys = array_flip($originalkeys);
-
-            // And then corresponding local if present and allowed.
-            if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
-                include("$this->localroot/en_local/$file.php");
-            }
-            // Now loop through all langs in correct order.
-            $deps = $this->get_language_dependencies($lang);
-            foreach ($deps as $dep) {
-                // The main lang string location.
-                if (file_exists("$this->otherroot/$dep/$file.php")) {
-                    include("$this->otherroot/$dep/$file.php");
-                }
-                if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
-                    include("$this->localroot/{$dep}_local/$file.php");
-                }
-            }
-
-        } else {
-            if (!$location = core_component::get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) {
-                return array();
-            }
-            if ($plugintype === 'mod') {
-                // Bloody mod hack.
-                $file = $pluginname;
-            } else {
-                $file = $plugintype . '_' . $pluginname;
-            }
-            $string = array();
-            // First load English pack.
-            if (!file_exists("$location/lang/en/$file.php")) {
-                // English pack does not exist, so do not try to load anything else.
-                return array();
-            }
-            include("$location/lang/en/$file.php");
-            $originalkeys = array_keys($string);
-            $originalkeys = array_flip($originalkeys);
-            // And then corresponding local english if present.
-            if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
-                include("$this->localroot/en_local/$file.php");
-            }
-
-            // Now loop through all langs in correct order.
-            $deps = $this->get_language_dependencies($lang);
-            foreach ($deps as $dep) {
-                // Legacy location - used by contrib only.
-                if (file_exists("$location/lang/$dep/$file.php")) {
-                    include("$location/lang/$dep/$file.php");
-                }
-                // The main lang string location.
-                if (file_exists("$this->otherroot/$dep/$file.php")) {
-                    include("$this->otherroot/$dep/$file.php");
-                }
-                // Local customisations.
-                if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
-                    include("$this->localroot/{$dep}_local/$file.php");
-                }
-            }
-        }
-
-        // We do not want any extra strings from other languages - everything must be in en lang pack.
-        $string = array_intersect_key($string, $originalkeys);
-
-        if (!$disablelocal) {
-            // Now we have a list of strings from all possible sources. put it into both in-memory and on-disk
-            // caches so we do not need to do all this merging and dependencies resolving again.
-            $this->cache->set($cachekey, $string);
-        }
-        return $string;
-    }
-
-    /**
-     * Does the string actually exist?
-     *
-     * get_string() is throwing debug warnings, sometimes we do not want them
-     * or we want to display better explanation of the problem.
-     * Note: Use with care!
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @return boot true if exists
-     */
-    public function string_exists($identifier, $component) {
-        $lang = current_language();
-        $string = $this->load_component_strings($component, $lang);
-        return isset($string[$identifier]);
-    }
-
-    /**
-     * Get String returns a requested string
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @param string|object|array $a An object, string or number that can be used
-     *      within translation strings
-     * @param string $lang moodle translation language, null means use current
-     * @return string The String !
-     */
-    public function get_string($identifier, $component = '', $a = null, $lang = null) {
-        $this->countgetstring++;
-        // There are very many uses of these time formating strings without the 'langconfig' component,
-        // it would not be reasonable to expect that all of them would be converted during 2.0 migration.
-        static $langconfigstrs = array(
-                'strftimedate' => 1,
-                'strftimedatefullshort' => 1,
-                'strftimedateshort' => 1,
-                'strftimedatetime' => 1,
-                'strftimedatetimeshort' => 1,
-                'strftimedaydate' => 1,
-                'strftimedaydatetime' => 1,
-                'strftimedayshort' => 1,
-                'strftimedaytime' => 1,
-                'strftimemonthyear' => 1,
-                'strftimerecent' => 1,
-                'strftimerecentfull' => 1,
-                'strftimetime' => 1);
-
-        if (empty($component)) {
-            if (isset($langconfigstrs[$identifier])) {
-                $component = 'langconfig';
-            } else {
-                $component = 'moodle';
-            }
-        }
-
-        if ($lang === null) {
-            $lang = current_language();
-        }
-
-        $string = $this->load_component_strings($component, $lang);
-
-        if (!isset($string[$identifier])) {
-            if ($component === 'pix' or $component === 'core_pix') {
-                // This component contains only alt tags for emoticons, not all of them are supposed to be defined.
-                return '';
-            }
-            if ($identifier === 'parentlanguage' and ($component === 'langconfig' or $component === 'core_langconfig')) {
-                // Identifier parentlanguage is a special string, undefined means use English if not defined.
-                return 'en';
-            }
-            if ($this->usecache) {
-                // Maybe the on-disk cache is dirty - let the last attempt be to find the string in original sources,
-                // do NOT write the results to disk cache because it may end up in race conditions see MDL-31904.
-                $this->usecache = false;
-                $string = $this->load_component_strings($component, $lang, true);
-                $this->usecache = true;
-            }
-            if (!isset($string[$identifier])) {
-                // The string is still missing - should be fixed by developer.
-                list($plugintype, $pluginname) = core_component::normalize_component($component);
-                if ($plugintype == 'core') {
-                    $file = "lang/en/{$component}.php";
-                } else if ($plugintype == 'mod') {
-                    $file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
-                } else {
-                    $path = core_component::get_plugin_directory($plugintype, $pluginname);
-                    $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
-                }
-                debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
-                        "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER);
-                return "[[$identifier]]";
-            }
-        }
-
-        $string = $string[$identifier];
-
-        if ($a !== null) {
-            // Process array's and objects (except lang_strings).
-            if (is_array($a) or (is_object($a) && !($a instanceof lang_string))) {
-                $a = (array)$a;
-                $search = array();
-                $replace = array();
-                foreach ($a as $key => $value) {
-                    if (is_int($key)) {
-                        // We do not support numeric keys - sorry!
-                        continue;
-                    }
-                    if (is_array($value) or (is_object($value) && !($value instanceof lang_string))) {
-                        // We support just string or lang_string as value.
-                        continue;
-                    }
-                    $search[]  = '{$a->'.$key.'}';
-                    $replace[] = (string)$value;
-                }
-                if ($search) {
-                    $string = str_replace($search, $replace, $string);
-                }
-            } else {
-                $string = str_replace('{$a}', (string)$a, $string);
-            }
-        }
-
-        return $string;
-    }
-
-    /**
-     * Returns information about the string_manager performance.
-     *
-     * @return array
-     */
-    public function get_performance_summary() {
-        return array(array(
-            'langcountgetstring' => $this->countgetstring,
-        ), array(
-            'langcountgetstring' => 'get_string calls',
-        ));
-    }
-
-    /**
-     * Returns a localised list of all country names, sorted by localised name.
-     *
-     * @param bool $returnall return all or just enabled
-     * @param string $lang moodle translation language, null means use current
-     * @return array two-letter country code => translated name.
-     */
-    public function get_list_of_countries($returnall = false, $lang = null) {
-        global $CFG;
-
-        if ($lang === null) {
-            $lang = current_language();
-        }
-
-        $countries = $this->load_component_strings('core_countries', $lang);
-        core_collator::asort($countries);
-        if (!$returnall and !empty($CFG->allcountrycodes)) {
-            $enabled = explode(',', $CFG->allcountrycodes);
-            $return = array();
-            foreach ($enabled as $c) {
-                if (isset($countries[$c])) {
-                    $return[$c] = $countries[$c];
-                }
-            }
-            return $return;
-        }
-
-        return $countries;
-    }
-
-    /**
-     * Returns a localised list of languages, sorted by code keys.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @param string $standard language list standard
-     *    - iso6392: three-letter language code (ISO 639-2/T) => translated name
-     *    - iso6391: two-letter langauge code (ISO 639-1) => translated name
-     * @return array language code => translated name
-     */
-    public function get_list_of_languages($lang = null, $standard = 'iso6391') {
-        if ($lang === null) {
-            $lang = current_language();
-        }
-
-        if ($standard === 'iso6392') {
-            $langs = $this->load_component_strings('core_iso6392', $lang);
-            ksort($langs);
-            return $langs;
-
-        } else if ($standard === 'iso6391') {
-            $langs2 = $this->load_component_strings('core_iso6392', $lang);
-            static $mapping = array('aar' => 'aa', 'abk' => 'ab', 'afr' => 'af', 'aka' => 'ak', 'sqi' => 'sq', 'amh' => 'am', 'ara' => 'ar', 'arg' => 'an', 'hye' => 'hy',
-                'asm' => 'as', 'ava' => 'av', 'ave' => 'ae', 'aym' => 'ay', 'aze' => 'az', 'bak' => 'ba', 'bam' => 'bm', 'eus' => 'eu', 'bel' => 'be', 'ben' => 'bn', 'bih' => 'bh',
-                'bis' => 'bi', 'bos' => 'bs', 'bre' => 'br', 'bul' => 'bg', 'mya' => 'my', 'cat' => 'ca', 'cha' => 'ch', 'che' => 'ce', 'zho' => 'zh', 'chu' => 'cu', 'chv' => 'cv',
-                'cor' => 'kw', 'cos' => 'co', 'cre' => 'cr', 'ces' => 'cs', 'dan' => 'da', 'div' => 'dv', 'nld' => 'nl', 'dzo' => 'dz', 'eng' => 'en', 'epo' => 'eo', 'est' => 'et',
-                'ewe' => 'ee', 'fao' => 'fo', 'fij' => 'fj', 'fin' => 'fi', 'fra' => 'fr', 'fry' => 'fy', 'ful' => 'ff', 'kat' => 'ka', 'deu' => 'de', 'gla' => 'gd', 'gle' => 'ga',
-                'glg' => 'gl', 'glv' => 'gv', 'ell' => 'el', 'grn' => 'gn', 'guj' => 'gu', 'hat' => 'ht', 'hau' => 'ha', 'heb' => 'he', 'her' => 'hz', 'hin' => 'hi', 'hmo' => 'ho',
-                'hrv' => 'hr', 'hun' => 'hu', 'ibo' => 'ig', 'isl' => 'is', 'ido' => 'io', 'iii' => 'ii', 'iku' => 'iu', 'ile' => 'ie', 'ina' => 'ia', 'ind' => 'id', 'ipk' => 'ik',
-                'ita' => 'it', 'jav' => 'jv', 'jpn' => 'ja', 'kal' => 'kl', 'kan' => 'kn', 'kas' => 'ks', 'kau' => 'kr', 'kaz' => 'kk', 'khm' => 'km', 'kik' => 'ki', 'kin' => 'rw',
-                'kir' => 'ky', 'kom' => 'kv', 'kon' => 'kg', 'kor' => 'ko', 'kua' => 'kj', 'kur' => 'ku', 'lao' => 'lo', 'lat' => 'la', 'lav' => 'lv', 'lim' => 'li', 'lin' => 'ln',
-                'lit' => 'lt', 'ltz' => 'lb', 'lub' => 'lu', 'lug' => 'lg', 'mkd' => 'mk', 'mah' => 'mh', 'mal' => 'ml', 'mri' => 'mi', 'mar' => 'mr', 'msa' => 'ms', 'mlg' => 'mg',
-                'mlt' => 'mt', 'mon' => 'mn', 'nau' => 'na', 'nav' => 'nv', 'nbl' => 'nr', 'nde' => 'nd', 'ndo' => 'ng', 'nep' => 'ne', 'nno' => 'nn', 'nob' => 'nb', 'nor' => 'no',
-                'nya' => 'ny', 'oci' => 'oc', 'oji' => 'oj', 'ori' => 'or', 'orm' => 'om', 'oss' => 'os', 'pan' => 'pa', 'fas' => 'fa', 'pli' => 'pi', 'pol' => 'pl', 'por' => 'pt',
-                'pus' => 'ps', 'que' => 'qu', 'roh' => 'rm', 'ron' => 'ro', 'run' => 'rn', 'rus' => 'ru', 'sag' => 'sg', 'san' => 'sa', 'sin' => 'si', 'slk' => 'sk', 'slv' => 'sl',
-                'sme' => 'se', 'smo' => 'sm', 'sna' => 'sn', 'snd' => 'sd', 'som' => 'so', 'sot' => 'st', 'spa' => 'es', 'srd' => 'sc', 'srp' => 'sr', 'ssw' => 'ss', 'sun' => 'su',
-                'swa' => 'sw', 'swe' => 'sv', 'tah' => 'ty', 'tam' => 'ta', 'tat' => 'tt', 'tel' => 'te', 'tgk' => 'tg', 'tgl' => 'tl', 'tha' => 'th', 'bod' => 'bo', 'tir' => 'ti',
-                'ton' => 'to', 'tsn' => 'tn', 'tso' => 'ts', 'tuk' => 'tk', 'tur' => 'tr', 'twi' => 'tw', 'uig' => 'ug', 'ukr' => 'uk', 'urd' => 'ur', 'uzb' => 'uz', 'ven' => 've',
-                'vie' => 'vi', 'vol' => 'vo', 'cym' => 'cy', 'wln' => 'wa', 'wol' => 'wo', 'xho' => 'xh', 'yid' => 'yi', 'yor' => 'yo', 'zha' => 'za', 'zul' => 'zu');
-            $langs1 = array();
-            foreach ($mapping as $c2 => $c1) {
-                $langs1[$c1] = $langs2[$c2];
-            }
-            ksort($langs1);
-            return $langs1;
-
-        } else {
-            debugging('Unsupported $standard parameter in get_list_of_languages() method: '.$standard);
-        }
-
-        return array();
-    }
-
-    /**
-     * Checks if the translation exists for the language
-     *
-     * @param string $lang moodle translation language code
-     * @param bool $includeall include also disabled translations
-     * @return bool true if exists
-     */
-    public function translation_exists($lang, $includeall = true) {
-
-        if (strpos($lang, '_local') !== false) {
-            // Local packs are not real translations.
-            return false;
-        }
-        if (!$includeall and !empty($this->translist)) {
-            if (!in_array($lang, $this->translist)) {
-                return false;
-            }
-        }
-        if ($lang === 'en') {
-            // Part of distribution.
-            return true;
-        }
-        return file_exists("$this->otherroot/$lang/langconfig.php");
-    }
-
-    /**
-     * Returns localised list of installed translations
-     *
-     * @param bool $returnall return all or just enabled
-     * @return array moodle translation code => localised translation name
-     */
-    public function get_list_of_translations($returnall = false) {
-        global $CFG;
-
-        $languages = array();
-
-        if (!empty($CFG->langcache) and is_readable($this->menucache)) {
-            // Try to re-use the cached list of all available languages.
-            $cachedlist = json_decode(file_get_contents($this->menucache), true);
-
-            if (is_array($cachedlist) and !empty($cachedlist)) {
-                // The cache file is restored correctly.
-
-                if (!$returnall and !empty($this->translist)) {
-                    // Return just enabled translations.
-                    foreach ($cachedlist as $langcode => $langname) {
-                        if (in_array($langcode, $this->translist)) {
-                            $languages[$langcode] = $langname;
-                        }
-                    }
-                    return $languages;
-
-                } else {
-                    // Return all translations.
-                    return $cachedlist;
-                }
-            }
-        }
-
-        // The cached list of languages is not available, let us populate the list.
-        if (!$returnall and !empty($this->translist)) {
-            // Return only some translations.
-            foreach ($this->translist as $lang) {
-                $lang = trim($lang);   // Just trim spaces to be a bit more permissive.
-                if (strstr($lang, '_local') !== false) {
-                    continue;
-                }
-                if (strstr($lang, '_utf8') !== false) {
-                    continue;
-                }
-                if ($lang !== 'en' and !file_exists("$this->otherroot/$lang/langconfig.php")) {
-                    // Some broken or missing lang - can not switch to it anyway.
-                    continue;
-                }
-                $string = $this->load_component_strings('langconfig', $lang);
-                if (!empty($string['thislanguage'])) {
-                    $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
-                }
-                unset($string);
-            }
-
-        } else {
-            // Return all languages available in system.
-            $langdirs = get_list_of_plugins('', '', $this->otherroot);
-
-            $langdirs = array_merge($langdirs, array("$CFG->dirroot/lang/en" => 'en'));
-            // Sort all.
-
-            // Loop through all langs and get info.
-            foreach ($langdirs as $lang) {
-                if (strstr($lang, '_local') !== false) {
-                    continue;
-                }
-                if (strstr($lang, '_utf8') !== false) {
-                    continue;
-                }
-                $string = $this->load_component_strings('langconfig', $lang);
-                if (!empty($string['thislanguage'])) {
-                    $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
-                }
-                unset($string);
-            }
-
-            if (!empty($CFG->langcache) and !empty($this->menucache)) {
-                // Cache the list so that it can be used next time.
-                core_collator::asort($languages);
-                check_dir_exists(dirname($this->menucache), true, true);
-                file_put_contents($this->menucache, json_encode($languages));
-                @chmod($this->menucache, $CFG->filepermissions);
-            }
-        }
-
-        core_collator::asort($languages);
-
-        return $languages;
-    }
-
-    /**
-     * Returns localised list of currencies.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @return array currency code => localised currency name
-     */
-    public function get_list_of_currencies($lang = null) {
-        if ($lang === null) {
-            $lang = current_language();
-        }
-
-        $currencies = $this->load_component_strings('core_currencies', $lang);
-        asort($currencies);
-
-        return $currencies;
-    }
-
-    /**
-     * Clears both in-memory and on-disk caches
-     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
-     */
-    public function reset_caches($phpunitreset = false) {
-        global $CFG;
-        require_once("$CFG->libdir/filelib.php");
-
-        // Clear the on-disk disk with aggregated string files.
-        $this->cache->purge();
-
-        if (!$phpunitreset) {
-            // Increment the revision counter.
-            $langrev = get_config('core', 'langrev');
-            $next = time();
-            if ($langrev !== false and $next <= $langrev and $langrev - $next < 60*60) {
-                // This resolves problems when reset is requested repeatedly within 1s,
-                // the < 1h condition prevents accidental switching to future dates
-                // because we might not recover from it.
-                $next = $langrev+1;
-            }
-            set_config('langrev', $next);
-        }
-
-        // Clear the cache containing the list of available translations
-        // and re-populate it again.
-        fulldelete($this->menucache);
-        $this->get_list_of_translations(true);
-
-        // Lang packs use PHP files in dataroot, it is better to invalidate opcode caches.
-        if (function_exists('opcache_reset')) {
-            opcache_reset();
-        }
-    }
-
-    /**
-     * Returns string revision counter, this is incremented after any string cache reset.
-     * @return int lang string revision counter, -1 if unknown
-     */
-    public function get_revision() {
-        global $CFG;
-        if (isset($CFG->langrev)) {
-            return (int)$CFG->langrev;
-        } else {
-            return -1;
-        }
-    }
-
-    // End of external API.
-
-    /**
-     * Helper method that recursively loads all parents of the given language.
-     *
-     * @see self::get_language_dependencies()
-     * @param string $lang language code
-     * @param array $stack list of parent languages already populated in previous recursive calls
-     * @return array list of all parents of the given language with the $lang itself added as the last element
-     */
-    protected function populate_parent_languages($lang, array $stack = array()) {
-
-        // English does not have a parent language.
-        if ($lang === 'en') {
-            return $stack;
-        }
-
-        // Prevent circular dependency (and thence the infinitive recursion loop).
-        if (in_array($lang, $stack)) {
-            return $stack;
-        }
-
-        // Load language configuration and look for the explicit parent language.
-        if (!file_exists("$this->otherroot/$lang/langconfig.php")) {
-            return $stack;
-        }
-        $string = array();
-        include("$this->otherroot/$lang/langconfig.php");
-
-        if (empty($string['parentlanguage']) or $string['parentlanguage'] === 'en') {
-            unset($string);
-            return array_merge(array($lang), $stack);
-
-        } else {
-            $parentlang = $string['parentlanguage'];
-            unset($string);
-            return $this->populate_parent_languages($parentlang, array_merge(array($lang), $stack));
-        }
-    }
-}
-
-
-/**
- * Fetches minimum strings for installation
- *
- * Minimalistic string fetching implementation
- * that is used in installer before we fetch the wanted
- * language pack from moodle.org lang download site.
- *
- * @package    core
- * @copyright  2010 Petr Skoda (http://skodak.org)
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class install_string_manager implements string_manager {
-    /** @var string location of pre-install packs for all langs */
-    protected $installroot;
-
-    /**
-     * Crate new instance of install string manager
-     */
-    public function __construct() {
-        global $CFG;
-        $this->installroot = "$CFG->dirroot/install/lang";
-    }
-
-    /**
-     * Load all strings for one component
-     * @param string $component The module the string is associated with
-     * @param string $lang
-     * @param bool $disablecache Do not use caches, force fetching the strings from sources
-     * @param bool $disablelocal Do not use customized strings in xx_local language packs
-     * @return array of all string for given component and lang
-     */
-    public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) {
-        // Not needed in installer.
-        return array();
-    }
-
-    /**
-     * Does the string actually exist?
-     *
-     * get_string() is throwing debug warnings, sometimes we do not want them
-     * or we want to display better explanation of the problem.
-     *
-     * Use with care!
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @return boot true if exists
-     */
-    public function string_exists($identifier, $component) {
-        // Simple old style hack ;).
-        $str = get_string($identifier, $component);
-        return (strpos($str, '[[') === false);
-    }
-
-    /**
-     * Get String returns a requested string
-     *
-     * @param string $identifier The identifier of the string to search for
-     * @param string $component The module the string is associated with
-     * @param string|object|array $a An object, string or number that can be used
-     *      within translation strings
-     * @param string $lang moodle translation language, null means use current
-     * @return string The String !
-     */
-    public function get_string($identifier, $component = '', $a = null, $lang = null) {
-        if (!$component) {
-            $component = 'moodle';
-        }
-
-        if ($lang === null) {
-            $lang = current_language();
-        }
-
-        // Get parent lang.
-        $parent = '';
-        if ($lang !== 'en' and $identifier !== 'parentlanguage' and $component !== 'langconfig') {
-            if (file_exists("$this->installroot/$lang/langconfig.php")) {
-                $string = array();
-                include("$this->installroot/$lang/langconfig.php");
-                if (isset($string['parentlanguage'])) {
-                    $parent = $string['parentlanguage'];
-                }
-                unset($string);
-            }
-        }
-
-        // Include en string first.
-        if (!file_exists("$this->installroot/en/$component.php")) {
-            return "[[$identifier]]";
-        }
-        $string = array();
-        include("$this->installroot/en/$component.php");
-
-        // Now override en with parent if defined.
-        if ($parent and $parent !== 'en' and file_exists("$this->installroot/$parent/$component.php")) {
-            include("$this->installroot/$parent/$component.php");
-        }
-
-        // Finally override with requested language.
-        if ($lang !== 'en' and file_exists("$this->installroot/$lang/$component.php")) {
-            include("$this->installroot/$lang/$component.php");
-        }
-
-        if (!isset($string[$identifier])) {
-            return "[[$identifier]]";
-        }
-
-        $string = $string[$identifier];
-
-        if ($a !== null) {
-            if (is_object($a) or is_array($a)) {
-                $a = (array)$a;
-                $search = array();
-                $replace = array();
-                foreach ($a as $key => $value) {
-                    if (is_int($key)) {
-                        // We do not support numeric keys - sorry!
-                        continue;
-                    }
-                    $search[]  = '{$a->'.$key.'}';
-                    $replace[] = (string)$value;
-                }
-                if ($search) {
-                    $string = str_replace($search, $replace, $string);
-                }
-            } else {
-                $string = str_replace('{$a}', (string)$a, $string);
-            }
-        }
-
-        return $string;
-    }
-
-    /**
-     * Returns a localised list of all country names, sorted by country keys.
-     *
-     * @param bool $returnall return all or just enabled
-     * @param string $lang moodle translation language, null means use current
-     * @return array two-letter country code => translated name.
-     */
-    public function get_list_of_countries($returnall = false, $lang = null) {
-        // Not used in installer.
-        return array();
-    }
-
-    /**
-     * Returns a localised list of languages, sorted by code keys.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @param string $standard language list standard
-     *                     iso6392: three-letter language code (ISO 639-2/T) => translated name.
-     * @return array language code => translated name
-     */
-    public function get_list_of_languages($lang = null, $standard = 'iso6392') {
-        // Not used in installer.
-        return array();
-    }
-
-    /**
-     * Checks if the translation exists for the language
-     *
-     * @param string $lang moodle translation language code
-     * @param bool $includeall include also disabled translations
-     * @return bool true if exists
-     */
-    public function translation_exists($lang, $includeall = true) {
-        return file_exists($this->installroot.'/'.$lang.'/langconfig.php');
-    }
-
-    /**
-     * Returns localised list of installed translations
-     * @param bool $returnall return all or just enabled
-     * @return array moodle translation code => localised translation name
-     */
-    public function get_list_of_translations($returnall = false) {
-        // Return all is ignored here - we need to know all langs in installer.
-        $languages = array();
-        // Get raw list of lang directories.
-        $langdirs = get_list_of_plugins('install/lang');
-        asort($langdirs);
-        // Get some info from each lang.
-        foreach ($langdirs as $lang) {
-            if (file_exists($this->installroot.'/'.$lang.'/langconfig.php')) {
-                $string = array();
-                include($this->installroot.'/'.$lang.'/langconfig.php');
-                if (!empty($string['thislanguage'])) {
-                    $languages[$lang] = $string['thislanguage'].' ('.$lang.')';
-                }
-            }
-        }
-        // Return array.
-        return $languages;
-    }
-
-    /**
-     * Returns localised list of currencies.
-     *
-     * @param string $lang moodle translation language, null means use current
-     * @return array currency code => localised currency name
-     */
-    public function get_list_of_currencies($lang = null) {
-        // Not used in installer.
-        return array();
-    }
-
-    /**
-     * This implementation does not use any caches.
-     *
-     * @param bool $phpunitreset true means called from our PHPUnit integration test reset
-     */
-    public function reset_caches($phpunitreset = false) {
-        // Nothing to do.
-    }
-
-    /**
-     * Returns string revision counter, this is incremented after any string cache reset.
-     * @return int lang string revision counter, -1 if unknown
-     */
-    public function get_revision() {
-        return -1;
-    }
-}
-
-
 /**
  * Returns a localized string.
  *
@@ -8124,34 +7163,6 @@ function is_valid_plugin_name($name) {
     return core_component::is_valid_plugin_name('tool', $name);
 }
 
-/**
- * Get a list of all the plugins of a given type that contain a particular file.
- *
- * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
- * @param string $file the name of file that must be present in the plugin.
- *      (e.g. 'view.php', 'db/install.xml').
- * @param bool $include if true (default false), the file will be include_once-ed if found.
- * @return array with plugin name as keys (e.g. 'forum', 'courselist') and the path
- *      to the file relative to dirroot as value (e.g. "$CFG->dirroot/mod/forum/view.php").
- */
-function get_plugin_list_with_file($plugintype, $file, $include = false) {
-    global $CFG; // Necessary in case it is referenced by include()d PHP scripts.
-
-    $plugins = array();
-
-    foreach (core_component::get_plugin_list($plugintype) as $plugin => $dir) {
-        $path = $dir . '/' . $file;
-        if (file_exists($path)) {
-            if ($include) {
-                include_once($path);
-            }
-            $plugins[$plugin] = $path;
-        }
-    }
-
-    return $plugins;
-}
-
 /**
  * Get a list of all the plugins of a given type that define a certain API function
  * in a certain file. The plugin component names and function names are returned.
@@ -8167,7 +7178,8 @@ function get_plugin_list_with_file($plugintype, $file, $include = false) {
  */
 function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php') {
     $pluginfunctions = array();
-    foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
+    $pluginswithfile = core_component::get_plugin_list_with_file($plugintype, $file, true);
+    foreach ($pluginswithfile as $plugin => $notused) {
         $fullfunction = $plugintype . '_' . $plugin . '_' . $function;
 
         if (function_exists($fullfunction)) {
@@ -8834,111 +7846,31 @@ function get_browser_version_classes() {
 }
 
 /**
- * Determine if moodle installation requires update
+ * Determine if moodle installation requires update.
  *
- * Checks version numbers of main code and all modules to see
- * if there are any mismatches
+ * Checks version numbers of main code and all plugins to see
+ * if there are any mismatches.
  *
  * @return bool
  */
 function moodle_needs_upgrading() {
-    global $CFG, $DB;
+    global $CFG;
 
     if (empty($CFG->version)) {
         return true;
     }
 
-    // We have to purge plugin related caches now to be sure we have fresh data
-    // and new plugins can be detected.
-    cache::make('core', 'plugininfo_base')->purge();
-    cache::make('core', 'plugininfo_mod')->purge();
-    cache::make('core', 'plugininfo_block')->purge();
-    cache::make('core', 'plugininfo_filter')->purge();
-    cache::make('core', 'plugininfo_repository')->purge();
-    cache::make('core', 'plugininfo_portfolio')->purge();
+    // There is no need to purge plugininfo caches here because
+    // these caches are not used during upgrade and they are purged after
+    // every upgrade.
 
-    // Check the main version first.
-    $version = null;
-    include($CFG->dirroot.'/version.php');  // Defines $version and upgrades.
-    if ($version > $CFG->version) {
+    if (empty($CFG->allversionshash)) {
         return true;
     }
 
-    // Modules.
-    $mods = core_component::get_plugin_list('mod');
-    $installed = $DB->get_records('modules', array(), '', 'name, version');
-    foreach ($mods as $mod => $fullmod) {
-        if ($mod === 'NEWMODULE') {   // Someone has unzipped the template, ignore it.
-            continue;
-        }
-        $module = new stdClass();
-        $plugin = new stdClass();
-        if (!is_readable($fullmod.'/version.php')) {
-            continue;
-        }
-        include($fullmod.'/version.php');  // Defines $module with version etc.
-        if (!isset($module->version) and isset($plugin->version)) {
-            $module = $plugin;
-        }
-        if (empty($installed[$mod])) {
-            return true;
-        } else if ($module->version > $installed[$mod]->version) {
-            return true;
-        }
-    }
-    unset($installed);
+    $hash = core_component::get_all_versions_hash();
 
-    // Blocks.
-    $blocks = core_component::get_plugin_list('block');
-    $installed = $DB->get_records('block', array(), '', 'name, version');
-    require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
-    foreach ($blocks as $blockname => $fullblock) {
-        if ($blockname === 'NEWBLOCK') {   // Someone has unzipped the template, ignore it.
-            continue;
-        }
-        if (!is_readable($fullblock.'/version.php')) {
-            continue;
-        }
-        $plugin = new stdClass();
-        $plugin->version = null;
-        include($fullblock.'/version.php');
-        if (empty($installed[$blockname])) {
-            return true;
-        } else if ($plugin->version > $installed[$blockname]->version) {
-            return true;
-        }
-    }
-    unset($installed);
-
-    // Now the rest of plugins.
-    $plugintypes = core_component::get_plugin_types();
-    unset($plugintypes['mod']);
-    unset($plugintypes['block']);
-
-    $versions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin, value');
-    foreach ($plugintypes as $type => $unused) {
-        $plugs = core_component::get_plugin_list($type);
-        foreach ($plugs as $plug => $fullplug) {
-            $component = $type.'_'.$plug;
-            if (!is_readable($fullplug.'/version.php')) {
-                continue;
-            }
-            $plugin = new stdClass();
-            include($fullplug.'/version.php');  // Defines $plugin with version etc.
-            if (array_key_exists($component, $versions)) {
-                $installedversion = $versions[$component];
-            } else {
-                $installedversion = get_config($component, 'version');
-            }
-            if (empty($installedversion)) { // New installation.
-                return true;
-            } else if ($installedversion < $plugin->version) { // Upgrade.
-                return true;
-            }
-        }
-    }
-
-    return false;
+    return ($hash !== $CFG->allversionshash);
 }
 
 /**
index 42d309d..1fa0f0c 100644 (file)
@@ -1250,7 +1250,7 @@ class global_navigation extends navigation_node {
         }
 
         // Give the local plugins a chance to include some navigation if they want.
-        foreach (get_plugin_list_with_file('local', 'lib.php', true) as $plugin => $file) {
+        foreach (core_component::get_plugin_list_with_file('local', 'lib.php', true) as $plugin => $file) {
             $function = "local_{$plugin}_extends_navigation";
             $oldfunction = "{$plugin}_extends_navigation";
             if (function_exists($function)) {
@@ -4049,7 +4049,7 @@ class settings_navigation extends navigation_node {
         // Portfolio
         if ($currentuser && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', $systemcontext)) {
             require_once($CFG->libdir . '/portfoliolib.php');
-            if (portfolio_instances(true, false)) {
+            if (portfolio_has_visible_instances()) {
                 $portfolio = $usersetting->add(get_string('portfolios', 'portfolio'), null, self::TYPE_SETTING);
 
                 $url = new moodle_url('/user/portfolio.php', array('courseid'=>$course->id));
index 771daec..be2dfea 100644 (file)
@@ -499,7 +499,10 @@ class core_renderer extends renderer_base {
                 $link= '<a title="' . $title . '" href="' . $url . '">' . $txt . '</a>';
                 $output .= '<div class="profilingfooter">' . $link . '</div>';
             }
-            $output .= '<div class="purgecaches"><a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/purgecaches.php?confirm=1&amp;sesskey='.sesskey().'">'.get_string('purgecaches', 'admin').'</a></div>';
+            $purgeurl = new moodle_url('/admin/purgecaches.php', array('confirm' => 1,
+                'sesskey' => sesskey(), 'returnurl' => $this->page->url->out_as_local_url(false)));
+            $output .= '<div class="purgecaches">' .
+                    html_writer::link($purgeurl, get_string('purgecaches', 'admin')) . '</div>';
         }
         if (!empty($CFG->debugvalidators)) {
             // NOTE: this is not a nice hack, $PAGE->url is not always accurate and $FULLME neither, it is not a bug if it fails. --skodak
index d7cf4c0..cc87af4 100644 (file)
@@ -300,6 +300,11 @@ class page_requirements_manager {
             if (!empty($page->cm->id)) {
                 $params['cmid'] = $page->cm->id;
             }
+            // Strings for drag and drop.
+            $this->strings_for_js(array('movecontent',
+                                        'aftercontent',
+                                        'emptydragdropregion'),
+                                  'moodle');
             $page->requires->yui_module('moodle-core-blocks', 'M.core_blocks.init_dragdrop', array($params), null, true);
         }
     }
@@ -658,7 +663,7 @@ class page_requirements_manager {
                                                         array('nofilesavailable', 'repository'), array('norepositoriesavailable', 'repository'),
                                                         array('fileexistsdialogheader', 'repository'), array('fileexistsdialog_editor', 'repository'),
                                                         array('fileexistsdialog_filemanager', 'repository'), array('renameto', 'repository'),
-                                                        array('referencesexist', 'repository')
+                                                        array('referencesexist', 'repository'), array('edit', 'moodle')
                                                     ));
                     break;
                 case 'core_comment':
index e5e130a..552e78a 100644 (file)
@@ -72,7 +72,7 @@ class phpunit_util extends testing_util {
             initialise_cfg();
             return;
         }
-        if ($dbhash !== self::get_version_hash()) {
+        if ($dbhash !== core_component::get_all_versions_hash()) {
             // do not set CFG - the only way forward is to drop and reinstall
             return;
         }
index be29c32..228080f 100644 (file)
@@ -533,6 +533,16 @@ function portfolio_instances($visibleonly=true, $useronly=true) {
     return $instances;
 }
 
+/**
+ * Return whether there are visible instances in portfolio.
+ *
+ * @return bool true when there are some visible instances.
+ */
+function portfolio_has_visible_instances() {
+    global $DB;
+    return $DB->record_exists('portfolio_instance', array('visible' => 1));
+}
+
 /**
  * Supported formats currently in use.
  * Canonical place for a list of all formats
index a74a1d3..d15b3a4 100644 (file)
@@ -529,8 +529,7 @@ function get_exception_info($ex) {
     // Remove some absolute paths from message and debugging info.
     $searches = array();
     $replaces = array();
-    $cfgnames = array('tempdir', 'cachedir', 'localcachedir', 'themedir',
-        'langmenucachefile', 'langcacheroot', 'dataroot', 'dirroot');
+    $cfgnames = array('tempdir', 'cachedir', 'localcachedir', 'themedir', 'dataroot', 'dirroot');
     foreach ($cfgnames as $cfgname) {
         if (property_exists($CFG, $cfgname)) {
             $searches[] = $CFG->$cfgname;
@@ -1026,7 +1025,14 @@ function raise_memory_limit($newlimit) {
         }
 
     } else if ($newlimit == MEMORY_HUGE) {
+        // MEMORY_HUGE uses 2G or MEMORY_EXTRA, whichever is bigger.
         $newlimit = get_real_size('2G');
+        if (!empty($CFG->extramemorylimit)) {
+            $extra = get_real_size($CFG->extramemorylimit);
+            if ($extra > $newlimit) {
+                $newlimit = $extra;
+            }
+        }
 
     } else {
         $newlimit = get_real_size($newlimit);
index 5cef5ea..fe30ecd 100644 (file)
@@ -137,7 +137,7 @@ abstract class testing_util {
             return false;
         }
 
-        $hash = self::get_version_hash();
+        $hash = core_component::get_all_versions_hash();
         $oldhash = file_get_contents($datarootpath . '/versionshash.txt');
 
         if ($hash !== $oldhash) {
@@ -195,7 +195,7 @@ abstract class testing_util {
         global $CFG;
 
         $framework = self::get_framework();
-        $hash = self::get_version_hash();
+        $hash = core_component::get_all_versions_hash();
 
         // add test db flag
         set_config($framework . 'test', $hash);
@@ -669,54 +669,4 @@ abstract class testing_util {
             }
         }
     }
-
-    /**
-     * Calculate unique version hash for all plugins and core.
-     * @static
-     * @return string sha1 hash
-     */
-    public static function get_version_hash() {
-        global $CFG;
-
-        if (self::$versionhash) {
-            return self::$versionhash;
-        }
-
-        $versions = array();
-
-        // main version first
-        $version = null;
-        include($CFG->dirroot.'/version.php');
-        $versions['core'] = $version;
-
-        // modules
-        $mods = core_component::get_plugin_list('mod');
-        ksort($mods);
-        foreach ($mods as $mod => $fullmod) {
-            $module = new stdClass();
-            $module->version = null;
-            include($fullmod.'/version.php');
-            $versions[$mod] = $module->version;
-        }
-
-        // now the rest of plugins
-        $plugintypes = core_component::get_plugin_types();
-        unset($plugintypes['mod']);
-        ksort($plugintypes);
-        foreach ($plugintypes as $type => $unused) {
-            $plugs = core_component::get_plugin_list($type);
-            ksort($plugs);
-            foreach ($plugs as $plug => $fullplug) {
-                $plugin = new stdClass();
-                $plugin->version = null;
-                @include($fullplug.'/version.php');
-                $versions[$plug] = $plugin->version;
-            }
-        }
-
-        self::$versionhash = sha1(serialize($versions));
-
-        return self::$versionhash;
-    }
-
 }
index 6dee02c..d3c3c1e 100644 (file)
@@ -398,6 +398,27 @@ class core_accesslib_testcase extends advanced_testcase {
         $this->assertTrue($result);
         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
         $this->assertEmpty($permission);
+
+        // Test event trigger.
+        $rolecapabilityevent = \core\event\role_capabilities_updated::create(array('context' => $syscontext,
+                                                                                  'objectid' => $student->id,
+                                                                                  'other' => array('name' => $student->shortname)
+                                                                                 ));
+        $expectedlegacylog = array(SITEID, 'role', 'view', 'admin/roles/define.php?action=view&roleid=' . $student->id,
+                            $student->shortname, '', $user->id);
+        $rolecapabilityevent->set_legacy_logdata($expectedlegacylog);
+        $rolecapabilityevent->add_record_snapshot('role', $student);
+
+        $sink = $this->redirectEvents();
+        $rolecapabilityevent->trigger();
+        $events = $sink->get_events();
+        $sink->close();
+        $event = array_pop($events);
+
+        $this->assertInstanceOf('\core\event\role_capabilities_updated', $event);
+        $expectedurl = new moodle_url('admin/roles/define.php', array('action' => 'view', 'roleid' => $student->id));
+        $this->assertEquals($expectedurl, $event->get_url());
+        $this->assertEventLegacyLogData($expectedlegacylog, $event);
     }
 
     /**
@@ -487,13 +508,18 @@ class core_accesslib_testcase extends advanced_testcase {
         $this->assertSame('', $event->other['component']);
         $this->assertEquals(0, $event->other['itemid']);
         $this->assertSame('role_assigned', $event::get_legacy_eventname());
+        $roles = get_all_roles();
+        $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
+        $expectedlegacylog = array($course->id, 'role', 'assign',
+            'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
+        $this->assertEventLegacyLogData($expectedlegacylog, $event);
     }
 
     /**
      * Test role unassigning.
      */
     public function test_role_unassign() {
-        global $DB;
+        global $DB, $USER;
 
         $this->resetAfterTest();
 
@@ -530,6 +556,11 @@ class core_accesslib_testcase extends advanced_testcase {
         $this->assertCount(3, $event->other);
         $this->assertSame('', $event->other['component']);
         $this->assertEquals(0, $event->other['itemid']);
+        $roles = get_all_roles();
+        $rolenames = role_fix_names($roles, $context,