Merge branch 'MDL-35892-master' of git://github.com/FMCorz/moodle
authorSam Hemelryk <sam@moodle.com>
Tue, 16 Oct 2012 02:31:56 +0000 (10:31 +0800)
committerSam Hemelryk <sam@moodle.com>
Tue, 16 Oct 2012 02:31:56 +0000 (10:31 +0800)
113 files changed:
admin/settings/plugins.php
admin/tool/generator/locallib.php
backup/util/checks/tests/checks_test.php
blocks/navigation/block_navigation.php
blocks/site_main_menu/block_site_main_menu.php
blocks/social_activities/block_social_activities.php
cache/classes/config.php
cache/classes/loaders.php
cache/locallib.php
cohort/lib.php
comment/locallib.php
course/dnduploadlib.php
course/edit.php
course/externallib.php
course/format/renderer.php
course/format/topics/format.php
course/format/upgrade.txt
course/format/weeks/format.php
course/lib.php
course/mod.php
course/modedit.php
course/recent.php
course/recent_form.php
course/resources.php
course/rest.php
course/tests/courselib_test.php
course/view.php
enrol/imsenterprise/lib.php
enrol/imsenterprise/settings.php
enrol/manual/cli/sync.php
enrol/manual/lib.php
enrol/manual/tests/lib_test.php
index.php
lang/en/admin.php
lib/completionlib.php
lib/deprecatedlib.php
lib/editor/tinymce/adminlib.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/plugins/spellchecker/lib.php
lib/editor/tinymce/settings.php
lib/form/text.php
lib/messagelib.php
lib/modinfolib.php
lib/navigationlib.php
lib/phpunit/classes/advanced_testcase.php
lib/phpunit/classes/data_generator.php
lib/phpunit/classes/message_sink.php [new file with mode: 0644]
lib/phpunit/classes/module_generator.php
lib/phpunit/classes/util.php
lib/phpunit/lib.php
lib/phpunit/tests/advanced_test.php
lib/phpunit/tests/generator_test.php
lib/pluginlib.php
lib/tests/conditionlib_test.php
lib/upgrade.txt
mod/assign/adminlib.php
mod/assign/db/events.php
mod/assign/gradeform.php
mod/assign/locallib.php
mod/assign/settings.php
mod/assign/upgradelib.php
mod/assign/version.php
mod/assignment/index.php
mod/assignment/type/online/all.php
mod/book/index.php
mod/book/view.php
mod/chat/index.php
mod/choice/index.php
mod/data/field/date/field.class.php
mod/data/index.php
mod/data/lang/en/data.php
mod/feedback/index.php
mod/folder/index.php
mod/forum/discuss.php
mod/forum/index.php
mod/forum/lib.php
mod/glossary/import.php
mod/glossary/index.php
mod/imscp/index.php
mod/lesson/importppt.php [deleted file]
mod/lesson/importpptlib.php [deleted file]
mod/lesson/index.php
mod/lesson/lang/en/lesson.php
mod/lesson/renderer.php
mod/lesson/version.php
mod/lti/index.php
mod/page/index.php
mod/quiz/index.php
mod/quiz/settings.php
mod/resource/index.php
mod/scorm/datamodel.php
mod/scorm/datamodels/aicc.js.php
mod/scorm/datamodels/callback.js.php
mod/scorm/datamodels/scorm_12.js.php
mod/scorm/datamodels/scorm_13.js.php
mod/scorm/datamodels/scorm_13lib.php
mod/scorm/datamodels/scormlib.php
mod/scorm/datamodels/sequencinghandler.php [new file with mode: 0644]
mod/scorm/datamodels/sequencinglib.php
mod/scorm/index.php
mod/scorm/loaddatamodel.php
mod/scorm/locallib.php
mod/scorm/module.js
mod/scorm/player.php
mod/survey/index.php
mod/url/index.php
mod/wiki/index.php
mod/workshop/index.php
question/type/numerical/edit_numerical_form.php
report/log/locallib.php
report/outline/index.php
report/outline/user.php
version.php

index c2b2b22..29d0665 100644 (file)
@@ -5,66 +5,34 @@
  */
 
 if ($hassiteconfig) {
+    require_once("$CFG->libdir/pluginlib.php");
+    $allplugins = plugin_manager::instance()->get_plugins();
+
     $ADMIN->add('modules', new admin_page_pluginsoverview());
+
+    // activity modules
     $ADMIN->add('modules', new admin_category('modsettings', new lang_string('activitymodules')));
     $ADMIN->add('modsettings', new admin_page_managemods());
-    $modules = $DB->get_records('modules', array(), "name ASC");
-    foreach ($modules as $module) {
-        $modulename = $module->name;
-        if (!file_exists("$CFG->dirroot/mod/$modulename/lib.php")) {
-            continue;
-        }
-        $strmodulename = new lang_string('modulename', 'mod_'.$modulename);
-        if (file_exists($CFG->dirroot.'/mod/'.$modulename.'/settings.php')) {
-            // do not show disabled modules in tree, keep only settings link on manage page
-            $settings = new admin_settingpage('modsetting'.$modulename, $strmodulename, 'moodle/site:config', !$module->visible);
-            include($CFG->dirroot.'/mod/'.$modulename.'/settings.php');
-            if ($settings) {
-                $ADMIN->add('modsettings', $settings);
-            }
-        }
+    foreach ($allplugins['mod'] as $module) {
+        $module->load_settings($ADMIN, 'modsettings', $hassiteconfig);
     }
 
     // hidden script for converting journals to online assignments (or something like that) linked from elsewhere
     $ADMIN->add('modsettings', new admin_externalpage('oacleanup', 'Online Assignment Cleanup', $CFG->wwwroot.'/'.$CFG->admin.'/oacleanup.php', 'moodle/site:config', true));
 
+    // blocks
     $ADMIN->add('modules', new admin_category('blocksettings', new lang_string('blocks')));
     $ADMIN->add('blocksettings', new admin_page_manageblocks());
-    $blocks = $DB->get_records('block', array(), "name ASC");
-    foreach ($blocks as $block) {
-        $blockname = $block->name;
-        if (!file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
-            continue;
-        }
-        $strblockname = new lang_string('pluginname', 'block_'.$blockname);
-        if (file_exists($CFG->dirroot.'/blocks/'.$blockname.'/settings.php')) {
-            $settings = new admin_settingpage('blocksetting'.$blockname, $strblockname, 'moodle/site:config', !$block->visible);
-            include($CFG->dirroot.'/blocks/'.$blockname.'/settings.php');
-            if ($settings) {
-                $ADMIN->add('blocksettings', $settings);
-            }
-        }
+    foreach ($allplugins['block'] as $block) {
+        $block->load_settings($ADMIN, 'blocksettings', $hassiteconfig);
     }
 
     // message outputs
     $ADMIN->add('modules', new admin_category('messageoutputs', new lang_string('messageoutputs', 'message')));
     $ADMIN->add('messageoutputs', new admin_page_managemessageoutputs());
     $ADMIN->add('messageoutputs', new admin_page_defaultmessageoutputs());
-    require_once($CFG->dirroot.'/message/lib.php');
-    $processors = get_message_processors();
-    foreach ($processors as $processor) {
-        $processorname = $processor->name;
-        if (!$processor->available) {
-            continue;
-        }
-        if ($processor->hassettings) {
-            $strprocessorname = new lang_string('pluginname', 'message_'.$processorname);
-            $settings = new admin_settingpage('messagesetting'.$processorname, $strprocessorname, 'moodle/site:config', !$processor->enabled);
-            include($CFG->dirroot.'/message/output/'.$processor->name.'/settings.php');
-            if ($settings) {
-                $ADMIN->add('messageoutputs', $settings);
-            }
-        }
+    foreach ($allplugins['message'] as $processor) {
+        $processor->load_settings($ADMIN, 'messageoutputs', $hassiteconfig);
     }
 
     // authentication plugins
@@ -92,72 +60,27 @@ if ($hassiteconfig) {
     $temp->add(new admin_setting_configtext('recaptchaprivatekey', new lang_string('recaptchaprivatekey', 'admin'), new lang_string('configrecaptchaprivatekey', 'admin'), '', PARAM_NOTAGS));
     $ADMIN->add('authsettings', $temp);
 
-
-    $auths = get_plugin_list('auth');
-    $authsenabled = get_enabled_auth_plugins();
-    foreach ($auths as $authname => $authdir) {
-        $strauthname = new lang_string('pluginname', "auth_{$authname}");
-        // do not show disabled auths in tree, keep only settings link on manage page
-        $enabled = in_array($authname, $authsenabled);
-        if (file_exists($authdir.'/settings.php')) {
-            // TODO: finish implementation of common settings - locking, etc.
-            $settings = new admin_settingpage('authsetting'.$authname, $strauthname, 'moodle/site:config', !$enabled);
-            include($authdir.'/settings.php');
-            if ($settings) {
-                $ADMIN->add('authsettings', $settings);
-            }
-
-        } else {
-            $ADMIN->add('authsettings', new admin_externalpage('authsetting'.$authname, $strauthname, "$CFG->wwwroot/$CFG->admin/auth_config.php?auth=$authname", 'moodle/site:config', !$enabled));
-        }
+    foreach ($allplugins['auth'] as $auth) {
+        $auth->load_settings($ADMIN, 'authsettings', $hassiteconfig);
     }
 
-
     // Enrolment plugins
     $ADMIN->add('modules', new admin_category('enrolments', new lang_string('enrolments', 'enrol')));
     $temp = new admin_settingpage('manageenrols', new lang_string('manageenrols', 'enrol'));
     $temp->add(new admin_setting_manageenrols());
-    if (empty($CFG->enrol_plugins_enabled)) {
-        $enabled = array();
-    } else {
-        $enabled = explode(',', $CFG->enrol_plugins_enabled);
-    }
-    $enrols = get_plugin_list('enrol');
     $ADMIN->add('enrolments', $temp);
-    foreach($enrols as $enrol=>$enrolpath) {
-        if (!file_exists("$enrolpath/settings.php")) {
-            continue;
-        }
-
-        $settings = new admin_settingpage('enrolsettings'.$enrol, new lang_string('pluginname', 'enrol_'.$enrol), 'moodle/site:config', !in_array($enrol, $enabled));
-        // settings.php may create a subcategory or unset the settings completely
-        include("$enrolpath/settings.php");
-        if ($settings) {
-            $ADMIN->add('enrolments', $settings);
-        }
-
+    foreach($allplugins['enrol'] as $enrol) {
+        $enrol->load_settings($ADMIN, 'enrolments', $hassiteconfig);
     }
-    unset($enabled);
-    unset($enrols);
 
 
 /// Editor plugins
     $ADMIN->add('modules', new admin_category('editorsettings', new lang_string('editors', 'editor')));
     $temp = new admin_settingpage('manageeditors', new lang_string('editorsettings', 'editor'));
     $temp->add(new admin_setting_manageeditors());
-    $htmleditors = editors_get_available();
     $ADMIN->add('editorsettings', $temp);
-
-    $editors_available = editors_get_available();
-    foreach ($editors_available as $editor=>$editorstr) {
-        if (file_exists($CFG->dirroot . '/lib/editor/'.$editor.'/settings.php')) {
-            $settings = new admin_settingpage('editorsettings'.$editor, new lang_string('pluginname', 'editor_'.$editor), 'moodle/site:config');
-            // settings.php may create a subcategory or unset the settings completely
-            include($CFG->dirroot . '/lib/editor/'.$editor.'/settings.php');
-            if ($settings) {
-                $ADMIN->add('editorsettings', $settings);
-            }
-        }
+    foreach ($allplugins['editor'] as $editor) {
+        $editor->load_settings($ADMIN, 'editorsettings', $hassiteconfig);
     }
 
 /// License types
@@ -218,17 +141,8 @@ if ($hassiteconfig) {
     }
     $ADMIN->add('filtersettings', $temp);
 
-    $activefilters = filter_get_globally_enabled();
-    $filternames = filter_get_all_installed();
-    foreach ($filternames as $filterpath => $strfiltername) {
-        if (file_exists("$CFG->dirroot/$filterpath/filtersettings.php")) {
-            $settings = new admin_settingpage('filtersetting'.str_replace('/', '', $filterpath),
-                    $strfiltername, 'moodle/site:config', !isset($activefilters[$filterpath]));
-            include("$CFG->dirroot/$filterpath/filtersettings.php");
-            if ($settings) {
-                $ADMIN->add('filtersettings', $settings);
-            }
-        }
+    foreach ($allplugins['filter'] as $filter) {
+        $filter->load_settings($ADMIN, 'filtersettings', $hassiteconfig);
     }
 
 
@@ -325,19 +239,9 @@ if ($hassiteconfig) {
     $ADMIN->add('repositorysettings', new admin_externalpage('repositoryinstanceedit',
         new lang_string('editrepositoryinstance', 'repository'), $url, 'moodle/site:config', true),
         '', $url);
-    foreach (repository::get_types() as $repositorytype) {
-      //display setup page for plugins with: general options or multiple instances (e.g. has instance config)
-      $typeoptionnames = repository::static_function($repositorytype->get_typename(), 'get_type_option_names');
-      $instanceoptionnames = repository::static_function($repositorytype->get_typename(), 'get_instance_option_names');
-      if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
-
-          $params = array('action'=>'edit', 'sesskey'=>sesskey(), 'repos'=>$repositorytype->get_typename());
-          $settingsurl = new moodle_url("/$CFG->admin/repository.php", $params);
-          $repositoryexternalpage = new admin_externalpage('repositorysettings'.$repositorytype->get_typename(), $repositorytype->get_readablename(), $settingsurl);
-          $ADMIN->add('repositorysettings', $repositoryexternalpage);
-      }
+    foreach ($allplugins['repository'] as $repositorytype) {
+        $repositorytype->load_settings($ADMIN, 'repositorysettings', $hassiteconfig);
     }
-}
 
 /// Web services
     $ADMIN->add('modules', new admin_category('webservicesettings', new lang_string('webservices', 'webservice')));
@@ -375,17 +279,8 @@ if ($hassiteconfig) {
                         'admin'), new lang_string('configenablewsdocumentation', 'admin', $wsdoclink), false));
     $ADMIN->add('webservicesettings', $temp);
     /// links to protocol pages
-    $webservices_available = get_plugin_list('webservice');
-    $active_webservices = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
-    foreach ($webservices_available as $webservice => $location) {
-        if (file_exists("$location/settings.php")) {
-            $name = new lang_string('pluginname', 'webservice_'.$webservice);
-            $settings = new admin_settingpage('webservicesetting'.$webservice, $name, 'moodle/site:config', !in_array($webservice, $active_webservices) or empty($CFG->enablewebservices));
-            include("$location/settings.php");
-            if ($settings) {
-                $ADMIN->add('webservicesettings', $settings);
-            }
-        }
+    foreach ($allplugins['webservice'] as $webservice) {
+        $webservice->load_settings($ADMIN, 'webservicesettings', $hassiteconfig);
     }
     /// manage token page link
     $ADMIN->add('webservicesettings', new admin_externalpage('addwebservicetoken', new lang_string('managetokens', 'webservice'), "$CFG->wwwroot/$CFG->admin/webservice/tokens.php", 'moodle/site:config', true));
@@ -395,6 +290,7 @@ if ($hassiteconfig) {
         $temp->add(new admin_setting_heading('webservicesaredisabled', '', new lang_string('disabledwarning', 'webservice')));
     }
     $ADMIN->add('webservicesettings', $temp);
+}
 
 // Question type settings
 if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) {
@@ -405,17 +301,8 @@ if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext))
     // Question type settings.
     $ADMIN->add('modules', new admin_category('qtypesettings', new lang_string('questiontypes', 'admin')));
     $ADMIN->add('qtypesettings', new admin_page_manageqtypes());
-    $qtypes = get_plugin_list('qtype');
-    foreach ($qtypes as $qtype => $path) {
-        $settingsfile = $path . '/settings.php';
-        if (file_exists($settingsfile)) {
-            $settings = new admin_settingpage('qtypesetting' . $qtype,
-                    new lang_string('pluginname', 'qtype_' . $qtype), 'moodle/question:config');
-            include($settingsfile);
-            if ($settings) {
-                $ADMIN->add('qtypesettings', $settings);
-            }
-        }
+    foreach ($allplugins['qtype'] as $qtype) {
+        $qtype->load_settings($ADMIN, 'qtypesettings', $hassiteconfig);
     }
 }
 
@@ -425,10 +312,8 @@ if ($hassiteconfig && !empty($CFG->enableplagiarism)) {
     $ADMIN->add('plagiarism', new admin_externalpage('manageplagiarismplugins', new lang_string('manageplagiarism', 'plagiarism'),
         $CFG->wwwroot . '/' . $CFG->admin . '/plagiarism.php'));
 
-    foreach (get_plugin_list('plagiarism') as $plugin => $plugindir) {
-        if (file_exists($plugindir.'/settings.php')) {
-            $ADMIN->add('plagiarism', new admin_externalpage('plagiarism'.$plugin, new lang_string($plugin, 'plagiarism_'.$plugin), "$CFG->wwwroot/plagiarism/$plugin/settings.php", 'moodle/site:config'));
-        }
+    foreach ($allplugins['plagiarism'] as $plugin) {
+        $plugin->load_settings($ADMIN, 'plagiarism', $hassiteconfig);
     }
 }
 $ADMIN->add('reports', new admin_externalpage('comments', new lang_string('comments'), $CFG->wwwroot.'/comment/', 'moodle/site:viewreports'));
@@ -515,6 +400,8 @@ if ($hassiteconfig) {
                                                         $CFG->wwwroot . '/' . $CFG->admin . '/localplugins.php'));
 }
 
+// extend settings for each local plugin. Note that their settings may be in any part of the
+// settings tree and may be visible not only for administrators. We can not use $allplugins here
 foreach (get_plugin_list('local') as $plugin => $plugindir) {
     $settings_path = "$plugindir/settings.php";
     if (file_exists($settings_path)) {
index 169f643..117e1ac 100644 (file)
@@ -556,7 +556,7 @@ class generator {
                         $module->name = ucfirst($moduledata->name) . ' ' . $moduledata->count++;
 
                         $module->course = $courseid;
-                        $module->section = $i;
+                        $module->section = 0;
                         $module->module = $moduledata->id;
                         $module->modulename = $moduledata->name;
                         $module->add = $moduledata->name;
@@ -564,10 +564,7 @@ class generator {
                         $module->coursemodule = '';
                         $add_instance_function = $moduledata->name . '_add_instance';
 
-                        $section = get_course_section($i, $courseid);
-                        $module->section = $section->id;
                         $module->coursemodule = add_course_module($module);
-                        $module->section = $i;
 
                         if (function_exists($add_instance_function)) {
                             $this->verbose("Calling module function $add_instance_function");
@@ -580,13 +577,12 @@ class generator {
                             }
                         }
 
-                        add_mod_to_section($module);
+                        $module->section = course_add_cm_to_section($courseid, $module->coursemodule, $i);
 
                         $module->cmidnumber = set_coursemodule_idnumber($module->coursemodule, '');
 
                         $this->verbose("A $moduledata->name module was added to section $i (id $module->section) "
                             ."of course $courseid.");
-                        rebuild_course_cache($courseid);
 
                         $module_instance = $DB->get_field('course_modules', 'instance', array('id' => $module->coursemodule));
                         $module_record = $DB->get_record($moduledata->name, array('id' => $module_instance));
index ccee3ba..e9905c4 100644 (file)
@@ -45,7 +45,7 @@ class backup_check_testcase extends advanced_testcase {
 
         $this->resetAfterTest(true);
 
-        $course = $this->getDataGenerator()->create_course();
+        $course = $this->getDataGenerator()->create_course(array(), array('createsections' => true));
         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id), array('section'=>3));
         $coursemodule = $DB->get_record('course_modules', array('id'=>$page->cmid));
 
index d5610c0..7f37c48 100644 (file)
@@ -177,9 +177,10 @@ class block_navigation extends block_base {
             $trimlength = (int)$this->config->trimlength;
         }
 
-        // Initialise (only actually happens if it hasn't already been done yet
-        $this->page->navigation->initialise();
-        $navigation = clone($this->page->navigation);
+        // Get the navigation object or don't display the block if none provided.
+        if (!$navigation = $this->get_navigation()) {
+            return null;
+        }
         $expansionlimit = null;
         if (!empty($this->config->expansionlimit)) {
             $expansionlimit = $this->config->expansionlimit;
@@ -204,7 +205,7 @@ class block_navigation extends block_base {
         $options['linkcategories'] = (!empty($this->config->linkcategories) && $this->config->linkcategories == 'yes');
 
         // Grab the items to display
-        $renderer = $this->page->get_renderer('block_navigation');
+        $renderer = $this->page->get_renderer($this->blockname);
         $this->content = new stdClass();
         $this->content->text = $renderer->navigation_tree($navigation, $expansionlimit, $options);
 
@@ -214,6 +215,17 @@ class block_navigation extends block_base {
         return $this->content;
     }
 
+    /**
+     * Returns the navigation
+     *
+     * @return navigation_node The navigation object to display
+     */
+    protected function get_navigation() {
+        // Initialise (only actually happens if it hasn't already been done yet)
+        $this->page->navigation->initialise();
+        return clone($this->page->navigation);
+    }
+
     /**
      * Returns the attributes to set for this block
      *
index 5d2c515..39a550e 100644 (file)
@@ -29,10 +29,10 @@ class block_site_main_menu extends block_list {
         require_once($CFG->dirroot.'/course/lib.php');
         $context = context_course::instance($course->id);
         $isediting = $this->page->user_is_editing() && has_capability('moodle/course:manageactivities', $context);
-        $modinfo = get_fast_modinfo($course);
 
 /// extra fast view mode
         if (!$isediting) {
+            $modinfo = get_fast_modinfo($course);
             if (!empty($modinfo->sections[0])) {
                 $options = array('overflowdiv'=>true);
                 foreach($modinfo->sections[0] as $cmid) {
@@ -61,9 +61,9 @@ class block_site_main_menu extends block_list {
 
 /// slow & hacky editing mode
         $ismoving = ismoving($course->id);
-        $section  = get_course_section(0, $course->id);
-
-        get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
+        course_create_sections_if_missing($course, 0);
+        $modinfo = get_fast_modinfo($course);
+        $section = $modinfo->get_section_info(0);
 
         $groupbuttons = $course->groupmode;
         $groupbuttonslink = (!$course->groupmodeforce);
@@ -82,14 +82,13 @@ class block_site_main_menu extends block_list {
             $this->content->items[] = $USER->activitycopyname.'&nbsp;(<a href="'.$CFG->wwwroot.'/course/mod.php?cancelcopy=true&amp;sesskey='.sesskey().'">'.$strcancel.'</a>)';
         }
 
-        if (!empty($section->sequence)) {
-            $sectionmods = explode(',', $section->sequence);
+        if (!empty($modinfo->sections[0])) {
             $options = array('overflowdiv'=>true);
-            foreach ($sectionmods as $modnumber) {
-                if (empty($mods[$modnumber])) {
+            foreach ($modinfo->sections[0] as $modnumber) {
+                $mod = $modinfo->cms[$modnumber];
+                if (!$mod->uservisible) {
                     continue;
                 }
-                $mod = $mods[$modnumber];
                 if (!$ismoving) {
                     if ($groupbuttons) {
                         if (! $mod->groupmodelink = $groupbuttonslink) {
@@ -135,11 +134,7 @@ class block_site_main_menu extends block_list {
             $this->content->icons[] = '';
         }
 
-        if (!empty($modnames)) {
-            $this->content->footer = print_section_add_menus($course, 0, $modnames, true, true);
-        } else {
-            $this->content->footer = '';
-        }
+        $this->content->footer = print_section_add_menus($course, 0, null, true, true);
 
         return $this->content;
     }
index d9f98b0..b98f44f 100644 (file)
@@ -64,15 +64,8 @@ class block_social_activities extends block_list {
 
 /// slow & hacky editing mode
         $ismoving = ismoving($course->id);
-        $sections = get_all_sections($course->id);
-
-        if(!empty($sections) && isset($sections[0])) {
-            $section = $sections[0];
-        }
-
-        if (!empty($section)) {
-            get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
-        }
+        $modinfo = get_fast_modinfo($course);
+        $section = $modinfo->get_section_info(0);
 
         $groupbuttons = $course->groupmode;
         $groupbuttonslink = (!$course->groupmodeforce);
@@ -91,14 +84,13 @@ class block_social_activities extends block_list {
             $this->content->items[] = $USER->activitycopyname.'&nbsp;(<a href="'.$CFG->wwwroot.'/course/mod.php?cancelcopy=true&amp;sesskey='.sesskey().'">'.$strcancel.'</a>)';
         }
 
-        if (!empty($section) && !empty($section->sequence)) {
-            $sectionmods = explode(',', $section->sequence);
+        if (!empty($modinfo->sections[0])) {
             $options = array('overflowdiv'=>true);
-            foreach ($sectionmods as $modnumber) {
-                if (empty($mods[$modnumber])) {
+            foreach ($modinfo->sections[0] as $modnumber) {
+                $mod = $modinfo->cms[$modnumber];
+                if (!$mod->uservisible) {
                     continue;
                 }
-                $mod = $mods[$modnumber];
                 if (!$ismoving) {
                     if ($groupbuttons) {
                         if (! $mod->groupmodelink = $groupbuttonslink) {
@@ -145,11 +137,7 @@ class block_social_activities extends block_list {
             $this->content->icons[] = '';
         }
 
-        if ($modnames) {
-            $this->content->footer = print_section_add_menus($course, 0, $modnames, true, true);
-        } else {
-            $this->content->footer = '';
-        }
+        $this->content->footer = print_section_add_menus($course, 0, null, true, true);
 
         return $this->content;
     }
index 0917a83..682ef18 100644 (file)
@@ -170,7 +170,7 @@ class cache_config {
             $plugin = $store['plugin'];
             $class = 'cachestore_'.$plugin;
             $exists = array_key_exists($plugin, $availableplugins);
-            if (!$exists && (!class_exists($class) || !is_subclass_of($class, 'cache_store'))) {
+            if (!$exists) {
                 // Not a valid plugin, or has been uninstalled, just skip it an carry on.
                 debugging('Invalid cache store in config. Not an available plugin.', DEBUG_DEVELOPER);
                 continue;
index 8595808..aa6a0ed 100644 (file)
@@ -40,7 +40,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cache implements cache_loader, cache_is_key_aware {
+class cache implements cache_loader {
 
     /**
      * We need a timestamp to use within the cache API.
index 2bc3797..08a3ca9 100644 (file)
@@ -136,7 +136,8 @@ class cache_config_writer extends cache_config {
                 throw new cache_exception('Invalid cache plugin specified. The plugin does not contain the required class.');
             }
         }
-        if (!is_subclass_of($class, 'cache_store')) {
+        $reflection = new ReflectionClass($class);
+        if (!$reflection->implementsInterface('cache_store')) {
             throw new cache_exception('Invalid cache plugin specified. The plugin does not extend the required class.');
         }
         if (!$class::are_requirements_met()) {
@@ -400,7 +401,7 @@ class cache_config_writer extends cache_config {
                 $definition['component'] = $component;
                 $definition['area'] = $area;
                 if (array_key_exists($id, $definitions)) {
-                    debugging('Error: duplicate cache definition found with name '.$name, DEBUG_DEVELOPER);
+                    debugging('Error: duplicate cache definition found with id: '.$id, DEBUG_DEVELOPER);
                     continue;
                 }
                 $definitions[$id] = $definition;
index 89a79e6..b71b00b 100644 (file)
@@ -125,7 +125,7 @@ function cohort_delete_category($category) {
 }
 
 /**
- * Remove cohort member
+ * Add cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
@@ -146,7 +146,7 @@ function cohort_add_member($cohortid, $userid) {
 }
 
 /**
- * Add cohort member
+ * Remove cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
index c53fa80..e7e3690 100644 (file)
@@ -94,7 +94,7 @@ class comment_manager {
      */
     private function setup_course($courseid) {
         global $PAGE, $DB;
-        if (!empty($this->course)) {
+        if (!empty($this->course) && $this->course->id == $courseid) {
             // already set, stop
             return;
         }
index 038c09b..6fb00b0 100644 (file)
@@ -612,15 +612,15 @@ class dndupload_ajax_processor {
         }
 
         $DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
+        // Rebuild the course cache after update action
+        rebuild_course_cache($this->course->id, true);
+        $this->course->modinfo = null; // Otherwise we will just get the old version back again.
 
-        $sectionid = add_mod_to_section($this->cm);
-        $DB->set_field('course_modules', 'section', $sectionid, array('id' => $this->cm->id));
+        $sectionid = course_add_cm_to_section($this->course, $this->cm->id, $this->section);
 
         set_coursemodule_visible($this->cm->id, true);
 
-        // Rebuild the course cache and retrieve the final info about this module.
-        rebuild_course_cache($this->course->id, true);
-        $this->course->modinfo = null; // Otherwise we will just get the old version back again.
+        // retrieve the final info about this module.
         $info = get_fast_modinfo($this->course);
         if (!isset($info->cms[$this->cm->id])) {
             // The course module has not been properly created in the course - undo everything.
index 3147bad..6a5aefb 100644 (file)
@@ -132,7 +132,6 @@ if ($editform->is_cancelled()) {
         // Save any changes to the files used in the editor
         update_course($data, $editoroptions);
     }
-    rebuild_course_cache($course->id);
 
     switch ($returnto) {
         case 'category':
index 22e2e4e..c2a5c94 100644 (file)
@@ -105,13 +105,12 @@ class core_course_external extends external_api {
 
             //retrieve sections
             $modinfo = get_fast_modinfo($course);
-            $sections = get_all_sections($course->id);
+            $sections = $modinfo->get_section_info_all();
 
             //for each sections (first displayed to last displayed)
             foreach ($sections as $key => $section) {
 
-                $showsection = (has_capability('moodle/course:viewhiddensections', $context) or $section->visible or !$course->hiddensections);
-                if (!$showsection) {
+                if (!$section->uservisible) {
                     continue;
                 }
 
index fc8ac72..26f28c2 100644 (file)
@@ -271,7 +271,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
      *
      * @param stdClass $section The course_section entry from DB
      * @param stdClass $course The course entry from DB
-     * @param array    $mods course modules indexed by id (from get_all_mods)
+     * @param array    $mods (argument not used)
      * @return string HTML to output.
      */
     protected function section_summary($section, $course, $mods) {
@@ -303,7 +303,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $o.= html_writer::start_tag('div', array('class' => 'summarytext'));
         $o.= $this->format_summary_text($section);
         $o.= html_writer::end_tag('div');
-        $o.= $this->section_activity_summary($section, $course, $mods);
+        $o.= $this->section_activity_summary($section, $course, null);
 
         $o.= $this->section_availability_message($section);
 
@@ -318,11 +318,12 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
      *
      * @param stdClass $section The course_section entry from DB
      * @param stdClass $course the course record from DB
-     * @param array    $mods course modules indexed by id (from get_all_mods)
+     * @param array    $mods (argument not used)
      * @return string HTML to output.
      */
     private function section_activity_summary($section, $course, $mods) {
-        if (empty($section->sequence)) {
+        $modinfo = get_fast_modinfo($course);
+        if (empty($modinfo->sections[$section->section])) {
             return '';
         }
 
@@ -332,9 +333,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $complete = 0;
         $cancomplete = isloggedin() && !isguestuser();
         $completioninfo = new completion_info($course);
-        $modsequence = explode(',', $section->sequence);
-        foreach ($modsequence as $cmid) {
-            $thismod = $mods[$cmid];
+        foreach ($modinfo->sections[$section->section] as $cmid) {
+            $thismod = $modinfo->cms[$cmid];
 
             if ($thismod->modname == 'label') {
                 // Labels are special (not interesting for students)!
@@ -343,9 +343,10 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
 
             if ($thismod->uservisible) {
                 if (isset($sectionmods[$thismod->modname])) {
+                    $sectionmods[$thismod->modname]['name'] = $thismod->modplural;
                     $sectionmods[$thismod->modname]['count']++;
                 } else {
-                    $sectionmods[$thismod->modname]['name'] = $thismod->modplural;
+                    $sectionmods[$thismod->modname]['name'] = $thismod->modfullname;
                     $sectionmods[$thismod->modname]['count'] = 1;
                 }
                 if ($cancomplete && $completioninfo->is_enabled($thismod) != COMPLETION_TRACKING_NONE) {
@@ -531,26 +532,25 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
      * Output the html for a single section page .
      *
      * @param stdClass $course The course entry from DB
-     * @param array $sections The course_sections entries from the DB
-     * @param array $mods used for print_section()
-     * @param array $modnames used for print_section()
-     * @param array $modnamesused used for print_section()
+     * @param array $sections (argument not used)
+     * @param array $mods (argument not used)
+     * @param array $modnames (argument not used)
+     * @param array $modnamesused (argument not used)
      * @param int $displaysection The section number in the course which is being displayed
      */
     public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) {
         global $PAGE;
 
-        // Can we view the section in question?
-        $context = context_course::instance($course->id);
-        $canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
+        $modinfo = get_fast_modinfo($course);
 
-        if (!isset($sections[$displaysection])) {
+        // Can we view the section in question?
+        if (!($sectioninfo = $modinfo->get_section_info($displaysection))) {
             // This section doesn't exist
             print_error('unknowncoursesection', 'error', null, $course->fullname);
             return;
         }
 
-        if (!$sections[$displaysection]->visible && !$canviewhidden) {
+        if (!$sectioninfo->uservisible) {
             if (!$course->hiddensections) {
                 echo $this->start_section_list();
                 echo $this->section_hidden($displaysection);
@@ -562,15 +562,13 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
 
         // Copy activity clipboard..
         echo $this->course_activity_clipboard($course, $displaysection);
-
-        // General section if non-empty.
-        $thissection = $sections[0];
-        if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) {
+        $thissection = $modinfo->get_section_info(0);
+        if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
             echo $this->start_section_list();
             echo $this->section_header($thissection, $course, true, $displaysection);
-            print_section($course, $thissection, $mods, $modnamesused, true, "100%", false, $displaysection);
+            print_section($course, $thissection, null, null, true, "100%", false, $displaysection);
             if ($PAGE->user_is_editing()) {
-                print_section_add_menus($course, 0, $modnames, false, false, $displaysection);
+                print_section_add_menus($course, 0, null, false, false, $displaysection);
             }
             echo $this->section_footer();
             echo $this->end_section_list();
@@ -579,34 +577,35 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         // Start single-section div
         echo html_writer::start_tag('div', array('class' => 'single-section'));
 
+        // The requested section page.
+        $thissection = $modinfo->get_section_info($displaysection);
+
         // Title with section navigation links.
-        $sectionnavlinks = $this->get_nav_links($course, $sections, $displaysection);
+        $sectionnavlinks = $this->get_nav_links($course, $modinfo->get_section_info_all(), $displaysection);
         $sectiontitle = '';
         $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation header headingblock'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
         // Title attributes
         $titleattr = 'mdl-align title';
-        if (!$sections[$displaysection]->visible) {
+        if (!$thissection->visible) {
             $titleattr .= ' dimmed_text';
         }
-        $sectiontitle .= html_writer::tag('div', get_section_name($course, $sections[$displaysection]), array('class' => $titleattr));
+        $sectiontitle .= html_writer::tag('div', get_section_name($course, $displaysection), array('class' => $titleattr));
         $sectiontitle .= html_writer::end_tag('div');
         echo $sectiontitle;
 
         // Now the list of sections..
         echo $this->start_section_list();
 
-        // The requested section page.
-        $thissection = $sections[$displaysection];
         echo $this->section_header($thissection, $course, true, $displaysection);
         // Show completion help icon.
         $completioninfo = new completion_info($course);
         echo $completioninfo->display_help_icon();
 
-        print_section($course, $thissection, $mods, $modnamesused, true, '100%', false, $displaysection);
+        print_section($course, $thissection, null, null, true, '100%', false, $displaysection);
         if ($PAGE->user_is_editing()) {
-            print_section_add_menus($course, $displaysection, $modnames, false, false, $displaysection);
+            print_section_add_menus($course, $displaysection, null, false, false, $displaysection);
         }
         echo $this->section_footer();
         echo $this->end_section_list();
@@ -629,14 +628,16 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
      * Output the html for a multiple section page
      *
      * @param stdClass $course The course entry from DB
-     * @param array $sections The course_sections entries from the DB
-     * @param array $mods used for print_section()
-     * @param array $modnames used for print_section()
-     * @param array $modnamesused used for print_section()
+     * @param array $sections (argument not used)
+     * @param array $mods (argument not used)
+     * @param array $modnames (argument not used)
+     * @param array $modnamesused (argument not used)
      */
     public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) {
         global $PAGE;
 
+        $modinfo = get_fast_modinfo($course);
+
         $context = context_course::instance($course->id);
         // Title with completion help icon.
         $completioninfo = new completion_info($course);
@@ -649,32 +650,22 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         // Now the list of sections..
         echo $this->start_section_list();
 
-        // General section if non-empty.
-        $thissection = $sections[0];
-        unset($sections[0]);
-        if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) {
-            echo $this->section_header($thissection, $course, false, 0);
-            print_section($course, $thissection, $mods, $modnamesused, true, "100%", false, 0);
-            if ($PAGE->user_is_editing()) {
-                print_section_add_menus($course, 0, $modnames, false, false, 0);
+        foreach ($modinfo->get_section_info_all() as $section => $thissection) {
+            if ($section == 0) {
+                // 0-section is displayed a little different then the others
+                if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
+                    echo $this->section_header($thissection, $course, false, 0);
+                    print_section($course, $thissection, null, null, true, "100%", false, 0);
+                    if ($PAGE->user_is_editing()) {
+                        print_section_add_menus($course, 0, null, false, false, 0);
+                    }
+                    echo $this->section_footer();
+                }
+                continue;
             }
-            echo $this->section_footer();
-        }
-
-        $canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
-        for ($section = 1; $section <= $course->numsections; $section++) {
-            if (!empty($sections[$section])) {
-                $thissection = $sections[$section];
-            } else {
-                // This will create a course section if it doesn't exist..
-                $thissection = get_course_section($section, $course->id);
-
-                // The returned section is only a bare database object rather than
-                // a section_info object - we will need at least the uservisible
-                // field in it.
-                $thissection->uservisible = true;
-                $thissection->availableinfo = null;
-                $thissection->showavailability = 0;
+            if ($section > $course->numsections) {
+                // activities inside this section are 'orphaned', this section will be printed as 'stealth' below
+                continue;
             }
             // Show the section if the user is permitted to access it, OR if it's not available
             // but showavailability is turned on
@@ -687,36 +678,33 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
                     echo $this->section_hidden($section);
                 }
 
-                unset($sections[$section]);
                 continue;
             }
 
             if (!$PAGE->user_is_editing() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
                 // Display section summary only.
-                echo $this->section_summary($thissection, $course, $mods);
+                echo $this->section_summary($thissection, $course, null);
             } else {
                 echo $this->section_header($thissection, $course, false, 0);
                 if ($thissection->uservisible) {
-                    print_section($course, $thissection, $mods, $modnamesused, true, "100%", false, 0);
+                    print_section($course, $thissection, null, null, true, "100%", false, 0);
                     if ($PAGE->user_is_editing()) {
-                        print_section_add_menus($course, $section, $modnames, false, false, 0);
+                        print_section_add_menus($course, $section, null, false, false, 0);
                     }
                 }
                 echo $this->section_footer();
             }
-
-            unset($sections[$section]);
         }
 
         if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) {
             // Print stealth sections if present.
-            $modinfo = get_fast_modinfo($course);
-            foreach ($sections as $section => $thissection) {
-                if (empty($modinfo->sections[$section])) {
+            foreach ($modinfo->get_section_info_all() as $section => $thissection) {
+                if ($section <= $course->numsections or empty($modinfo->sections[$section])) {
+                    // this is not stealth section or it is empty
                     continue;
                 }
                 echo $this->stealth_section_header($section);
-                print_section($course, $thissection, $mods, $modnamesused, true, "100%", false, $displaysection);
+                print_section($course, $thissection, null, null, true, "100%", false, 0);
                 echo $this->stealth_section_footer();
             }
 
index 51acd38..5108a86 100644 (file)
@@ -44,12 +44,15 @@ if (($marker >=0) && has_capability('moodle/course:setcurrentsection', $context)
     course_set_marker($course->id, $marker);
 }
 
+// make sure all sections are created
+course_create_sections_if_missing($course, range(0, $course->numsections));
+
 $renderer = $PAGE->get_renderer('format_topics');
 
 if (!empty($displaysection)) {
-    $renderer->print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection);
+    $renderer->print_single_section_page($course, null, null, null, null, $displaysection);
 } else {
-    $renderer->print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused);
+    $renderer->print_multiple_section_page($course, null, null, null, null);
 }
 
 // Include course format js module
index 6105465..6beb68b 100644 (file)
@@ -10,6 +10,8 @@ format.
 
 * Function settings_navigation::add_course_editing_links() is completely removed, course format
   functions callback_XXXX_request_key() are no longer used (where XXXX is the course format name)
+* functions get_generic_section_name(), get_all_sections(), add_mod_to_section(), get_all_mods()
+  are deprecated. See their phpdocs in lib/deprecatedlib.php on how to replace them
 
 === 2.3 ===
 
index 2f0eb51..86fac1e 100644 (file)
@@ -37,12 +37,15 @@ if ($week = optional_param('week', 0, PARAM_INT)) {
 }
 // End backwards-compatible aliasing..
 
+// make sure all sections are created
+course_create_sections_if_missing($course, range(0, $course->numsections));
+
 $renderer = $PAGE->get_renderer('format_weeks');
 
 if (!empty($displaysection)) {
-    $renderer->print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection);
+    $renderer->print_single_section_page($course, null, null, null, null, $displaysection);
 } else {
-    $renderer->print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused);
+    $renderer->print_multiple_section_page($course, null, null, null, null);
 }
 
 $PAGE->requires->js('/course/format/weeks/format.js');
index 72d94e9..ec2cedf 100644 (file)
@@ -603,7 +603,7 @@ function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
         $coursecontext = context_course::instance($course->id);
         $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
-        $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);\r
+        $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
         $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action.' ('.$actionurl.')', $log->info);
         $csvexporter->add_data($row);
     }
@@ -1209,81 +1209,30 @@ function get_array_of_activities($courseid) {
     return $mod;
 }
 
-
 /**
- * Returns a number of useful structures for course displays
+ * Returns the localised human-readable names of all used modules
+ *
+ * @param bool $plural if true returns the plural forms of the names
+ * @return array where key is the module name (component name without 'mod_') and
+ *     the value is the human-readable string. Array sorted alphabetically by value
  */
-function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
-    global $CFG, $DB, $COURSE;
-
-    $mods          = array();    // course modules indexed by id
-    $modnames      = array();    // all course module names (except resource!)
-    $modnamesplural= array();    // all course module names (plural form)
-    $modnamesused  = array();    // course module names used
-
-    if ($allmods = $DB->get_records("modules")) {
-        foreach ($allmods as $mod) {
-            if (!file_exists("$CFG->dirroot/mod/$mod->name/lib.php")) {
-                continue;
-            }
-            if ($mod->visible) {
-                $modnames[$mod->name] = get_string("modulename", "$mod->name");
-                $modnamesplural[$mod->name] = get_string("modulenameplural", "$mod->name");
-            }
-        }
-        collatorlib::asort($modnames);
-    } else {
-        print_error("nomodules", 'debug');
-    }
-
-    $course = ($courseid==$COURSE->id) ? $COURSE : $DB->get_record('course',array('id'=>$courseid));
-    $modinfo = get_fast_modinfo($course);
-
-    if ($rawmods=$modinfo->cms) {
-        foreach($rawmods as $mod) {    // Index the mods
-            if (empty($modnames[$mod->modname])) {
-                continue;
-            }
-            $mods[$mod->id] = $mod;
-            $mods[$mod->id]->modfullname = $modnames[$mod->modname];
-            if (!$mod->visible and !has_capability('moodle/course:viewhiddenactivities', context_course::instance($courseid))) {
-                continue;
-            }
-            // Check groupings
-            if (!groups_course_module_visible($mod)) {
-                continue;
+function get_module_types_names($plural = false) {
+    static $modnames = null;
+    global $DB, $CFG;
+    if ($modnames === null) {
+        $modnames = array(0 => array(), 1 => array());
+        if ($allmods = $DB->get_records("modules")) {
+            foreach ($allmods as $mod) {
+                if (file_exists("$CFG->dirroot/mod/$mod->name/lib.php") && $mod->visible) {
+                    $modnames[0][$mod->name] = get_string("modulename", "$mod->name");
+                    $modnames[1][$mod->name] = get_string("modulenameplural", "$mod->name");
+                }
             }
-            $modnamesused[$mod->modname] = $modnames[$mod->modname];
+            collatorlib::asort($modnames[0]);
+            collatorlib::asort($modnames[1]);
         }
-        if ($modnamesused) {
-            collatorlib::asort($modnamesused);
-        }
-    }
-}
-
-/**
- * Returns an array of sections for the requested course id
- *
- * This function stores the sections against the course id within a staticvar encase
- * of subsequent requests. This is used all over + in some standard libs and course
- * format callbacks so subsequent requests are a reality.
- *
- * Note: Since Moodle 2.3, it is more efficient to get this data by calling
- * get_fast_modinfo, then using $modinfo->get_section_info or get_section_info_all.
- *
- * @staticvar array $coursesections
- * @param int $courseid
- * @return array Array of sections
- */
-function get_all_sections($courseid) {
-    global $DB;
-    static $coursesections = array();
-    if (!array_key_exists($courseid, $coursesections)) {
-        $coursesections[$courseid] = $DB->get_records("course_sections", array("course"=>"$courseid"), "section",
-                'section, id, course, name, summary, summaryformat, sequence, visible, ' .
-                'availablefrom, availableuntil, showavailability, groupingid');
     }
-    return $coursesections[$courseid];
+    return $modnames[(int)$plural];
 }
 
 /**
@@ -1319,7 +1268,7 @@ function set_section_visible($courseid, $sectionnumber, $visibility) {
                 set_coursemodule_visible($moduleid, $visibility, true);
             }
         }
-        rebuild_course_cache($courseid);
+        rebuild_course_cache($courseid, true);
 
         // Determine which modules are visible for AJAX update
         if (!empty($modules)) {
@@ -1383,9 +1332,9 @@ function get_print_section_cm_text(cm_info $cm, $course) {
  * Prints a section full of activity modules
  *
  * @param stdClass $course The course
- * @param stdClass $section The section
- * @param array $mods The modules in the section
- * @param array $modnamesused An array containing the list of modules and their names
+ * @param stdClass|section_info $section The section object containing properties id and section
+ * @param array $mods (argument not used)
+ * @param array $modnamesused (argument not used)
  * @param bool $absolute All links are absolute
  * @param string $width Width of the container
  * @param bool $hidecompletion Hide completion status
@@ -1404,7 +1353,6 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
     static $strmovehere;
     static $strmovefull;
     static $strunreadpostsone;
-    static $modulenames;
 
     if (!isset($initialised)) {
         $groupbuttons     = ($course->groupmode or (!$course->groupmodeforce));
@@ -1415,7 +1363,6 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             $strmovehere  = get_string("movehere");
             $strmovefull  = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
         }
-        $modulenames      = array();
         $initialised = true;
     }
 
@@ -1423,61 +1370,36 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
     $completioninfo = new completion_info($course);
 
     //Accessibility: replace table with list <ul>, but don't output empty list.
-    if (!empty($section->sequence)) {
+    if (!empty($modinfo->sections[$section->section])) {
 
         // Fix bug #5027, don't want style=\"width:$width\".
         echo "<ul class=\"section img-text\">\n";
-        $sectionmods = explode(",", $section->sequence);
-
-        foreach ($sectionmods as $modnumber) {
-            if (empty($mods[$modnumber])) {
-                continue;
-            }
 
-            /**
-             * @var cm_info
-             */
-            $mod = $mods[$modnumber];
+        foreach ($modinfo->sections[$section->section] as $modnumber) {
+            $mod = $modinfo->cms[$modnumber];
 
             if ($ismoving and $mod->id == $USER->activitycopy) {
                 // do not display moving mod
                 continue;
             }
 
-            if (isset($modinfo->cms[$modnumber])) {
-                // We can continue (because it will not be displayed at all)
-                // if:
-                // 1) The activity is not visible to users
-                // and
-                // 2a) The 'showavailability' option is not set (if that is set,
-                //     we need to display the activity so we can show
-                //     availability info)
-                // or
-                // 2b) The 'availableinfo' is empty, i.e. the activity was
-                //     hidden in a way that leaves no info, such as using the
-                //     eye icon.
-                if (!$modinfo->cms[$modnumber]->uservisible &&
-                    (empty($modinfo->cms[$modnumber]->showavailability) ||
-                      empty($modinfo->cms[$modnumber]->availableinfo))) {
-                    // visibility shortcut
-                    continue;
-                }
-            } else {
-                if (!file_exists("$CFG->dirroot/mod/$mod->modname/lib.php")) {
-                    // module not installed
-                    continue;
-                }
-                if (!coursemodule_visible_for_user($mod) &&
-                    empty($mod->showavailability)) {
-                    // full visibility check
-                    continue;
-                }
-            }
-
-            if (!isset($modulenames[$mod->modname])) {
-                $modulenames[$mod->modname] = get_string('modulename', $mod->modname);
+            // We can continue (because it will not be displayed at all)
+            // if:
+            // 1) The activity is not visible to users
+            // and
+            // 2a) The 'showavailability' option is not set (if that is set,
+            //     we need to display the activity so we can show
+            //     availability info)
+            // or
+            // 2b) The 'availableinfo' is empty, i.e. the activity was
+            //     hidden in a way that leaves no info, such as using the
+            //     eye icon.
+            if (!$mod->uservisible &&
+                (empty($mod->showavailability) ||
+                  empty($mod->availableinfo))) {
+                // visibility shortcut
+                continue;
             }
-            $modulename = $modulenames[$mod->modname];
 
             // In some cases the activity is visible to user, but it is
             // dimmed. This is done if viewhiddenactivities is true and if:
@@ -1580,7 +1502,7 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                     // Display link itself
                     echo '<a ' . $linkcss . $mod->extra . $onclick .
                             ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
-                            '" class="activityicon" alt="' . $modulename . '" /> ' .
+                            '" class="activityicon" alt="' . $mod->modfullname . '" /> ' .
                             $accesstext . '<span class="instancename">' .
                             $instancename . $altname . '</span></a>';
 
@@ -1761,7 +1683,7 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
              ' alt="'.$strmovehere.'" /></a></li>
              ';
     }
-    if (!empty($section->sequence) || $ismoving) {
+    if (!empty($modinfo->sections[$section->section]) || $ismoving) {
         echo "</ul><!--class='section'-->\n\n";
     }
 }
@@ -1771,18 +1693,28 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
  *
  * @param stdClass $course The course
  * @param int $section relative section number (field course_sections.section)
- * @param array $modnames An array containing the list of modules and their names
+ * @param null|array $modnames An array containing the list of modules and their names
+ *     if omitted will be taken from get_module_types_names()
  * @param bool $vertical Vertical orientation
  * @param bool $return Return the menus or send them to output
  * @param int $sectionreturn The section to link back to
  * @return void|string depending on $return
  */
-function print_section_add_menus($course, $section, $modnames, $vertical=false, $return=false, $sectionreturn=null) {
+function print_section_add_menus($course, $section, $modnames = null, $vertical=false, $return=false, $sectionreturn=null) {
     global $CFG, $OUTPUT;
 
-    // check to see if user can add menus
-    if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))) {
-        return false;
+    if ($modnames === null) {
+        $modnames = get_module_types_names();
+    }
+
+    // check to see if user can add menus and there are modules to add
+    if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))
+            || empty($modnames)) {
+        if ($return) {
+            return '';
+        } else {
+            return false;
+        }
     }
 
     // Retrieve all modules with associated metadata
@@ -2755,87 +2687,105 @@ function add_course_module($mod) {
     $mod->added = time();
     unset($mod->id);
 
-    return $DB->insert_record("course_modules", $mod);
+    $cmid = $DB->insert_record("course_modules", $mod);
+    rebuild_course_cache($mod->course, true);
+    return $cmid;
 }
 
 /**
- * Returns course section - creates new if does not exist yet.
- * @param int $relative section number
- * @param int $courseid
- * @return object $course_section object
+ * Creates missing course section(s) and rebuilds course cache
+ *
+ * @param int|stdClass $courseorid course id or course object
+ * @param int|array $sections list of relative section numbers to create
+ * @return bool if there were any sections created
  */
-function get_course_section($section, $courseid) {
+function course_create_sections_if_missing($courseorid, $sections) {
     global $DB;
-
-    if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
-        return $cw;
-    }
-    $cw = new stdClass();
-    $cw->course   = $courseid;
-    $cw->section  = $section;
-    $cw->summary  = "";
-    $cw->summaryformat = FORMAT_HTML;
-    $cw->sequence = "";
-    $id = $DB->insert_record("course_sections", $cw);
-    rebuild_course_cache($courseid, true);
-    return $DB->get_record("course_sections", array("id"=>$id));
+    if (!is_array($sections)) {
+        $sections = array($sections);
+    }
+    $existing = array_keys(get_fast_modinfo($courseorid)->get_section_info_all());
+    if (is_object($courseorid)) {
+        $courseorid = $courseorid->id;
+    }
+    $coursechanged = false;
+    foreach ($sections as $sectionnum) {
+        if (!in_array($sectionnum, $existing)) {
+            $cw = new stdClass();
+            $cw->course   = $courseorid;
+            $cw->section  = $sectionnum;
+            $cw->summary  = '';
+            $cw->summaryformat = FORMAT_HTML;
+            $cw->sequence = '';
+            $id = $DB->insert_record("course_sections", $cw);
+            $coursechanged = true;
+        }
+    }
+    if ($coursechanged) {
+        rebuild_course_cache($courseorid, true);
+    }
+    return $coursechanged;
 }
 
 /**
- * Given a full mod object with section and course already defined, adds this module to that section.
+ * Adds an existing module to the section
  *
- * @param object $mod
- * @param int $beforemod An existing ID which we will insert the new module before
- * @return int The course_sections ID where the mod is inserted
+ * Updates both tables {course_sections} and {course_modules}
+ *
+ * @param int|stdClass $courseorid course id or course object
+ * @param int $modid id of the module already existing in course_modules table
+ * @param int $sectionnum relative number of the section (field course_sections.section)
+ *     If section does not exist it will be created
+ * @param int|stdClass $beforemod id or object with field id corresponding to the module
+ *     before which the module needs to be included. Null for inserting in the
+ *     end of the section
+ * @return int The course_sections ID where the module is inserted
  */
-function add_mod_to_section($mod, $beforemod=NULL) {
-    global $DB;
-
-    if ($section = $DB->get_record("course_sections", array("course"=>$mod->course, "section"=>$mod->section))) {
-
-        $section->sequence = trim($section->sequence);
-
-        if (empty($section->sequence)) {
-            $newsequence = "$mod->coursemodule";
-
-        } else if ($beforemod) {
-            $modarray = explode(",", $section->sequence);
-
-            if ($key = array_keys($modarray, $beforemod->id)) {
-                $insertarray = array($mod->id, $beforemod->id);
-                array_splice($modarray, $key[0], 1, $insertarray);
-                $newsequence = implode(",", $modarray);
-
-            } else {  // Just tack it on the end anyway
-                $newsequence = "$section->sequence,$mod->coursemodule";
-            }
-
-        } else {
-            $newsequence = "$section->sequence,$mod->coursemodule";
-        }
-
-        $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
-        return $section->id;     // Return course_sections ID that was used.
-
-    } else {  // Insert a new record
-        $section = new stdClass();
-        $section->course   = $mod->course;
-        $section->section  = $mod->section;
-        $section->summary  = "";
-        $section->summaryformat = FORMAT_HTML;
-        $section->sequence = $mod->coursemodule;
-        return $DB->insert_record("course_sections", $section);
+function course_add_cm_to_section($courseorid, $modid, $sectionnum, $beforemod = null) {
+    global $DB, $COURSE;
+    if (is_object($beforemod)) {
+        $beforemod = $beforemod->id;
+    }
+    course_create_sections_if_missing($courseorid, $sectionnum);
+    $section = get_fast_modinfo($courseorid)->get_section_info($sectionnum);
+    $modarray = explode(",", trim($section->sequence));
+    if (empty($modarray)) {
+        $newsequence = "$modid";
+    } else if ($beforemod && ($key = array_keys($modarray, $beforemod))) {
+        $insertarray = array($modid, $beforemod);
+        array_splice($modarray, $key[0], 1, $insertarray);
+        $newsequence = implode(",", $modarray);
+    } else {
+        $newsequence = "$section->sequence,$modid";
+    }
+    $DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
+    $DB->set_field('course_modules', 'section', $section->id, array('id' => $modid));
+    if (is_object($courseorid)) {
+        rebuild_course_cache($courseorid->id, true);
+    } else {
+        rebuild_course_cache($courseorid, true);
     }
+    return $section->id;     // Return course_sections ID that was used.
 }
 
 function set_coursemodule_groupmode($id, $groupmode) {
     global $DB;
-    return $DB->set_field("course_modules", "groupmode", $groupmode, array("id"=>$id));
+    $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,groupmode', MUST_EXIST);
+    if ($cm->groupmode != $groupmode) {
+        $DB->set_field('course_modules', 'groupmode', $groupmode, array('id' => $cm->id));
+        rebuild_course_cache($cm->course, true);
+    }
+    return ($cm->groupmode != $groupmode);
 }
 
 function set_coursemodule_idnumber($id, $idnumber) {
     global $DB;
-    return $DB->set_field("course_modules", "idnumber", $idnumber, array("id"=>$id));
+    $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,idnumber', MUST_EXIST);
+    if ($cm->idnumber != $idnumber) {
+        $DB->set_field('course_modules', 'idnumber', $idnumber, array('id' => $cm->id));
+        rebuild_course_cache($cm->course, true);
+    }
+    return ($cm->idnumber != $idnumber);
 }
 
 /**
@@ -2879,10 +2829,13 @@ function set_coursemodule_visible($id, $visible, $prevstateoverrides=false) {
             $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id'=>$id));
         } else {
             // Get the previous saved visible states.
-            return $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
+            $DB->set_field('course_modules', 'visible', $cm->visibleold, array('id'=>$id));
         }
+    } else {
+        $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
     }
-    return $DB->set_field("course_modules", "visible", $visible, array("id"=>$id));
+    rebuild_course_cache($cm->course, true);
+    return true;
 }
 
 /**
@@ -2923,20 +2876,24 @@ function delete_course_module($id) {
                                                             'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
 
     delete_context(CONTEXT_MODULE, $cm->id);
-    return $DB->delete_records('course_modules', array('id'=>$cm->id));
+    $DB->delete_records('course_modules', array('id'=>$cm->id));
+    rebuild_course_cache($cm->course, true);
+    return true;
 }
 
-function delete_mod_from_section($mod, $section) {
+function delete_mod_from_section($modid, $sectionid) {
     global $DB;
 
-    if ($section = $DB->get_record("course_sections", array("id"=>$section)) ) {
+    if ($section = $DB->get_record("course_sections", array("id"=>$sectionid)) ) {
 
         $modarray = explode(",", $section->sequence);
 
-        if ($key = array_keys ($modarray, $mod)) {
+        if ($key = array_keys ($modarray, $modid)) {
             array_splice($modarray, $key[0], 1);
             $newsequence = implode(",", $modarray);
-            return $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
+            $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
+            rebuild_course_cache($section->course, true);
+            return true;
         } else {
             return false;
         }
@@ -2958,7 +2915,7 @@ function move_section($course, $section, $move) {
     debugging('This function will be removed before 2.5 is released please use move_section_to', DEBUG_DEVELOPER);
 
 /// Moves a whole course section up and down within the course
-    global $USER, $DB;
+    global $USER;
 
     if (!$move) {
         return true;
@@ -2971,10 +2928,6 @@ function move_section($course, $section, $move) {
     }
 
     $retval = move_section_to($course, $section, $sectiondest);
-    // If section moved, then rebuild course cache.
-    if ($retval) {
-        rebuild_course_cache($course->id, true);
-    }
     return $retval;
 }
 
@@ -3033,6 +2986,7 @@ function move_section_to($course, $section, $destination) {
     }
 
     $transaction->allow_commit();
+    rebuild_course_cache($course->id, true);
     return true;
 }
 
@@ -3114,34 +3068,20 @@ function reorder_sections($sections, $origin_position, $target_position) {
  * All parameters are objects
  */
 function moveto_module($mod, $section, $beforemod=NULL) {
-    global $DB, $OUTPUT;
+    global $OUTPUT;
 
 /// Remove original module from original section
     if (! delete_mod_from_section($mod->id, $mod->section)) {
         echo $OUTPUT->notification("Could not delete module from existing section");
     }
 
-/// Update module itself if necessary
-
-    if ($mod->section != $section->id) {
-        $mod->section = $section->id;
-        $DB->update_record("course_modules", $mod);
-        // if moving to a hidden section then hide module
-        if (!$section->visible) {
-            set_coursemodule_visible($mod->id, 0);
-        }
+    // if moving to a hidden section then hide module
+    if (!$section->visible && $mod->visible) {
+        set_coursemodule_visible($mod->id, 0);
     }
 
 /// Add the module into the new section
-
-    $mod->course       = $section->course;
-    $mod->section      = $section->section;  // need relative reference
-    $mod->coursemodule = $mod->id;
-
-    if (! add_mod_to_section($mod, $beforemod)) {
-        return false;
-    }
-
+    course_add_cm_to_section($section->course, $mod->id, $section->section, $beforemod);
     return true;
 }
 
@@ -3396,8 +3336,6 @@ function course_format_name ($course,$max=100) {
  * @return bool whether the current user is allowed to add this type of module to this course.
  */
 function course_allowed_module($course, $modname) {
-    global $DB;
-
     if (is_numeric($modname)) {
         throw new coding_exception('Function course_allowed_module no longer
                 supports numeric module ids. Please update your code to pass the module name.');
@@ -3826,11 +3764,8 @@ function create_course($data, $editoroptions = NULL) {
     // Setup the blocks
     blocks_add_default_course_blocks($course);
 
-    $section = new stdClass();
-    $section->course        = $course->id;   // Create a default section.
-    $section->section       = 0;
-    $section->summaryformat = FORMAT_HTML;
-    $DB->insert_record('course_sections', $section);
+    // Create a default section.
+    course_create_sections_if_missing($course, 0);
 
     fix_course_sortorder();
 
@@ -3925,6 +3860,8 @@ function update_course($data, $editoroptions = NULL) {
 
     // Update with the new data
     $DB->update_record('course', $data);
+    // make sure the modinfo cache is reset
+    rebuild_course_cache($data->id);
 
     $course = $DB->get_record('course', array('id'=>$data->id));
 
index b88eba0..b1ed617 100644 (file)
@@ -188,8 +188,6 @@ if (!empty($add)) {
                "view.php?id=$cm->course",
                "$cm->modname $cm->instance", $cm->id);
 
-    rebuild_course_cache($course->id);
-
     redirect($return);
 }
 
@@ -230,8 +228,6 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
     unset($USER->activitycopyname);
     unset($USER->activitycopysectionreturn);
 
-    rebuild_course_cache($section->course);
-
     redirect(course_get_url($course, $section->section, array('sr' => $sectionreturn)));
 
 } else if (!empty($indent) and confirm_sesskey()) {
@@ -268,8 +264,6 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
 
     set_coursemodule_visible($cm->id, 0);
 
-    rebuild_course_cache($cm->course);
-
     redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
 
 } else if (!empty($show) and confirm_sesskey()) {
@@ -287,7 +281,6 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
 
     if ($module->visible and ($section->visible or (SITEID == $cm->course))) {
         set_coursemodule_visible($cm->id, 1);
-        rebuild_course_cache($cm->course);
     }
 
     redirect(course_get_url($course, $section->section, array('sr' => $sectionreturn)));
@@ -305,8 +298,6 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
 
     set_coursemodule_groupmode($cm->id, $groupmode);
 
-    rebuild_course_cache($cm->course);
-
     redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
 
 } else if (!empty($copy) and confirm_sesskey()) { // value = course module
index a5b441c..56bf28a 100644 (file)
@@ -59,7 +59,8 @@ if (!empty($add)) {
     $context = context_course::instance($course->id);
     require_capability('moodle/course:manageactivities', $context);
 
-    $cw = get_course_section($section, $course->id);
+    course_create_sections_if_missing($course, $section);
+    $cw = get_fast_modinfo($course)->get_section_info($section);
 
     if (!course_allowed_module($course, $module->name)) {
         print_error('moduledisable');
@@ -474,9 +475,7 @@ if ($mform->is_cancelled()) {
 
         // course_modules and course_sections each contain a reference
         // to each other, so we have to update one of them twice.
-        $sectionid = add_mod_to_section($fromform);
-
-        $DB->set_field('course_modules', 'section', $sectionid, array('id'=>$fromform->coursemodule));
+        $sectionid = course_add_cm_to_section($course, $fromform->coursemodule, $fromform->section);
 
         // make sure visibility is set correctly (in particular in calendar)
         // note: allow them to set it even without moodle/course:activityvisibility
index 71c83f5..df0f0d5 100644 (file)
@@ -83,7 +83,7 @@ echo $OUTPUT->heading(format_string($course->fullname) . ": $userinfo", 2);
 $mform->display();
 
 $modinfo = get_fast_modinfo($course);
-get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
+$modnames = get_module_types_names();
 
 if (has_capability('moodle/course:viewhiddensections', $context)) {
     $hiddenfilter = "";
@@ -91,11 +91,9 @@ if (has_capability('moodle/course:viewhiddensections', $context)) {
     $hiddenfilter = "AND cs.visible = 1";
 }
 $sections = array();
-$rawsections = array_slice(get_all_sections($course->id), 0, $course->numsections+1, true);
-$canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
-foreach ($rawsections as $section) {
-    if ($canviewhidden || !empty($section->visible)) {
-        $sections[$section->section] = $section;
+foreach ($modinfo->get_section_info_all() as $i => $section) {
+    if (!empty($section->uservisible)) {
+        $sections[$i] = $section;
     }
 }
 
@@ -115,20 +113,18 @@ if ($param->modid === 'all') {
     }
 
 } else if (is_numeric($param->modid)) {
-    $section = $sections[$modinfo->cms[$param->modid]->sectionnum];
-    $section->sequence = $param->modid;
-    $sections = array($section->sequence=>$section);
+    $sectionnum = $modinfo->cms[$param->modid]->sectionnum;
+    $filter_modid = $param->modid;
+    $sections = array($sectionnum => $sections[$sectionnum]);
 }
 
 
-if (is_null($modinfo->groups)) {
-    $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
-}
+$modinfo->get_groups(); // load all my groups and cache it in modinfo
 
 $activities = array();
 $index = 0;
 
-foreach ($sections as $section) {
+foreach ($sections as $sectionnum => $section) {
 
     $activity = new stdClass();
     $activity->type = 'section';
@@ -141,17 +137,11 @@ foreach ($sections as $section) {
     $activity->visible = $section->visible;
     $activities[$index++] = $activity;
 
-    if (empty($section->sequence)) {
+    if (empty($modinfo->sections[$sectionnum])) {
         continue;
     }
 
-    $sectionmods = explode(",", $section->sequence);
-
-    foreach ($sectionmods as $cmid) {
-        if (!isset($mods[$cmid]) or !isset($modinfo->cms[$cmid])) {
-            continue;
-        }
-
+    foreach ($modinfo->sections[$sectionnum] as $cmid) {
         $cm = $modinfo->cms[$cmid];
 
         if (!$cm->uservisible) {
@@ -162,6 +152,10 @@ foreach ($sections as $section) {
             continue;
         }
 
+        if (!empty($filter_modid) and $cmid != $filter_modid) {
+            continue;
+        }
+
         $libfile = "$CFG->dirroot/mod/$cm->modname/lib.php";
 
         if (file_exists($libfile)) {
index 46a3137..349fd58 100644 (file)
@@ -36,7 +36,6 @@ class recent_form extends moodleform {
         $mform =& $this->_form;
         $context = context_course::instance($COURSE->id);
         $modinfo = get_fast_modinfo($COURSE);
-        $sections = get_all_sections($COURSE->id);
 
         $mform->addElement('header', 'filters', get_string('managefilters')); //TODO: add better string
 
@@ -127,7 +126,7 @@ class recent_form extends moodleform {
         }
 
         foreach ($modinfo->sections as $section=>$cmids) {
-            $options["section/$section"] = "-- ".get_section_name($COURSE, $sections[$section])." --";
+            $options["section/$section"] = "-- ".get_section_name($COURSE, $section)." --";
             foreach ($cmids as $cmid) {
                 $cm = $modinfo->cms[$cmid];
                 if (empty($modsused[$cm->modname]) or !$cm->uservisible) {
index abf50b2..6debe85 100644 (file)
@@ -65,9 +65,6 @@ echo $OUTPUT->header();
 
 $modinfo = get_fast_modinfo($course);
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 $cms = array();
 $resources = array();
 foreach ($modinfo->cms as $cm) {
@@ -116,7 +113,7 @@ foreach ($cms as $cm) {
         $printsection = '';
         if ($cm->sectionnum !== $currentsection) {
             if ($cm->sectionnum) {
-                $printsection = get_section_name($course, $sections[$cm->sectionnum]);
+                $printsection = get_section_name($course, $cm->sectionnum);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 35f685a..12371f8 100644 (file)
@@ -97,7 +97,6 @@ switch($requestmethod) {
                         }
                         break;
                 }
-                rebuild_course_cache($course->id);
                 break;
 
             case 'resource':
@@ -117,6 +116,7 @@ switch($requestmethod) {
                         $cm->indent = $value;
                         if ($cm->indent >= 0) {
                             $DB->update_record('course_modules', $cm);
+                            rebuild_course_cache($cm->course);
                         }
                         break;
 
@@ -159,6 +159,7 @@ switch($requestmethod) {
 
                         if (!empty($module->name)) {
                             $DB->update_record($cm->modname, $module);
+                            rebuild_course_cache($cm->course);
                         } else {
                             $module->name = $cm->name;
                         }
@@ -169,7 +170,6 @@ switch($requestmethod) {
                         echo json_encode(array('instancename' => format_string($module->name, true,  $stringoptions)));
                         break;
                 }
-                rebuild_course_cache($course->id);
                 break;
 
             case 'course':
@@ -222,8 +222,6 @@ switch($requestmethod) {
                 $eventdata->userid     = $USER->id;
                 events_trigger('mod_deleted', $eventdata);
 
-                rebuild_course_cache($course->id);
-
                 add_to_log($courseid, "course", "delete mod",
                            "view.php?id=$courseid",
                            "$cm->modname $cm->instance", $cm->id);
index 87fa03d..c964414 100644 (file)
@@ -62,6 +62,41 @@ class courselib_testcase extends advanced_testcase {
         $this->assertGreaterThan(0, $blockcount);
     }
 
+    public function test_create_course_with_generator() {
+        global $DB;
+        $this->resetAfterTest(true);
+        $course = $this->getDataGenerator()->create_course();
+
+        // Ensure default section is created.
+        $sectioncreated = $DB->record_exists('course_sections', array('course' => $course->id, 'section' => 0));
+        $this->assertTrue($sectioncreated);
+    }
+
+    public function test_create_course_sections() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        $course = $this->getDataGenerator()->create_course(
+                array('shortname' => 'GrowingCourse',
+                    'fullname' => 'Growing Course',
+                    'numsections' => 5),
+                array('createsections' => true));
+
+        // Ensure all 6 (0-5) sections were created and modinfo/sectioninfo cache works properly
+        $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
+        $this->assertEquals(range(0, $course->numsections), $sectionscreated);
+
+        // this will do nothing, section already exists
+        $this->assertFalse(course_create_sections_if_missing($course, $course->numsections));
+
+        // this will create new section
+        $this->assertTrue(course_create_sections_if_missing($course, $course->numsections + 1));
+
+        // Ensure all 7 (0-6) sections were created and modinfo/sectioninfo cache works properly
+        $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
+        $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
+    }
+
     public function test_reorder_sections() {
         global $DB;
         $this->resetAfterTest(true);
@@ -260,4 +295,23 @@ class courselib_testcase extends advanced_testcase {
         $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
         $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
     }
+
+    public function test_move_module_in_course() {
+        $this->resetAfterTest(true);
+        // Setup fixture
+        $course = $this->getDataGenerator()->create_course(array('numsections'=>5));
+        $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
+
+        $cms = get_fast_modinfo($course)->get_cms();
+        $cm = reset($cms);
+
+        course_create_sections_if_missing($course, 3);
+        $section3 = get_fast_modinfo($course)->get_section_info(3);
+
+        moveto_module($cm, $section3);
+
+        $modinfo = get_fast_modinfo($course);
+        $this->assertTrue(empty($modinfo->sections[0]));
+        $this->assertFalse(empty($modinfo->sections[3]));
+    }
 }
index f0c3079..e428a26 100644 (file)
                 if (!empty($move) and has_capability('moodle/course:movesections', $context) and confirm_sesskey()) {
                     $destsection = $section + $move;
                     if (move_section_to($course, $section, $destsection)) {
-                        // Rebuild course cache, after moving section
-                        rebuild_course_cache($course->id, true);
                         if ($course->id == SITEID) {
                             redirect($CFG->wwwroot . '/?redirect=0');
                         } else {
     // Course wrapper start.
     echo html_writer::start_tag('div', array('class'=>'course-content'));
 
-    $modinfo = get_fast_modinfo($COURSE);
-    get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
-    foreach($mods as $modid=>$unused) {
-        if (!isset($modinfo->cms[$modid])) {
-            rebuild_course_cache($course->id);
-            $modinfo = get_fast_modinfo($COURSE);
-            debugging('Rebuilding course cache', DEBUG_DEVELOPER);
-            break;
-        }
-    }
-
-    if (!$sections = $modinfo->get_section_info_all()) {   // No sections found
-        $section = new stdClass;
-        $section->course = $course->id;   // Create a default section.
-        $section->section = 0;
-        $section->visible = 1;
-        $section->summaryformat = FORMAT_HTML;
-        $section->id = $DB->insert_record('course_sections', $section);
-        rebuild_course_cache($course->id);
-        $modinfo = get_fast_modinfo($COURSE);
-        if (!$sections = $modinfo->get_section_info_all()) {      // Try again
-            print_error('cannotcreateorfindstructs', 'error');
-        }
-    }
+    // make sure that section 0 exists (this function will create one if it is missing)
+    course_create_sections_if_missing($course, 0);
+
+    // get information about course modules and existing module types
+    // format.php in course formats may rely on presence of these variables
+    $modinfo = get_fast_modinfo($course);
+    $modnames = get_module_types_names();
+    $modnamesplural = get_module_types_names(true);
+    $modnamesused = $modinfo->get_used_module_names();
+    $mods = $modinfo->get_cms();
+    $sections = $modinfo->get_section_info_all();
 
     // CAUTION, hacky fundamental variable defintion to follow!
     // Note that because of the way course fromats are constructed though
index 0e00925..f70b07f 100644 (file)
@@ -434,11 +434,8 @@ function process_group_tag($tagcontents) {
                     $course = $DB->get_record('course', array('id' => $courseid));
                     blocks_add_default_course_blocks($course);
 
-                    $section = new stdClass();
-                    $section->course = $course->id;   // Create a default section.
-                    $section->section = 0;
-                    $section->summaryformat = FORMAT_HTML;
-                    $section->id = $DB->insert_record("course_sections", $section);
+                    // Create default 0-section
+                    course_create_sections_if_missing($course, 0);
 
                     add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
 
index 9040b21..1c7f205 100644 (file)
@@ -27,7 +27,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 if ($ADMIN->fulltree) {
-    require_once('locallib.php');
+    require_once($CFG->dirroot.'/enrol/imsenterprise/locallib.php');
 
     $settings->add(new admin_setting_heading('enrol_imsenterprise_settings', '', get_string('pluginname_desc', 'enrol_imsenterprise')));
 
index acdb2eb..442822c 100644 (file)
@@ -58,6 +58,7 @@ Example:
 
 $verbose = !empty($options['verbose']);
 
+/** @var $plugin enrol_manual_plugin */
 $plugin = enrol_get_plugin('manual');
 
 $result = $plugin->sync(null, $verbose);
index 3f7d8ed..f699be0 100644 (file)
@@ -446,6 +446,9 @@ class enrol_manual_plugin extends enrol_plugin {
 
             if ($ue->timeend - $ue->expirythreshold + 86400 < $timenow) {
                 // Notify enrolled users only once at the start of the threshold.
+                if ($verbose) {
+                    mtrace("  user $ue->userid was already notified that enrolment in course $ue->courseid expires on ".userdate($ue->timeend, '', $CFG->timezone));
+                }
                 continue;
             }
 
index 6d712f7..ffd850a 100644 (file)
@@ -44,6 +44,7 @@ class enrol_manual_lib_testcase extends advanced_testcase {
 
         $this->resetAfterTest();
 
+        /** @var $manplugin enrol_manual_plugin */
         $manplugin = enrol_get_plugin('manual');
 
         // Setup a few courses and users.
@@ -206,6 +207,7 @@ class enrol_manual_lib_testcase extends advanced_testcase {
         global $DB;
         $this->resetAfterTest();
 
+        /** @var $manualplugin enrol_manual_plugin */
         $manualplugin = enrol_get_plugin('manual');
 
         $now = time();
@@ -301,4 +303,166 @@ class enrol_manual_lib_testcase extends advanced_testcase {
         $this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id)));
         $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id)));
     }
+
+    public function test_send_expiry_notifications() {
+        global $DB, $CFG;
+        $this->resetAfterTest();
+        $this->preventResetByRollback(); // Messaging does not like transactions...
+
+        /** @var $manualplugin enrol_manual_plugin */
+        $manualplugin = enrol_get_plugin('manual');
+        $now = time();
+        $admin = get_admin();
+
+        // Note: hopefully nobody executes the unit tests the last second before midnight...
+
+        $manualplugin->set_config('notifylast', $now - 60*60*24);
+        $manualplugin->set_config('notifyhour', 0);
+
+        $studentrole = $DB->get_record('role', array('shortname'=>'student'));
+        $this->assertNotEmpty($studentrole);
+        $editingteacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'));
+        $this->assertNotEmpty($editingteacherrole);
+        $managerrole = $DB->get_record('role', array('shortname'=>'manager'));
+        $this->assertNotEmpty($managerrole);
+
+        $user1 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser1'));
+        $user2 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser2'));
+        $user3 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser3'));
+        $user4 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser4'));
+        $user5 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser5'));
+        $user6 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6'));
+        $user7 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6'));
+        $user8 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6'));
+
+        $course1 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse1'));
+        $course2 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse2'));
+        $course3 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse3'));
+        $course4 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse4'));
+
+        $this->assertEquals(4, $DB->count_records('enrol', array('enrol'=>'manual')));
+
+        $instance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $instance1->expirythreshold = 60*60*24*4;
+        $instance1->expirynotify    = 1;
+        $instance1->notifyall       = 1;
+        $DB->update_record('enrol', $instance1);
+
+        $instance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $instance2->expirythreshold = 60*60*24*1;
+        $instance2->expirynotify    = 1;
+        $instance2->notifyall       = 1;
+        $DB->update_record('enrol', $instance2);
+
+        $instance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $instance3->expirythreshold = 60*60*24*1;
+        $instance3->expirynotify    = 1;
+        $instance3->notifyall       = 0;
+        $DB->update_record('enrol', $instance3);
+
+        $instance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $instance4->expirythreshold = 60*60*24*1;
+        $instance4->expirynotify    = 0;
+        $instance4->notifyall       = 0;
+        $DB->update_record('enrol', $instance4);
+
+        $manualplugin->enrol_user($instance1, $user1->id, $editingteacherrole->id, 0, $now + 60*60*24*1, ENROL_USER_SUSPENDED); // Suspended users are not notified.
+        $manualplugin->enrol_user($instance1, $user2->id, $studentrole->id, 0, $now + 60*60*24*5);                       // Above threshold are not notified.
+        $manualplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now + 60*60*24*3 + 60*60);               // Less than one day after threshold - should be notified.
+        $manualplugin->enrol_user($instance1, $user4->id, $studentrole->id, 0, $now + 60*60*24*4 - 60*3);                // Less than one day after threshold - should be notified.
+        $manualplugin->enrol_user($instance1, $user5->id, $studentrole->id, 0, $now + 60*60);                            // Should have been already notified.
+        $manualplugin->enrol_user($instance1, $user6->id, $studentrole->id, 0, $now - 60);                               // Already expired.
+        $manualplugin->enrol_user($instance1, $user7->id, $editingteacherrole->id);
+        $manualplugin->enrol_user($instance1, $user8->id, $managerrole->id);                                             // Highest role --> enroller.
+
+        $manualplugin->enrol_user($instance2, $user1->id, $studentrole->id);
+        $manualplugin->enrol_user($instance2, $user2->id, $studentrole->id, 0, $now + 60*60*24*1 + 60*3);                // Above threshold are not notified.
+        $manualplugin->enrol_user($instance2, $user3->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60);               // Less than one day after threshold - should be notified.
+
+        $manualplugin->enrol_user($instance3, $user1->id, $editingteacherrole->id);
+        $manualplugin->enrol_user($instance3, $user2->id, $studentrole->id, 0, $now + 60*60*24*1 + 60);                  // Above threshold are not notified.
+        $manualplugin->enrol_user($instance3, $user3->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60);               // Less than one day after threshold - should be notified.
+
+        $manualplugin->enrol_user($instance4, $user4->id, $editingteacherrole->id);
+        $manualplugin->enrol_user($instance4, $user5->id, $studentrole->id, 0, $now + 60*60*24*1 + 60);
+        $manualplugin->enrol_user($instance4, $user6->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60);
+
+        // The notification is sent out in fixed order first individual users,
+        // then summary per course by enrolid, user lastname, etc.
+        $this->assertGreaterThan($instance1->id, $instance2->id);
+        $this->assertGreaterThan($instance2->id, $instance3->id);
+
+        $sink = $this->redirectMessages();
+
+        $manualplugin->send_notifications(false);
+
+        $messages = $sink->get_messages();
+
+        $this->assertEquals(2+1 + 1+1 + 1 + 0, count($messages));
+
+        // First individual notifications from course1.
+        $this->assertEquals($user3->id, $messages[0]->useridto);
+        $this->assertEquals($user8->id, $messages[0]->useridfrom);
+        $this->assertContains('xcourse1', $messages[0]->fullmessagehtml);
+
+        $this->assertEquals($user4->id, $messages[1]->useridto);
+        $this->assertEquals($user8->id, $messages[1]->useridfrom);
+        $this->assertContains('xcourse1', $messages[1]->fullmessagehtml);
+
+        // Then summary for course1.
+        $this->assertEquals($user8->id, $messages[2]->useridto);
+        $this->assertEquals($admin->id, $messages[2]->useridfrom);
+        $this->assertContains('xcourse1', $messages[2]->fullmessagehtml);
+        $this->assertNotContains('xuser1', $messages[2]->fullmessagehtml);
+        $this->assertNotContains('xuser2', $messages[2]->fullmessagehtml);
+        $this->assertContains('xuser3', $messages[2]->fullmessagehtml);
+        $this->assertContains('xuser4', $messages[2]->fullmessagehtml);
+        $this->assertContains('xuser5', $messages[2]->fullmessagehtml);
+        $this->assertNotContains('xuser6', $messages[2]->fullmessagehtml);
+
+        // First individual notifications from course2.
+        $this->assertEquals($user3->id, $messages[3]->useridto);
+        $this->assertEquals($admin->id, $messages[3]->useridfrom);
+        $this->assertContains('xcourse2', $messages[3]->fullmessagehtml);
+
+        // Then summary for course2.
+        $this->assertEquals($admin->id, $messages[4]->useridto);
+        $this->assertEquals($admin->id, $messages[4]->useridfrom);
+        $this->assertContains('xcourse2', $messages[4]->fullmessagehtml);
+        $this->assertNotContains('xuser1', $messages[4]->fullmessagehtml);
+        $this->assertNotContains('xuser2', $messages[4]->fullmessagehtml);
+        $this->assertContains('xuser3', $messages[4]->fullmessagehtml);
+        $this->assertNotContains('xuser4', $messages[4]->fullmessagehtml);
+        $this->assertNotContains('xuser5', $messages[4]->fullmessagehtml);
+        $this->assertNotContains('xuser6', $messages[4]->fullmessagehtml);
+
+        // Only summary in course3.
+        $this->assertEquals($user1->id, $messages[5]->useridto);
+        $this->assertEquals($admin->id, $messages[5]->useridfrom);
+        $this->assertContains('xcourse3', $messages[5]->fullmessagehtml);
+        $this->assertNotContains('xuser1', $messages[5]->fullmessagehtml);
+        $this->assertNotContains('xuser2', $messages[5]->fullmessagehtml);
+        $this->assertContains('xuser3', $messages[5]->fullmessagehtml);
+        $this->assertNotContains('xuser4', $messages[5]->fullmessagehtml);
+        $this->assertNotContains('xuser5', $messages[5]->fullmessagehtml);
+        $this->assertNotContains('xuser6', $messages[5]->fullmessagehtml);
+
+
+        // Make sure that notifications are not repeated.
+        $sink->clear();
+
+        $manualplugin->send_notifications(false);
+        $this->assertEquals(0, $sink->count());
+
+        // use invalid notification hour to verify that before the hour the notifications are not sent.
+        $manualplugin->set_config('notifylast', time() - 60*60*24);
+        $manualplugin->set_config('notifyhour', '24');
+
+        $manualplugin->send_notifications(false);
+        $this->assertEquals(0, $sink->count());
+
+        $manualplugin->set_config('notifyhour', '0');
+        $manualplugin->send_notifications(false);
+        $this->assertEquals(6, $sink->count());
+    }
 }
index dd35032..b416975 100644 (file)
--- a/index.php
+++ b/index.php
     echo $OUTPUT->header();
 
 /// Print Section or custom info
-    get_all_mods($SITE->id, $mods, $modnames, $modnamesplural, $modnamesused);
+    $modinfo = get_fast_modinfo($SITE);
+    $modnames = get_module_types_names();
+    $modnamesplural = get_module_types_names(true);
+    $modnamesused = $modinfo->get_used_module_names();
+    $mods = $modinfo->get_cms();
+
     if (!empty($CFG->customfrontpageinclude)) {
         include($CFG->customfrontpageinclude);
 
-    } else if ($SITE->numsections > 0) {
-
-        if (!$section = $DB->get_record('course_sections', array('course'=>$SITE->id, 'section'=>1))) {
-            $DB->delete_records('course_sections', array('course'=>$SITE->id, 'section'=>1)); // Just in case
-            $section = new stdClass();
-            $section->course = $SITE->id;
-            $section->section = 1;
-            $section->summary = '';
-            $section->summaryformat = FORMAT_HTML;
-            $section->sequence = '';
-            $section->visible = 1;
-            $section->id = $DB->insert_record('course_sections', $section);
-            rebuild_course_cache($SITE->id, true);
+    } else {
+        if ($editing) {
+            // make sure section with number 1 exists
+            course_create_sections_if_missing($SITE, 1);
+            // re-request modinfo in case section was created
+            $modinfo = get_fast_modinfo($SITE);
         }
-
-        if (!empty($section->sequence) or !empty($section->summary) or $editing) {
+        $section = $modinfo->get_section_info(1);
+        if (($section && (!empty($modinfo->sections[1]) or !empty($section->summary))) or $editing) {
             echo $OUTPUT->box_start('generalbox sitetopic');
 
             /// If currently moving a file then show the current clipboard
index 6a2c0c8..f8be051 100644 (file)
@@ -260,7 +260,7 @@ $string['configmycoursesperpage'] = 'Maximum number of courses to display in any
 $string['configmymoodleredirect'] = 'This setting forces redirects to /my on login for non-admins and replaces the top level site navigation with /my';
 $string['configmypagelocked'] = 'This setting prevents the default page from being edited by any non-admins';
 $string['confignavcourselimit'] = 'Limits the number of courses shown to the user when they are either not logged in or are not enrolled in any courses.';
-$string['confignavshowallcourses'] = 'If enabled users will see courses they are enrolled in both within the My Courses branch and the course structure. When disabled users with enrolments will only see the My Courses branch of the navigaiton.';
+$string['confignavshowallcourses'] = 'If enabled users will see courses they are enrolled in both within the My Courses branch and the course structure. When disabled users with enrolments will only see the My Courses branch of the navigaiton. The number of course shown would still be limited by "Course limit(navcourselimit)" setting when user is either not logged in or not enrolled in any course.';
 $string['confignavshowcategories'] = 'Show course categories in the navigation bar and navigation blocks. This does not occur with courses the user is currently enrolled in, they will still be listed under mycourses without categories.';
 $string['confignotifyloginfailures'] = 'If login failures have been recorded, email notifications can be sent out.  Who should see these notifications?';
 $string['confignotifyloginthreshold'] = 'If notifications about failed logins are active, how many failed login attempts by one user or one IP address is it worth notifying about?';
index 4d6ea82..41b7921 100644 (file)
@@ -971,8 +971,8 @@ class completion_info {
 
         if ($data->userid == $USER->id) {
             $SESSION->completioncache[$cm->course][$cm->id] = $data;
-            $reset = 'reset';
-            get_fast_modinfo($reset);
+            // reset modinfo for user (no need to call rebuild_course_cache())
+            get_fast_modinfo($cm->course, 0, true);
         }
     }
 
index bc1d24d..1a2b205 100644 (file)
@@ -2924,3 +2924,123 @@ function get_generic_section_name($format, stdClass $section) {
     debugging('get_generic_section_name() is deprecated. Please use appropriate functionality from class format_base', DEBUG_DEVELOPER);
     return get_string('sectionname', "format_$format") . ' ' . $section->section;
 }
+
+/**
+ * Returns an array of sections for the requested course id
+ *
+ * It is usually not recommended to display the list of sections used
+ * in course because the course format may have it's own way to do it.
+ *
+ * If you need to just display the name of the section please call:
+ * get_section_name($course, $section)
+ * {@link get_section_name()}
+ * from 2.4 $section may also be just the field course_sections.section
+ *
+ * If you need the list of all sections it is more efficient to get this data by calling
+ * $modinfo = get_fast_modinfo($courseorid);
+ * $sections = $modinfo->get_section_info_all()
+ * {@link get_fast_modinfo()}
+ * {@link course_modinfo::get_section_info_all()}
+ *
+ * Information about one section (instance of section_info):
+ * get_fast_modinfo($courseorid)->get_sections_info($section)
+ * {@link course_modinfo::get_section_info()}
+ *
+ * @deprecated since 2.4
+ *
+ * @param int $courseid
+ * @return array Array of section_info objects
+ */
+function get_all_sections($courseid) {
+    global $DB;
+    debugging('get_all_sections() is deprecated. See phpdocs for this function', DEBUG_DEVELOPER);
+    return get_fast_modinfo($courseid)->get_section_info_all();
+}
+
+/**
+ * Given a full mod object with section and course already defined, adds this module to that section.
+ *
+ * This function is deprecated, please use {@link course_add_cm_to_section()}
+ * Note that course_add_cm_to_section() also updates field course_modules.section and
+ * calls rebuild_course_cache()
+ *
+ * @deprecated since 2.4
+ *
+ * @param object $mod
+ * @param int $beforemod An existing ID which we will insert the new module before
+ * @return int The course_sections ID where the mod is inserted
+ */
+function add_mod_to_section($mod, $beforemod = null) {
+    debugging('Function add_mod_to_section() is deprecated, please use course_add_cm_to_section()', DEBUG_DEVELOPER);
+    global $DB;
+    return course_add_cm_to_section($mod->course, $mod->coursemodule, $mod->section, $beforemod);
+}
+
+/**
+ * Returns a number of useful structures for course displays
+ *
+ * Function get_all_mods() is deprecated in 2.4
+ * Instead of:
+ * <code>
+ * get_all_mods($courseid, $mods, $modnames, $modnamesplural, $modnamesused);
+ * </code>
+ * please use:
+ * <code>
+ * $mods = get_fast_modinfo($courseorid)->get_cms();
+ * $modnames = get_module_types_names();
+ * $modnamesplural = get_module_types_names(true);
+ * $modnamesused = get_fast_modinfo($courseorid)->get_used_module_names();
+ * </code>
+ *
+ * @deprecated since 2.4
+ *
+ * @param int $courseid id of the course to get info about
+ * @param array $mods (return) list of course modules
+ * @param array $modnames (return) list of names of all module types installed and available
+ * @param array $modnamesplural (return) list of names of all module types installed and available in the plural form
+ * @param array $modnamesused (return) list of names of all module types used in the course
+ */
+function get_all_mods($courseid, &$mods, &$modnames, &$modnamesplural, &$modnamesused) {
+    debugging('Function get_all_mods() is deprecated. Use get_fast_modinfo() and get_module_types_names() instead. See phpdocs for details', DEBUG_DEVELOPER);
+
+    global $COURSE;
+    $modnames      = get_module_types_names();
+    $modnamesplural= get_module_types_names(true);
+    $modinfo = get_fast_modinfo($courseid);
+    $mods = $modinfo->get_cms();
+    $modnamesused = $modinfo->get_used_module_names();
+}
+
+/**
+ * Returns course section - creates new if does not exist yet
+ *
+ * This function is deprecated. To create a course section call:
+ * course_create_sections_if_missing($courseorid, $sections);
+ * to get the section call:
+ * get_fast_modinfo($courseorid)->get_section_info($sectionnum);
+ *
+ * @see course_create_sections_if_missing()
+ * @see get_fast_modinfo()
+ * @deprecated since 2.4
+ *
+ * @param int $section relative section number (field course_sections.section)
+ * @param int $courseid
+ * @return stdClass record from table {course_sections}
+ */
+function get_course_section($section, $courseid) {
+    global $DB;
+    debugging('Function get_course_section() is deprecated. Please use course_create_sections_if_missing() and get_fast_modinfo() instead.', DEBUG_DEVELOPER);
+
+    if ($cw = $DB->get_record("course_sections", array("section"=>$section, "course"=>$courseid))) {
+        return $cw;
+    }
+    $cw = new stdClass();
+    $cw->course   = $courseid;
+    $cw->section  = $section;
+    $cw->summary  = "";
+    $cw->summaryformat = FORMAT_HTML;
+    $cw->sequence = "";
+    $id = $DB->insert_record("course_sections", $cw);
+    rebuild_course_cache($courseid, true);
+    return $DB->get_record("course_sections", array("id"=>$id));
+}
index 5c15e1b..013319c 100644 (file)
@@ -39,12 +39,23 @@ class plugininfo_tinymce extends plugininfo_base {
         return new moodle_url('/lib/editor/tinymce/subplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
     }
 
-    public function get_settings_url() {
-        global $CFG;
-        if (file_exists("$CFG->dirroot/lib/editor/tinymce/plugins/$this->name/settings.php")) {
-            return new moodle_url('/admin/settings.php', array('section'=>'tinymce'.$this->name.'settings'));
-        } else {
-            return null;
+    public function get_settings_section_name() {
+        return 'tinymce'.$this->name.'settings';
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+
+        $settings = null;
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $section = $this->get_settings_section_name();
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 
index a91c8a7..b3479c0 100644 (file)
@@ -156,6 +156,7 @@ class tinymce_texteditor extends texteditor {
                 'searchreplace,paste,directionality,fullscreen,nonbreaking,contextmenu,' .
                 'insertdatetime,save,iespell,preview,print,noneditable,visualchars,' .
                 'xhtmlxtras,template,pagebreak',
+            'gecko_spellcheck' => true,
             'theme_advanced_font_sizes' => "1,2,3,4,5,6,7",
             'theme_advanced_layout_manager' => "SimpleLayout",
             'theme_advanced_toolbar_align' => "left",
index b2aee6d..b4f5118 100644 (file)
@@ -35,6 +35,9 @@ class tinymce_spellchecker extends editor_tinymce_plugin {
         // Check at least one language is supported.
         $spelllanguagelist = $this->get_config('spelllanguagelist', '');
         if ($spelllanguagelist !== '') {
+            // Prevent the built-in spell checker in Firefox, Safari and other sane browsers.
+            unset($params['gecko_spellcheck']);
+
             // Add button after code button in advancedbuttons3.
             $added = $this->add_button_after($params, 3, 'spellchecker', 'code', false);
 
index 56ee3d0..5b16a61 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$ADMIN->add('editorsettings', new admin_category('editortinymce', new lang_string('pluginname', 'editor_tinymce')));
+$ADMIN->add('editorsettings', new admin_category('editortinymce', $editor->displayname, $editor->is_enabled() === false));
 
 $settings = new admin_settingpage('editorsettingstinymce', new lang_string('settings', 'editor_tinymce'));
 if ($ADMIN->fulltree) {
@@ -45,20 +45,11 @@ bullist,numlist,outdent,indent,|,link,unlink,|,image,nonbreaking,charmap,table,|
 $ADMIN->add('editortinymce', $settings);
 unset($settings);
 
-$subplugins = get_plugin_list('tinymce');
-$disabled = array(); // Disabling of subplugins to be implemented later.
-foreach ($subplugins as $name=>$dir) {
-    if (file_exists("$dir/settings.php")) {
-        $settings = new admin_settingpage('tinymce'.$name.'settings', new lang_string('pluginname', 'tinymce_'.$name), 'moodle/site:config', in_array($name, $disabled));
-        // settings.php may create a subcategory or unset the settings completely.
-        include("$dir/settings.php");
-        if ($settings) {
-            $ADMIN->add('editortinymce', $settings);
-        }
-    }
+require_once("$CFG->libdir/pluginlib.php");
+$allplugins = plugin_manager::instance()->get_plugins();
+foreach ($allplugins['tinymce'] as $plugin) {
+    $plugin->load_settings($ADMIN, 'editortinymce', $hassiteconfig);
 }
-unset($subplugins);
-unset($disabled);
 
 // TinyMCE does not have standard settings page.
 $settings = null;
index 307c4f6..57bbd94 100644 (file)
@@ -64,6 +64,33 @@ class MoodleQuickForm_text extends HTML_QuickForm_text{
         $this->_hiddenLabel = $hiddenLabel;
     }
 
+    /**
+     * Freeze the element so that only its value is returned and set persistantfreeze to false
+     *
+     * @since     2.4
+     * @access    public
+     * @return    void
+     */
+    function freeze()
+    {
+        $this->_flagFrozen = true;
+        // No hidden element is needed refer MDL-30845
+        $this->setPersistantFreeze(false);
+    } //end func freeze
+
+    /**
+     * Returns the html to be used when the element is frozen
+     *
+     * @since     2.4
+     * @return    string Frozen html
+     */
+    function getFrozenHtml()
+    {
+        $attributes = array('readonly' => 'readonly');
+        $this->updateAttributes($attributes);
+        return $this->_getTabs() . '<input' . $this->_getAttrString($this->_attributes) . ' />' . $this->_getPersistantData();
+    } //end func getFrozenHtml
+
     /**
      * Returns HTML for this form element.
      *
@@ -88,18 +115,4 @@ class MoodleQuickForm_text extends HTML_QuickForm_text{
         return $this->_helpbutton;
     }
 
-    /**
-     * Slightly different container template when frozen. Don't want to use a label tag
-     * with a for attribute in that case for the element label but instead use a div.
-     * Templates are defined in renderer constructor.
-     *
-     * @return string
-     */
-    function getElementTemplateType(){
-        if ($this->_flagFrozen){
-            return 'static';
-        } else {
-            return 'default';
-        }
-    }
 }
index 140ae0e..d007432 100644 (file)
@@ -114,6 +114,32 @@ function message_send($eventdata) {
 
     $savemessage->timecreated = time();
 
+    if (PHPUNIT_TEST and class_exists('phpunit_util')) {
+        // Add some more tests to make sure the normal code can actually work.
+        $componentdir = get_component_directory($eventdata->component);
+        if (!$componentdir or !is_dir($componentdir)) {
+            throw new coding_exception('Invalid component specified in message-send(): '.$eventdata->component);
+        }
+        if (!file_exists("$componentdir/db/messages.php")) {
+            throw new coding_exception("$eventdata->component does not contain db/messages.php necessary for message_send()");
+        }
+        $messageproviders = null;
+        include("$componentdir/db/messages.php");
+        if (!isset($messageproviders[$eventdata->name])) {
+            throw new coding_exception("Missing messaging defaults for event '$eventdata->name' in '$eventdata->component' messages.php file");
+        }
+        unset($componentdir);
+        unset($messageproviders);
+        // Now ask phpunit if it wants to catch this message.
+        if (phpunit_util::is_redirecting_messages()) {
+            $savemessage->timeread = time();
+            $messageid = $DB->insert_record('message_read', $savemessage);
+            $message = $DB->get_record('message_read', array('id'=>$messageid));
+            phpunit_util::message_sent($message);
+            return $messageid;
+        }
+    }
+
     // Fetch enabled processors
     $processors = get_message_processors(true);
     // Fetch default (site) preferences
index 6af2ec3..405fd7b 100644 (file)
@@ -158,6 +158,24 @@ class course_modinfo extends stdClass {
         return $this->instances;
     }
 
+    /**
+     * Returns array of localised human-readable module names used in this course
+     *
+     * @param bool $plural if true returns the plural form of modules names
+     * @return array
+     */
+    public function get_used_module_names($plural = false) {
+        $modnames = get_module_types_names($plural);
+        $modnamesused = array();
+        foreach ($this->get_cms() as $cmid => $mod) {
+            if (isset($modnames[$mod->modname]) && $mod->uservisible) {
+                $modnamesused[$mod->modname] = $modnames[$mod->modname];
+            }
+        }
+        collatorlib::asort($modnamesused);
+        return $modnamesused;
+    }
+
     /**
      * Obtains all instances of a particular module on this course.
      * @param $modname Name of module (not full frankenstyle) e.g. 'label'
@@ -348,7 +366,7 @@ class course_modinfo extends stdClass {
 
         // Remove unnecessary data and add availability
         foreach ($sections as $number => $section) {
-            // Clone just in case it is reused elsewhere (get_all_sections cache)
+            // Clone just in case it is reused elsewhere
             $compressedsections[$number] = clone($section);
             section_info::convert_for_section_cache($compressedsections[$number]);
         }
@@ -610,14 +628,6 @@ class cm_info extends stdClass {
      */
     public $conditionsfield;
 
-    /**
-     * Plural name of module type, e.g. 'Forums' - from lang file
-     * @deprecated Do not use this value (you can obtain it by calling get_string instead); it
-     *   will be removed in a future version (see later TODO in this file)
-     * @var string
-     */
-    public $modplural;
-
     /**
      * True if this course-module is available to students i.e. if all availability conditions
      * are met - obtained dynamically
@@ -692,6 +702,24 @@ class cm_info extends stdClass {
      */
     private $afterediticons;
 
+    /**
+     * Magic method getter
+     *
+     * @param string $name
+     * @return mixed
+     */
+    public function __get($name) {
+        switch ($name) {
+            case 'modplural':
+                return $this->get_module_type_name(true);
+            case 'modfullname':
+                return $this->get_module_type_name();
+            default:
+                debugging('Invalid cm_info property accessed: '.$name);
+                return null;
+        }
+    }
+
     /**
      * @return bool True if this module has a 'view' page that should be linked to in navigation
      *   etc (note: modules may still have a view.php file, but return false if this is not
@@ -793,6 +821,21 @@ class cm_info extends stdClass {
         return $icon;
     }
 
+    /**
+     * Returns a localised human-readable name of the module type
+     *
+     * @param bool $plural return plural form
+     * @return string
+     */
+    public function get_module_type_name($plural = false) {
+        $modnames = get_module_types_names($plural);
+        if (isset($modnames[$this->modname])) {
+            return $modnames[$this->modname];
+        } else {
+            return null;
+        }
+    }
+
     /**
      * @return course_modinfo Modinfo object that this came from
      */
@@ -1013,16 +1056,6 @@ class cm_info extends stdClass {
         $this->conditionsfield = isset($mod->conditionsfield)
                 ? $mod->conditionsfield : array();
 
-        // Get module plural name.
-        // TODO This was a very old performance hack and should now be removed as the information
-        // certainly doesn't belong in modinfo. On a 'normal' page this is only used in the
-        // activity_modules block, so if it needs caching, it should be cached there.
-        static $modplurals;
-        if (!isset($modplurals[$this->modname])) {
-            $modplurals[$this->modname] = get_string('modulenameplural', $this->modname);
-        }
-        $this->modplural = $modplurals[$this->modname];
-
         static $modviews;
         if (!isset($modviews[$this->modname])) {
             $modviews[$this->modname] = !plugin_supports('mod', $this->modname,
@@ -1183,16 +1216,14 @@ class cm_info extends stdClass {
  * Returns reference to full info about modules in course (including visibility).
  * Cached and as fast as possible (0 or 1 db query).
  *
- * @global object
- * @global object
- * @global moodle_database
  * @uses MAX_MODINFO_CACHE_SIZE
- * @param mixed $course object or 'reset' string to reset caches, modinfo may be updated in db
- * @param int $userid Defaults to current user id
- * @return course_modinfo Module information for course, or null if resetting
+ * @param int|stdClass $courseorid object or 'reset' string to reset caches, modinfo may be updated in db
+ * @param int $userid Set 0 (default) for current user
+ * @param bool $resetonly whether we want to get modinfo or just reset the cache
+ * @return course_modinfo|null Module information for course, or null if resetting
  */
-function get_fast_modinfo(&$course, $userid=0) {
-    global $CFG, $USER, $DB;
+function get_fast_modinfo($courseorid, $userid = 0, $resetonly = false) {
+    global $CFG, $USER;
     require_once($CFG->dirroot.'/course/lib.php');
 
     if (!empty($CFG->enableavailability)) {
@@ -1201,17 +1232,45 @@ function get_fast_modinfo(&$course, $userid=0) {
 
     static $cache = array();
 
-    if ($course === 'reset') {
-        $cache = array();
+    // compartibility with syntax prior to 2.4:
+    if ($courseorid === 'reset') {
+        debugging("Using the string 'reset' as the first argument of get_fast_modinfo() is deprecated. Use get_fast_modinfo(0,0,true) instead.", DEBUG_DEVELOPER);
+        $courseorid = 0;
+        $resetonly = true;
+    }
+
+    if (is_object($courseorid)) {
+        $course = $courseorid;
+    } else {
+        $course = (object)array('id' => $courseorid, 'modinfo' => null, 'sectioncache' => null);
+    }
+
+    // Function is called with $reset = true
+    if ($resetonly) {
+        if (isset($course->id) && $course->id > 0) {
+            $cache[$course->id] = false;
+        } else {
+            foreach (array_keys($cache) as $key) {
+                $cache[$key] = false;
+            }
+        }
         return null;
     }
 
+    // Function is called with $reset = false, retrieve modinfo
     if (empty($userid)) {
         $userid = $USER->id;
     }
 
-    if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) {
-        return $cache[$course->id];
+    if (array_key_exists($course->id, $cache)) {
+        if ($cache[$course->id] === false) {
+            // this course has been recently reset, do not rely on modinfo and sectioncache in $course
+            $course->modinfo = null;
+            $course->sectioncache = null;
+        } else if ($cache[$course->id]->userid == $userid) {
+            // this course's modinfo for the same user was recently retrieved, return cached
+            return $cache[$course->id];
+        }
     }
 
     if (!property_exists($course, 'modinfo')) {
@@ -1269,8 +1328,7 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
             $COURSE->sectioncache = null;
         }
         // reset the fast modinfo cache
-        $reset = 'reset';
-        get_fast_modinfo($reset);
+        get_fast_modinfo($courseid, 0, true);
         return;
     }
 
@@ -1298,8 +1356,7 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
     }
     $rs->close();
     // reset the fast modinfo cache
-    $reset = 'reset';
-    get_fast_modinfo($reset);
+    get_fast_modinfo($courseid, 0, true);
 }
 
 
index e5dc06f..1a5a932 100644 (file)
@@ -3427,10 +3427,9 @@ class settings_navigation extends navigation_node {
      */
     protected function get_course_modules($course) {
         global $CFG;
-        $mods = $modnames = $modnamesplural = $modnamesused = array();
         // This function is included when we include course/lib.php at the top
         // of this file
-        get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
+        $modnames = get_module_types_names();
         $resources = array();
         $activities = array();
         foreach($modnames as $modname=>$modnamestr) {
index e4bace9..c63eed9 100644 (file)
@@ -306,6 +306,19 @@ abstract class advanced_testcase extends PHPUnit_Framework_TestCase {
         $this->assertEquals(0, $count, $message);
     }
 
+    /**
+     * Starts message redirection.
+     *
+     * You can verify if messages were sent or not by inspecting the messages
+     * array in the returned messaging sink instance. The redirection
+     * can be stopped by calling $sink->close();
+     *
+     * @return phpunit_message_sink
+     */
+    public function redirectMessages() {
+        return phpunit_util::start_message_redirection();
+    }
+
     /**
      * Cleanup after all tests are executed.
      *
index 515f062..93f28f9 100644 (file)
@@ -157,9 +157,11 @@ EOD;
         }
 
         if (!isset($record['username'])) {
-            $record['username'] = textlib::strtolower($record['firstname']).textlib::strtolower($record['lastname']);
+            $record['username'] = 'username'.$i;
+            $j = 2;
             while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
-                $record['username'] = $record['username'].'_'.$i;
+                $record['username'] = 'username'.$i.'_'.$j;
+                $j++;
             }
         }
 
@@ -369,10 +371,11 @@ EOD;
 
         $course = create_course((object)$record);
         context_course::instance($course->id);
-
         if (!empty($options['createsections'])) {
-            for($i=1; $i<$record['numsections']; $i++) {
-                self::create_course_section(array('course'=>$course->id, 'section'=>$i));
+            if (isset($course->numsections)) {
+                course_create_sections_if_missing($course, range(0, $course->numsections));
+            } else {
+                course_create_sections_if_missing($course, 0);
             }
         }
 
@@ -381,7 +384,7 @@ EOD;
 
     /**
      * Create course section if does not exist yet
-     * @param mixed $record
+     * @param array|stdClass $record must contain 'course' and 'section' attributes
      * @param array|null $options
      * @return stdClass
      * @throws coding_exception
@@ -399,31 +402,8 @@ EOD;
             throw new coding_exception('section must be present in phpunit_util::create_course_section() $record');
         }
 
-        if (!isset($record['name'])) {
-            $record['name'] = '';
-        }
-
-        if (!isset($record['summary'])) {
-            $record['summary'] = '';
-        }
-
-        if (!isset($record['summaryformat'])) {
-            $record['summaryformat'] = FORMAT_MOODLE;
-        }
-
-        if ($section = $DB->get_record('course_sections', array('course'=>$record['course'], 'section'=>$record['section']))) {
-            return $section;
-        }
-
-        $section = new stdClass();
-        $section->course        = $record['course'];
-        $section->section       = $record['section'];
-        $section->name          = $record['name'];
-        $section->summary       = $record['summary'];
-        $section->summaryformat = $record['summaryformat'];
-        $id = $DB->insert_record('course_sections', $section);
-
-        return $DB->get_record('course_sections', array('id'=>$id));
+        course_create_sections_if_missing($record['course'], $record['section']);
+        return get_fast_modinfo($record['course'])->get_section_info($record['section']);
     }
 
     /**
diff --git a/lib/phpunit/classes/message_sink.php b/lib/phpunit/classes/message_sink.php
new file mode 100644 (file)
index 0000000..ffbf83e
--- /dev/null
@@ -0,0 +1,85 @@
+<?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/>.
+
+/**
+ * Message sink.
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2012 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/**
+ * Message sink.
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2012 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class phpunit_message_sink {
+    /** @var array of records from message_read table */
+    protected $messages = array();
+
+    /**
+     * Stop message redirection.
+     *
+     * Use if you do not want message redirected any more.
+     */
+    public function close() {
+        phpunit_util::stop_message_redirection();
+    }
+
+    /**
+     * To be called from phpunit_util only!
+     *
+     * @param stdClass $message record from message_read table
+     */
+    public function add_message($message) {
+        /* Number messages from 0. */
+        $this->messages[] = $message;
+    }
+
+    /**
+     * Returns all redirected messages.
+     *
+     * The instances are records form the message_read table.
+     * The array indexes are numbered from 0 and the order is matching
+     * the creation of events.
+     *
+     * @return array
+     */
+    public function get_messages() {
+        return $this->messages;
+    }
+
+    /**
+     * Return number of messages redirected to this sink.
+     * @return int
+     */
+    public function count() {
+        return count($this->messages);
+    }
+
+    /**
+     * Removes all previously stored messages.
+     */
+    public function clear() {
+        $this->messages = array();
+    }
+}
index e684657..aae6919 100644 (file)
@@ -82,12 +82,13 @@ abstract class phpunit_module_generator {
         require_once("$CFG->dirroot/course/lib.php");
 
         $modulename = $this->get_modulename();
+        $sectionnum = isset($options['section']) ? $options['section'] : 0;
 
         $cm = new stdClass();
         $cm->course             = $courseid;
         $cm->module             = $DB->get_field('modules', 'id', array('name'=>$modulename));
         $cm->instance           = 0;
-        $cm->section            = isset($options['section']) ? $options['section'] : 0;
+        $cm->section            = 0;
         $cm->idnumber           = isset($options['idnumber']) ? $options['idnumber'] : 0;
         $cm->added              = time();
 
@@ -103,9 +104,8 @@ abstract class phpunit_module_generator {
         }
 
         $cm->id = $DB->insert_record('course_modules', $cm);
-        $cm->coursemodule = $cm->id;
 
-        add_mod_to_section($cm);
+        course_add_cm_to_section($courseid, $cm->id, $sectionnum);
 
         return $cm->id;
     }
index 0f95b2f..8e44d80 100644 (file)
@@ -60,6 +60,9 @@ class phpunit_util {
     /** @var array list of debugging messages triggered during the last test execution */
     protected static $debuggings = array();
 
+    /** @var phpunit_message_sink alternative target for moodle messaging */
+    protected static $messagesink = null;
+
     /**
      * Prevent parallel test execution - this can not work in Moodle because we modify database and dataroot.
      *
@@ -553,6 +556,9 @@ class phpunit_util {
     public static function reset_all_data($logchanges = false) {
         global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $GROUPLIB_CACHE;
 
+        // Stop any message redirection.
+        phpunit_util::stop_message_redirection();
+
         // Release memory and indirectly call destroy() methods to release resource handles, etc.
         gc_collect_cycles();
 
@@ -640,8 +646,7 @@ class phpunit_util {
             // If file containing class is not loaded, there is no cache there anyway.
             format_base::reset_course_cache(0);
         }
-        $reset = 'reset';
-        get_fast_modinfo($reset);
+        get_fast_modinfo(0, 0, true);
 
         // purge dataroot directory
         self::reset_dataroot();
@@ -1250,4 +1255,53 @@ class phpunit_util {
 
         return true;
     }
+
+    /**
+     * Start message redirection.
+     *
+     * Note: Do not call directly from tests,
+     *       use $sink = $this->redirectMessages() instead.
+     *
+     * @return phpunit_message_sink
+     */
+    public static function start_message_redirection() {
+        if (self::$messagesink) {
+            self::stop_message_redirection();
+        }
+        self::$messagesink = new phpunit_message_sink();
+        return self::$messagesink;
+    }
+
+    /**
+     * End message redirection.
+     *
+     * Note: Do not call directly from tests,
+     *       use $sink->close() instead.
+     */
+    public static function stop_message_redirection() {
+        self::$messagesink = null;
+    }
+
+    /**
+     * Are messages redirected to some sink?
+     *
+     * Note: to be called from messagelib.php only!
+     *
+     * @return bool
+     */
+    public static function is_redirecting_messages() {
+        return !empty(self::$messagesink);
+    }
+
+    /**
+     * To be called from messagelib.php only!
+     *
+     * @param stdClass $message record from message_read table
+     * @return bool true means send message, false means message "sent" to sink.
+     */
+    public static function message_sent($message) {
+        if (self::$messagesink) {
+            self::$messagesink->add_message($message);
+        }
+    }
 }
index a700157..95b7f73 100644 (file)
@@ -29,6 +29,7 @@ require_once('PHPUnit/Autoload.php');
 require_once('PHPUnit/Extensions/Database/Autoload.php');
 
 require_once(__DIR__.'/classes/util.php');
+require_once(__DIR__.'/classes/message_sink.php');
 require_once(__DIR__.'/classes/basic_testcase.php');
 require_once(__DIR__.'/classes/database_driver_testcase.php');
 require_once(__DIR__.'/classes/arraydataset.php');
index 4b6e75f..d1d1db8 100644 (file)
@@ -317,4 +317,154 @@ class core_phpunit_advanced_testcase extends advanced_testcase {
         $this->assertTrue($DB->record_exists('user', array('username'=>'noidea')));
         $this->assertTrue($DB->record_exists('user', array('username'=>'onemore')));
     }
+
+    public function test_message_redirection() {
+        global $DB;
+
+        $this->preventResetByRollback(); // Messaging is not compatible with transactions...
+        $this->resetAfterTest(false);
+
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+
+        // Any core message will do here.
+        $message1 = new stdClass();
+        $message1->component         = 'moodle';
+        $message1->name              = 'instantmessage';
+        $message1->userfrom          = $user1;
+        $message1->userto            = $user2;
+        $message1->subject           = 'message subject 1';
+        $message1->fullmessage       = 'message body';
+        $message1->fullmessageformat = FORMAT_MARKDOWN;
+        $message1->fullmessagehtml   = '<p>message body</p>';
+        $message1->smallmessage      = 'small message';
+
+        $message2 = new stdClass();
+        $message2->component         = 'moodle';
+        $message2->name              = 'instantmessage';
+        $message2->userfrom          = $user2;
+        $message2->userto            = $user1;
+        $message2->subject           = 'message subject 2';
+        $message2->fullmessage       = 'message body';
+        $message2->fullmessageformat = FORMAT_MARKDOWN;
+        $message2->fullmessagehtml   = '<p>message body</p>';
+        $message2->smallmessage      = 'small message';
+
+        // There should be debugging message without redirection.
+        message_send($message1);
+        $this->assertDebuggingCalled(null, null, 'message_send() must print debug message that messaging is disabled in phpunit tests.');
+
+        // Sink should catch messages;
+        $sink = $this->redirectMessages();
+        $mid1 = message_send($message1);
+        $mid2 = message_send($message2);
+
+        $this->assertDebuggingNotCalled('message redirection must prevent debug messages from the message_send()');
+        $this->assertEquals(2, $sink->count());
+        $this->assertGreaterThanOrEqual(1, $mid1);
+        $this->assertGreaterThanOrEqual($mid1, $mid2);
+
+        $messages = $sink->get_messages();
+        $this->assertTrue(is_array($messages));
+        $this->assertEquals(2, count($messages));
+        $this->assertEquals($mid1, $messages[0]->id);
+        $this->assertEquals($message1->userto->id, $messages[0]->useridto);
+        $this->assertEquals($message1->userfrom->id, $messages[0]->useridfrom);
+        $this->assertEquals($message1->smallmessage, $messages[0]->smallmessage);
+        $this->assertEquals($mid2, $messages[1]->id);
+        $this->assertEquals($message2->userto->id, $messages[1]->useridto);
+        $this->assertEquals($message2->userfrom->id, $messages[1]->useridfrom);
+        $this->assertEquals($message2->smallmessage, $messages[1]->smallmessage);
+
+        // Test resetting.
+        $sink->clear();
+        $messages = $sink->get_messages();
+        $this->assertTrue(is_array($messages));
+        $this->assertEquals(0, count($messages));
+
+        message_send($message1);
+        $messages = $sink->get_messages();
+        $this->assertTrue(is_array($messages));
+        $this->assertEquals(1, count($messages));
+
+        // Test closing.
+        $sink->close();
+        $messages = $sink->get_messages();
+        $this->assertTrue(is_array($messages));
+        $this->assertEquals(1, count($messages), 'Messages in sink are supposed to stay there after close');
+
+        // Test debugging is enabled again.
+        message_send($message1);
+        $this->assertDebuggingCalled(null, null, 'message_send() must print debug message that messaging is disabled in phpunit tests.');
+
+        // Test invalid names and components.
+
+        $sink = $this->redirectMessages();
+
+        $message3 = new stdClass();
+        $message3->component         = 'xxxx_yyyyy';
+        $message3->name              = 'instantmessage';
+        $message3->userfrom          = $user2;
+        $message3->userto            = $user1;
+        $message3->subject           = 'message subject 2';
+        $message3->fullmessage       = 'message body';
+        $message3->fullmessageformat = FORMAT_MARKDOWN;
+        $message3->fullmessagehtml   = '<p>message body</p>';
+        $message3->smallmessage      = 'small message';
+
+        try {
+            message_send($message3);
+            $this->fail('coding expcetion expected if invalid component specified');
+        } catch (coding_exception $e) {
+            $this->assertTrue(true);
+        }
+
+        $message3->component = 'moodle';
+        $message3->name      = 'yyyyyy';
+        try {
+            message_send($message3);
+            $this->fail('coding expcetion expected if invalid name specified');
+        } catch (coding_exception $e) {
+            $this->assertTrue(true);
+        }
+
+        message_send($message1);
+        $this->assertEquals(1, $sink->count());
+
+        // Test if sink can be carried over to next test.
+        $this->assertTrue(phpunit_util::is_redirecting_messages());
+        return $sink;
+    }
+
+    /**
+     * @depends test_message_redirection
+     */
+    public function test_message_redirection_noreset($sink) {
+        $this->preventResetByRollback(); // Messaging is not compatible with transactions...
+        $this->resetAfterTest(true);
+
+        $this->assertTrue(phpunit_util::is_redirecting_messages());
+        $this->assertEquals(1, $sink->count());
+
+        $message = new stdClass();
+        $message->component         = 'moodle';
+        $message->name              = 'instantmessage';
+        $message->userfrom          = get_admin();
+        $message->userto            = get_admin();
+        $message->subject           = 'message subject 1';
+        $message->fullmessage       = 'message body';
+        $message->fullmessageformat = FORMAT_MARKDOWN;
+        $message->fullmessagehtml   = '<p>message body</p>';
+        $message->smallmessage      = 'small message';
+
+        message_send($message);
+        $this->assertEquals(2, $sink->count());
+    }
+
+    /**
+     * @depends test_message_redirection_noreset
+     */
+    public function test_message_redirection_reset() {
+        $this->assertFalse(phpunit_util::is_redirecting_messages(), 'Test reset must stop message redirection.');
+    }
 }
index 9c6f4f6..1a3efc3 100644 (file)
@@ -44,6 +44,11 @@ class core_phpunit_generator_testcase extends advanced_testcase {
         $count = $DB->count_records('user');
         $user = $generator->create_user();
         $this->assertEquals($count+1, $DB->count_records('user'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
+        $user = $generator->create_user(array('firstname'=>'Žluťoučký', 'lastname'=>'Koníček'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
 
         $count = $DB->count_records('course_categories');
         $category = $generator->create_category();
index 6c8d1fc..be6a466 100644 (file)
@@ -1770,6 +1770,15 @@ abstract class plugininfo_base {
         return $updates;
     }
 
+    /**
+     * Returns the node name used in admin settings menu for this plugin settings (if applicable)
+     *
+     * @return null|string node name or null if plugin does not create settings node (default)
+     */
+    public function get_settings_section_name() {
+        return null;
+    }
+
     /**
      * Returns the URL of the plugin settings screen
      *
@@ -1779,7 +1788,31 @@ abstract class plugininfo_base {
      * @return null|moodle_url
      */
     public function get_settings_url() {
-        return null;
+        $section = $this->get_settings_section_name();
+        if ($section === null) {
+            return null;
+        }
+        $settings = admin_get_root()->locate($section);
+        if ($settings && $settings instanceof admin_settingpage) {
+            return new moodle_url('/admin/settings.php', array('section' => $section));
+        } else if ($settings && $settings instanceof admin_externalpage) {
+            return new moodle_url($settings->url);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Loads plugin settings to the settings tree
+     *
+     * This function usually includes settings.php file in plugins folder.
+     * Alternatively it can create a link to some settings page (instance of admin_externalpage)
+     *
+     * @param part_of_admin_tree $adminroot
+     * @param string $parentnodename
+     * @param bool $hassiteconfig whether the current user has moodle/site:config capability
+     */
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
     }
 
     /**
@@ -1875,6 +1908,23 @@ class plugininfo_block extends plugininfo_base {
         return $blocks;
     }
 
+    /**
+     * Magic method getter, redirects to read only values.
+     *
+     * For block plugins pretends the object has 'visible' property for compatibility
+     * with plugins developed for Moodle version below 2.4
+     *
+     * @param string $name
+     * @return mixed
+     */
+    public function __get($name) {
+        if ($name === 'visible') {
+            debugging('This is now an instance of plugininfo_block, please use $block->is_enabled() instead of $block->visible', DEBUG_DEVELOPER);
+            return ($this->is_enabled() !== false);
+        }
+        return parent::__get($name);
+    }
+
     public function init_display_name() {
 
         if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
@@ -1911,21 +1961,35 @@ class plugininfo_block extends plugininfo_base {
         }
     }
 
-    public function get_settings_url() {
+    public function get_settings_section_name() {
+        return 'blocksetting' . $this->name;
+    }
 
-        if (($block = block_instance($this->name)) === false) {
-            return parent::get_settings_url();
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $block = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
 
-        } else if ($block->has_config()) {
+        if (!$hassiteconfig || (($blockinstance = block_instance($this->name)) === false)) {
+            return;
+        }
+
+        $settings = null;
+        if ($blockinstance->has_config()) {
             if (file_exists($this->full_path('settings.php'))) {
-                return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
+                $settings = new admin_settingpage($section, $this->displayname,
+                        'moodle/site:config', $this->is_enabled() === false);
+                include($this->full_path('settings.php')); // this may also set $settings to null
             } else {
                 $blocksinfo = self::get_blocks_info();
-                return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
+                $settingsurl = new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
+                $settings = new admin_externalpage($section, $this->displayname,
+                        $settingsurl, 'moodle/site:config', $this->is_enabled() === false);
             }
-
-        } else {
-            return parent::get_settings_url();
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 
@@ -2066,14 +2130,30 @@ class plugininfo_filter extends plugininfo_base {
         return null;
     }
 
-    public function get_settings_url() {
-
+    public function get_settings_section_name() {
         $globalstates = self::get_global_states();
+        if (!isset($globalstates[$this->name])) {
+            return parent::get_settings_section_name();
+        }
         $legacyname = $globalstates[$this->name]->legacyname;
-        if (filter_has_global_settings($legacyname)) {
-            return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
-        } else {
-            return null;
+        return 'filtersetting' . str_replace('/', '', $legacyname);
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $filter = $this; // also can be used inside settings.php
+
+        $globalstates = self::get_global_states();
+        $settings = null;
+        if ($hassiteconfig && isset($globalstates[$this->name]) && file_exists($this->full_path('filtersettings.php'))) {
+            $section = $this->get_settings_section_name();
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('filtersettings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 
@@ -2175,6 +2255,23 @@ class plugininfo_mod extends plugininfo_base {
         return $modules;
     }
 
+    /**
+     * Magic method getter, redirects to read only values.
+     *
+     * For module plugins we pretend the object has 'visible' property for compatibility
+     * with plugins developed for Moodle version below 2.4
+     *
+     * @param string $name
+     * @return mixed
+     */
+    public function __get($name) {
+        if ($name === 'visible') {
+            debugging('This is now an instance of plugininfo_mod, please use $module->is_enabled() instead of $module->visible', DEBUG_DEVELOPER);
+            return ($this->is_enabled() !== false);
+        }
+        return parent::__get($name);
+    }
+
     public function init_display_name() {
         if (get_string_manager()->string_exists('pluginname', $this->component)) {
             $this->displayname = get_string('pluginname', $this->component);
@@ -2220,12 +2317,25 @@ class plugininfo_mod extends plugininfo_base {
         }
     }
 
-    public function get_settings_url() {
+    public function get_settings_section_name() {
+        return 'modsetting' . $this->name;
+    }
 
-        if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
-        } else {
-            return parent::get_settings_url();
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $module = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $modulesinfo = self::get_modules_info();
+        $settings = null;
+        if ($hassiteconfig && isset($modulesinfo[$this->name]) && file_exists($this->full_path('settings.php'))) {
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 
@@ -2283,6 +2393,27 @@ class plugininfo_qtype extends plugininfo_base {
         return new moodle_url('/admin/qtypes.php',
                 array('delete' => $this->name, 'sesskey' => sesskey()));
     }
+
+    public function get_settings_section_name() {
+        return 'qtypesetting' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $qtype = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
+        }
+    }
 }
 
 
@@ -2308,11 +2439,31 @@ class plugininfo_auth extends plugininfo_base {
         return isset($enabled[$this->name]);
     }
 
-    public function get_settings_url() {
-        if (file_exists($this->full_path('settings.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
-        } else {
-            return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
+    public function get_settings_section_name() {
+        return 'authsetting' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $auth = $this; // also to be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
+        if ($hassiteconfig) {
+            if (file_exists($this->full_path('settings.php'))) {
+                // TODO: finish implementation of common settings - locking, etc.
+                $settings = new admin_settingpage($section, $this->displayname,
+                        'moodle/site:config', $this->is_enabled() === false);
+                include($this->full_path('settings.php')); // this may also set $settings to null
+            } else {
+                $settingsurl = new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
+                $settings = new admin_externalpage($section, $this->displayname,
+                        $settingsurl, 'moodle/site:config', $this->is_enabled() === false);
+            }
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 }
@@ -2340,12 +2491,24 @@ class plugininfo_enrol extends plugininfo_base {
         return isset($enabled[$this->name]);
     }
 
-    public function get_settings_url() {
+    public function get_settings_section_name() {
+        return 'enrolsettings' . $this->name;
+    }
 
-        if ($this->is_enabled() or file_exists($this->full_path('settings.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
-        } else {
-            return parent::get_settings_url();
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $enrol = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
 
@@ -2360,15 +2523,31 @@ class plugininfo_enrol extends plugininfo_base {
  */
 class plugininfo_message extends plugininfo_base {
 
-    public function get_settings_url() {
+    public function get_settings_section_name() {
+        return 'messagesetting' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        if (!$hassiteconfig) {
+            return;
+        }
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
         $processors = get_message_processors();
         if (isset($processors[$this->name])) {
             $processor = $processors[$this->name];
             if ($processor->available && $processor->hassettings) {
-                return new moodle_url('settings.php', array('section' => 'messagesetting'.$processor->name));
+                $settings = new admin_settingpage($section, $this->displayname,
+                        'moodle/site:config', $this->is_enabled() === false);
+                include($this->full_path('settings.php')); // this may also set $settings to null
             }
         }
-        return parent::get_settings_url();
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
+        }
     }
 
     /**
@@ -2389,7 +2568,7 @@ class plugininfo_message extends plugininfo_base {
     public function get_uninstall_url() {
         $processors = get_message_processors();
         if (isset($processors[$this->name])) {
-            return new moodle_url('message.php', array('uninstall' => $processors[$this->name]->id, 'sesskey' => sesskey()));
+            return new moodle_url('/admin/message.php', array('uninstall' => $processors[$this->name]->id, 'sesskey' => sesskey()));
         } else {
             return parent::get_uninstall_url();
         }
@@ -2409,12 +2588,19 @@ class plugininfo_repository extends plugininfo_base {
         return isset($enabled[$this->name]);
     }
 
-    public function get_settings_url() {
+    public function get_settings_section_name() {
+        return 'repositorysettings'.$this->name;
+    }
 
-        if ($this->is_enabled()) {
-            return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
-        } else {
-            return parent::get_settings_url();
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        if ($hassiteconfig && $this->is_enabled()) {
+            // completely no access to repository setting when it is not enabled
+            $sectionname = $this->get_settings_section_name();
+            $settingsurl = new moodle_url('/admin/repository.php',
+                    array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
+            $settings = new admin_externalpage($sectionname, $this->displayname,
+                    $settingsurl, 'moodle/site:config', false);
+            $adminroot->add($parentnodename, $settings);
         }
     }
 
@@ -2543,12 +2729,117 @@ class plugininfo_local extends plugininfo_base {
     public function get_uninstall_url() {
         return new moodle_url('/admin/localplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
     }
+}
 
-    public function get_settings_url() {
-        if (file_exists($this->full_path('settings.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'local_' . $this->name));
-        } else {
-            return parent::get_settings_url();
+/**
+ * Class for HTML editors
+ */
+class plugininfo_editor extends plugininfo_base {
+
+    public function get_settings_section_name() {
+        return 'editorsettings' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $editor = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
         }
     }
+
+    /**
+     * Returns the information about plugin availability
+     *
+     * True means that the plugin is enabled. False means that the plugin is
+     * disabled. Null means that the information is not available, or the
+     * plugin does not support configurable availability or the availability
+     * can not be changed.
+     *
+     * @return null|bool
+     */
+    public function is_enabled() {
+        global $CFG;
+        if (empty($CFG->texteditors)) {
+            $CFG->texteditors = 'tinymce,textarea';
+        }
+        if (in_array($this->name, explode(',', $CFG->texteditors))) {
+            return true;
+        }
+        return false;
+    }
+}
+
+/**
+ * Class for plagiarism plugins
+ */
+class plugininfo_plagiarism extends plugininfo_base {
+
+    public function get_settings_section_name() {
+        return 'plagiarism'. $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        // plagiarism plugin just redirect to settings.php in the plugins directory
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $section = $this->get_settings_section_name();
+            $settingsurl = new moodle_url($this->get_dir().'/settings.php');
+            $settings = new admin_externalpage($section, $this->displayname,
+                    $settingsurl, 'moodle/site:config', $this->is_enabled() === false);
+            $adminroot->add($parentnodename, $settings);
+        }
+    }
+}
+
+/**
+ * Class for webservice protocols
+ */
+class plugininfo_webservice extends plugininfo_base {
+
+    public function get_settings_section_name() {
+        return 'webservicesetting' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // in case settings.php wants to refer to them
+        $ADMIN = $adminroot; // may be used in settings.php
+        $webservice = $this; // also can be used inside settings.php
+        $section = $this->get_settings_section_name();
+
+        $settings = null;
+        if ($hassiteconfig && file_exists($this->full_path('settings.php'))) {
+            $settings = new admin_settingpage($section, $this->displayname,
+                    'moodle/site:config', $this->is_enabled() === false);
+            include($this->full_path('settings.php')); // this may also set $settings to null
+        }
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
+        }
+    }
+
+    public function is_enabled() {
+        global $CFG;
+        if (empty($CFG->enablewebservices)) {
+            return false;
+        }
+        $active_webservices = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
+        if (in_array($this->name, $active_webservices)) {
+            return true;
+        }
+        return false;
+    }
+
+    public function get_uninstall_url() {
+        return new moodle_url('/admin/webservice/protocols.php',
+                array('sesskey' => sesskey(), 'action' => 'uninstall', 'webservice' => $this->name));
+    }
 }
index b9046b3..75fed3c 100644 (file)
@@ -43,10 +43,6 @@ class conditionlib_testcase extends advanced_testcase {
         $CFG->enablecompletion = 1;
         $user = $this->getDataGenerator()->create_user();;
         $this->setUser($user);
-
-        // Reset modinfo cache before each request
-        $reset = 'reset';
-        get_fast_modinfo($reset);
     }
 
     function test_constructor() {
@@ -208,7 +204,9 @@ class conditionlib_testcase extends advanced_testcase {
         foreach($params as $name=>$value) {
             $settings->{$name}=$value;
         }
-        return $DB->insert_record('course_modules',$settings);
+        $cmid = $DB->insert_record('course_modules', $settings);
+        rebuild_course_cache($courseid, true);
+        return $cmid;
     }
 
     private function make_section($courseid, $cmids, $sectionnum=0, $params=array()) {
@@ -220,7 +218,9 @@ class conditionlib_testcase extends advanced_testcase {
         foreach ($params as $name => $value) {
             $record->{$name} = $value;
         }
-        return $DB->insert_record('course_sections', $record);
+        $sectionid = $DB->insert_record('course_sections', $record);
+        rebuild_course_cache($courseid, true);
+        return $sectionid;
     }
 
     private function make_grouping($courseid, $name) {
@@ -272,8 +272,7 @@ class conditionlib_testcase extends advanced_testcase {
         ));
 
         // Okay sweet, now get modinfo
-        $course = $DB->get_record('course',array('id'=>$courseid));
-        $modinfo=get_fast_modinfo($course);
+        $modinfo=get_fast_modinfo($courseid);
 
         // Test basic data
         $this->assertEquals(1,$modinfo->cms[$cmid1]->showavailability);
@@ -321,9 +320,9 @@ class conditionlib_testcase extends advanced_testcase {
             'grademin' => 5.5
         ));
 
+        rebuild_course_cache($courseid, true);
         // Okay sweet, now get modinfo
-        $course = $DB->get_record('course', array('id' => $courseid));
-        $modinfo = get_fast_modinfo($course);
+        $modinfo = get_fast_modinfo($courseid);
 
         // Test basic data
         $section1 = $modinfo->get_section_info(1);
@@ -476,8 +475,6 @@ class conditionlib_testcase extends advanced_testcase {
 
         // Need to reset modinfo after changing the options
         rebuild_course_cache($courseid);
-        $reset = 'reset';
-        get_fast_modinfo($reset);
 
         $ci=new condition_info((object)array('id'=>$cmid),CONDITION_MISSING_EVERYTHING);
         $ci->add_completion_condition($oldid,COMPLETION_COMPLETE);
@@ -626,8 +623,6 @@ class conditionlib_testcase extends advanced_testcase {
 
         // Completion: Reset modinfo after changing the options
         rebuild_course_cache($courseid);
-        $reset = 'reset';
-        get_fast_modinfo($reset);
 
         // Completion: Add condition
         $ci = new condition_info_section((object)array('id' => $sectionid),
index 559a0ab..3bee4ba 100644 (file)
@@ -7,7 +7,6 @@ information provided here is intended especially for developers.
   and page_generic_activity.
 * use $CFG->googlemapkey3 instead of removed $CFG->googlemapkey and migrate to Google Maps API V3
 * Function settings_navigation::add_course_editing_links() is completely removed
-* function get_generic_section_name() is deprecated
 * function global_navigation::format_display_course_content() is removed completely (the
   functionality is moved to course format class)
 * in the function global_navigation::load_generic_course_sections() the argument $courseformat is
@@ -16,6 +15,8 @@ information provided here is intended especially for developers.
   group memberships using 'xx_yy_allow_group_member_remove' callback and there is also a new restore
   callback 'xx_yy_restore_group_member()'.
 * New general role assignment restore plugin callback 'xx_yy_restore_role_assignment()'.
+* functions get_generic_section_name(), get_all_sections(), add_mod_to_section(), get_all_mods()
+  are deprecated. See their phpdocs in lib/deprecatedlib.php on how to replace them
 
 YUI changes:
 * moodle-enrol-notification has been renamed to moodle-core-notification
index 43c1e7d..fcc5311 100644 (file)
@@ -447,10 +447,10 @@ class assign_plugin_manager {
      * @param string $subtype - The type of plugin (submission or feedback)
      * @param part_of_admin_tree $admin - The handle to the admin menu
      * @param admin_settingpage $settings - The handle to current node in the navigation tree
-     * @param stdClass $module - The handle to the current module
+     * @param stdClass|plugininfo_mod $module - The handle to the current module
      * @return None
      */
-    static function add_admin_assign_plugin_settings($subtype, part_of_admin_tree $admin, admin_settingpage $settings, stdClass $module) {
+    static function add_admin_assign_plugin_settings($subtype, part_of_admin_tree $admin, admin_settingpage $settings, $module) {
         global $CFG;
 
         $plugins = get_plugin_list_with_file($subtype, 'settings.php', false);
@@ -463,7 +463,7 @@ class assign_plugin_manager {
 
         foreach ($pluginsbyname as $pluginname => $plugin) {
             $settings = new admin_settingpage($subtype . '_'.$plugin,
-                    $pluginname, 'moodle/site:config', !$module->visible);
+                    $pluginname, 'moodle/site:config', $module->is_enabled() === false);
             if ($admin->fulltree) {
                 $shortsubtype = substr($subtype, strlen('assign'));
                 include($CFG->dirroot . "/mod/assign/$shortsubtype/$plugin/settings.php");
index edd84cb..02f8da0 100644 (file)
@@ -32,7 +32,7 @@ assessable_content_uploaded
     ->itemid         = // The submission id of the user submission.
     ->courseid       = // The course id of the course the assign belongs to.
     ->userid         = // The user id that the attempt belongs to.
-    ->content        = // The text content entered by the user (empty if no content submitted)
+    ->content        = // The text content entered by the user (empty if no content submitted).
     ->pathnamehashes = // An array of pathnamehashes of the files submitted by the user (var not passed if not set).
 
 assessable_file_uploaded
@@ -43,10 +43,12 @@ assessable_file_uploaded
     ->userid         = // The user id that the attempt belongs to.
     ->pathnamehashes = // An array of pathnamehashes of the files submitted by the user (var not passed if not set).
 
-assessable_content_done
+assessable_submitted
     ->modulename     = 'assign';
     ->cmid           = // The cmid of the assign.
     ->itemid         = // The submission id of the user submission.
     ->courseid       = // The course id of the course the assign belongs to.
     ->userid         = // The user id that the attempt belongs to.
+    ->params         = // Array of module specific parameters.
+        -> submission_editable = // Whether user can edit submission before assessment has been done.
 */
index 8f73ac1..366d5d8 100644 (file)
@@ -59,6 +59,17 @@ class mod_assign_grade_form extends moodleform {
         }
     }
 
+    /**
+     * This is required so when using "Save and next", each form is not defaulted to the previous form.
+     * Giving each form a unique identitifer is enough to prevent this (include the rownum in the form name).
+     *
+     * @return string - The unique identifier for this form.
+     */
+    protected function get_form_identifier() {
+        $params = $this->_customdata[2];
+        return get_class($this) . '_' . $params['rownum'];
+    }
+
     /**
      * Perform minimal validation on the grade form
      * @param array $data
index f03751d..0c1a2ad 100644 (file)
@@ -3325,14 +3325,17 @@ class assign {
                 $this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
                 $this->notify_graders($submission);
                 $this->notify_student_submission_receipt($submission);
-                // Trigger assessable_content_done event to show completion
+                // Trigger assessable_submitted event on submission.
                 $eventdata = new stdClass();
                 $eventdata->modulename   = 'assign';
                 $eventdata->cmid         = $this->get_course_module()->id;
                 $eventdata->itemid       = $submission->id;
                 $eventdata->courseid     = $this->get_course()->id;
                 $eventdata->userid       = $USER->id;
-                events_trigger('assessable_content_done', $eventdata);
+                $eventdata->params       = array(
+                    'submission_editable' => false,
+                );
+                events_trigger('assessable_submitted', $eventdata);
             }
         }
         return true;
@@ -3752,6 +3755,17 @@ class assign {
             if (!$this->get_instance()->submissiondrafts) {
                 $this->notify_student_submission_receipt($submission);
                 $this->notify_graders($submission);
+                // Trigger assessable_submitted event on submission.
+                $eventdata = new stdClass();
+                $eventdata->modulename   = 'assign';
+                $eventdata->cmid         = $this->get_course_module()->id;
+                $eventdata->itemid       = $submission->id;
+                $eventdata->courseid     = $this->get_course()->id;
+                $eventdata->userid       = $USER->id;
+                $eventdata->params       = array(
+                    'submission_editable' => true,
+                );
+                events_trigger('assessable_submitted', $eventdata);
             }
             return true;
         }
index ea7427f..fd29d76 100644 (file)
@@ -27,12 +27,12 @@ defined('MOODLE_INTERNAL') || die;
 require_once($CFG->dirroot . '/mod/assign/adminlib.php');
 
 $ADMIN->add('modules', new admin_category('assignmentplugins',
-                new lang_string('assignmentplugins', 'assign'), !$module->visible));
+                new lang_string('assignmentplugins', 'assign'), $module->is_enabled() === false));
 $ADMIN->add('assignmentplugins', new admin_category('assignsubmissionplugins',
-                new lang_string('submissionplugins', 'assign'), !$module->visible));
+                new lang_string('submissionplugins', 'assign'), $module->is_enabled() === false));
 $ADMIN->add('assignsubmissionplugins', new assign_admin_page_manage_assign_plugins('assignsubmission'));
 $ADMIN->add('assignmentplugins', new admin_category('assignfeedbackplugins',
-                new lang_string('feedbackplugins', 'assign'), !$module->visible));
+                new lang_string('feedbackplugins', 'assign'), $module->is_enabled() === false));
 $ADMIN->add('assignfeedbackplugins', new assign_admin_page_manage_assign_plugins('assignfeedback'));
 
 
index f9872c6..bdb6c99 100644 (file)
@@ -337,12 +337,7 @@ class assign_upgrade_manager {
             return false;
         }
 
-        $mod = new stdClass();
-        $mod->course = $newcm->course;
-        $mod->section = $section->section;
-        $mod->coursemodule = $newcm->id;
-        $mod->id = $newcm->id;
-        $newcm->section = add_mod_to_section($mod, $cm);
+        $newcm->section = course_add_cm_to_section($newcm->course, $newcm->id, $section->section);
 
         // make sure visibility is set correctly (in particular in calendar)
         // note: allow them to set it even without moodle/course:activityvisibility
index e713902..4744128 100644 (file)
@@ -25,7 +25,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics)
-$module->version  = 2012082401;    // The current module version (Date: YYYYMMDDXX)
+$module->version  = 2012082402;    // The current module version (Date: YYYYMMDDXX)
 $module->requires = 2012061700;    // Requires this Moodle version
 $module->cron     = 60;
 
index 71eb023..5adcbf2 100644 (file)
@@ -37,9 +37,6 @@ if (!$cms = get_coursemodules_in_course('assignment', $course->id, 'cm.idnumber,
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $timenow = time();
 
@@ -74,7 +71,7 @@ foreach ($modinfo->instances['assignment'] as $cm) {
     if ($usesections) {
         if ($cm->sectionnum !== $currentsection) {
             if ($cm->sectionnum) {
-                $printsection = get_section_name($course, $sections[$cm->sectionnum]);
+                $printsection = get_section_name($course, $cm->sectionnum);
             }
             if ($currentsection !== "") {
                 $table->data[] = 'hr';
index 4b0ab8c..ceef73d 100644 (file)
@@ -47,8 +47,6 @@ $PAGE->navbar->add($str->onlinetext);
 // get all the assignments in the course
 $assignments = get_all_instances_in_course('assignment',$course, $USER->id );
 
-$sections = get_all_sections($course->id);
-
 // array to hold display data
 $views = array();
 
@@ -98,7 +96,7 @@ foreach( $assignments as $assignment ) {
     $view = new stdClass;
 
     // start to build view object
-    $view->section = get_section_name($course, $sections[$assignment->section]);
+    $view->section = get_section_name($course, $assignment->section);
 
     $view->name = $assignment->name;
     $view->submitted = $submitted;
index b413ba3..7546188 100644 (file)
@@ -57,9 +57,6 @@ if (!$books = get_all_instances_in_course('book', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -80,7 +77,7 @@ foreach ($books as $book) {
         $printsection = '';
         if ($book->section !== $currentsection) {
             if ($book->section) {
-                $printsection = get_section_name($course, $sections[$book->section]);
+                $printsection = get_section_name($course, $book->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 6b9365d..18f6395 100644 (file)
@@ -152,15 +152,8 @@ if ($nextid) {
     $chnavigation .= '<a title="'.get_string('navnext', 'book').'" href="view.php?id='.$cm->id.
             '&amp;chapterid='.$nextid.'"><img src="'.$OUTPUT->pix_url('nav_next', 'mod_book').'" class="bigicon" alt="'.get_string('navnext', 'book').'" /></a>';
 } else {
-    $sec = '';
-    if ($section = $DB->get_record('course_sections', array('id'=>$cm->section))) {
-        $sec = $section->section;
-    }
-    if ($course->id == $SITE->id) {
-        $returnurl = "$CFG->wwwroot/";
-    } else {
-        $returnurl = "$CFG->wwwroot/course/view.php?id=$course->id#section-$sec";
-    }
+    $sec = $DB->get_field('course_sections', 'section', array('id' => $cm->section));
+    $returnurl = course_get_url($course, $sec);
     $chnavigation .= '<a title="'.get_string('navexit', 'book').'" href="'.$returnurl.'"><img src="'.$OUTPUT->pix_url('nav_exit', 'mod_book').
             '" class="bigicon" alt="'.get_string('navexit', 'book').'" /></a>';
 
index e000978..a56e489 100644 (file)
@@ -38,9 +38,6 @@ if (! $chats = get_all_instances_in_course('chat', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 /// Print the list of instances (your module will probably extend this)
 
@@ -69,7 +66,7 @@ foreach ($chats as $chat) {
     $printsection = '';
     if ($chat->section !== $currentsection) {
         if ($chat->section) {
-            $printsection = get_section_name($course, $sections[$chat->section]);
+            $printsection = get_section_name($course, $chat->section);
         }
         if ($currentsection !== '') {
             $table->data[] = 'hr';
index e63b876..1d0ca00 100644 (file)
@@ -29,9 +29,6 @@
     }
 
     $usesections = course_format_uses_sections($course->format);
-    if ($usesections) {
-        $sections = get_all_sections($course->id);
-    }
 
     $sql = "SELECT cha.*
               FROM {choice} ch, {choice_answers} cha
@@ -76,7 +73,7 @@
             $printsection = "";
             if ($choice->section !== $currentsection) {
                 if ($choice->section) {
-                    $printsection = get_section_name($course, $sections[$choice->section]);
+                    $printsection = get_section_name($course, $choice->section);
                 }
                 if ($currentsection !== "") {
                     $table->data[] = 'hr';
index 313058c..2fa245d 100644 (file)
@@ -55,12 +55,13 @@ class data_field_date extends data_field_base {
 
     //Enable the following three functions once core API issues have been addressed.
     function display_search_field($value=0) {
-        $selectors = html_writer::select_time('days', 'f_'.$this->field->id.'_d', $value)
-           . html_writer::select_time('months', 'f_'.$this->field->id.'_m', $value)
-           . html_writer::select_time('years', 'f_'.$this->field->id.'_y', $value);
-       return $selectors;
+        $selectors = html_writer::select_time('days', 'f_'.$this->field->id.'_d', $value['timestamp'])
+           . html_writer::select_time('months', 'f_'.$this->field->id.'_m', $value['timestamp'])
+           . html_writer::select_time('years', 'f_'.$this->field->id.'_y', $value['timestamp']);
+        $datecheck = html_writer::checkbox('f_'.$this->field->id.'_z', 1, $value['usedate']);
+        $str = $selectors . ' ' . $datecheck . ' ' . get_string('usedate', 'data');
 
-        //return print_date_selector('f_'.$this->field->id.'_d', 'f_'.$this->field->id.'_m', 'f_'.$this->field->id.'_y', $value, true);
+        return $str;
     }
 
     function generate_sql($tablealias, $value) {
@@ -70,21 +71,22 @@ class data_field_date extends data_field_base {
         $i++;
         $name = "df_date_$i";
         $varcharcontent = $DB->sql_compare_text("{$tablealias}.content");
-        return array(" ({$tablealias}.fieldid = {$this->field->id} AND $varcharcontent = :$name) ", array($name=>$value));
+        return array(" ({$tablealias}.fieldid = {$this->field->id} AND $varcharcontent = :$name) ", array($name => $value['timestamp']));
     }
 
     function parse_search_field() {
-
         $day   = optional_param('f_'.$this->field->id.'_d', 0, PARAM_INT);
         $month = optional_param('f_'.$this->field->id.'_m', 0, PARAM_INT);
         $year  = optional_param('f_'.$this->field->id.'_y', 0, PARAM_INT);
-        if (!empty($day) && !empty($month) && !empty($year)) {
-            return make_timestamp($year, $month, $day, 12, 0, 0, 0, false);
-        }
-        else {
+        $usedate = optional_param('f_'.$this->field->id.'_z', 0, PARAM_INT);
+        $data = array();
+        if (!empty($day) && !empty($month) && !empty($year) && $usedate == 1) {
+            $data['timestamp'] = make_timestamp($year, $month, $day, 12, 0, 0, 0, false);
+            $data['usedate'] = 1;
+            return $data;
+        } else {
             return 0;
         }
-
     }
 
     function update_content($recordid, $value, $name='') {
index f443b7e..94a7323 100644 (file)
@@ -56,9 +56,6 @@ if (! $datas = get_all_instances_in_course("data", $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $timenow  = time();
 $strname  = get_string('name');
@@ -120,7 +117,7 @@ foreach ($datas as $data) {
     if ($usesections) {
         if ($data->section !== $currentsection) {
             if ($data->section) {
-                $printsection = get_section_name($course, $sections[$data->section]);
+                $printsection = get_section_name($course, $data->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 9963648..7697366 100644 (file)
@@ -342,6 +342,7 @@ $string['uploadrecords_help'] = 'Entries may be uploaded via text file. The form
 The field enclosure is a character that surrounds each field in each record. It can normally be left unset.';
 $string['uploadrecords_link'] = 'mod/data/import';
 $string['url'] = 'Url';
+$string['usedate'] = 'Include in search.';
 $string['usestandard'] = 'Use a preset';
 $string['usestandard_help'] = 'To use a preset available to the whole site, select it from the list. (If you have added a preset to the list using the save as preset feature then you have the option of deleting it.)';
 $string['viewfromdate'] = 'Read only from';
index 327264f..a1a42ff 100644 (file)
@@ -61,9 +61,6 @@ if (! $feedbacks = get_all_instances_in_course("feedback", $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 /// Print the list of instances (your module will probably extend this)
 
@@ -105,7 +102,7 @@ foreach ($feedbacks as $feedback) {
     $link = '<a '.$dimmedclass.' href="'.$viewurl->out().'">'.$feedback->name.'</a>';
 
     if ($usesections) {
-        $tabledata = array (get_section_name($course, $sections[$feedback->section]), $link);
+        $tabledata = array (get_section_name($course, $feedback->section), $link);
     } else {
         $tabledata = array ($link);
     }
index 5a3597d..9cbe696 100644 (file)
@@ -54,9 +54,6 @@ if (!$folders = get_all_instances_in_course('folder', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -77,7 +74,7 @@ foreach ($folders as $folder) {
         $printsection = '';
         if ($folder->section !== $currentsection) {
             if ($folder->section) {
-                $printsection = get_section_name($course, $sections[$folder->section]);
+                $printsection = get_section_name($course, $folder->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 84132a6..efbb8df 100644 (file)
         $modinfo = get_fast_modinfo($course);
         if (isset($modinfo->instances['forum'])) {
             $forummenu = array();
-            $sections = get_all_sections($course->id);
             // Check forum types and eliminate simple discussions.
             $forumcheck = $DB->get_records('forum', array('course' => $course->id),'', 'id, type');
             foreach ($modinfo->instances['forum'] as $forumcm) {
                     continue;
                 }
                 $section = $forumcm->sectionnum;
-                $sectionname = get_section_name($course, $sections[$section]);
+                $sectionname = get_section_name($course, $section);
                 if (empty($forummenu[$section])) {
                     $forummenu[$section] = array($sectionname => array());
                 }
index 5d0b5c5..fd91cc5 100644 (file)
@@ -104,7 +104,6 @@ if ($show_rss = (($can_subscribe || $course->id == SITEID) &&
 }
 
 $usesections = course_format_uses_sections($course->format);
-$sections = get_all_sections($course->id);
 
 $table = new html_table();
 
@@ -358,7 +357,7 @@ if ($course->id != SITEID) {    // Only real courses have learning forums
             $forum->intro = shorten_text(format_module_intro('forum', $forum, $cm->id), $CFG->forum_shortpost);
 
             if ($cm->sectionnum != $currentsection) {
-                $printsection = get_section_name($course, $sections[$cm->sectionnum]);
+                $printsection = get_section_name($course, $cm->sectionnum);
                 if ($currentsection) {
                     $learningtable->data[] = 'hr';
                 }
index 00195ff..1e20aa5 100644 (file)
@@ -3001,19 +3001,12 @@ function forum_get_course_forum($courseid, $type) {
     $mod->module = $module->id;
     $mod->instance = $forum->id;
     $mod->section = 0;
-    if (! $mod->coursemodule = add_course_module($mod) ) {   // assumes course/lib.php is loaded
+    include_once("$CFG->dirroot/course/lib.php");
+    if (! $mod->coursemodule = add_course_module($mod) ) {
         echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'");
         return false;
     }
-    if (! $sectionid = add_mod_to_section($mod) ) {   // assumes course/lib.php is loaded
-        echo $OUTPUT->notification("Could not add the new course module to that section");
-        return false;
-    }
-    $DB->set_field("course_modules", "section", $sectionid, array("id" => $mod->coursemodule));
-
-    include_once("$CFG->dirroot/course/lib.php");
-    rebuild_course_cache($courseid);
-
+    $sectionid = course_add_cm_to_section($courseid, $mod->coursemodule, 0);
     return $DB->get_record("forum", array("id" => "$forum->id"));
 }
 
@@ -7332,12 +7325,7 @@ function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
             if (!$cmid = add_course_module($mod)) {
                 print_error('cannotcreateinstanceforteacher', 'forum');
             } else {
-                $mod->coursemodule = $cmid;
-                if (!$sectionid = add_mod_to_section($mod)) {
-                    print_error('cannotaddteacherforumto', 'forum');
-                } else {
-                    $DB->set_field('course_modules', 'section', $sectionid, array('id' => $cmid));
-                }
+                $sectionid = course_add_cm_to_section($forum->course, $mod->coursemodule, 0);
             }
 
             // Change the forum type to general.
index a509e0e..388616a 100644 (file)
@@ -159,14 +159,11 @@ if ($xml = glossary_read_imported_file($result)) {
                     print_error('cannotaddcoursemodule');
                 }
 
-                if (! $sectionid = add_mod_to_section($mod) ) {
-                    print_error('cannotaddcoursemoduletosection');
-                }
+                $sectionid = course_add_cm_to_section($course, $mod->coursemodule, 0);
                 //We get the section's visible field status
                 $visible = $DB->get_field("course_sections", "visible", array("id"=>$sectionid));
 
                 $DB->set_field("course_modules", "visible", $visible, array("id"=>$mod->coursemodule));
-                $DB->set_field("course_modules", "section", $sectionid, array("id"=>$mod->coursemodule));
 
                 add_to_log($course->id, "course", "add mod",
                            "../mod/$mod->modulename/view.php?id=$mod->coursemodule",
index 7979c29..b20829d 100644 (file)
@@ -44,9 +44,6 @@ if (! $glossarys = get_all_instances_in_course("glossary", $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 /// Print the list of instances (your module will probably extend this)
 
@@ -88,7 +85,7 @@ foreach ($glossarys as $glossary) {
     if ($usesections) {
         if ($glossary->section !== $currentsection) {
             if ($glossary->section) {
-                $printsection = get_section_name($course, $sections[$glossary->section]);
+                $printsection = get_section_name($course, $glossary->section);
             }
             if ($currentsection !== "") {
                 $table->data[] = 'hr';
index be1d4fa..53505da 100644 (file)
@@ -54,9 +54,6 @@ if (!$imscps = get_all_instances_in_course('imscp', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -77,7 +74,7 @@ foreach ($imscps as $imscp) {
         $printsection = '';
         if ($imscp->section !== $currentsection) {
             if ($imscp->section) {
-                $printsection = get_section_name($course, $sections[$imscp->section]);
+                $printsection = get_section_name($course, $imscp->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
diff --git a/mod/lesson/importppt.php b/mod/lesson/importppt.php
deleted file mode 100644 (file)
index d2ed246..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This is a very rough importer for powerpoint slides
- * Export a powerpoint presentation with powerpoint as html pages
- * Do it with office 2002 (I think?) and no special settings
- * Then zip the directory with all of the html pages
- * and the zip file is what you want to upload
- *
- * The script supports book and lesson.
- *
- * @package    mod
- * @subpackage lesson
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- **/
-
-/** include required files */
-require_once("../../config.php");
-require_once($CFG->dirroot.'/mod/lesson/locallib.php');
-require_once($CFG->dirroot.'/mod/lesson/importpptlib.php');
-
-$id     = required_param('id', PARAM_INT);         // Course Module ID
-$pageid = optional_param('pageid', '', PARAM_INT); // Page ID
-
-$url = new moodle_url('/mod/lesson/importppt.php', array('id'=>$id));
-if ($pageid !== '') {
-    $url->param('pageid', $pageid);
-}
-$PAGE->set_url($url);
-
-$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);;
-$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
-$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
-
-$modname = 'lesson';
-$mod = $cm;
-require_login($course, false, $cm);
-
-require_login($course, false, $cm);
-$context = context_module::instance($cm->id);
-require_capability('mod/lesson:edit', $context);
-
-$strimportppt = get_string("importppt", "lesson");
-$strlessons = get_string("modulenameplural", "lesson");
-
-$data = new stdClass;
-$data->id = $cm->id;
-$data->pageid = $pageid;
-$mform = new lesson_importppt_form();
-$mform->set_data($data);
-
-if ($data = $mform->get_data()) {
-    $manager = lesson_page_type_manager::get($lesson);
-    if (!$filename = $mform->get_new_filename('pptzip')) {
-        print_error('invalidfile', 'lesson');
-    }
-    if (!$package = $mform->save_stored_file('pptzip', $context->id, 'mod_lesson', 'ppt_imports', $lesson->id, '/', $filename, true)) {
-        print_error('unabletosavefile', 'lesson');
-    }
-    // extract package content
-    $packer = get_file_packer('application/zip');
-    $package->extract_to_storage($packer, $context->id, 'mod_lesson', 'imported_files', $lesson->id, '/');
-
-    $fs = get_file_storage();
-    if ($files = $fs->get_area_files($context->id, 'mod_lesson', 'imported_files', $lesson->id)) {
-
-        $pages = array();
-        foreach ($files as $key=>$file) {
-            if ($file->get_mimetype() != 'text/html') {
-                continue;
-            }
-            $filenameinfo = pathinfo($file->get_filepath().$file->get_filename());
-
-            $page = new stdClass;
-            $page->title = '';
-            $page->contents = array();
-            $page->images = array();
-            $page->source = $filenameinfo['basename'];
-
-            $string = strip_tags($file->get_content(),'<div><img>');
-            $imgs = array();
-            preg_match_all("/<img[^>]*(src\=\"(".$filenameinfo['filename']."\_image[^>^\"]*)\"[^>]*)>/i", $string, $imgs);
-            foreach ($imgs[2] as $img) {
-                $imagename = basename($img);
-                foreach ($files as $file) {
-                    if ($imagename === $file->get_filename()) {
-                        $page->images[] = clone($file);
-                    }
-                }
-            }
-
-            $matches = array();
-            // this will look for a non nested tag that is closed
-            // want to allow <b><i>(maybe more) tags but when we do that
-            // the preg_match messes up.
-            preg_match_all("/(<([\w]+)[^>]*>)([^<\\2>]*)(<\/\\2>)/", $string, $matches);
-            $countmatches = count($matches[1]);
-            for($i = 0; $i < $countmatches; $i++) { // go through all of our div matches
-
-                $class = lesson_importppt_isolate_class($matches[1][$i]); // first step in isolating the class
-
-                // check for any static classes
-                switch ($class) {
-                    case 'T':  // class T is used for Titles
-                        $page->title = $matches[3][$i];
-                        break;
-                    case 'B':  // I would guess that all bullet lists would start with B then go to B1, B2, etc
-                    case 'B1': // B1-B4 are just insurance, should just hit B and all be taken care of
-                    case 'B2':
-                    case 'B3':
-                    case 'B4':
-                        $page->contents[] = lesson_importppt_build_list($matches, '<ul>', $i, 0);  // this is a recursive function that will grab all the bullets and rebuild the list in html
-                        break;
-                    default:
-                        if ($matches[3][$i] != '&#13;') {  // odd crap generated... sigh
-                            if (substr($matches[3][$i], 0, 1) == ':') {  // check for leading :    ... hate MS ...
-                                $page->contents[] = substr($matches[3][$i], 1);  // get rid of :
-                            } else {
-                                $page->contents[] = $matches[3][$i];
-                            }
-                        }
-                        break;
-                }
-            }
-            $pages[] = $page;
-        }
-
-        $branchtables = lesson_create_objects($pages, $lesson->id);
-
-        // first set up the prevpageid and nextpageid
-        if (empty($pageid)) { // adding it to the top of the lesson
-            $prevpageid = 0;
-            // get the id of the first page.  If not found, then no pages in the lesson
-            if (!$nextpageid = $DB->get_field('lesson_pages', 'id', array('prevpageid' => 0, 'lessonid' => $lesson->id))) {
-                $nextpageid = 0;
-            }
-        } else {
-            // going after an actual page
-            $prevpageid = $pageid;
-            $nextpageid = $DB->get_field('lesson_pages', 'nextpageid', array('id' => $pageid));
-        }
-
-        foreach ($branchtables as $branchtable) {
-
-            // set the doubly linked list
-            $branchtable->page->nextpageid = $nextpageid;
-            $branchtable->page->prevpageid = $prevpageid;
-
-            // insert the page
-            $id = $DB->insert_record('lesson_pages', $branchtable->page);
-
-            if (!empty($branchtable->page->images)) {
-                $changes = array('contextid'=>$context->id, 'component'=>'mod_lesson', 'filearea'=>'page_contents', 'itemid'=>$id, 'timemodified'=>time());
-                foreach ($branchtable->page->images as $image) {
-                    $fs->create_file_from_storedfile($changes, $image);
-                }
-            }
-
-            // update the link of the page previous to the one we just updated
-            if ($prevpageid != 0) {  // if not the first page
-                $DB->set_field("lesson_pages", "nextpageid", $id, array("id" => $prevpageid));
-            }
-
-            // insert the answers
-            foreach ($branchtable->answers as $answer) {
-                $answer->pageid = $id;
-                $DB->insert_record('lesson_answers', $answer);
-            }
-
-            $prevpageid = $id;
-        }
-
-        // all done with inserts.  Now check to update our last page (this is when we import between two lesson pages)
-        if ($nextpageid != 0) {  // if the next page is not the end of lesson
-            $DB->set_field("lesson_pages", "prevpageid", $id, array("id" => $nextpageid));
-        }
-    }
-
-    // Remove all unzipped files!
-    $fs->delete_area_files($context->id, 'mod_lesson', 'imported_files', $lesson->id);
-
-    redirect("$CFG->wwwroot/mod/$modname/view.php?id=$cm->id", get_string('pptsuccessfullimport', 'lesson'), 5);
-}
-
-$PAGE->navbar->add($strimportppt);
-$PAGE->set_title($strimportppt);
-$PAGE->set_heading($strimportppt);
-echo $OUTPUT->header();
-
-/// Print upload form
-echo $OUTPUT->heading_with_help($strimportppt, 'importppt', 'lesson');
-echo $OUTPUT->box_start('generalbox boxaligncenter');
-$mform->display();
-echo $OUTPUT->box_end();
-echo $OUTPUT->footer();
diff --git a/mod/lesson/importpptlib.php b/mod/lesson/importpptlib.php
deleted file mode 100644 (file)
index 8550e61..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Contains functions used by importppt.php that naturally pertain to importing
- * powerpoint presentations into the lesson module
- *
- * @package    mod
- * @subpackage lesson
- * @copyright  2009 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- **/
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * A recursive function to build a html list
- *
- * @param array $matches
- * @param string $list
- * @param int $i
- * @param int $depth
- * @return string
- */
-function lesson_importppt_build_list(array &$matches, $list, &$i, $depth) {
-    while($i < count($matches[1])) {
-
-        $class = lesson_importppt_isolate_class($matches[1][$i]);
-
-        if (strstr($class, 'B')) {  // make sure we are still working with bullet classes
-            if ($class == 'B') {
-                $this_depth = 0;  // calling class B depth 0
-            } else {
-                // set the depth number.  So B1 is depth 1 and B2 is depth 2 and so on
-                $this_depth = substr($class, 1);
-                if (!is_numeric($this_depth)) {
-                    print_error('invalidnum');
-                }
-            }
-            if ($this_depth < $depth) {
-                // we are moving back a level in the nesting
-                break;
-            }
-            if ($this_depth > $depth) {
-                // we are moving in a lvl in nesting
-                $list .= '<ul>';
-                $list = lesson_importppt_build_list($matches, $list, $i, $this_depth);
-                // once we return back, should go to the start of the while
-                continue;
-            }
-            // no depth changes, so add the match to our list
-            if ($cleanstring = lesson_importppt_clean_text($matches[3][$i])) {
-                $list .= '<li>'.lesson_importppt_clean_text($matches[3][$i]).'</li>';
-            }
-            $i++;
-        } else {
-            // not a B class, so get out of here...
-            break;
-        }
-    }
-    // end the list and return it
-    $list .= '</ul>';
-    return $list;
-
-}
-
-/**
- * Given an html tag, this function will
- *
- * @param string $string
- * @return string
- */
-function lesson_importppt_isolate_class($string) {
-    if($class = strstr($string, 'class=')) { // first step in isolating the class
-        $class = substr($class, strpos($class, '=')+1);  // this gets rid of <div blawblaw class=  there are no "" or '' around the class name   ...sigh...
-        if (strstr($class, ' ')) {
-            // spaces found, so cut off everything off after the first space
-            return substr($class, 0, strpos($class, ' '));
-        } else {
-            // no spaces so nothing else in the div tag, cut off the >
-            return substr($class, 0, strpos($class, '>'));
-        }
-    } else {
-        // no class defined in the tag
-        return '';
-    }
-}
-
-/**
- * This function strips off the random chars that ppt puts infront of bullet lists
- *
- * @param string $string
- * @return string
- */
-function lesson_importppt_clean_text($string) {
-    $chop = 1; // default: just a single char infront of the content
-
-    // look for any other crazy things that may be infront of the content
-    if (strstr($string, '&lt;') and strpos($string, '&lt;') == 0) {  // look for the &lt; in the sting and make sure it is in the front
-        $chop = 4;  // increase the $chop
-    }
-    // may need to add more later....
-
-    $string = substr($string, $chop);
-
-    if ($string != '&#13;') {
-        return $string;
-    } else {
-        return false;
-    }
-}
-
-/**
- *  Creates objects an object with the page and answers that are to be inserted into the database
- *
- * @param array $pageobjects
- * @param int $lessonid
- * @return array
- */
-function lesson_create_objects($pageobjects, $lessonid) {
-
-    $branchtables = array();
-    $branchtable = new stdClass;
-
-    // all pages have this info
-    $page = new stdClass();
-    $page->lessonid = $lessonid;
-    $page->prevpageid = 0;
-    $page->nextpageid = 0;
-    $page->qtype = LESSON_PAGE_BRANCHTABLE;
-    $page->qoption = 0;
-    $page->layout = 1;
-    $page->display = 1;
-    $page->timecreated = time();
-    $page->timemodified = 0;
-
-    // all answers are the same
-    $answer = new stdClass();
-    $answer->lessonid = $lessonid;
-    $answer->jumpto = LESSON_NEXTPAGE;
-    $answer->grade = 0;
-    $answer->score = 0;
-    $answer->flags = 0;
-    $answer->timecreated = time();
-    $answer->timemodified = 0;
-    $answer->answer = "Next";
-    $answer->response = "";
-
-    $answers[] = clone($answer);
-
-    $answer->jumpto = LESSON_PREVIOUSPAGE;
-    $answer->answer = "Previous";
-
-    $answers[] = clone($answer);
-
-    $branchtable->answers = $answers;
-
-    $i = 1;
-
-    foreach ($pageobjects as $pageobject) {
-        if ($pageobject->title == '') {
-            $page->title = "Page $i";  // no title set so make a generic one
-        } else {
-            $page->title = $pageobject->title;
-        }
-        $page->contents = '';
-
-        // nab all the images first
-        $page->images = $pageobject->images;
-        foreach ($page->images as $image) {
-            $imagetag = '<img src="@@PLUGINFILE@@'.$image->get_filepath().$image->get_filename().'" title="'.$image->get_filename().'" />';
-            $imagetag = str_replace("\n", '', $imagetag);
-            $imagetag = str_replace("\r", '', $imagetag);
-            $imagetag = str_replace("'", '"', $imagetag);  // imgstyle
-            $page->contents .= $imagetag;
-        }
-        // go through the contents array and put <p> tags around each element and strip out \n which I have found to be unneccessary
-        foreach ($pageobject->contents as $content) {
-            $content = str_replace("\n", '', $content);
-            $content = str_replace("\r", '', $content);
-            $content = str_replace('&#13;', '', $content);  // puts in returns?
-            $content = '<p>'.$content.'</p>';
-            $page->contents .= $content;
-        }
-
-        $branchtable->page = clone($page);  // add the page
-        $branchtables[] = clone($branchtable);  // add it all to our array
-        $i++;
-    }
-
-    return $branchtables;
-}
-
-/**
- * Form displayed to the user asking them to select a file to upload
- *
- * @copyright  2009 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class lesson_importppt_form extends moodleform {
-
-    public function definition() {
-        global $COURSE;
-
-        $mform = $this->_form;
-
-        $mform->addElement('hidden', 'id');
-        $mform->setType('id', PARAM_INT);
-
-        $mform->addElement('hidden', 'pageid');
-        $mform->setType('pageid', PARAM_INT);
-
-        $filepickeroptions = array();
-        $filepickeroptions['filetypes'] = array('*.zip');
-        $filepickeroptions['maxbytes'] = $COURSE->maxbytes;
-        $mform->addElement('filepicker', 'pptzip', get_string('upload'), null, $filepickeroptions);
-        $mform->addRule('pptzip', null, 'required', null, 'client');
-
-        $this->add_action_buttons(null, get_string("uploadthisfile"));
-    }
-
-}
\ No newline at end of file
index 27ae03a..67b717a 100644 (file)
@@ -62,9 +62,6 @@ if (! $lessons = get_all_instances_in_course("lesson", $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 /// Print the list of instances (your module will probably extend this)
 
@@ -113,7 +110,7 @@ foreach ($lessons as $lesson) {
                 $grade_value = $return[$USER->id]->rawgrade;
             }
         }
-        $table->data[] = array (get_section_name($course, $sections[$lesson->section]), $link, $grade_value, $due);
+        $table->data[] = array (get_section_name($course, $lesson->section), $link, $grade_value, $due);
     } else {
         $table->data[] = array ($link, $lesson->grade, $due);
     }
index b389979..353b82d 100644 (file)
@@ -193,8 +193,6 @@ $string['checkedthisone'] = 'Checked this one.';
 $string['checknavigation'] = 'Check navigation';
 $string['checkquestion'] = 'Check question';
 $string['importcount'] = 'Importing {$a} questions';
-$string['importppt'] = 'Import PowerPoint';
-$string['importppt_help'] = 'This feature enables a zip file of PowerPoint 2003 slides saved as web pages to be imported into the lesson.';
 $string['importquestions'] = 'Import questions';
 $string['importquestions_help'] = 'This feature enables questions in a variety of formats to be imported via text file.';
 $string['insertedpage'] = 'Inserted page';
@@ -339,7 +337,6 @@ $string['pluginname'] = 'Lesson';
 $string['pointsearned'] = 'Points earned';
 $string['postprocesserror'] = 'Error occurred during post-processing!';
 $string['postsuccess'] = 'Post successful';
-$string['pptsuccessfullimport'] = 'Successfully imported pages from the uploaded PowerPoint Presentation';
 $string['practice'] = 'Practice lesson';
 $string['practice_help'] = 'A practice lesson does not appear in the gradebook.';
 $string['preprocesserror'] = 'Error occurred during pre-processing!';
index d7e95ca..be96ee5 100644 (file)
@@ -369,9 +369,6 @@ class mod_lesson_renderer extends plugin_renderer_base {
         $importquestionsurl = new moodle_url('/mod/lesson/import.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
         $links[] = html_writer::link($importquestionsurl, get_string('importquestions', 'lesson'));
 
-        $importppturl = new moodle_url('/mod/lesson/importppt.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
-        $links[] = html_writer::link($importppturl, get_string('importppt', 'lesson'));
-
         $manager = lesson_page_type_manager::get($lesson);
         foreach ($manager->get_add_page_type_links($prevpageid) as $link) {
             $link['addurl']->param('firstpage', 1);
index e663af6..d75f2aa 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version   = 2012061700;       // The current module version (Date: YYYYMMDDXX)
+$module->version   = 2012061701;       // The current module version (Date: YYYYMMDDXX)
 $module->requires  = 2012061700;    // Requires this Moodle version
 $module->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
 $module->cron      = 0;
index 65f5989..346f6e1 100644 (file)
@@ -79,9 +79,6 @@ $timenow = time();
 $strname = get_string("name");
 $strsectionname  = get_string('sectionname', 'format_'.$course->format);
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -102,8 +99,8 @@ foreach ($basicltis as $basiclti) {
         $link = "<a href=\"view.php?id=$basiclti->coursemodule\">$basiclti->name</a>";
     }
 
-    if ($course->format == "weeks" or $course->format == "topics") {
-        $table->data[] = array ($basiclti->section, $link);
+    if ($usesections) {
+        $table->data[] = array (get_section_name($course, $basiclti->section), $link);
     } else {
         $table->data[] = array ($link);
     }
index 8ea9165..9843372 100644 (file)
@@ -54,9 +54,6 @@ if (!$pages = get_all_instances_in_course('page', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -77,7 +74,7 @@ foreach ($pages as $page) {
         $printsection = '';
         if ($page->section !== $currentsection) {
             if ($page->section) {
-                $printsection = get_section_name($course, $sections[$page->section]);
+                $printsection = get_section_name($course, $page->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 8249d68..1d30639 100644 (file)
@@ -62,7 +62,6 @@ if (!$quizzes = get_all_instances_in_course("quiz", $course)) {
     notice(get_string('thereareno', 'moodle', $strquizzes), "../../course/view.php?id=$course->id");
     die;
 }
-$sections = get_all_sections($course->id);
 
 // Check if we need the closing date header.
 $showclosingheader = false;
@@ -132,7 +131,7 @@ foreach ($quizzes as $quiz) {
     if ($quiz->section != $currentsection) {
         if ($quiz->section) {
             $strsection = $quiz->section;
-            $strsection = get_section_name($course, $sections[$quiz->section]);
+            $strsection = get_section_name($course, $quiz->section);
         }
         if ($currentsection) {
             $learningtable->data[] = 'hr';
index 78efa46..7efd526 100644 (file)
@@ -199,7 +199,7 @@ if (empty($reportsbyname)) {
     $ADMIN->add('modsettings', $quizsettings);
 } else {
     $ADMIN->add('modsettings', new admin_category('modsettingsquizcat',
-            get_string('modulename', 'quiz'), !$module->visible));
+            get_string('modulename', 'quiz'), $module->is_enabled() === false));
     $ADMIN->add('modsettingsquizcat', $quizsettings);
 
     // Add the report pages for the settings.php files in sub directories of mod/quiz/report.
@@ -207,7 +207,7 @@ if (empty($reportsbyname)) {
         $reportname = $report;
 
         $settings = new admin_settingpage('modsettingsquizcat'.$reportname,
-                $strreportname, 'moodle/site:config', !$module->visible);
+                $strreportname, 'moodle/site:config', $module->is_enabled() === false);
         if ($ADMIN->fulltree) {
             include($CFG->dirroot . "/mod/quiz/report/$reportname/settings.php");
         }
index f3c516c..3e9baf3 100644 (file)
@@ -54,9 +54,6 @@ if (!$resources = get_all_instances_in_course('resource', $course)) {
 }
 
 $usesections = course_format_uses_sections($course->format);
-if ($usesections) {
-    $sections = get_all_sections($course->id);
-}
 
 $table = new html_table();
 $table->attributes['class'] = 'generaltable mod_index';
@@ -77,7 +74,7 @@ foreach ($resources as $resource) {
         $printsection = '';
         if ($resource->section !== $currentsection) {
             if ($resource->section) {
-                $printsection = get_section_name($course, $sections[$resource->section]);
+                $printsection = get_section_name($course, $resource->section);
             }
             if ($currentsection !== '') {
                 $table->data[] = 'hr';
index 6a81130..eb1f1b5 100644 (file)
@@ -62,7 +62,7 @@ if (confirm_sesskey() && (!empty($scoid))) {
             }
             if (substr($element, 0, 15) == 'adl.nav.request') {
                 // SCORM 2004 Sequencing Request
-                require_once($CFG->dirroot.'/mod/scorm/datamodels/sequencinglib.php');
+                require_once($CFG->dirroot.'/mod/scorm/datamodels/scorm_13lib.php');
 
                 $search = array('@continue@', '@previous@', '@\{target=(\S+)\}choice@', '@exit@', '@exitAll@', '@abandon@', '@abandonAll@');
                 $replace = array('continue_', 'previous_', '\1', 'exit_', 'exitall_', 'abandon_', 'abandonall');
index 0b1964c..a5dd434 100644 (file)
@@ -234,13 +234,13 @@ require_once($CFG->dirroot.'/mod/scorm/datamodels/callback.js.php');
                 result = StoreData(cmi,true);
                 if (nav.event != '') {
                     if (nav.event == 'continue') {
-                        setTimeout('scorm_get_next();',500);
+                        setTimeout('mod_scorm_launch_next_sco();',500);
                     } else {
-                        setTimeout('scorm_get_prev();',500);
+                        setTimeout('mod_scorm_launch_prev_sco();',500);
                     }
                 } else {
                     if (<?php echo $scorm->auto ?> == 1) {
-                        setTimeout('scorm_get_next();',500);
+                        setTimeout('mod_scorm_launch_next_sco();',500);
                     }
                 }
                 // trigger TOC update
index 22b944e..a51fc44 100644 (file)
@@ -53,7 +53,7 @@
                         startNode = el_new_tree;
                     }
                     //var sXML = new XMLSerializer().serializeToString(startNode);
-                    scorm_tree_node.buildTreeFromMarkup(startNode);
+                    scorm_tree_node.buildTreeFromMarkup('scormtree123');
                     var el = document.getElementById('scormtree123');
                     el.parentNode.removeChild(el);
                     scorm_tree_node.expandAll();
index f6a4523..f8b6963 100644 (file)
@@ -211,13 +211,13 @@ function SCORMapi1_2() {
                 result = StoreData(cmi,true);
                 if (nav.event != '') {
                     if (nav.event == 'continue') {
-                        setTimeout('scorm_get_next();',500);
+                        setTimeout('mod_scorm_launch_next_sco();',500);
                     } else {
-                        setTimeout('scorm_get_prev();',500);
+                        setTimeout('mod_scorm_launch_prev_sco();',500);
                     }
                 } else {
                     if (<?php echo $scorm->auto ?> == 1) {
-                        setTimeout('scorm_get_next();',500);
+                        setTimeout('mod_scorm_launch_next_sco();',500);
                     }
                 }
                 <?php
index 37857f1..a4ad5db 100644 (file)
@@ -102,7 +102,7 @@ function SCORMapi1_3() {
     var CMIExit = '^time-out$|^suspend$|^logout$|^normal$|^$';
     var CMIType = '^true-false$|^choice$|^(long-)?fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$|^other$';
     var CMIResult = '^correct$|^incorrect$|^unanticipated$|^neutral$|^-?([0-9]{1,4})(\\.[0-9]{1,18})?$';
-    var NAVEvent = '^previous$|^continue$|^exit$|^exitAll$|^abandon$|^abandonAll$|^suspendAll$|^{target=\\S{0,200}[a-zA-Z0-9]}choice$';
+    var NAVEvent = '^previous$|^continue$|^exit$|^exitAll$|^abandon$|^abandonAll$|^suspendAll$|^\{target=\\S{0,200}[a-zA-Z0-9]\}choice|jump$';
     var NAVBoolean = '^unknown$|^true$|^false$';
     var NAVTarget = '^previous$|^continue$|^choice.{target=\\S{0,200}[a-zA-Z0-9]}$'
     // Children lists
@@ -343,10 +343,10 @@ function SCORMapi1_3() {
                     if (adl.nav.request != '_none_') {
                         switch (adl.nav.request) {
                             case 'continue':
-                                setTimeout('scorm_get_next();',500);
+                                setTimeout('mod_scorm_launch_next_sco();',500);
                             break;
                             case 'previous':
-                                setTimeout('scorm_get_prev();',500);
+                                setTimeout('mod_scorm_launch_prev_sco();',500);
                             break;
                             case 'choice':
                             break;
@@ -361,7 +361,7 @@ function SCORMapi1_3() {
                         }
                     } else {
                         if (<?php echo $scorm->auto ?> == 1) {
-                            setTimeout('scorm_get_next();',500);
+                            setTimeout('mod_scorm_launch_next_sco();',500);
                         }
                     }
                     // trigger TOC update
index a7b697d..906d288 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/mod/scorm/datamodels/scormlib.php');
+require_once($CFG->dirroot.'/mod/scorm/datamodels/sequencinglib.php');
+
+function scorm_seq_overall ($scoid, $userid, $request, $attempt) {
+    $seq = scorm_seq_navigation($scoid, $userid, $request, $attempt);
+    if ($seq->navigation) {
+        if ($seq->termination != null) {
+            $seq = scorm_seq_termination($scoid, $userid, $seq);
+        }
+        if ($seq->sequencing != null) {
+            $seq = scorm_seq_sequencing($scoid, $userid, $seq);
+            if ($seq->sequencing == 'exit') { // Return the control to the LTS.
+                return 'true';
+            }
+        }
+        if ($seq->delivery != null) {
+            $seq = scorm_sequencing_delivery($scoid, $userid, $seq);
+            $seq = scorm_content_delivery_environment ($seq, $userid);
+        }
+    }
+    if ($seq->exception != null) {
+        $seq = scorm_sequencing_exception($seq);
+    }
+    return 'true';
+}
+
+function scorm_seq_navigation ($scoid, $userid, $request, $attempt=0) {
+    global $DB;
+
+    // Sequencing structure.
+    $seq = new stdClass();
+    $seq->currentactivity = scorm_get_sco($scoid);
+    $seq->traversaldir = null;
+    $seq->nextactivity = null;
+    $seq->deliveryvalid = null;
+    $seq->attempt = $attempt;
+
+    $seq->identifiedactivity = null;
+    $seq->delivery = null;
+    $seq->deliverable = false;
+    $seq->active = scorm_seq_is('active', $scoid, $userid);
+    $seq->suspended = scorm_seq_is('suspended', $scoid, $userid);
+    $seq->navigation = null;
+    $seq->termination = null;
+    $seq->sequencing = null;
+    $seq->target = null;
+    $seq->endsession = null;
+    $seq->exception = null;
+    $seq->reachable = true;
+    $seq->prevact = true;
+
+    $sco = scorm_get_sco($scoid);
+
+    switch ($request) {
+        case 'start_':
+            if (empty($seq->currentactivity)) {
+                $seq->navigation = true;
+                $seq->sequencing = 'start';
+            } else {
+                $seq->exception = 'NB.2.1-1'; // Sequencing session already begun.
+            }
+        break;
+        case 'resumeall_':
+            if (empty($seq->currentactivity)) {
+                // TODO: I think it's suspend instead of suspendedactivity.
+                if ($track = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$scoid, 'userid'=>$userid, 'element'=>'suspendedactivity'))) {
+
+                    $seq->navigation = true;
+                    $seq->sequencing = 'resumeall';
+                } else {
+                    $seq->exception = 'NB.2.1-3'; // No suspended activity found.
+                }
+            } else {
+                $seq->exception = 'NB.2.1-1'; // Sequencing session already begun.
+            }
+        break;
+        case 'continue_':
+        case 'previous_':
+            if (!empty($seq->currentactivity)) {
+                $sco = $seq->currentactivity;
+                if ($sco->parent != '/') {
+                    if ($parentsco = scorm_get_parent($sco)) {
+
+                        if (isset($parentsco->flow) && ($parentsco->flow == true)) { // I think it's parentsco.
+                            // Current activity is active!
+                            if (scorm_seq_is('active', $sco->id, $userid)) {
+                                if ($request == 'continue_') {
+                                    $seq->navigation = true;
+                                    $seq->termination = 'exit';
+                                    $seq->sequencing = 'continue';
+                                } else {
+                                    if (!isset($parentsco->forwardonly) || ($parentsco->forwardonly == false)) {
+                                        $seq->navigation = true;
+                                        $seq->termination = 'exit';
+                                        $seq->sequencing = 'previous';
+                                    } else {
+                                        $seq->exception = 'NB.2.1-5'; // Violates control mode.
+                                    }
+                                }
+                            }
+                        }
+
+                    }
+                }
+            } else {
+                $seq->exception = 'NB.2.1-2'; // Current activity not defined.
+            }
+        break;
+        case 'forward_':
+        case 'backward_':
+            $seq->exception = 'NB.2.1-7'; // None to be done, behavior not defined.
+        break;
+        case 'exit_':
+        case 'abandon_':
+            if (!empty($seq->currentactivity)) {
+                // Current activity is active !
+                $seq->navigation = true;
+                $seq->termination = substr($request, 0, -1);
+                $seq->sequencing = 'exit';
+            } else {
+                $seq->exception = 'NB.2.1-2'; // Current activity not defined.
+            }
+        case 'exitall_':
+        case 'abandonall_':
+        case 'suspendall_':
+            if (!empty($seq->currentactivity)) {
+                $seq->navigation = true;
+                $seq->termination = substr($request, 0, -1);
+                $seq->sequencing = 'exit';
+            } else {
+                $seq->exception = 'NB.2.1-2'; // Current activity not defined.
+            }
+        break;
+        default: // Example {target=<STRING>}choice.
+            if ($targetsco = $DB->get_record('scorm_scoes', array('scorm'=>$sco->scorm, 'identifier'=>$request))) {
+                if ($targetsco->parent != '/') {
+                    $seq->target = $request;
+                } else {
+                    if ($parentsco = scorm_get_parent($targetsco)) {
+                        if (!isset($parentsco->choice) || ($parentsco->choice == true)) {
+                            $seq->target = $request;
+                        }
+                    }
+                }
+                if ($seq->target != null) {
+                    if (empty($seq->currentactivity)) {
+                        $seq->navigation = true;
+                        $seq->sequencing = 'choice';
+                    } else {
+                        if (!$sco = scorm_get_sco($scoid)) {
+                            return $seq;
+                        }
+                        if ($sco->parent != $targetsco->parent) {
+                            $ancestors = scorm_get_ancestors($sco);
+                            $commonpos = scorm_find_common_ancestor($ancestors, $targetsco);
+                            if ($commonpos !== false) {
+                                if ($activitypath = array_slice($ancestors, 0, $commonpos)) {
+                                    foreach ($activitypath as $activity) {
+                                        if (scorm_seq_is('active', $activity->id, $userid) &&
+                                            (isset($activity->choiceexit) && ($activity->choiceexit == false))) {
+                                            $seq->navigation = false;
+                                            $seq->termination = null;
+                                            $seq->sequencing = null;
+                                            $seq->target = null;
+                                            $seq->exception = 'NB.2.1-8'; // Violates control mode.
+                                            return $seq;
+                                        }
+                                    }
+                                } else {
+                                    $seq->navigation = false;
+                                    $seq->termination = null;
+                                    $seq->sequencing = null;
+                                    $seq->target = null;
+                                    $seq->exception = 'NB.2.1-9';
+                                }
+                            }
+                        }
+                        // Current activity is active !
+                        $seq->navigation = true;
+                        $seq->sequencing = 'choice';
+                    }
+                } else {
+                    $seq->exception = 'NB.2.1-10';  // Violates control mode.
+                }
+            } else {
+                $seq->exception = 'NB.2.1-11';  // Target activity does not exists.
+            }
+        break;
+    }
+    return $seq;
+}
+
+function scorm_seq_termination ($seq, $userid) {
+    if (empty($seq->currentactivity)) {
+        $seq->termination = false;
+        $seq->exception = 'TB.2.3-1';
+        return $seq;
+    }
+
+    $sco = $seq->currentactivity;
+
+    if ((($seq->termination == 'exit') || ($seq->termination == 'abandon')) && !$seq->active) {
+        $seq->termination = false;
+        $seq->exception = 'TB.2.3-2';
+        return $seq;
+    }
+    switch ($seq->termination) {
+        case 'exit':
+            scorm_seq_end_attempt($sco, $userid, $seq);
+            $seq = scorm_seq_exit_action_rules($seq, $userid);
+            do {
+                $exit = false;// I think this is false. Originally this was true.
+                $seq = scorm_seq_post_cond_rules($seq, $userid);
+                if ($seq->termination == 'exitparent') {
+                    if ($sco->parent != '/') {
+                        $sco = scorm_get_parent($sco);
+                        $seq->currentactivity = $sco;
+                        $seq->active = scorm_seq_is('active', $sco->id, $userid);
+                        scorm_seq_end_attempt($sco, $userid, $seq);
+                        $exit = true; // I think it's true. Originally this was false.
+                    } else {
+                        $seq->termination = false;
+                        $seq->exception = 'TB.2.3-4';
+                        return $seq;
+                    }
+                }
+            } while (($exit == false) && ($seq->termination == 'exit'));
+            if ($seq->termination == 'exit') {
+                $seq->termination = true;
+                return $seq;
+            }
+        case 'exitall':
+            if ($seq->active) {
+                scorm_seq_end_attempt($sco, $userid, $seq);
+            }
+            // Terminate Descendent Attempts Process.
+
+            if ($ancestors = scorm_get_ancestors($sco)) {
+                foreach ($ancestors as $ancestor) {
+                    scorm_seq_end_attempt($ancestor, $userid, $seq);
+                    $seq->currentactivity = $ancestor;
+                }
+            }
+
+            $seq->active = scorm_seq_is('active', $seq->currentactivity->id, $userid);
+            $seq->termination = true;
+            $seq->sequencing = exit;
+        break;
+        case 'suspendall':
+            if (($seq->active) || ($seq->suspended)) {
+                scorm_seq_set('suspended', $sco->id, $userid, $attempt);
+            } else {
+                if ($sco->parent != '/') {
+                    $parentsco = scorm_get_parent($sco);
+                    scorm_seq_set('suspended', $parentsco->id, $userid, $attempt);
+                } else {
+                    $seq->termination = false;
+                    $seq->exception = 'TB.2.3-3';
+                }
+            }
+            if ($ancestors = scorm_get_ancestors($sco)) {
+                foreach ($ancestors as $ancestor) {
+                    scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);
+                    scorm_seq_set('suspended', $ancestor->id, $userid, $attempt);
+                    $seq->currentactivity = $ancestor;
+                }
+                $seq->termination = true;
+                $seq->sequencing = 'exit';
+            } else {
+                $seq->termination = false;
+                $seq->exception = 'TB.2.3-5';
+            }
+        break;
+        case 'abandon':
+            scorm_seq_set('active', $sco->id, $userid, $attempt, false);
+            $seq->active = null;
+            $seq->termination = true;
+        break;
+        case 'abandonall':
+            if ($ancestors = scorm_get_ancestors($sco)) {
+                foreach ($ancestors as $ancestor) {
+                    scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);
+                    $seq->currentactivity = $ancestor;
+                }
+                $seq->termination = true;
+                $seq->sequencing = 'exit';
+            } else {
+                $seq->termination = false;
+                $seq->exception = 'TB.2.3-6';
+            }
+        break;
+        default:
+            $seq->termination = false;
+            $seq->exception = 'TB.2.3-7';
+        break;
+    }
+    return $seq;
+}
+
+function scorm_seq_end_attempt($sco, $userid, $seq) {
+    global $DB;
+    if (scorm_is_leaf($sco)) {
+        if (!isset($sco->tracked) || ($sco->tracked == 1)) {
+            if (!scorm_seq_is('suspended', $sco->id, $userid)) {
+                if (!isset($sco->completionsetbycontent) || ($sco->completionsetbycontent == 0)) {
+                    if (!scorm_seq_is('attemptprogressstatus', $sco->id, $userid, $seq->attempt)) {
+                        $incomplete = $DB->get_field('scorm_scoes_track', 'value',
+                            array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'cmi.completion_status'));
+                        if ($incomplete != 'incomplete') {
+                            scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt);
+                            scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt);
+                        }
+                    }
+                }
+                if (!isset($sco->objectivesetbycontent) || ($sco->objectivesetbycontent == 0)) {
+                    if ($objectives = $DB->get_records('scorm_seq_objective', array('scoid'=>$sco->id))) {
+                        foreach ($objectives as $objective) {
+                            if ($objective->primaryobj) {
+                                if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $seq->attempt)) {
+                                    scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $seq->attempt);
+                                    scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $seq->attempt);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    } else if ($children = scorm_get_children($sco)) {
+        $suspended = false;
+        foreach ($children as $child) {
+            if (scorm_seq_is('suspended', $child, $userid, $seq->attempt)) {
+                $suspended = true;
+                break;
+            }
+        }
+        if ($suspended) {
+            scorm_seq_set('suspended', $sco, $userid, $seq->attempt);
+        } else {
+            scorm_seq_set('suspended', $sco, $userid, $seq->attempt, false);
+        }
+    }
+    scorm_seq_set('active', $sco->id, $userid, $seq->attempt, false);
+    scorm_seq_overall_rollup($sco, $userid, $seq);
+}
+
+function scorm_seq_is($what, $scoid, $userid, $attempt=0) {
+    global $DB;
+
+    // Check if passed activity $what is active.
+    $active = false;
+    if ($track = $DB->get_record('scorm_scoes_track',
+        array('scoid'=>$scoid, 'userid'=>$userid, 'attempt'=>$attempt, 'element'=>$what))) {
+
+        $active = true;
+    }
+    return $active;
+}
+
+function scorm_seq_set($what, $scoid, $userid, $attempt=0, $value='true') {
+    global $DB;
+
+    $sco = scorm_get_sco($scoid);
+
+    // Set passed activity to active or not.
+    if ($value == false) {
+        $DB->delete_records('scorm_scoes_track', array('scoid'=>$scoid, 'userid'=>$userid, 'attempt'=>$attempt, 'element'=>$what));
+    } else {
+        scorm_insert_track($userid, $sco->scorm, $sco->id, $attempt, $what, $value);
+    }
+
+    // Update grades in gradebook.
+    $scorm = $DB->get_record('scorm', array('id'=>$sco->scorm));
+    scorm_update_grades($scorm, $userid, true);
+}
+
+function scorm_evaluate_condition ($rollupruleconds, $sco, $userid) {
+    global $DB;
+
+    $res = false;
+
+    if (strpos($rollupruleconds, 'and ')) {
+        $rollupruleconds = array_filter(explode(' and ', $rollupruleconds));
+        $conditioncombination = 'all';
+    } else {
+        $rollupruleconds = array_filter(explode(' or ', $rollupruleconds));
+        $conditioncombination = 'or';
+    }
+    foreach ($rollupruleconds as $rolluprulecond) {
+        $notflag = false;
+        if (strpos($rolluprulecond, 'not') !== false) {
+            $rolluprulecond = str_replace('not', '', $rolluprulecond);
+            $notflag = true;
+        }
+        $conditionarray['condition'] = $rolluprulecond;
+        $conditionarray['notflag'] = $notflag;
+        $conditions[] = $conditionarray;
+    }
+    foreach ($conditions as $condition) {
+        $checknot = true;
+        $res = false;
+        if ($condition['notflag']) {
+            $checknot = false;
+        }
+        switch ($condition['condition']) {
+            case 'satisfied':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectivesatisfiedstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $r = $DB->get_record('scorm_scoes_track',
+                        array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectiveprogressstatus'));
+                    if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                        $res = true;
+                    }
+                }
+                break;
+
+            case 'objectiveStatusKnown':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectiveprogressstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $res = true;
+                }
+                break;
+
+            case 'notobjectiveStatusKnown':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectiveprogressstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $res = true;
+                }
+                break;
+
+            case 'objectiveMeasureKnown':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectivemeasurestatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $res = true;
+                }
+                break;
+
+            case 'notobjectiveMeasureKnown':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectivemeasurestatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $res = true;
+                }
+                break;
+
+            case 'completed':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'attemptcompletionstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $r = $DB->get_record('scorm_scoes_track',
+                        array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'attemptprogressstatus'));
+                    if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                        $res = true;
+                    }
+                }
+                break;
+
+            case 'attempted':
+                $attempt = $DB->get_field('scorm_scoes_track', 'attempt',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'x.start.time'));
+                if ($checknot && $attempt > 0) {
+                    $res = true;
+                } else if (!$checknot && $attempt <= 0) {
+                    $res = true;
+                }
+                break;
+
+            case 'attemptLimitExceeded':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'activityprogressstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $r = $DB->get_record('scorm_scoes_track',
+                        array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'limitconditionattemptlimitcontrol'));
+                    if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                        if ($r = $DB->get_field('scorm_scoes_track',
+                            'attempt', array('scoid'=>$sco->id, 'userid'=>$userid)) &&
+                            $r2 = $DB->get_record('scorm_scoes_track',
+                                array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'limitconditionattemptlimit')) ) {
+
+                            if ($checknot && ($r->value >= $r2->value)) {
+                                $res = true;
+                            } else if (!$checknot && ($r->value < $r2->value)) {
+                                $res = true;
+                            }
+                        }
+                    }
+                }
+                break;
+
+            case 'activityProgressKnown':
+                $r = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'activityprogressstatus'));
+                if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                    $r = $DB->get_record('scorm_scoes_track',
+                        array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'attemptprogressstatus'));
+                    if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
+                        $res = true;
+                    }
+                }
+                break;
+        }
+
+        if ($conditioncombination == 'all' && !$res) {
+            break;
+        } else if ($conditioncombination == 'or' && $res) {
+            break;
+        }
+    }
+
+    return $res;
+}
+
+function scorm_check_activity ($activity, $userid) {
+    $act = scorm_seq_rules_check($activity, 'disabled');
+    if ($act != null) {
+        return true;
+    }
+    if (scorm_limit_cond_check ($activity, $userid)) {
+        return true;
+    }
+    return false;
+}
+
+function scorm_limit_cond_check ($activity, $userid) {
+    global $DB;
+
+    if (isset($activity->tracked) && ($activity->tracked == 0)) {
+
+        return false;
+    }
+
+    if (scorm_seq_is('active', $activity->id, $userid) || scorm_seq_is('suspended', $activity->id, $userid)) {
+        return false;
+    }
+
+    if (!isset($activity->limitcontrol) || ($activity->limitcontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'activityattemptcount'));
+        if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >=$activity->limitattempt)) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitabsdurcontrol) || ($activity->limitabsdurcontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'activityabsoluteduration'));
+        if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >=$activity->limitabsduration)) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitexpdurcontrol) || ($activity->limitexpdurcontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'activityexperiencedduration'));
+        if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >=$activity->limitexpduration)) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitattabsdurcontrol) || ($activity->limitattabsdurcontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'attemptabsoluteduration'));
+        if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >=$activity->limitattabsduration)) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitattexpdurcontrol) || ($activity->limitattexpdurcontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'attemptexperiencedduration'));
+        if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >=$activity->limitattexpduration)) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {
+        $r = $DB->get_record('scorm_scoes_track',
+            array('scoid'=>$activity->id, 'userid'=>$userid, 'element'=>'begintime'));
+        if (isset($activity->limitbegintime) && time() >= $activity->limitbegintime) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {
+        if (isset($activity->limitbegintime) && time() < $activity->limitbegintime) {
+            return true;
+        }
+    }
+
+    if (!isset($activity->limitendcontrol) || ($activity->limitendcontrol == 1)) {
+        if (isset($activity->limitendtime) && time() > $activity->limitendtime) {
+            return true;
+        }
+    }
+    return false;
+}
+
+function scorm_seq_rules_check ($sco, $action) {
+    global $DB;
+    $act = null;
+
+    if ($rules = $DB->get_records('scorm_seq_ruleconds', array('scoid' => $sco->id, 'action' => $action))) {
+        foreach ($rules as $rule) {
+            if ($act = scorm_seq_rule_check($sco, $rule)) {
+                return $act;
+            }
+        }
+    }
+    return $act;
+
+}
+
+function scorm_seq_rule_check ($sco, $rule) {
+    global $DB;
+
+    $bag = Array();
+    $cond = '';
+    $ruleconds = $DB->get_records('scorm_seq_rulecond', array('scoid'=>$sco->id, 'ruleconditionsid'=>$rule->id));
+    foreach ($ruleconds as $rulecond) {
+        if ($rulecond->operator == 'not') {
+            if ($rulecond->cond != 'unknown' ) {
+                $rulecond->cond = 'not'.$rulecond->cond;
+            }
+        }
+         $bag[] = $rulecond->cond;
+    }
+    if (empty($bag)) {
+        $cond = 'unknown';
+        return $cond;
+    }
+
+    if ($rule->conditioncombination == 'all') {
+        foreach ($bag as $con) {
+                $cond = $cond.' and '.$con;
+        }
+    } else {
+        foreach ($bag as $con) {
+            $cond = $cond.' or '.$con;
+        }
+    }
+    return $cond;
+}
+
+function scorm_seq_overall_rollup($sco, $userid, $seq) {
+    if ($ancestors = scorm_get_ancestors($sco)) {
+        foreach ($ancestors as $ancestor) {
+            if (!scorm_is_leaf($ancestor)) {
+                scorm_seq_measure_rollup($sco, $userid, $seq->attempt);
+            }
+            scorm_seq_objective_rollup($sco, $userid, $seq->attempt);
+            scorm_seq_activity_progress_rollup($sco, $userid, $seq);
+        }
+    }
+}
+
+function scorm_seq_measure_rollup($sco, $userid, $attempt = 0) {
+    global $DB;
+
+    $totalmeasure = 0; // Check if there is something similar in the database.
+    $valid = false; // Same as in the last line.
+    $countedmeasures = 0; // Same too.
+    $targetobjective = null;
+    $objectives = $DB->get_records('scorm_seq_objective', array('scoid'=>$sco->id));
+
+    foreach ($objectives as $objective) {
+        if ($objective->primaryobj == true) { // Objective contributes to rollup.
+            $targetobjective = $objective;
+            break;
+        }
+
+    }
+    if ($targetobjective != null) {
+        $children = scorm_get_children($sco);
+        if (!empty ($children)) {
+            foreach ($children as $child) {
+                $child = scorm_get_sco ($child);
+                if (!isset($child->tracked) || ($child->tracked == 1)) {
+                    $rolledupobjective = null;// We set the rolled up activity to undefined.
+                    $objectives = $DB->get_records('scorm_seq_objective', array('scoid'=>$child->id));
+                    foreach ($objectives as $objective) {
+                        if ($objective->primaryobj == true) {// Objective contributes to rollup I'm using primaryobj field, but not.
+                            $rolledupobjective = $objective;
+                            break;
+                        }
+                    }
+                    if ($rolledupobjective != null) {
+                        $child = scorm_get_sco($child->id);
+                        $countedmeasures = $countedmeasures + ($child->measureweight);
+                        if (!scorm_seq_is('objectivemeasurestatus', $sco->id, $userid, $attempt)) {
+                            $normalizedmeasure = $DB->get_record('scorm_scoes_track',
+                                array('scoid'=>$child->id, 'userid'=>$userid, 'element'=>'objectivenormalizedmeasure'));
+                            $totalmeasure = $totalmeasure + (($normalizedmeasure->value) * ($child->measureweight));
+                            $valid = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$valid) {
+            scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);
+        } else {
+            if ($countedmeasures > 0) {
+                scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt);
+                $val = $totalmeasure/$countedmeasures;
+                scorm_seq_set('objectivenormalizedmeasure', $sco->id, $userid, $attempt, $val);
+            } else {
+                scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);
+            }
+        }
+    }
+}
+
+function scorm_seq_objective_rollup($sco, $userid, $attempt = 0) {
+    global $DB;
+
+    scorm_seq_objective_rollup_measure($sco, $userid, $attempt);
+    scorm_seq_objective_rollup_rules($sco, $userid, $attempt);
+    scorm_seq_objective_rollup_default($sco, $userid, $attempt);
+
+    /*
+    if ($targetobjective->satisfiedbymeasure) {
+        scorm_seq_objective_rollup_measure($sco, $userid);
+    }
+    else{
+        if ((scorm_seq_rollup_rule_check($sco, $userid, 'incomplete'))
+             || (scorm_seq_rollup_rule_check($sco, $userid, 'completed'))) {
+            scorm_seq_objective_rollup_rules($sco, $userid);
+        }
+        else{
+
+            $rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid'=>$sco->id, 'userid'=>$userid));
+            foreach ($rolluprules as $rolluprule) {
+                $rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid'=>$rolluprule->id));
+                foreach ($rollupruleconds as $rolluprulecond) {
+
+                    switch ($rolluprulecond->cond!='satisfied'
+                            && $rolluprulecond->cond!='completed' && $rolluprulecond->cond!='attempted') {
+
+                           scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, false);
+
+                        break;
+                    }
+                }
+
+
+        }
+    }
+    */
+}
+
+function scorm_seq_objective_rollup_measure($sco, $userid, $attempt = 0) {
+    global $DB;
+
+    $targetobjective = null;
+
+    $objectives = $DB->get_records('scorm_seq_objective', array('scoid'=>$sco->id));
+    foreach ($objectives as $objective) {
+        if ($objective->primaryobj == true) {
+            $targetobjective = $objective;
+            break;
+        }
+    }
+    if ($targetobjective != null) {
+        if ($targetobjective->satisfiedbymeasure) {
+            if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $attempt)) {
+                scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt, false);
+            } else {
+                if (scorm_seq_is('active', $sco->id, $userid, $attempt)) {
+                    $isactive = true;
+                } else {
+                    $isactive = false;
+                }
+
+                $normalizedmeasure = $DB->get_record('scorm_scoes_track',
+                    array('scoid'=>$sco->id, 'userid'=>$userid, 'element'=>'objectivenormalizedmeasure'));
+
+                $sco = scorm_get_sco ($sco->id);
+
+                if (!$isactive || ($isactive &&
+                    (!isset($sco->measuresatisfactionifactive) || $sco->measuresatisfactionifactive == true))) {
+                    if (isset($normalizedmeasure-&g