Merge branch 'MDL-29857-3' of git://github.com/danpoltawski/moodle
authorSam Hemelryk <sam@moodle.com>
Fri, 1 Jun 2012 01:42:01 +0000 (13:42 +1200)
committerSam Hemelryk <sam@moodle.com>
Fri, 1 Jun 2012 01:42:01 +0000 (13:42 +1200)
119 files changed:
admin/cli/install.php
admin/cli/install_database.php
admin/cli/mysql_engine.php
admin/cli/upgrade.php
admin/index.php
admin/renderer.php
admin/tool/bloglevelupgrade/index.php
auth/shibboleth/index.php
backup/moodle2/backup_stepslib.php
backup/moodle2/backup_xml_transformer.class.php
backup/moodle2/restore_final_task.class.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/backup_controller_dbops.class.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
backup/util/plan/base_plan.class.php
backup/util/ui/backup_ui_stage.class.php
backup/util/ui/base_moodleform.class.php
calendar/lib.php
course/changenumsections.php
course/format/renderer.php
course/format/topics/format.js
course/format/topics/lib.php
course/format/weeks/format.js
course/format/weeks/lib.php
course/lib.php
course/recent.php
course/rest.php
course/view.php
course/yui/coursebase/coursebase.js
course/yui/dragdrop/dragdrop.js
files/renderer.php
grade/lib.php
lang/en/access.php
lang/en/admin.php
lang/en/moodle.php
lang/en/plugin.php
lib/completion/completion_criteria_activity.php
lib/completion/completion_criteria_course.php
lib/completion/completion_criteria_date.php
lib/completion/completion_criteria_duration.php
lib/completion/completion_criteria_grade.php
lib/conditionlib.php
lib/db/install.xml
lib/db/log.php
lib/db/upgrade.php
lib/dml/mssql_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/filebrowser/file_info_stored.php
lib/filelib.php
lib/filestorage/file_storage.php
lib/filestorage/stored_file.php
lib/filestorage/tests/file_storage_test.php
lib/form/dndupload.js
lib/form/filemanager.js
lib/form/filepicker.js
lib/gdlib.php
lib/installlib.php
lib/mathslib.php
lib/moodlelib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/pluginlib.php
lib/portfolio/exporter.php
lib/setup.php
lib/tests/conditionlib_test.php
lib/upgradelib.php
lib/yui/blocks/blocks.js
lib/yui/dragdrop/dragdrop.js
mod/assign/assignmentplugin.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/file/locallib.php
mod/assign/locallib.php
mod/assign/settings.php
mod/assign/submission/comments/locallib.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/book/lang/en/book.php
mod/book/locallib.php
mod/book/mod_form.php
mod/book/settings.php
mod/book/styles.css
mod/book/tool/exportimscp/lang/en/booktool_exportimscp.php
mod/book/tool/importhtml/lang/en/booktool_importhtml.php
mod/book/version.php
mod/folder/edit.php
mod/forum/rsslib.php
mod/forum/subscribe.php
mod/quiz/module.js
mod/resource/lib.php
mod/scorm/datamodels/aicclib.php
pix/u/f3.png [new file with mode: 0644]
question/import_form.php
question/type/essay/db/upgrade.php
question/type/essay/renderer.php
question/type/essay/version.php
report/log/index.php
repository/coursefiles/lib.php
theme/afterburner/config.php
theme/anomaly/config.php
theme/arialist/config.php
theme/base/config.php
theme/boxxie/config.php
theme/boxxie/style/boilerplate.css [deleted file]
theme/canvas/config.php
theme/formal_white/config.php
theme/formal_white/style/formal_white.css
theme/formfactor/config.php
theme/leatherbound/config.php
theme/magazine/config.php
theme/serenity/layout/embedded.php [deleted file]
theme/serenity/layout/frontpage.php [deleted file]
theme/serenity/layout/general.php [deleted file]
theme/splash/config.php
theme/standardold/config.php
theme/upgrade.txt
user/files.php
user/files_form.php
version.php

index dc65e31..79ff0b7 100644 (file)
@@ -425,7 +425,8 @@ if (isset($maturity)) {
                 exit(1);
             }
         } else {
-            cli_error(get_string('maturitycorewarning', 'admin'));
+            cli_problem(get_string('maturitycorewarning', 'admin', $maturitylevel));
+            cli_error(get_string('maturityallowunstable', 'admin'));
         }
     }
 }
@@ -677,7 +678,9 @@ if (!$envstatus) {
 
 // Test plugin dependencies.
 require_once($CFG->libdir . '/pluginlib.php');
-if (!plugin_manager::instance()->all_plugins_ok($version)) {
+$failed = array();
+if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+    cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
     cli_error(get_string('pluginschecktodo', 'admin'));
 }
 
index 2524c41..313f352 100644 (file)
@@ -161,7 +161,9 @@ if (!$envstatus) {
 
 // Test plugin dependencies.
 require_once($CFG->libdir . '/pluginlib.php');
-if (!plugin_manager::instance()->all_plugins_ok($version)) {
+$failed = array();
+if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+    cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
     cli_error(get_string('pluginschecktodo', 'admin'));
 }
 
index 3b51f41..6bc3642 100644 (file)
@@ -34,8 +34,8 @@ if ($DB->get_dbfamily() !== 'mysql') {
 }
 
 // now get cli options
-list($options, $unrecognized) = cli_get_params(array('help'=>false, 'list'=>false, 'engine'=>false),
-                                               array('h'=>'help', 'l'=>'list'));
+list($options, $unrecognized) = cli_get_params(array('help'=>false, 'list'=>false, 'engine'=>false, 'available'=>false),
+                                               array('h'=>'help', 'l'=>'list', 'a'=>'available'));
 
 if ($unrecognized) {
     $unrecognized = implode("\n  ", $unrecognized);
@@ -52,6 +52,7 @@ and does not support transactions.
 Options:
 --engine=ENGINE       Convert MySQL tables to different engine
 -l, --list            Show table information
+-a, --available       Show list of available engines
 -h, --help            Print out this help
 
 Example:
@@ -59,7 +60,11 @@ Example:
 ";
 
 if (!empty($options['engine'])) {
+    $engines = mysql_get_engines();
     $engine = clean_param($options['engine'], PARAM_ALPHA);
+    if (!isset($engines[strtoupper($engine)])) {
+        cli_error("Error: engine '$engine' is not available on this server!");
+    }
 
     echo "Converting tables to '$engine' for $CFG->wwwroot:\n";
     $prefix = $DB->get_prefix();
@@ -68,9 +73,11 @@ if (!empty($options['engine'])) {
     $rs = $DB->get_recordset_sql($sql);
     $converted = 0;
     $skipped   = 0;
+    $errors    = 0;
     foreach ($rs as $table) {
-        if ($table->engine === $engine) {
-            echo str_pad($table->name, 40). " - NO CONVERSION NEEDED\n";
+        if (strtoupper($table->engine) === strtoupper($engine)) {
+            $newengine = mysql_get_table_engine($table->name);
+            echo str_pad($table->name, 40). " - NO CONVERSION NEEDED ($newengine)\n";
             $skipped++;
             continue;
         }
@@ -78,16 +85,22 @@ if (!empty($options['engine'])) {
 
         try {
             $DB->change_database_structure("ALTER TABLE {$table->name} ENGINE = $engine");
+            $newengine = mysql_get_table_engine($table->name);
+            if (strtoupper($newengine) !== strtoupper($engine)) {
+                echo "ERROR ($newengine)\n";
+                $errors++;
+                continue;
+            }
+            echo "DONE ($newengine)\n";
+            $converted++;
         } catch (moodle_exception $e) {
             echo $e->getMessage()."\n";
-            $skipped++;
+            $errors++;
             continue;
         }
-        echo "DONE\n";
-        $converted++;
     }
     $rs->close();
-    echo "Converted: $converted, skipped: $skipped\n";
+    echo "Converted: $converted, skipped: $skipped, errors: $errors\n";
     exit(0); // success
 
 } else if (!empty($options['list'])) {
@@ -115,7 +128,53 @@ if (!empty($options['engine'])) {
     }
     exit(0); // success
 
+} else if (!empty($options['available'])) {
+    echo "List of available MySQL engines for $CFG->wwwroot:\n";
+    $engines = mysql_get_engines();
+    foreach ($engines as $engine) {
+        echo " $engine\n";
+    }
+    die;
+
 } else {
     echo $help;
     die;
 }
+
+
+
+// ========== Some functions ==============
+
+function mysql_get_engines() {
+    global $DB;
+
+    $sql = "SHOW Engines";
+    $rs = $DB->get_recordset_sql($sql);
+    $engines = array();
+    foreach ($rs as $engine) {
+        if (strtoupper($engine->support) !== 'YES' and strtoupper($engine->support) !== 'DEFAULT') {
+            continue;
+        }
+        $engines[strtoupper($engine->engine)] = $engine->engine;
+        if (strtoupper($engine->support) === 'DEFAULT') {
+            $engines[strtoupper($engine->engine)] .= ' (default)';
+        }
+    }
+    $rs->close();
+
+    return $engines;
+}
+
+function mysql_get_table_engine($tablename) {
+    global $DB;
+
+    $engine = null;
+    $sql = "SHOW TABLE STATUS WHERE Name = '$tablename'"; // no special chars expected here
+    $rs = $DB->get_recordset_sql($sql);
+    if ($rs->valid()) {
+        $record = $rs->current();
+        $engine = $record->engine;
+    }
+    $rs->close();
+    return $engine;
+}
index 33e66cc..1fae897 100644 (file)
@@ -109,7 +109,9 @@ if (!$envstatus) {
 }
 
 // Test plugin dependencies.
-if (!plugin_manager::instance()->all_plugins_ok($version)) {
+$failed = array();
+if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+    cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
     cli_error(get_string('pluginschecktodo', 'admin'));
 }
 
@@ -132,7 +134,8 @@ if (isset($maturity)) {
             echo get_string('morehelp') . ': ' . get_docs_url('admin/versions') . PHP_EOL;
             cli_separator();
         } else {
-            cli_error(get_string('maturitycorewarning', 'admin', $maturitylevel));
+            cli_problem(get_string('maturitycorewarning', 'admin', $maturitylevel));
+            cli_error(get_string('maturityallowunstable', 'admin'));
         }
     }
 }
index 5b01be2..bf3eea5 100644 (file)
@@ -152,6 +152,20 @@ if (!core_tables_exist()) {
         die();
     }
 
+    // check plugin dependencies
+    $failed = array();
+    if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+        $PAGE->navbar->add(get_string('pluginscheck', 'admin'));
+        $PAGE->set_title($strinstallation);
+        $PAGE->set_heading($strinstallation . ' - Moodle ' . $CFG->target_release);
+
+        $output = $PAGE->get_renderer('core', 'admin');
+        $url = new moodle_url('/admin/index.php', array('agreelicense' => 1, 'confirmrelease' => 1, 'lang' => $CFG->lang));
+        echo $output->unsatisfied_dependencies_page($version, $failed, $url);
+        die();
+    }
+    unset($failed);
+
     //TODO: add a page with list of non-standard plugins here
 
     $strdatabasesetup = get_string('databasesetup');
@@ -238,6 +252,15 @@ if ($version > $CFG->version) {  // upgrade
 
         $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
 
+        // check plugin dependencies first
+        $failed = array();
+        if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $output = $PAGE->get_renderer('core', 'admin');
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
+            die();
+        }
+        unset($failed);
+
         if ($fetchupdates) {
             // no sesskey support guaranteed here
             if (empty($CFG->disableupdatenotifications)) {
@@ -290,6 +313,16 @@ if (moodle_needs_upgrading()) {
             }
 
             $output = $PAGE->get_renderer('core', 'admin');
+
+            // check plugin dependencies first
+            $failed = array();
+            if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+                echo $output->unsatisfied_dependencies_page($version, $failed, $PAGE->url);
+                die();
+            }
+            unset($failed);
+
+            // dependencies check passed, let's rock!
             echo $output->upgrade_plugin_check_page(plugin_manager::instance(), available_update_checker::instance(),
                     $version, $showallplugins,
                     new moodle_url($PAGE->url),
index b7a1bab..9b689a6 100644 (file)
@@ -107,6 +107,29 @@ class core_admin_renderer extends plugin_renderer_base {
         return $output;
     }
 
+    /**
+     * Displays the list of plugins with unsatisfied dependencies
+     *
+     * @param double|string|int $version Moodle on-disk version
+     * @param array $failed list of plugins with unsatisfied dependecies
+     * @param moodle_url $reloadurl URL of the page to recheck the dependencies
+     * @return string HTML
+     */
+    public function unsatisfied_dependencies_page($version, array $failed, moodle_url $reloadurl) {
+        $output = '';
+
+        $output .= $this->header();
+        $output .= $this->heading(get_string('pluginscheck', 'admin'));
+        $output .= $this->warning(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
+        $output .= $this->plugins_check_table(plugin_manager::instance(), $version, array('xdep' => true));
+        $output .= $this->warning(get_string('pluginschecktodo', 'admin'));
+        $output .= $this->continue_button($reloadurl);
+
+        $output .= $this->footer();
+
+        return $output;
+    }
+
     /**
      * Display the 'You are about to upgrade Moodle' page. The first page
      * during upgrade.
@@ -197,19 +220,15 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->box_end();
         $output .= $this->upgrade_reload($reloadurl);
 
-        if ($pluginman->all_plugins_ok($version)) {
-            if ($pluginman->some_plugins_updatable()) {
-                $output .= $this->container_start('upgradepluginsinfo');
-                $output .= $this->help_icon('upgradepluginsinfo', 'core_admin', get_string('upgradepluginsfirst', 'core_admin'));
-                $output .= $this->container_end();
-            }
-            $button = new single_button($continueurl, get_string('upgradestart', 'admin'), 'get');
-            $button->class = 'continuebutton';
-            $output .= $this->render($button);
-        } else {
-            $output .= $this->box(get_string('pluginschecktodo', 'admin'), 'environmentbox errorbox');
+        if ($pluginman->some_plugins_updatable()) {
+            $output .= $this->container_start('upgradepluginsinfo');
+            $output .= $this->help_icon('upgradepluginsinfo', 'core_admin', get_string('upgradepluginsfirst', 'core_admin'));
+            $output .= $this->container_end();
         }
 
+        $button = new single_button($continueurl, get_string('upgradestart', 'admin'), 'get');
+        $button->class = 'continuebutton';
+        $output .= $this->render($button);
         $output .= $this->footer();
 
         return $output;
@@ -563,13 +582,14 @@ class core_admin_renderer extends plugin_renderer_base {
      * This default implementation renders all plugins into one big table. The rendering
      * options support:
      *     (bool)full = false: whether to display up-to-date plugins, too
+     *     (bool)xdep = false: display the plugins with unsatisified dependecies only
      *
      * @param plugin_manager $pluginman provides information about the plugins.
      * @param int $version the version of the Moodle code from version.php.
      * @param array $options rendering options
      * @return string HTML code
      */
-    public function plugins_check_table(plugin_manager $pluginman, $version, array $options = null) {
+    public function plugins_check_table(plugin_manager $pluginman, $version, array $options = array()) {
         global $CFG;
 
         $plugininfo = $pluginman->get_plugins();
@@ -578,11 +598,8 @@ class core_admin_renderer extends plugin_renderer_base {
             return '';
         }
 
-        if (empty($options)) {
-            $options = array(
-                'full' => false,
-            );
-        }
+        $options['full'] = isset($options['full']) ? (bool)$options['full'] : false;
+        $options['xdep'] = isset($options['xdep']) ? (bool)$options['xdep'] : false;
 
         $table = new html_table();
         $table->id = 'plugins-check';
@@ -666,16 +683,28 @@ class core_admin_renderer extends plugin_renderer_base {
 
                 $statusisboring = in_array($statuscode, array(
                         plugin_manager::PLUGIN_STATUS_NODB, plugin_manager::PLUGIN_STATUS_UPTODATE));
-                $dependenciesok = $pluginman->are_dependencies_satisfied(
-                        $plugin->get_other_required_plugins());
-                if ($isstandard and $statusisboring and $dependenciesok and empty($availableupdates)) {
+
+                $coredependency = $plugin->is_core_dependency_satisfied($version);
+                $otherpluginsdependencies = $pluginman->are_dependencies_satisfied($plugin->get_other_required_plugins());
+                $dependenciesok = $coredependency && $otherpluginsdependencies;
+
+                if ($options['xdep']) {
+                    // we want to see only plugins with failed dependencies
+                    if ($dependenciesok) {
+                        continue;
+                    }
+
+                } else if ($isstandard and $statusisboring and $dependenciesok and empty($availableupdates)) {
+                    // no change is going to happen to the plugin - display it only
+                    // if the user wants to see the full list
                     if (empty($options['full'])) {
                         continue;
                     }
-                } else {
-                    $numofhighlighted[$type]++;
                 }
 
+                // ok, the plugin should be displayed
+                $numofhighlighted[$type]++;
+
                 $row->cells = array($displayname, $rootdir, $source,
                     $versiondb, $versiondisk, $requires, $status);
                 $plugintyperows[] = $row;
@@ -691,7 +720,11 @@ class core_admin_renderer extends plugin_renderer_base {
 
         $sumofhighlighted = array_sum($numofhighlighted);
 
-        if ($sumofhighlighted == 0) {
+        if ($options['xdep']) {
+            // we do not want to display no heading and links in this mode
+            $out = '';
+
+        } else if ($sumofhighlighted == 0) {
             $out  = $this->output->container_start('nonehighlighted', 'plugins-check-info');
             $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin'));
             if (empty($options['full'])) {
index 9c87eec..bd2a531 100644 (file)
@@ -121,7 +121,7 @@ function bloglevelupgrade_entries($blogentries, $forum, $cm, $groupid=-1) {
         $discussion->groupid = $groupid;
         $message = '';
 
-        $discussionid = forum_add_discussion($discussion, null, $message);
+        $discussionid = forum_add_discussion($discussion, null, $message, $blogentry->userid);
 
         // Copy file attachment records
         $fs = get_file_storage();
index 7bc8f90..a1e4a1c 100644 (file)
@@ -30,6 +30,7 @@
 
 /// If we can find the Shibboleth attribute, save it in session and return to main login page
     if (!empty($_SERVER[$pluginconfig->user_attribute])) {    // Shibboleth auto-login
+        $frm = new stdClass();
         $frm->username = strtolower($_SERVER[$pluginconfig->user_attribute]);
         $frm->password = substr(base64_encode($_SERVER[$pluginconfig->user_attribute]),0,8);
         // The random password consists of the first 8 letters of the base 64 encoded user ID
index d1be602..a2fa948 100644 (file)
@@ -1414,7 +1414,7 @@ class backup_final_files_structure_step extends backup_structure_step {
 
         $file->set_source_sql("SELECT f.*, r.repositoryid, r.reference
                                  FROM {files} f
-                                 JOIN {files_reference} r
+                                 LEFT JOIN {files_reference} r
                                       ON r.id = f.referencefileid
                                  JOIN {backup_ids_temp} bi
                                       ON f.id = bi.itemid
index a3770d0..25c8503 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+// Cache for storing link encoders, so that we don't need to call
+// register_link_encoders each time backup_xml_transformer is constructed
+// TODO MDL-25290 replace global with MUC code.
+global $LINKS_ENCODERS_CACHE;
+
+$LINKS_ENCODERS_CACHE = array();
+
 /**
  * Class implementing the @xml_contenttrasnformed logic to be applied in moodle2 backups
  *
@@ -131,7 +138,19 @@ class backup_xml_transformer extends xml_contenttransformer {
         return $result;
     }
 
+    /**
+     * Register all available content link encoders
+     *
+     * @return array encoder
+     * @todo MDL-25290 replace LINKS_ENCODERS_CACHE global with MUC code
+     */
     private function register_link_encoders() {
+        global $LINKS_ENCODERS_CACHE;
+        // If encoder is linked, then return cached encoder.
+        if (!empty($LINKS_ENCODERS_CACHE)) {
+            return $LINKS_ENCODERS_CACHE;
+        }
+
         $encoders = array();
 
         // Add the course encoder
@@ -160,6 +179,7 @@ class backup_xml_transformer extends xml_contenttransformer {
         // Add local encodes
         // TODO: Any interest? 1.9 never had that.
 
+        $LINKS_ENCODERS_CACHE = $encoders;
         return $encoders;
     }
 }
index e8831d7..377011e 100644 (file)
@@ -135,6 +135,7 @@ class restore_final_task extends restore_task {
         $rules[] = new restore_log_rule('course', 'report outline', 'report/outline/index.php?id={course}', '{course}');
         $rules[] = new restore_log_rule('course', 'report participation', 'report/participation/index.php?id={course}', '{course}');
         $rules[] = new restore_log_rule('course', 'report stats', 'report/stats/index.php?id={course}', '{course}');
+        $rules[] = new restore_log_rule('course', 'view section', 'view.php?id={course}&section={course_sectionnumber}', '{course_section}');
 
         // module 'user' rules
         $rules[] = new restore_log_rule('user', 'view', 'view.php?id={user}&course={course}', '{user}');
index 4d610e5..d87cdb9 100644 (file)
@@ -1047,6 +1047,7 @@ class restore_section_structure_step extends restore_structure_step {
         global $CFG, $DB;
         $data = (object)$data;
         $oldid = $data->id; // We'll need this later
+        $oldsection = $data->number;
 
         $restorefiles = false;
 
@@ -1099,10 +1100,12 @@ class restore_section_structure_step extends restore_structure_step {
 
             $DB->update_record('course_sections', $section);
             $newitemid = $secrec->id;
+            $oldsection = $secrec->section;
         }
 
         // Annotate the section mapping, with restorefiles option if needed
         $this->set_mapping('course_section', $oldid, $newitemid, $restorefiles);
+        $this->set_mapping('course_sectionnumber', $oldsection, $section->section, $restorefiles);
 
         // set the new course_section id in the task
         $this->task->set_sectionid($newitemid);
@@ -2541,7 +2544,7 @@ class restore_module_structure_step extends restore_structure_step {
 
         $data = (object)$data;
         $oldid = $data->id;
-
+        $oldsection = $data->sectionnumber;
         $this->task->set_old_moduleversion($data->version);
 
         $data->course = $this->task->get_courseid();
@@ -2568,6 +2571,7 @@ class restore_module_structure_step extends restore_structure_step {
                 'course' => $this->get_courseid(),
                 'section' => 1);
             $data->section = $DB->insert_record('course_sections', $sectionrec); // section 1
+            $this->set_mapping('course_sectionnumber', $oldsection, $sectionrec->section, $restorefiles);
         }
         $data->groupingid= $this->get_mappingid('grouping', $data->groupingid);      // grouping
         if (!$CFG->enablegroupmembersonly) {                                         // observe groupsmemberonly
index ee98987..68c1752 100644 (file)
@@ -420,7 +420,7 @@ abstract class backup_controller_dbops extends backup_dbops {
 
         $sql = "SELECT count(r.repositoryid)
                   FROM {files} f
-                  JOIN {files_reference} r
+                  LEFT JOIN {files_reference} r
                        ON r.id = f.referencefileid
                   JOIN {backup_ids_temp} bi
                        ON f.id = bi.itemid
index e50c6e6..81dbaf6 100644 (file)
  * TODO: Finish phpdocs
  */
 abstract class restore_dbops {
+    /**
+     * Keep cache of backup records.
+     * @var array
+     * @todo MDL-25290 static should be replaced with MUC code.
+     */
+    private static $backupidscache = array();
+    /**
+     * Keep track of backup ids which are cached.
+     * @var array
+     * @todo MDL-25290 static should be replaced with MUC code.
+     */
+    private static $backupidsexist = array();
+    /**
+     * Count is expensive, so manually keeping track of
+     * backupidscache, to avoid memory issues.
+     * @var int
+     * @todo MDL-25290 static should be replaced with MUC code.
+     */
+    private static $backupidscachesize = 2048;
+    /**
+     * Count is expensive, so manually keeping track of
+     * backupidsexist, to avoid memory issues.
+     * @var int
+     * @todo MDL-25290 static should be replaced with MUC code.
+     */
+    private static $backupidsexistsize = 10240;
+    /**
+     * Slice backupids cache to add more data.
+     * @var int
+     * @todo MDL-25290 static should be replaced with MUC code.
+     */
+    private static $backupidsslice = 512;
 
     /**
      * Return one array containing all the tasks that have been included
@@ -151,6 +183,135 @@ abstract class restore_dbops {
         return $problems;
     }
 
+    /**
+     * Return cached backup id's
+     *
+     * @param int $restoreid id of backup
+     * @param string $itemname name of the item
+     * @param int $itemid id of item
+     * @return array backup id's
+     * @todo MDL-25290 replace static backupids* with MUC code
+     */
+    protected static function get_backup_ids_cached($restoreid, $itemname, $itemid) {
+        global $DB;
+
+        $key = "$itemid $itemname $restoreid";
+
+        // If record exists in cache then return.
+        if (isset(self::$backupidsexist[$key]) && isset(self::$backupidscache[$key])) {
+            // Return a copy of cached data, to avoid any alterations in cached data.
+            return clone self::$backupidscache[$key];
+        }
+
+        // Clean cache, if it's full.
+        if (self::$backupidscachesize <= 0) {
+            // Remove some records, to keep memory in limit.
+            self::$backupidscache = array_slice(self::$backupidscache, self::$backupidsslice, null, true);
+            self::$backupidscachesize = self::$backupidscachesize + self::$backupidsslice;
+        }
+        if (self::$backupidsexistsize <= 0) {
+            self::$backupidsexist = array_slice(self::$backupidsexist, self::$backupidsslice, null, true);
+            self::$backupidsexistsize = self::$backupidsexistsize + self::$backupidsslice;
+        }
+
+        // Retrive record from database.
+        $record = array(
+            'backupid' => $restoreid,
+            'itemname' => $itemname,
+            'itemid'   => $itemid
+        );
+        if ($dbrec = $DB->get_record('backup_ids_temp', $record)) {
+            self::$backupidsexist[$key] = $dbrec->id;
+            self::$backupidscache[$key] = $dbrec;
+            self::$backupidscachesize--;
+            self::$backupidsexistsize--;
+            return $dbrec;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Cache backup ids'
+     *
+     * @param int $restoreid id of backup
+     * @param string $itemname name of the item
+     * @param int $itemid id of item
+     * @param array $extrarecord extra record which needs to be updated
+     * @return void
+     * @todo MDL-25290 replace static BACKUP_IDS_* with MUC code
+     */
+    protected static function set_backup_ids_cached($restoreid, $itemname, $itemid, $extrarecord) {
+        global $DB;
+
+        $key = "$itemid $itemname $restoreid";
+
+        $record = array(
+            'backupid' => $restoreid,
+            'itemname' => $itemname,
+            'itemid'   => $itemid,
+        );
+
+        // If record is not cached then add one.
+        if (!isset(self::$backupidsexist[$key])) {
+            // If we have this record in db, then just update this.
+            if ($existingrecord = $DB->get_record('backup_ids_temp', $record)) {
+                self::$backupidsexist[$key] = $existingrecord->id;
+                self::$backupidsexistsize--;
+                self::update_backup_cached_record($record, $extrarecord, $key, $existingrecord);
+            } else {
+                // Add new record to cache and db.
+                $recorddefault = array (
+                    'newitemid' => 0,
+                    'parentitemid' => null,
+                    'info' => null);
+                $record = array_merge($record, $recorddefault, $extrarecord);
+                $record['id'] = $DB->insert_record('backup_ids_temp', $record);
+                self::$backupidsexist[$key] = $record['id'];
+                self::$backupidsexistsize--;
+                if (self::$backupidscachesize > 0) {
+                    // Cache new records if we haven't got many yet.
+                    self::$backupidscache[$key] = (object) $record;
+                    self::$backupidscachesize--;
+                }
+            }
+        } else {
+            self::update_backup_cached_record($record, $extrarecord, $key);
+        }
+    }
+
+    /**
+     * Updates existing backup record
+     *
+     * @param array $record record which needs to be updated
+     * @param array $extrarecord extra record which needs to be updated
+     * @param string $key unique key which is used to identify cached record
+     * @param stdClass $existingrecord (optional) existing record
+     */
+    protected static function update_backup_cached_record($record, $extrarecord, $key, $existingrecord = null) {
+        global $DB;
+        // Update only if extrarecord is not empty.
+        if (!empty($extrarecord)) {
+            $extrarecord['id'] = self::$backupidsexist[$key];
+            $DB->update_record('backup_ids_temp', $extrarecord);
+            // Update existing cache or add new record to cache.
+            if (isset(self::$backupidscache[$key])) {
+                $record = array_merge((array)self::$backupidscache[$key], $extrarecord);
+                self::$backupidscache[$key] = (object) $record;
+            } else if (self::$backupidscachesize > 0) {
+                if ($existingrecord) {
+                    self::$backupidscache[$key] = $existingrecord;
+                } else {
+                    // Retrive record from database and cache updated records.
+                    self::$backupidscache[$key] = $DB->get_record('backup_ids_temp', $record);
+                }
+                $record = array_merge((array)self::$backupidscache[$key], $extrarecord);
+                self::$backupidscache[$key] = (object) $record;
+                self::$backupidscachesize--;
+            }
+        }
+    }
+
     /**
      * Given one role, as loaded from XML, perform the best possible matching against the assignable
      * roles, using different fallback alternatives (shortname, archetype, editingteacher => teacher, defaultcourseroleid)
@@ -1219,16 +1380,7 @@ abstract class restore_dbops {
         $DB->insert_record('backup_files_temp', $filerec);
     }
 
-
     public static function set_backup_ids_record($restoreid, $itemname, $itemid, $newitemid = 0, $parentitemid = null, $info = null) {
-        global $DB;
-
-        // Build the basic (mandatory) record info
-        $record = array(
-            'backupid' => $restoreid,
-            'itemname' => $itemname,
-            'itemid'   => $itemid
-        );
         // Build conditionally the extra record info
         $extrarecord = array();
         if ($newitemid != 0) {
@@ -1241,34 +1393,16 @@ abstract class restore_dbops {
             $extrarecord['info'] = base64_encode(serialize($info));
         }
 
-        // TODO: Analyze if some static (and limited) cache by the 3 params could save us a bunch of get_record() calls
-        // Note: Sure it will! And also will improve getter
-        if (!$dbrec = $DB->get_record('backup_ids_temp', $record)) { // Need to insert the complete record
-            $DB->insert_record('backup_ids_temp', array_merge($record, $extrarecord));
-
-        } else { // Need to update the extra record info if there is something to
-            if (!empty($extrarecord)) {
-                $extrarecord['id'] = $dbrec->id;
-                $DB->update_record('backup_ids_temp', $extrarecord);
-            }
-        }
+        self::set_backup_ids_cached($restoreid, $itemname, $itemid, $extrarecord);
     }
 
     public static function get_backup_ids_record($restoreid, $itemname, $itemid) {
-        global $DB;
+        $dbrec = self::get_backup_ids_cached($restoreid, $itemname, $itemid);
 
-        // Build the basic (mandatory) record info to look for
-        $record = array(
-            'backupid' => $restoreid,
-            'itemname' => $itemname,
-            'itemid'   => $itemid
-        );
-        // TODO: Analyze if some static (and limited) cache by the 3 params could save us a bunch of get_record() calls
-        if ($dbrec = $DB->get_record('backup_ids_temp', $record)) {
-            if ($dbrec->info != null) {
-                $dbrec->info = unserialize(base64_decode($dbrec->info));
-            }
+        if ($dbrec && isset($dbrec->info) && is_string($dbrec->info)) {
+            $dbrec->info = unserialize(base64_decode($dbrec->info));
         }
+
         return $dbrec;
     }
 
index d25c794..94a7a87 100644 (file)
@@ -315,6 +315,7 @@ abstract class backup_cron_automated_helper {
      */
     public static function launch_automated_backup($course, $starttime, $userid) {
 
+        $outcome = true;
         $config = get_config('backup');
         $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid);
 
@@ -347,7 +348,7 @@ abstract class backup_cron_automated_helper {
 
             $bc->set_status(backup::STATUS_AWAITING);
 
-            $outcome = $bc->execute_plan();
+            $bc->execute_plan();
             $results = $bc->get_results();
             $file = $results['backup_destination'];
             $dir = $config->backup_auto_destination;
@@ -363,16 +364,17 @@ abstract class backup_cron_automated_helper {
                 }
             }
 
-            $outcome = true;
-        } catch (backup_exception $e) {
-            $bc->log('backup_auto_failed_on_course', backup::LOG_WARNING, $course->shortname);
+        } catch (moodle_exception $e) {
+            $bc->log('backup_auto_failed_on_course', backup::LOG_ERROR, $course->shortname); // Log error header.
+            $bc->log('Exception: ' . $e->errorcode, backup::LOG_ERROR, $e->a, 1); // Log original exception problem.
+            $bc->log('Debug: ' . $e->debuginfo, backup::LOG_DEBUG, null, 1); // Log original debug information.
             $outcome = false;
         }
 
         $bc->destroy();
         unset($bc);
 
-        return true;
+        return $outcome;
     }
 
     /**
index 3ed176b..eab1869 100644 (file)
@@ -61,7 +61,12 @@ abstract class base_plan implements checksumable, executable {
         // Append task settings to plan array, if not present, for comodity
         foreach ($task->get_settings() as $key => $setting) {
             if (!in_array($setting, $this->settings)) {
-                $this->settings[] = $setting;
+                $name = $setting->get_name();
+                if (!isset($this->settings[$name])) {
+                    $this->settings[$name] = $setting;
+                } else {
+                    throw new base_plan_exception('multiple_settings_by_name_found', $name);
+                }
             }
         }
     }
@@ -84,23 +89,16 @@ abstract class base_plan implements checksumable, executable {
 
     /**
      * return one setting by name, useful to request root/course settings
-     * that are, by definition, unique by name. Throws exception if multiple
-     * are found
+     * that are, by definition, unique by name.
      *
-     * TODO: Change this to string indexed array for quicker lookup. Not critical
+     * @param string $name name of the setting
+     * @throws base_plan_exception if setting name is not found.
      */
     public function get_setting($name) {
         $result = null;
-        foreach ($this->settings as $key => $setting) {
-            if ($setting->get_name() == $name) {
-                if ($result != null) {
-                    throw new base_plan_exception('multiple_settings_by_name_found', $name);
-                } else {
-                    $result = $setting;
-                }
-            }
-        }
-        if (!$result) {
+        if (isset($this->settings[$name])) {
+            $result = $this->settings[$name];
+        } else {
             throw new base_plan_exception('setting_by_name_not_found', $name);
         }
         return $result;
index 1ce9053..7995561 100644 (file)
@@ -124,6 +124,8 @@ class backup_ui_stage_initial extends backup_ui_stage {
             // Store as a variable so we can iterate by reference
             $tasks = $this->ui->get_tasks();
             // Iterate all tasks by reference
+            $add_settings = array();
+            $dependencies = array();
             foreach ($tasks as &$task) {
                 // For the initial stage we are only interested in the root settings
                 if ($task instanceof backup_root_task) {
@@ -134,17 +136,23 @@ class backup_ui_stage_initial extends backup_ui_stage {
                         if ($setting->get_name() == 'filename') {
                             continue;
                         }
-                        $form->add_setting($setting, $task);
+                        $add_settings[] = array($setting, $task);
                     }
                     // Then add all dependencies
                     foreach ($settings as &$setting) {
                         if ($setting->get_name() == 'filename') {
                             continue;
                         }
-                        $form->add_dependencies($setting);
+                        $dependencies[] = $setting;
                     }
                 }
             }
+            // Add all settings at once.
+            $form->add_settings($add_settings);
+            // Add dependencies.
+            foreach ($dependencies as $depsetting) {
+                $form->add_dependencies($depsetting);
+            }
             $this->stageform = $form;
         }
         // Return the form
@@ -226,6 +234,8 @@ class backup_ui_stage_schema extends backup_ui_stage {
             $tasks = $this->ui->get_tasks();
             $content = '';
             $courseheading = false;
+            $add_settings = array();
+            $dependencies = array();
             foreach ($tasks as $task) {
                 if (!($task instanceof backup_root_task)) {
                     if (!$courseheading) {
@@ -235,11 +245,11 @@ class backup_ui_stage_schema extends backup_ui_stage {
                     }
                     // First add each setting
                     foreach ($task->get_settings() as $setting) {
-                        $form->add_setting($setting, $task);
+                        $add_settings[] = array($setting, $task);
                     }
                     // The add all the dependencies
                     foreach ($task->get_settings() as $setting) {
-                        $form->add_dependencies($setting);
+                        $dependencies[] = $setting;
                     }
                 } else if ($this->ui->enforce_changed_dependencies()) {
                     // Only show these settings if dependencies changed them.
@@ -254,6 +264,10 @@ class backup_ui_stage_schema extends backup_ui_stage {
                     }
                 }
             }
+            $form->add_settings($add_settings);
+            foreach ($dependencies as $depsetting) {
+                $form->add_dependencies($depsetting);
+            }
             $this->stageform = $form;
         }
         return $this->stageform;
index ad99260..e0ffc0f 100644 (file)
@@ -136,24 +136,38 @@ abstract class base_moodleform extends moodleform {
      * @return bool
      */
     function add_setting(backup_setting $setting, base_task $task=null) {
+        return $this->add_settings(array(array($setting, $task)));
+    }
+    /**
+     * Adds multiple backup_settings as elements to the form
+     * @param array $settingstasks Consists of array($setting, $task) elements
+     * @return bool
+     */
+    public function add_settings(array $settingstasks) {
         global $OUTPUT;
 
-        // If the setting cant be changed or isn't visible then add it as a fixed setting.
-        if (!$setting->get_ui()->is_changeable() || $setting->get_visibility() != backup_setting::VISIBLE) {
-            return $this->add_fixed_setting($setting, $task);
-        }
+        $defaults = array();
+        foreach ($settingstasks as $st) {
+            list($setting, $task) = $st;
+            // If the setting cant be changed or isn't visible then add it as a fixed setting.
+            if (!$setting->get_ui()->is_changeable() || $setting->get_visibility() != backup_setting::VISIBLE) {
+                $this->add_fixed_setting($setting, $task);
+                continue;
+            }
 
-        // First add the formatting for this setting
-        $this->add_html_formatting($setting);
+            // First add the formatting for this setting
+            $this->add_html_formatting($setting);
 
-        // The call the add method with the get_element_properties array
-        call_user_func_array(array($this->_form, 'addElement'), $setting->get_ui()->get_element_properties($task, $OUTPUT));
-        $this->_form->setDefault($setting->get_ui_name(), $setting->get_value());
-        if ($setting->has_help()) {
-            list($identifier, $component) = $setting->get_help();
-            $this->_form->addHelpButton($setting->get_ui_name(), $identifier, $component);
+            // Then call the add method with the get_element_properties array
+            call_user_func_array(array($this->_form, 'addElement'), $setting->get_ui()->get_element_properties($task, $OUTPUT));
+            $defaults[$setting->get_ui_name()] = $setting->get_value();
+            if ($setting->has_help()) {
+                list($identifier, $component) = $setting->get_help();
+                $this->_form->addHelpButton($setting->get_ui_name(), $identifier, $component);
+            }
+            $this->_form->addElement('html', html_writer::end_tag('div'));
         }
-        $this->_form->addElement('html', html_writer::end_tag('div'));
+        $this->_form->setDefaults($defaults);
         return true;
     }
     /**
@@ -317,4 +331,4 @@ abstract class base_moodleform extends moodleform {
             $this->definition_after_data();
         }
     }
-}
\ No newline at end of file
+}
index 184449a..8152990 100644 (file)
@@ -242,7 +242,6 @@ function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_y
     $days_title = calendar_get_days();
 
     $summary = get_string('calendarheading', 'calendar', userdate(make_timestamp($y, $m), get_string('strftimemonthyear')));
-    $summary = get_string('tabledata', 'access', $summary);
     $content .= '<table class="minicalendar calendartable" summary="'.$summary.'">'; // Begin table
     $content .= '<tr class="weekdays">'; // Header row: day names
 
index f2af45a..3354f1c 100644 (file)
@@ -53,5 +53,7 @@ if ($course->numsections >= 0) {
     $DB->update_record('course', $course);
 }
 
+$url = course_get_url($course);
+$url->set_anchor('changenumsections');
 // Redirect to where we were..
-redirect(course_get_url($course));
+redirect($url);
index fb59c38..f0dd91f 100644 (file)
@@ -56,6 +56,21 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
      */
     abstract protected function page_title();
 
+    /**
+     * Generate the section title
+     *
+     * @param stdClass $section The course_section entry from DB
+     * @param stdClass $course The course entry from DB
+     * @return string HTML to output.
+     */
+    public function section_title($section, $course) {
+        $title = get_section_name($course, $section);
+        if ($section->section != 0 && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
+            $title = html_writer::link(course_get_url($course, $section->section), $title);
+        }
+        return $title;
+    }
+
     /**
      * Generate the content to displayed on the right part of a section
      * before course modules are included
@@ -106,7 +121,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 bool $onsectionpage true if being printed on a section page
+     * @param bool $onsectionpage true if being printed on a single-section page
      * @return string HTML to output.
      */
     protected function section_header($section, $course, $onsectionpage) {
@@ -115,7 +130,6 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $o = '';
         $currenttext = '';
         $sectionstyle = '';
-        $linktitle = false;
 
         if ($section->section != 0) {
             // Only in the non-general sections.
@@ -124,7 +138,6 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
             } else if ($this->is_section_current($section, $course)) {
                 $sectionstyle = ' current';
             }
-            $linktitle = ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE);
         }
 
         $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
@@ -138,11 +151,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $o.= html_writer::start_tag('div', array('class' => 'content'));
 
         if (!$onsectionpage) {
-            $title = get_section_name($course, $section);
-            if ($linktitle) {
-                $title = html_writer::link(course_get_url($course, $section->section), $title);
-            }
-            $o.= $this->output->heading($title, 3, 'sectionname');
+            $o.= $this->output->heading($this->section_title($section, $course), 3, 'sectionname');
         }
 
         $o.= html_writer::start_tag('div', array('class' => 'summary'));
@@ -506,7 +515,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $completioninfo = new completion_info($course);
         echo $completioninfo->display_help_icon();
 
-        print_section($course, $thissection, $mods, $modnamesused, true);
+        print_section($course, $thissection, $mods, $modnamesused, true, '100%', false, true);
         if ($PAGE->user_is_editing()) {
             print_section_add_menus($course, $displaysection, $modnames);
         }
@@ -624,7 +633,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
 
             echo $this->end_section_list();
 
-            echo html_writer::start_tag('div', array('class' => 'mdl-right'));
+            echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
 
             // Increase number of sections.
             $straddsection = get_string('increasesections', 'moodle');
index 218492d..71e8797 100644 (file)
@@ -1,4 +1,4 @@
-// Javascript functions for course format
+// Javascript functions for Topics course format
 
 M.course = M.course || {};
 
@@ -36,13 +36,32 @@ M.course.format.get_config = function() {
 M.course.format.swap_sections = function(Y, node1, node2) {
     var CSS = {
         COURSECONTENT : 'course-content',
-        LEFT : 'left',
         SECTIONADDMENUS : 'section_add_menus'
     };
 
     var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
-    // Swap left block
-    sectionlist.item(node1).one('.'+CSS.LEFT).swap(sectionlist.item(node2).one('.'+CSS.LEFT));
     // Swap menus
     sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
 }
+
+/**
+ * Process sections after ajax response
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+M.course.format.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) {
+    var CSS = {
+        SECTIONNAME : 'sectionname'
+    };
+
+    if (response.action == 'move') {
+        // update titles in all affected sections
+        for (var i = sectionfrom; i <= sectionto; i++) {
+            sectionlist.item(i).one('.'+CSS.SECTIONNAME).setContent(response.sectiontitles[i]);
+        }
+    }
+}
index 710abaf..e8632fc 100644 (file)
@@ -80,3 +80,24 @@ function callback_topics_ajax_support() {
     $ajaxsupport->testedbrowsers = array('MSIE' => 6.0, 'Gecko' => 20061111, 'Safari' => 531, 'Chrome' => 6.0);
     return $ajaxsupport;
 }
+
+/**
+ * Callback function to do some action after section move
+ *
+ * @param stdClass $course The course entry from DB
+ * @return array This will be passed in ajax respose.
+ */
+function callback_topics_ajax_section_move($course) {
+    global $COURSE, $PAGE;
+
+    $titles = array();
+    rebuild_course_cache($course->id);
+    $modinfo = get_fast_modinfo($COURSE);
+    $renderer = $PAGE->get_renderer('format_topics');
+    if ($renderer && ($sections = $modinfo->get_section_info_all())) {
+        foreach ($sections as $number => $section) {
+            $titles[$number] = $renderer->section_title($section, $course);
+        }
+    }
+    return array('sectiontitles' => $titles, 'action' => 'move');
+}
index 159f94b..f410e07 100644 (file)
@@ -1,4 +1,4 @@
-// Javascript functions for course format
+// Javascript functions for Weeks course format
 
 M.course = M.course || {};
 
@@ -36,16 +36,32 @@ M.course.format.get_config = function() {
 M.course.format.swap_sections = function(Y, node1, node2) {
     var CSS = {
         COURSECONTENT : 'course-content',
-        LEFT : 'left',
         SECTIONADDMENUS : 'section_add_menus',
-        WEEKDATES: 'sectionname'
     };
 
     var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
-    // Swap left block
-    sectionlist.item(node1).one('.'+CSS.LEFT).swap(sectionlist.item(node2).one('.'+CSS.LEFT));
     // Swap menus
     sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
-    // Swap week dates
-    sectionlist.item(node1).one('.'+CSS.WEEKDATES).swap(sectionlist.item(node2).one('.'+CSS.WEEKDATES));
+}
+
+/**
+ * Process sections after ajax response
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+M.course.format.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) {
+    var CSS = {
+        SECTIONNAME : 'sectionname'
+    };
+
+    if (response.action == 'move') {
+        // update titles in all affected sections
+        for (var i = sectionfrom; i <= sectionto; i++) {
+            sectionlist.item(i).one('.'+CSS.SECTIONNAME).setContent(response.sectiontitles[i]);
+        }
+    }
 }
index 834adda..a1e1ea9 100644 (file)
@@ -117,3 +117,24 @@ function format_weeks_get_section_dates($section, $course) {
 
     return $dates;
 }
+
+/**
+ * Callback function to do some action after section move
+ *
+ * @param stdClass $course The course entry from DB
+ * @return array This will be passed in ajax respose.
+ */
+function callback_weeks_ajax_section_move($course) {
+    global $COURSE, $PAGE;
+
+    $titles = array();
+    rebuild_course_cache($course->id);
+    $modinfo = get_fast_modinfo($COURSE);
+    $renderer = $PAGE->get_renderer('format_weeks');
+    if ($renderer && ($sections = $modinfo->get_section_info_all())) {
+        foreach ($sections as $number => $section) {
+            $titles[$number] = $renderer->section_title($section, $course);
+        }
+    }
+    return array('sectiontitles' => $titles, 'action' => 'move');
+}
index 8e07373..a5566d6 100644 (file)
@@ -1728,7 +1728,7 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             // see the activity itself, or for staff)
             if (!$mod->uservisible) {
                 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
-            } else if ($canviewhidden && !empty($CFG->enableavailability)) {
+            } else if ($canviewhidden && !empty($CFG->enableavailability) && $mod->visible) {
                 $ci = new condition_info($mod);
                 $fullinfo = $ci->get_full_information();
                 if($fullinfo) {
@@ -2995,6 +2995,8 @@ function move_section($course, $section, $move) {
         }
         $n++;
     }
+    // After moving section, rebuild course cache.
+    rebuild_course_cache($course->id, true);
     return true;
 }
 
index f33ef54..8b31e29 100644 (file)
@@ -221,7 +221,9 @@ if (!empty($activities)) {
                 echo $OUTPUT->spacer(array('height'=>30, 'br'=>true)); // should be done with CSS instead
             }
             echo $OUTPUT->box_start();
-            echo "<h2>$activity->name</h2>";
+            if (!empty($activity->name)) {
+                echo html_writer::tag('h2', $activity->name);
+            }
             $inbox = true;
 
         } else if ($activity->type == 'activity') {
@@ -230,16 +232,17 @@ if (!empty($activities)) {
                 $cm = $modinfo->cms[$activity->cmid];
 
                 if ($cm->visible) {
-                    $linkformat = '';
+                    $class = '';
                 } else {
-                    $linkformat = 'class="dimmed"';
+                    $class = 'dimmed';
                 }
                 $name        = format_string($cm->name);
                 $modfullname = $modnames[$cm->modname];
 
-                $image = "<img src=\"" . $OUTPUT->pix_url('icon', $cm->modname) . "\" class=\"icon\" alt=\"$modfullname\" />";
-                echo "<h3>$image $modfullname".
-                     " <a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id=$cm->id\" $linkformat>$name</a></h3>";
+                $image = $OUTPUT->pix_icon('icon', $modfullname, $cm->modname, array('class' => 'icon smallicon'));
+                $link = html_writer::link(new moodle_url("/mod/$cm->modname/view.php",
+                            array("id" => $cm->id)), $name, array('class' => $class));
+                echo html_writer::tag('h3', "$image $modfullname $link");
            }
 
         } else {
@@ -269,7 +272,7 @@ if (!empty($activities)) {
 
 } else {
 
-    echo '<h3><center>' . get_string('norecentactivity') . '</center></h3>';
+    echo html_writer::tag('h3', get_string('norecentactivity'), array('class' => 'mdl-align'));
 
 }
 
index 5152563..6efd228 100644 (file)
@@ -89,6 +89,15 @@ switch($requestmethod) {
 
                     case 'move':
                         move_section_to($course, $id, $value);
+                        // See if format wants to do something about it
+                        $libfile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php';
+                        $functionname = 'callback_'.$course->format.'_ajax_section_move';
+                        if (!function_exists($functionname) && file_exists($libfile)) {
+                            require_once $libfile;
+                        }
+                        if (function_exists($functionname)) {
+                            echo json_encode($functionname($course));
+                        }
                         break;
                 }
                 rebuild_course_cache($course->id);
index a7b1784..de37981 100644 (file)
 
     require_once($CFG->dirroot.'/calendar/lib.php');    /// This is after login because it needs $USER
 
-    //TODO: danp do we need different urls?
-    add_to_log($course->id, 'course', 'view', "view.php?id=$course->id", "$course->id");
+    $logparam = 'id='. $course->id;
+    $loglabel = 'view';
+    $infoid = $course->id;
+    if(!empty($section)) {
+        $logparam .= '&section='. $section;
+        $loglabel = 'view section';
+        $sectionparams = array('course' => $course->id, 'section' => $section);
+        if ($coursesections = $DB->get_record('course_sections', $sectionparams, 'id', MUST_EXIST)) {
+            $infoid = $coursesections->id;
+    }
+    }
+    add_to_log($course->id, 'course', $loglabel, "view.php?". $logparam, $infoid);
 
     $course->format = clean_param($course->format, PARAM_ALPHA);
     if (!file_exists($CFG->dirroot.'/course/format/'.$course->format.'/format.php')) {
index 3dbbf0b..b02c362 100644 (file)
@@ -68,6 +68,21 @@ YUI.add('moodle-course-coursebase', function(Y) {
         return null;
     }
 
+   /**
+    * Process sections after ajax response (should be defined in format.js)
+    * If some response is expected, we pass it over to format, as it knows better
+    * hot to process it.
+    *
+    * @param {YUI} Y YUI3 instance
+    * @param {NodeList} list of sections
+    * @param {array} response ajax response
+    * @param {string} sectionfrom first affected section
+    * @param {string} sectionto last affected section
+    * @return void
+    */
+    M.course.format.process_sections = M.course.format.process_sections || function(Y, sectionlist, response, sectionfrom, sectionto) {
+        return null;
+    }
 
    /**
     * Get sections config for this format, for examples see function definition
index 012355e..080cace 100644 (file)
@@ -119,6 +119,13 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             drag.get('dragNode').addClass(CSS.COURSECONTENT);
         },
 
+        drag_dropmiss : function(e) {
+            // Missed the target, but we assume the user intended to drop it
+            // on the last last ghost node location, e.drag and e.drop should be
+            // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+            this.drop_hit(e);
+        },
+
         drop_hit : function(e) {
             var drag = e.drag;
             // Get a reference to our drag node
@@ -172,9 +179,16 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                         lightbox.show();
                     },
                     success: function(tid, response) {
-                        window.setTimeout(function(e) {
-                            lightbox.hide();
-                        }, 250);
+                        // Update section titles, we can't simply swap them as
+                        // they might have custom title
+                        try {
+                            var responsetext = Y.JSON.parse(response.responseText);
+                            if (responsetext.error) {
+                                new M.core.ajaxException(responsetext);
+                            }
+                            M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+                        } catch (e) {}
+
                         // Classic bubble sort algorithm is applied to the section
                         // nodes between original drag node location and the new one.
                         do {
@@ -193,6 +207,11 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                             }
                             loopend = loopend - 1;
                         } while (swapped);
+
+                        // Finally, hide the lightbox
+                        window.setTimeout(function(e) {
+                            lightbox.hide();
+                        }, 250);
                     },
                     failure: function(tid, response) {
                         this.ajax_failure(response);
@@ -309,6 +328,13 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
         },
 
+        drag_dropmiss : function(e) {
+            // Missed the target, but we assume the user intended to drop it
+            // on the last last ghost node location, e.drag and e.drop should be
+            // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+            this.drop_hit(e);
+        },
+
         drop_hit : function(e) {
             var drag = e.drag;
             // Get a reference to our drag node
index ac8e582..3728a23 100644 (file)
@@ -185,7 +185,6 @@ class core_files_renderer extends plugin_renderer_base {
         $strmakedir  = get_string('makeafolder', 'moodle');
         $strdownload = get_string('downloadfolder', 'repository');
         $strloading  = get_string('loading', 'repository');
-        $strnofilesattached = get_string('nofilesattached', 'repository');
         $strdroptoupload = get_string('droptoupload', 'moodle');
         $icon_progress = $OUTPUT->pix_icon('i/loading_small', $strloading).'';
         $restrictions = $this->fm_print_restrictions($fm);
@@ -220,7 +219,7 @@ class core_files_renderer extends plugin_renderer_base {
     <div class="filemanager-container" >
         <div class="fm-content-wrapper">
             <div class="fp-content"></div>
-            <div class="fm-empty-container <!--mdl-align-->">'.$strnofilesattached.'
+            <div class="fm-empty-container <!--mdl-align-->">
                 <span class="dndupload-message">'.$strdndenabledinbox.'<br/><span class="dndupload-arrow"></span></span>
             </div>
             <div class="dndupload-target">'.$strdroptoupload.'<br/><span class="dndupload-arrow"></span></div>
index e1937be..9d1bda9 100644 (file)
@@ -32,16 +32,56 @@ require_once $CFG->libdir.'/gradelib.php';
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class graded_users_iterator {
-    public $course;
-    public $grade_items;
-    public $groupid;
-    public $users_rs;
-    public $grades_rs;
-    public $gradestack;
-    public $sortfield1;
-    public $sortorder1;
-    public $sortfield2;
-    public $sortorder2;
+
+    /**
+     * The couse whose users we are interested in
+     */
+    protected $course;
+
+    /**
+     * An array of grade items or null if only user data was requested
+     */
+    protected $grade_items;
+
+    /**
+     * The group ID we are interested in. 0 means all groups.
+     */
+    protected $groupid;
+
+    /**
+     * A recordset of graded users
+     */
+    protected $users_rs;
+
+    /**
+     * A recordset of user grades (grade_grade instances)
+     */
+    protected $grades_rs;
+
+    /**
+     * Array used when moving to next user while iterating through the grades recordset
+     */
+    protected $gradestack;
+
+    /**
+     * The first field of the users table by which the array of users will be sorted
+     */
+    protected $sortfield1;
+
+    /**
+     * Should sortfield1 be ASC or DESC
+     */
+    protected $sortorder1;
+
+    /**
+     * The second field of the users table by which the array of users will be sorted
+     */
+    protected $sortfield2;
+
+    /**
+     * Should sortfield2 be ASC or DESC
+     */
+    protected $sortorder2;
 
     /**
      * Should users whose enrolment has been suspended be ignored?
@@ -59,7 +99,7 @@ class graded_users_iterator {
      * @param string $sortfield2 The second field of the users table by which the array of users will be sorted
      * @param string $sortorder2 The order in which the second sorting field will be sorted (ASC or DESC)
      */
-    public function graded_users_iterator($course, $grade_items=null, $groupid=0,
+    public function __construct($course, $grade_items=null, $groupid=0,
                                           $sortfield1='lastname', $sortorder1='ASC',
                                           $sortfield2='firstname', $sortorder2='ASC') {
         $this->course      = $course;
@@ -75,6 +115,7 @@ class graded_users_iterator {
 
     /**
      * Initialise the iterator
+     *
      * @return boolean success
      */
     public function init() {
@@ -177,7 +218,7 @@ class graded_users_iterator {
      * Returns information about the next user
      * @return mixed array of user info, all grades and feedback or null when no more users found
      */
-    function next_user() {
+    public function next_user() {
         if (!$this->users_rs) {
             return false; // no users present
         }
@@ -244,10 +285,9 @@ class graded_users_iterator {
     }
 
     /**
-     * Close the iterator, do not forget to call this function.
-     * @return void
+     * Close the iterator, do not forget to call this function
      */
-    function close() {
+    public function close() {
         if ($this->users_rs) {
             $this->users_rs->close();
             $this->users_rs = null;
@@ -273,23 +313,23 @@ class graded_users_iterator {
 
 
     /**
-     * _push
+     * Add a grade_grade instance to the grade stack
      *
      * @param grade_grade $grade Grade object
      *
      * @return void
      */
-    function _push($grade) {
+    private function _push($grade) {
         array_push($this->gradestack, $grade);
     }
 
 
     /**
-     * _pop
+     * Remove a grade_grade instance from the grade stack
      *
-     * @return object current grade object
+     * @return grade_grade current grade object
      */
-    function _pop() {
+    private function _pop() {
         global $DB;
         if (empty($this->gradestack)) {
             if (empty($this->grades_rs) || !$this->grades_rs->valid()) {
index b338d42..ee81afc 100644 (file)
@@ -39,8 +39,6 @@ $string['skipa'] = 'Skip {$a}';
 $string['skipblock'] = 'Skip block';
 $string['skipnavigation'] = 'Skip navigation';
 $string['skipto'] = 'Skip to {$a}';
-$string['tabledata'] = 'Data table, {$a}';
-$string['tablelayout'] = 'Layout table, {$a}';
 $string['tocontent'] = 'Skip to main content';
 $string['tonavigation'] = 'Go to navigation';
 $string['youarehere'] = 'You are here';
index 2ac7b1e..6dbb859 100644 (file)
@@ -636,6 +636,7 @@ $string['maturity50'] = 'Alpha';
 $string['maturity100'] = 'Beta';
 $string['maturity150'] = 'Release candidate';
 $string['maturity200'] = 'Stable version';
+$string['maturityallowunstable'] = 'Hint: You may want to run this script with --allow-unstable option';
 $string['maturitycoreinfo'] = 'Your site is currently running unstable "{$a}" development code.';
 $string['maturitycorewarning'] = 'The version of Moodle that you are about to install or upgrade to contains
 unstable "{$a}" development code that is not suitable for use on most production
@@ -753,6 +754,8 @@ $string['pleaserefreshregistration'] = 'Your site has been registered with moodl
 $string['pleaseregister'] = 'Please register your site to remove this button';
 $string['plugin'] = 'Plugin';
 $string['plugins'] = 'Plugins';
+$string['pluginscheck'] = 'Plugin dependencies check';
+$string['pluginscheckfailed'] = 'Dependencies check failed for {$a->pluginslist}';
 $string['pluginschecktodo'] = 'You must solve all the plugin requirements before proceeding to install this Moodle version!';
 $string['pluginsoverview'] = 'Plugins overview';
 $string['profilecategory'] = 'Category';
index 073d3eb..224b741 100644 (file)
@@ -466,7 +466,7 @@ $string['displayonpage'] = 'Display on page';
 $string['dndenabled'] = 'Drag and drop available';
 $string['dndenabled_help'] = 'You can drag one or more files from your desktop and drop them onto the box below to upload them.<br />Note: this may not work with other web browsers';
 $string['dndenabled_insentence'] = 'drag and drop available';
-$string['dndenabled_inbox'] = 'drag and drop files here to upload them';
+$string['dndenabled_inbox'] = 'You can drag and drop files here to add them.';
 $string['dnduploadwithoutcontent'] = 'This upload does not have any content';
 $string['dndworkingfiletextlink'] = 'Drag and drop files, text or links onto course sections to upload them';
 $string['dndworkingfilelink'] = 'Drag and drop files or links onto course sections to upload them';
index 3bc89ef..3bba405 100644 (file)
@@ -31,7 +31,7 @@ $string['checkforupdates'] = 'Check for available updates';
 $string['checkforupdateslast'] = 'Last check done on {$a}';
 $string['displayname'] = 'Plugin name';
 $string['moodleversion'] = 'Moodle {$a}';
-$string['nonehighlighted'] = 'No plugins require your attention during this upgrade';
+$string['nonehighlighted'] = 'No plugins require your attention now';
 $string['nonehighlightedinfo'] = 'Display the list of all installed plugins anyway';
 $string['noneinstalled'] = 'No plugins of this type are installed';
 $string['notes'] = 'Notes';
@@ -52,7 +52,7 @@ $string['requiredby'] = 'Required by: {$a}';
 $string['requires'] = 'Requires';
 $string['rootdir'] = 'Directory';
 $string['settings'] = 'Settings';
-$string['somehighlighted'] = 'Number of plugins requiring attention during this upgrade: {$a}';
+$string['somehighlighted'] = 'Number of plugins requiring your attention: {$a}';
 $string['somehighlightedinfo'] = 'Display the full list of installed plugins';
 $string['somehighlightedonly'] = 'Display only plugins requiring your attention';
 $string['source'] = 'Source';
index ba27546..375c966 100644 (file)
@@ -235,7 +235,7 @@ class completion_criteria_activity extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
             $completion->mark_complete($record->timecompleted);
         }
         $rs->close();
index 04be448..a25dea2 100644 (file)
@@ -193,8 +193,8 @@ class completion_criteria_course extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
-            $completion->mark_complete($record['timecompleted']);
+            $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record->timecompleted);
         }
         $rs->close();
     }
index bacc144..baecc20 100644 (file)
@@ -179,8 +179,8 @@ class completion_criteria_date extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql, array(time()));
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
-            $completion->mark_complete($record['timeend']);
+            $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record->timeend);
         }
         $rs->close();
     }
index 962b052..ead1a55 100644 (file)
@@ -229,7 +229,7 @@ class completion_criteria_duration extends completion_criteria {
         $now = time();
         $rs = $DB->get_recordset_sql($sql, array($now, $now));
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
 
             // Use time start if not 0, otherwise use timeenrolled
             if ($record->otimestart) {
index 3e0da11..6bc32ed 100644 (file)
@@ -215,8 +215,8 @@ class completion_criteria_grade extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
-            $completion->mark_complete($record['timecompleted']);
+            $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record->timecompleted);
         }
         $rs->close();
     }
index 9bcd4bb..0d8b3cb 100644 (file)
@@ -69,7 +69,8 @@ class condition_info extends condition_info_base {
      * @param object $cm Moodle course-module object. May have extra fields
      *   ->conditionsgrade, ->conditionscompletion which should come from
      *   get_fast_modinfo. Should have ->availablefrom, ->availableuntil,
-     *   and ->showavailability, ->course; but the only required thing is ->id.
+     *   and ->showavailability, ->course, ->visible; but the only required
+     *   thing is ->id.
      * @param int $expectingmissing Used to control whether or not a developer
      *   debugging message (performance warning) will be displayed if some of
      *   the above data is missing and needs to be retrieved; a
@@ -426,7 +427,8 @@ abstract class condition_info_base {
      * @return array Array of field names
      */
     protected function get_main_table_fields() {
-        return array('id', 'course', 'availablefrom', 'availableuntil', 'showavailability');
+        return array('id', 'course', 'visible',
+                'availablefrom', 'availableuntil', 'showavailability');
     }
 
     /**
@@ -846,6 +848,16 @@ abstract class condition_info_base {
             }
         }
 
+        // If the item is marked as 'not visible' then we don't change the available
+        // flag (visible/available are treated distinctly), but we remove any
+        // availability info. If the item is hidden with the eye icon, it doesn't
+        // make sense to show 'Available from <date>' or similar, because even
+        // when that date arrives it will still not be available unless somebody
+        // toggles the eye icon.
+        if (!$this->item->visible) {
+            $information = '';
+        }
+
         $information = trim($information);
         return $available;
     }
index 0463430..d4b7077 100644 (file)
       </KEYS>
       <INDEXES>
         <INDEX NAME="course" UNIQUE="false" FIELDS="course" NEXT="criteriatype"/>
-        <INDEX NAME="criteriatype" UNIQUE="false" FIELDS="criteriatype" PREVIOUS="course"/>
+        <INDEX NAME="criteriatype" UNIQUE="false" FIELDS="criteriatype" PREVIOUS="course" NEXT="coursecriteriatype"/>
+        <INDEX NAME="coursecriteriatype" UNIQUE="true" FIELDS="course, criteriatype" PREVIOUS="criteriatype"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="course_completion_criteria" COMMENT="Course completion criteria" PREVIOUS="course_completion_aggr_methd" NEXT="course_completion_crit_compl">
         <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" NEXT="course"/>
         <INDEX NAME="course" UNIQUE="false" FIELDS="course" PREVIOUS="userid" NEXT="criteriaid"/>
         <INDEX NAME="criteriaid" UNIQUE="false" FIELDS="criteriaid" PREVIOUS="course" NEXT="timecompleted"/>
-        <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="criteriaid"/>
+        <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="criteriaid" NEXT="useridcoursecriteriaid"/>
+        <INDEX NAME="useridcoursecriteriaid" UNIQUE="true" FIELDS="userid, course, criteriaid" PREVIOUS="timecompleted"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="course_completion_notify" COMMENT="Course completion notification emails" PREVIOUS="course_completion_crit_compl" NEXT="course_completions">
       <INDEXES>
         <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" NEXT="course"/>
         <INDEX NAME="course" UNIQUE="false" FIELDS="course" PREVIOUS="userid" NEXT="timecompleted"/>
-        <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="course"/>
+        <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="course" NEXT="useridcourse"/>
+        <INDEX NAME="useridcourse" UNIQUE="true" FIELDS="userid, course" PREVIOUS="timecompleted"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="enrol" COMMENT="Instances of enrolment plugins used in courses, fields marked as custom have a plugin defined meaning, core does not touch them. Create a new linked table if you need even more custom fields." PREVIOUS="course_completions" NEXT="user_enrolments">
       </KEYS>
     </TABLE>
   </TABLES>
-</XMLDB>
\ No newline at end of file
+</XMLDB>
index f404e01..f0475f6 100644 (file)
@@ -38,6 +38,7 @@ global $DB; // TODO: this is a hack, we should really do something with the SQL
 $logs = array(
     array('module'=>'course', 'action'=>'user report', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')),
     array('module'=>'course', 'action'=>'view', 'mtable'=>'course', 'field'=>'fullname'),
+    array('module'=>'course', 'action'=>'view section', 'mtable'=>'course_sections', 'field'=>'COALESCE(name, section)'),
     array('module'=>'course', 'action'=>'update', 'mtable'=>'course', 'field'=>'fullname'),
     array('module'=>'course', 'action'=>'enrol', 'mtable'=>'course', 'field'=>'fullname'), // there should be some way to store user id of the enrolled user!
     array('module'=>'course', 'action'=>'unenrol', 'mtable'=>'course', 'field'=>'fullname'), // there should be some way to store user id of the enrolled user!
index c74c724..ba53f99 100644 (file)
@@ -648,5 +648,126 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012052100.00);
     }
 
+    if ($oldversion < 2012052500.03) { // fix invalid course_completion_records MDL-27368
+        //first get all instances of duplicate records
+        $sql = 'SELECT userid, course FROM {course_completions} WHERE (deleted IS NULL OR deleted <> 1) GROUP BY userid, course HAVING (count(id) > 1)';
+        $duplicates = $DB->get_recordset_sql($sql, array());
+
+        foreach ($duplicates as $duplicate) {
+            $pointer = 0;
+            //now get all the records for this user/course
+            $sql = 'userid = ? AND course = ? AND (deleted IS NULL OR deleted <> 1)';
+            $completions = $DB->get_records_select('course_completions', $sql,
+                array($duplicate->userid, $duplicate->course), 'timecompleted DESC, timestarted DESC');
+            $needsupdate = false;
+            $origcompletion = null;
+            foreach ($completions as $completion) {
+                $pointer++;
+                if ($pointer === 1) { //keep 1st record but delete all others.
+                    $origcompletion = $completion;
+                } else {
+                    //we need to keep the "oldest" of all these fields as the valid completion record.
+                    $fieldstocheck = array('timecompleted', 'timestarted', 'timeenrolled');
+                    foreach ($fieldstocheck as $f) {
+                        if ($origcompletion->$f > $completion->$f) {
+                            $origcompletion->$f = $completion->$f;
+                            $needsupdate = true;
+                        }
+                    }
+                    $DB->delete_records('course_completions', array('id'=>$completion->id));
+                }
+            }
+            if ($needsupdate) {
+                $DB->update_record('course_completions', $origcompletion);
+            }
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052500.03);
+    }
+
+    if ($oldversion < 2012052900.00) {
+        // Clean up all duplicate records in the course_completions table in preparation
+        // for adding a new index there.
+        upgrade_course_completion_remove_duplicates(
+            'course_completions',
+            array('userid', 'course'),
+            array('timecompleted', 'timestarted', 'timeenrolled')
+        );
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.00);
+    }
+
+    if ($oldversion < 2012052900.01) {
+        // Add indexes to prevent new duplicates in the course_completions table.
+        // Define index useridcourse (unique) to be added to course_completions
+        $table = new xmldb_table('course_completions');
+        $index = new xmldb_index('useridcourse', XMLDB_INDEX_UNIQUE, array('userid', 'course'));
+
+        // Conditionally launch add index useridcourse
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.01);
+    }
+
+    if ($oldversion < 2012052900.02) {
+        // Clean up all duplicate records in the course_completion_crit_compl table in preparation
+        // for adding a new index there.
+        upgrade_course_completion_remove_duplicates(
+            'course_completion_crit_compl',
+            array('userid', 'course', 'criteriaid'),
+            array('timecompleted')
+        );
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.02);
+    }
+
+    if ($oldversion < 2012052900.03) {
+        // Add indexes to prevent new duplicates in the course_completion_crit_compl table.
+        // Define index useridcoursecriteraid (unique) to be added to course_completion_crit_compl
+        $table = new xmldb_table('course_completion_crit_compl');
+        $index = new xmldb_index('useridcoursecriteraid', XMLDB_INDEX_UNIQUE, array('userid', 'course', 'criteriaid'));
+
+        // Conditionally launch add index useridcoursecriteraid
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.03);
+    }
+
+    if ($oldversion < 2012052900.04) {
+        // Clean up all duplicate records in the course_completion_aggr_methd table in preparation
+        // for adding a new index there.
+        upgrade_course_completion_remove_duplicates(
+            'course_completion_aggr_methd',
+            array('course', 'criteriatype')
+        );
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.04);
+    }
+
+    if ($oldversion < 2012052900.05) {
+        // Add indexes to prevent new duplicates in the course_completion_aggr_methd table.
+        // Define index coursecriteratype (unique) to be added to course_completion_aggr_methd
+        $table = new xmldb_table('course_completion_aggr_methd');
+        $index = new xmldb_index('coursecriteriatype', XMLDB_INDEX_UNIQUE, array('course', 'criteriatype'));
+
+        // Conditionally launch add index coursecriteratype
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012052900.05);
+    }
+
     return true;
-}
+}
\ No newline at end of file
index 028538c..5ae728e 100644 (file)
@@ -826,7 +826,7 @@ class mssql_native_moodle_database extends moodle_database {
         } else {
             unset($params['id']);
             if ($returnid) {
-                $returning = "; SELECT SCOPE_IDENTITY()";
+                $returning = "OUTPUT inserted.id";
             }
         }
 
@@ -838,18 +838,29 @@ class mssql_native_moodle_database extends moodle_database {
         $qms    = array_fill(0, count($params), '?');
         $qms    = implode(',', $qms);
 
-        $sql = "INSERT INTO {" . $table . "} ($fields) VALUES($qms) $returning";
+        $sql = "INSERT INTO {" . $table . "} ($fields) $returning VALUES ($qms)";
 
         list($sql, $params, $type) = $this->fix_sql_params($sql, $params);
         $rawsql = $this->emulate_bound_params($sql, $params);
 
         $this->query_start($sql, $params, SQL_QUERY_INSERT);
         $result = mssql_query($rawsql, $this->mssql);
-        $this->query_end($result);
+        // Expected results are:
+        //     - true: insert ok and there isn't returned information.
+        //     - false: insert failed and there isn't returned information.
+        //     - resource: insert executed, need to look for returned (output)
+        //           values to know if the insert was ok or no. Posible values
+        //           are false = failed, integer = insert ok, id returned.
+        $end = false;
+        if (is_bool($result)) {
+            $end = $result;
+        } else if (is_resource($result)) {
+            $end = mssql_result($result, 0, 0); // Fetch 1st column from 1st row.
+        }
+        $this->query_end($end); // End the query with the calculated $end.
 
         if ($returning !== "") {
-            $row = mssql_fetch_assoc($result);
-            $params['id'] = reset($row);
+            $params['id'] = $end;
         }
         $this->free_result($result);
 
index 0ef6eca..410c7ff 100644 (file)
@@ -2078,6 +2078,34 @@ class dml_testcase extends database_driver_testcase {
         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
+
+        // Test that inserting data violating one unique key leads to error.
+        // Empty the table completely.
+        $this->assertTrue($DB->delete_records($tablename));
+
+        // Add one unique constraint (index).
+        $key = new xmldb_key('testuk', XMLDB_KEY_UNIQUE, array('course', 'oneint'));
+        $dbman->add_key($table, $key);
+
+        // Let's insert one record violating the constraint multiple times.
+        $record = (object)array('course' => 1, 'oneint' => 1);
+        $this->assertTrue($DB->insert_record($tablename, $record, false)); // insert 1st. No problem expected.
+
+        // Re-insert same record, not returning id. dml_exception expected.
+        try {
+            $DB->insert_record($tablename, $record, false);
+            $this->fail("Expecting an exception, none occurred");
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+        }
+
+        // Re-insert same record, returning id. dml_exception expected.
+        try {
+            $DB->insert_record($tablename, $record, true);
+            $this->fail("Expecting an exception, none occurred");
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+        }
     }
 
     public function test_import_record() {
index a5a37a3..2f3ea57 100644 (file)
@@ -505,7 +505,7 @@ class file_info_stored extends file_info {
         if ($this->is_directory()) {
             $filepath = $this->lf->get_filepath();
             $fs = get_file_storage();
-            $storedfiles = $fs->get_area_files($this->context->id, $this->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), "");
+            $storedfiles = $fs->get_area_files($this->context->id, $this->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
             foreach ($storedfiles as $file) {
                 if (strpos($file->get_filepath(), $filepath) === 0) {
                     $file->delete();
index aa46e62..7aa8841 100644 (file)
@@ -2287,14 +2287,22 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
     if (!empty($options['preview'])) {
         // replace the file with its preview
         $fs = get_file_storage();
-        $stored_file = $fs->get_file_preview($stored_file, $options['preview']);
-        if (!$stored_file) {
-            // unable to create a preview of the file
-            send_header_404();
-            die();
+        $preview_file = $fs->get_file_preview($stored_file, $options['preview']);
+        if (!$preview_file) {
+            // unable to create a preview of the file, send its default mime icon instead
+            if ($options['preview'] === 'tinyicon') {
+                $size = 24;
+            } else if ($options['preview'] === 'thumb') {
+                $size = 90;
+            } else {
+                $size = 256;
+            }
+            $fileicon = file_file_icon($stored_file, $size);
+            send_file($CFG->dirroot.'/pix/'.$fileicon.'.png', basename($fileicon).'.png');
         } else {
             // preview images have fixed cache lifetime and they ignore forced download
             // (they are generated by GD and therefore they are considered reasonably safe).
+            $stored_file = $preview_file;
             $lifetime = DAYSECS;
             $filter = 0;
             $forcedownload = false;
@@ -3612,7 +3620,7 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
             }
 
             // fix file name automatically
-            if ($filename !== 'f1' and $filename !== 'f2') {
+            if ($filename !== 'f1' and $filename !== 'f2' and $filename !== 'f3') {
                 $filename = 'f1';
             }
 
@@ -3625,20 +3633,28 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 redirect($theme->pix_url('u/'.$filename, 'moodle')); // intentionally not cached
             }
 
-            if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.png')) {
-                if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')) {
-                    // bad reference - try to prevent future retries as hard as possible!
-                    if ($user = $DB->get_record('user', array('id'=>$context->instanceid), 'id, picture')) {
-                        if ($user->picture == 1 or $user->picture > 10) {
-                            $DB->set_field('user', 'picture', 0, array('id'=>$user->id));
+            if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'.png')) {
+                if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'.jpg')) {
+                    if ($filename === 'f3') {
+                        // f3 512x512px was introduced in 2.3, there might be only the smaller version.
+                        if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', 'f1.png')) {
+                            $file = $fs->get_file($context->id, 'user', 'icon', 0, '/', 'f1.jpg');
                         }
                     }
-                    // no redirect here because it is not cached
-                    $theme = theme_config::load($themename);
-                    $imagefile = $theme->resolve_image_location('u/'.$filename, 'moodle');
-                    send_file($imagefile, basename($imagefile), 60*60*24*14);
                 }
             }
+            if (!$file) {
+                // bad reference - try to prevent future retries as hard as possible!
+                if ($user = $DB->get_record('user', array('id'=>$context->instanceid), 'id, picture')) {
+                    if ($user->picture > 0) {
+                        $DB->set_field('user', 'picture', 0, array('id'=>$user->id));
+                    }
+                }
+                // no redirect here because it is not cached
+                $theme = theme_config::load($themename);
+                $imagefile = $theme->resolve_image_location('u/'.$filename, 'moodle');
+                send_file($imagefile, basename($imagefile), 60*60*24*14);
+            }
 
             send_stored_file($file, 60*60*24*365, 0, false, array('preview' => $preview)); // enable long caching, there are many images on each page
 
index edac5a5..d5ce7c1 100644 (file)
@@ -366,7 +366,7 @@ class file_storage {
      * Returns all files belonging to given repository
      *
      * @param int $repositoryid
-     * @param string $sort
+     * @param string $sort A fragment of SQL to use for sorting
      */
     public function get_external_files($repositoryid, $sort = 'sortorder, itemid, filepath, filename') {
         global $DB;
@@ -374,8 +374,10 @@ class file_storage {
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
-                 WHERE r.repositoryid = ?
-              ORDER BY $sort";
+                 WHERE r.repositoryid = ?";
+        if (!empty($sort)) {
+            $sql .= " ORDER BY {$sort}";
+        }
 
         $result = array();
         $filerecords = $DB->get_records_sql($sql, array($repositoryid));
@@ -392,11 +394,11 @@ class file_storage {
      * @param string $component component
      * @param string $filearea file area
      * @param int $itemid item ID or all files if not specified
-     * @param string $sort sort fields
+     * @param string $sort A fragment of SQL to use for sorting
      * @param bool $includedirs whether or not include directories
      * @return array of stored_files indexed by pathanmehash
      */
-    public function get_area_files($contextid, $component, $filearea, $itemid = false, $sort="sortorder, itemid, filepath, filename", $includedirs = true) {
+    public function get_area_files($contextid, $component, $filearea, $itemid = false, $sort = "sortorder, itemid, filepath, filename", $includedirs = true) {
         global $DB;
 
         $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea);
@@ -414,8 +416,10 @@ class file_storage {
                  WHERE f.contextid = :contextid
                        AND f.component = :component
                        AND f.filearea = :filearea
-                       $itemidsql
-              ORDER BY $sort";
+                       $itemidsql";
+        if (!empty($sort)) {
+            $sql .= " ORDER BY {$sort}";
+        }
 
         $result = array();
         $filerecords = $DB->get_records_sql($sql, $conditions);
@@ -489,7 +493,7 @@ class file_storage {
      * @param int $filepath directory path
      * @param bool $recursive include all subdirectories
      * @param bool $includedirs include files and directories
-     * @param string $sort sort fields
+     * @param string $sort A fragment of SQL to use for sorting
      * @return array of stored_files indexed by pathanmehash
      */
     public function get_directory_files($contextid, $component, $filearea, $itemid, $filepath, $recursive = false, $includedirs = true, $sort = "filepath, filename") {
@@ -499,6 +503,8 @@ class file_storage {
             return array();
         }
 
+        $orderby = (!empty($sort)) ? " ORDER BY {$sort}" : '';
+
         if ($recursive) {
 
             $dirs = $includedirs ? "" : "AND filename <> '.'";
@@ -512,7 +518,7 @@ class file_storage {
                            AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
                            AND f.id <> :dirid
                            $dirs
-                  ORDER BY $sort";
+                           $orderby";
             $params = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'dirid'=>$directory->get_id());
 
             $files = array();
@@ -542,7 +548,7 @@ class file_storage {
                                AND f.itemid = :itemid AND f.filename = '.'
                                AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
                                AND f.id <> :dirid
-                      ORDER BY $sort";
+                               $orderby";
                 $reqlevel = substr_count($filepath, '/') + 1;
                 $filerecords = $DB->get_records_sql($sql, $params);
                 foreach ($filerecords as $filerecord) {
@@ -559,7 +565,7 @@ class file_storage {
                            ON f.referencefileid = r.id
                      WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
                            AND f.filepath = :filepath AND f.filename <> '.'
-                  ORDER BY $sort";
+                           $orderby";
 
             $filerecords = $DB->get_records_sql($sql, $params);
             foreach ($filerecords as $filerecord) {
@@ -1028,7 +1034,7 @@ class file_storage {
 
         $newrecord->timecreated  = $filerecord->timecreated;
         $newrecord->timemodified = $filerecord->timemodified;
-        $newrecord->mimetype     = empty($filerecord->mimetype) ? $this->mimetype($pathname) : $filerecord->mimetype;
+        $newrecord->mimetype     = empty($filerecord->mimetype) ? $this->mimetype($pathname, $filerecord->filename) : $filerecord->mimetype;
         $newrecord->userid       = empty($filerecord->userid) ? null : $filerecord->userid;
         $newrecord->source       = empty($filerecord->source) ? null : $filerecord->source;
         $newrecord->author       = empty($filerecord->author) ? null : $filerecord->author;
@@ -1154,7 +1160,7 @@ class file_storage {
         list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_string_to_pool($content);
         $filepathname = $this->path_from_hash($newrecord->contenthash) . '/' . $newrecord->contenthash;
         // get mimetype by magic bytes
-        $newrecord->mimetype = empty($filerecord->mimetype) ? $this->mimetype($filepathname) : $filerecord->mimetype;
+        $newrecord->mimetype = empty($filerecord->mimetype) ? $this->mimetype($filepathname, $filerecord->filename) : $filerecord->mimetype;
 
         $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename);
 
@@ -1260,6 +1266,8 @@ class file_storage {
             $filerecord->timemodified = $now;
         }
 
+        $transaction = $DB->start_delegated_transaction();
+
         // Insert file reference record.
         try {
             $referencerecord = new stdClass;
@@ -1292,6 +1300,8 @@ class file_storage {
 
         $this->create_directory($filerecord->contextid, $filerecord->component, $filerecord->filearea, $filerecord->itemid, $filerecord->filepath, $filerecord->userid);
 
+        $transaction->allow_commit();
+
         // Adding repositoryid and reference to file record to create stored_file instance
         $filerecord->repositoryid = $repositoryid;
         $filerecord->reference = $reference;
@@ -1801,11 +1811,15 @@ class file_storage {
      * If file has a known extension, we return the mimetype based on extension.
      * Otherwise (when possible) we try to get the mimetype from file contents.
      *
-     * @param string $pathname
+     * @param string $pathname full path to the file
+     * @param string $filename correct file name with extension, if omitted will be taken from $path
      * @return string
      */
-    public static function mimetype($pathname) {
-        $type = mimeinfo('type', $pathname);
+    public static function mimetype($pathname, $filename = null) {
+        if (empty($filename)) {
+            $filename = $pathname;
+        }
+        $type = mimeinfo('type', $filename);
         if ($type === 'document/unknown' && class_exists('finfo') && file_exists($pathname)) {
             $finfo = new finfo(FILEINFO_MIME_TYPE);
             $type = mimeinfo_from_type('type', $finfo->file($pathname));
index a17d45f..c936716 100644 (file)
@@ -205,6 +205,8 @@ class stored_file {
         // Remove repository info.
         $this->repository = null;
 
+        $transaction = $DB->start_delegated_transaction();
+
         // Remove reference info from DB.
         $DB->delete_records('files_reference', array('id'=>$this->file_record->referencefileid));
 
@@ -216,6 +218,8 @@ class stored_file {
         $filerecord->referencefileid = null;
         $this->update($filerecord);
 
+        $transaction->allow_commit();
+
         // unset object variable
         unset($this->file_record->repositoryid);
         unset($this->file_record->reference);
@@ -247,6 +251,9 @@ class stored_file {
      */
     public function delete() {
         global $DB;
+
+        $transaction = $DB->start_delegated_transaction();
+
         // If other files referring to this file, we need convert them
         if ($files = $this->fs->get_references_by_storedfile($this)) {
             foreach ($files as $file) {
@@ -256,6 +263,9 @@ class stored_file {
         // Now delete file records in DB
         $DB->delete_records('files', array('id'=>$this->file_record->id));
         $DB->delete_records('files_reference', array('id'=>$this->file_record->referencefileid));
+
+        $transaction->allow_commit();
+
         // moves pool file to trash if content not needed any more
         $this->fs->deleted_file_cleanup($this->file_record->contenthash);
         return true; // BC only
index d571beb..41feea1 100644 (file)
@@ -77,6 +77,30 @@ class filestoragelib_testcase extends advanced_testcase {
         $previewtinyicon = $fs->get_file_preview($file, 'thumb');
         $this->assertInstanceOf('stored_file', $previewtinyicon);
         $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
+
+        $this->setExpectedException('file_exception');
+        $fs->get_file_preview($file, 'amodewhichdoesntexist');
+    }
+
+    public function test_get_file_preview_nonimage() {
+        $this->resetAfterTest(true);
+        $syscontext = context_system::instance();
+        $filerecord = array(
+            'contextid' => $syscontext->id,
+            'component' => 'core',
+            'filearea'  => 'unittest',
+            'itemid'    => 0,
+            'filepath'  => '/textfiles/',
+            'filename'  => 'testtext.txt',
+        );
+
+        $fs = get_file_storage();
+        $fs->create_file_from_string($filerecord, 'text contents');
+        $textfile = $fs->get_file($syscontext->id, $filerecord['component'], $filerecord['filearea'],
+            $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename']);
+
+        $preview = $fs->get_file_preview($textfile, 'thumb');
+        $this->assertFalse($preview);
     }
 
     /**
@@ -209,20 +233,14 @@ class filestoragelib_testcase extends advanced_testcase {
         $this->assertEquals($content, $importedfile->get_content());
     }
 
-    /**
-     * TODO: the tests following this line were added to demonstrate specific Oracle problems in
-     * MDL-33172. They need to be improved to properly evalulate the results of the tests. This is
-     * tracked in MDL-33326.
-     */
     private function setup_three_private_files() {
-        global $USER, $DB;
 
         $this->resetAfterTest(true);
 
         $generator = $this->getDataGenerator();
         $user = $generator->create_user();
+        $this->setUser($user->id);
         $usercontext = context_user::instance($user->id);
-        $USER = $DB->get_record('user', array('id'=>$user->id));
         // create a user private file
         $file1 = new stdClass;
         $file1->contextid = $usercontext->id;
@@ -235,53 +253,120 @@ class filestoragelib_testcase extends advanced_testcase {
 
         $fs = get_file_storage();
         $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
+        $this->assertInstanceOf('stored_file', $userfile1);
+
         $file2 = clone($file1);
         $file2->filename = '2.txt';
         $userfile2 = $fs->create_file_from_string($file2, 'file2 content');
+        $this->assertInstanceOf('stored_file', $userfile2);
 
         $file3 = clone($file1);
         $file3->filename = '3.txt';
         $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
+        $this->assertInstanceOf('stored_file', $userfile3);
 
         $user->ctxid = $usercontext->id;
 
         return $user;
     }
 
-
     public function test_get_area_files() {
         $user = $this->setup_three_private_files();
         $fs = get_file_storage();
 
         // Get area files with default options.
         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+
         // Should be the two files we added plus the folder.
         $this->assertEquals(4, count($areafiles));
 
+        // Verify structure.
+        foreach ($areafiles as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
+
         // Get area files without a folder.
-        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
+        $folderlessfiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
         // Should be the two files without folder.
-        $this->assertEquals(3, count($areafiles));
+        $this->assertEquals(3, count($folderlessfiles));
+
+        // Verify structure.
+        foreach ($folderlessfiles as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
 
         // Get area files ordered by id (was breaking on oracle).
-        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
+        $filesbyid  = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
         // Should be the two files without folder.
-        $this->assertEquals(3, count($areafiles));
+        $this->assertEquals(3, count($filesbyid));
+
+        // Verify structure.
+        foreach ($filesbyid as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
 
         // Test with an itemid with no files
         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
-        // Should none
-        $this->assertEquals(0, count($areafiles));
+        // Should be none.
+        $this->assertEmpty($areafiles);
     }
 
     public function test_get_area_tree() {
         $user = $this->setup_three_private_files();
         $fs = get_file_storage();
 
+
         // Get area files with default options.
-        $areafiles = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
-        $areafiles = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
-        //TODO: verify result!! MDL-33326
+        $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
+        $this->assertEmpty($areatree['subdirs']);
+        $this->assertNotEmpty($areatree['files']);
+        $this->assertCount(3, $areatree['files']);
+
+        // Ensure an empty try with a fake itemid.
+        $emptytree = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
+        $this->assertEmpty($emptytree['subdirs']);
+        $this->assertEmpty($emptytree['files']);
+
+        // Create a subdir.
+        $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
+        $this->assertInstanceOf('stored_file', $dir);
+
+        // Add a file to the subdir.
+        $filerecord = array(
+            'contextid' => $user->ctxid,
+            'component' => 'user',
+            'filearea'  => 'private',
+            'itemid'    => 0,
+            'filepath'  => '/testsubdir/',
+            'filename'  => 'test-get-area-tree.txt',
+        );
+
+        $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
+        $this->assertInstanceOf('stored_file', $directoryfile);
+
+        $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
+
+        // At the top level there should still be 3 files.
+        $this->assertCount(3, $areatree['files']);
+
+        // There should now be a subdirectory.
+        $this->assertCount(1, $areatree['subdirs']);
+
+        // The test subdir is named testsubdir.
+        $subdir = $areatree['subdirs']['testsubdir'];
+        $this->assertNotEmpty($subdir);
+        // It should have one file we added.
+        $this->assertCount(1, $subdir['files']);
+        // And no subdirs itself.
+        $this->assertCount(0, $subdir['subdirs']);
+
+        // Verify the file is the one we added.
+        $subdirfile = reset($subdir['files']);
+        $this->assertInstanceOf('stored_file', $subdirfile);
+        $this->assertEquals($filerecord['filename'], $subdirfile->get_filename());
     }
 
     public function test_get_file_by_id() {
@@ -294,6 +379,10 @@ class filestoragelib_testcase extends advanced_testcase {
         $filebyid = reset($areafiles);
         $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
         $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
+
+        // Test an id which doens't exist.
+        $doesntexist = $fs->get_file_by_id(99999);
+        $this->assertFalse($doesntexist);
     }
 
     public function test_get_file_by_hash() {
@@ -305,6 +394,10 @@ class filestoragelib_testcase extends advanced_testcase {
         $filebyhash = reset($areafiles);
         $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
         $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
+
+        // Test an hash which doens't exist.
+        $doesntexist = $fs->get_file_by_hash('DOESNTEXIST');
+        $this->assertFalse($doesntexist);
     }
 
     public function test_get_references_by_storedfile() {
@@ -316,7 +409,7 @@ class filestoragelib_testcase extends advanced_testcase {
 
         $testfile = reset($areafiles);
         $references = $fs->get_references_by_storedfile($testfile);
-        //TODO: verify result!! MDL-33326
+        // TODO MDL-33368 Verify result!!
     }
 
     public function test_get_external_files() {
@@ -327,45 +420,132 @@ class filestoragelib_testcase extends advanced_testcase {
         $userrepository = reset($repos);
         $this->assertInstanceOf('repository', $userrepository);
 
-        // this should break on oracle
+        // This should break on oracle.
         $fs->get_external_files($userrepository->id, 'id');
-        //TODO: verify result!! MDL-33326
-     }
+        // TODO MDL-33368 Verify result!!
+    }
+
+    public function test_create_directory_contextid_negative() {
+        $fs = get_file_storage();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory(-1, 'core', 'unittest', 0, '/');
+    }
+
+    public function test_create_directory_contextid_invalid() {
+        $fs = get_file_storage();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory('not an int', 'core', 'unittest', 0, '/');
+    }
+
+    public function test_create_directory_component_invalid() {
+        $fs = get_file_storage();
+        $syscontext = context_system::instance();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory($syscontext->id, 'bad/component', 'unittest', 0, '/');
+    }
+
+    public function test_create_directory_filearea_invalid() {
+        $fs = get_file_storage();
+        $syscontext = context_system::instance();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory($syscontext->id, 'core', 'bad-filearea', 0, '/');
+    }
+
+    public function test_create_directory_itemid_negative() {
+        $fs = get_file_storage();
+        $syscontext = context_system::instance();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory($syscontext->id, 'core', 'unittest', -1, '/');
+    }
+
+    public function test_create_directory_itemid_invalid() {
+        $fs = get_file_storage();
+        $syscontext = context_system::instance();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory($syscontext->id, 'core', 'unittest', 'notanint', '/');
+    }
+
+    public function test_create_directory_filepath_invalid() {
+        $fs = get_file_storage();
+        $syscontext = context_system::instance();
+
+        $this->setExpectedException('file_exception');
+        $fs->create_directory($syscontext->id, 'core', 'unittest', 0, '/not-with-trailing/or-leading-slash');
+    }
 
     public function test_get_directory_files() {
         $user = $this->setup_three_private_files();
         $fs = get_file_storage();
 
-        // This should also break on oracle.
-        $fs->create_directory($user->ctxid, 'user', 'private', 0, '/');
-        //TODO: verify result!! MDL-33326
+        $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
+        $this->assertInstanceOf('stored_file', $dir);
 
-        // Don't recurse with dirs
-        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
-        //TODO: verify result!! MDL-33326
+        // Add a file to the subdir.
+        $filerecord = array(
+            'contextid' => $user->ctxid,
+            'component' => 'user',
+            'filearea'  => 'private',
+            'itemid'    => 0,
+            'filepath'  => '/testsubdir/',
+            'filename'  => 'test-get-area-tree.txt',
+        );
 
-        // Don't recurse without dirs
-        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
-        //TODO: verify result!! MDL-33326
+        $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
+        $this->assertInstanceOf('stored_file', $directoryfile);
 
-        // Recurse with dirs
-        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
-        //TODO: verify result!! MDL-33326
-        // Recurse without dirs
-        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
-        //TODO: verify result!! MDL-33326
+        // Don't recurse without dirs
+        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
+        // 3 files only.
+        $this->assertCount(3, $files);
+        foreach ($files as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
+
+        // Don't recurse with dirs.
+        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
+        // 3 files + 1 directory.
+        $this->assertCount(4, $files);
+        foreach ($files as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
+
+        // Recurse with dirs.
+        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
+        // 3 files + 1 directory +  1 subdir file.
+        $this->assertCount(5, $files);
+        foreach ($files as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
+
+        // Recurse without dirs.
+        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
+        // 3 files +  1 subdir file.
+        $this->assertCount(4, $files);
+        foreach ($files as $key => $file) {
+            $this->assertInstanceOf('stored_file', $file);
+            $this->assertEquals($key, $file->get_pathnamehash());
+        }
     }
 
     public function test_search_references() {
         $fs = get_file_storage();
         $references = $fs->search_references('testsearch');
-        //TODO: verify result!! MDL-33326
+        // TODO MDL-33368 Verify result!!
     }
 
     public function test_search_references_count() {
         $fs = get_file_storage();
         $references = $fs->search_references_count('testsearch');
-        //TODO: verify result!! MDL-33326
+        // TODO MDL-33368 Verify result!!
     }
 
     public function test_delete_area_files() {
@@ -383,6 +563,20 @@ class filestoragelib_testcase extends advanced_testcase {
         $this->assertEquals(0, count($areafiles));
     }
 
+    public function test_delete_area_files_itemid() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // Get area files with default options.
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(4, count($areafiles));
+        $fs->delete_area_files($user->ctxid, 'user', 'private', 9999);
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        $this->assertEquals(4, count($areafiles));
+    }
+
     public function test_delete_area_files_select() {
         $user = $this->setup_three_private_files();
         $fs = get_file_storage();
@@ -397,4 +591,579 @@ class filestoragelib_testcase extends advanced_testcase {
         // Should be the two files we added plus the folder.
         $this->assertEquals(0, count($areafiles));
     }
+
+    public function test_create_file_from_url() {
+        $this->resetAfterTest(true);
+
+        $syscontext = context_system::instance();
+        $filerecord = array(
+            'contextid' => $syscontext->id,
+            'component' => 'core',
+            'filearea'  => 'unittest',
+            'itemid'    => 0,
+            'filepath'  => '/downloadtest/',
+        );
+        $url = 'http://download.moodle.org/unittest/test.html';
+
+        $fs = get_file_storage();
+
+        // Test creating file without filename.
+        $file1 = $fs->create_file_from_url($filerecord, $url);
+        $this->assertInstanceOf('stored_file', $file1);
+
+        // Set filename.
+        $filerecord['filename'] = 'unit-test-filename.html';
+        $file2 = $fs->create_file_from_url($filerecord, $url);
+        $this->assertInstanceOf('stored_file', $file2);
+
+        // Use temporary file.
+        $filerecord['filename'] = 'unit-test-with-temp-file.html';
+        $file3 = $fs->create_file_from_url($filerecord, $url, null, true);
+        $file3 = $this->assertInstanceOf('stored_file', $file3);
+    }
+
+    public function test_cron() {
+        $this->resetAfterTest(true);
+
+        // Note: this is only testing DB compatibility atm, rather than
+        // that work is done.
+        $fs = get_file_storage();
+
+        $this->expectOutputRegex('/Cleaning up/');
+        $fs->cron();
+    }
+
+    public function test_is_area_empty() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        $this->assertFalse($fs->is_area_empty($user->ctxid, 'user', 'private'));
+
+        // File area with madeup itemid should be empty.
+        $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999));
+        // Still empty with dirs included.
+        $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999, false));
+    }
+
+    public function test_move_area_files_to_new_context() {
+        $this->resetAfterTest(true);
+
+        // Create a course with a page resource.
+        $course = $this->getDataGenerator()->create_course();
+        $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
+        $page1context = context_module::instance($page1->cmid);
+
+        // Add a file to the page.
+        $fs = get_file_storage();
+        $filerecord = array(
+            'contextid' => $page1context->id,
+            'component' => 'mod_page',
+            'filearea'  => 'content',
+            'itemid'    => 0,
+            'filepath'  => '/',
+            'filename'  => 'unit-test-file.txt',
+        );
+
+        $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
+        $this->assertInstanceOf('stored_file', $originalfile);
+
+        $pagefiles = $fs->get_area_files($page1context->id, 'mod_page', 'content', 0, 'sortorder', false);
+        // Should be one file in filearea.
+        $this->assertFalse($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
+
+        // Create a new page.
+        $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
+        $page2context = context_module::instance($page2->cmid);
+
+        // Newly created page area is empty.
+        $this->assertTrue($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
+
+        // Move the files.
+        $fs->move_area_files_to_new_context($page1context->id, $page2context->id, 'mod_page', 'content');
+
+        // Page2 filearea should no longer be empty.
+        $this->assertFalse($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
+
+        // Page1 filearea should now be empty.
+        $this->assertTrue($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
+
+        $page2files = $fs->get_area_files($page2context->id, 'mod_page', 'content', 0, 'sortorder', false);
+        $movedfile = reset($page2files);
+
+        // The two files should have the same content hash.
+        $this->assertEquals($movedfile->get_contenthash(), $originalfile->get_contenthash());
+    }
+
+    public function test_convert_image() {
+        global $CFG;
+
+        $this->resetAfterTest(false);
+
+        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+        $syscontext = context_system::instance();
+        $filerecord = array(
+            'contextid' => $syscontext->id,
+            'component' => 'core',
+            'filearea'  => 'unittest',
+            'itemid'    => 0,
+            'filepath'  => '/images/',
+            'filename'  => 'testimage.jpg',
+        );
+
+        $fs = get_file_storage();
+        $original = $fs->create_file_from_pathname($filerecord, $filepath);
+
+        $filerecord['filename'] = 'testimage-converted-10x10.jpg';
+        $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
+        $this->assertInstanceOf('stored_file', $converted);
+
+        $filerecord['filename'] = 'testimage-convereted-nosize.jpg';
+        $converted = $fs->convert_image($filerecord, $original);
+        $this->assertInstanceOf('stored_file', $converted);
+    }
+
+    private function generate_file_record() {
+        $syscontext = context_system::instance();
+        $filerecord = new stdClass();
+        $filerecord->contextid = $syscontext->id;
+        $filerecord->component = 'core';
+        $filerecord->filearea = 'phpunit';
+        $filerecord->filepath = '/';
+        $filerecord->filename = 'testfile.txt';
+        $filerecord->itemid = 0;
+
+        return $filerecord;
+    }
+
+    public function test_create_file_from_storedfile_file_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $this->setExpectedException('file_exception');
+        // Create a file from a file id which doesn't exist.
+        $fs->create_file_from_storedfile($filerecord,  9999);
+    }
+
+    public function test_create_file_from_storedfile_contextid_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->contextid = 'invalid';
+
+        $this->setExpectedException('file_exception', 'Invalid contextid');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_component_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->component = 'bad/component';
+
+        $this->setExpectedException('file_exception', 'Invalid component');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_filearea_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->filearea = 'bad-filearea';
+
+        $this->setExpectedException('file_exception', 'Invalid filearea');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_itemid_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->itemid = 'bad-itemid';
+
+        $this->setExpectedException('file_exception', 'Invalid itemid');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_filepath_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->filepath = 'a-/bad/-filepath';
+
+        $this->setExpectedException('file_exception', 'Invalid file path');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_filename_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = '';
+
+        $this->setExpectedException('file_exception', 'Invalid file name');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_timecreated_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->timecreated = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timecreated');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_timemodified_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'invalid.txt';
+        $filerecord->timemodified  = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timemodified');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile_duplicate() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+
+        $fs = get_file_storage();
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        // Creating a file validating unique constraint.
+        $this->setExpectedException('stored_file_creation_exception');
+        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+    }
+
+    public function test_create_file_from_storedfile() {
+        $this->resetAfterTest(true);
+
+        $syscontext = context_system::instance();
+
+        $filerecord = new stdClass();
+        $filerecord->contextid = $syscontext->id;
+        $filerecord->component = 'core';
+        $filerecord->filearea = 'phpunit';
+        $filerecord->filepath = '/';
+        $filerecord->filename = 'testfile.txt';
+        $filerecord->itemid = 0;
+
+        $fs = get_file_storage();
+
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+        $this->assertInstanceOf('stored_file', $file1);
+
+        $filerecord->filename = 'test-create-file-from-storedfile.txt';
+        $file2 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+        $this->assertInstanceOf('stored_file', $file2);
+
+        // These will be normalised to current time..
+        $filerecord->timecreated = -100;
+        $filerecord->timemodified= -100;
+        $filerecord->filename = 'test-create-file-from-storedfile-bad-dates.txt';
+
+        $file3 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
+        $this->assertInstanceOf('stored_file', $file3);
+
+        $this->assertNotEquals($file3->get_timemodified(), $filerecord->timemodified);
+        $this->assertNotEquals($file3->get_timecreated(), $filerecord->timecreated);
+    }
+
+    public function test_create_file_from_string_contextid_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->contextid = 'invalid';
+
+        $this->setExpectedException('file_exception', 'Invalid contextid');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_component_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->component = 'bad/component';
+
+        $this->setExpectedException('file_exception', 'Invalid component');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_filearea_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filearea = 'bad-filearea';
+
+        $this->setExpectedException('file_exception', 'Invalid filearea');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_itemid_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->itemid = 'bad-itemid';
+
+        $this->setExpectedException('file_exception', 'Invalid itemid');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_filepath_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filepath = 'a-/bad/-filepath';
+
+        $this->setExpectedException('file_exception', 'Invalid file path');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_filename_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filename = '';
+
+        $this->setExpectedException('file_exception', 'Invalid file name');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_timecreated_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->timecreated = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timecreated');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_timemodified_invalid() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->timemodified  = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timemodified');
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_string_duplicate() {
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
+
+        // Creating a file validating unique constraint.
+        $this->setExpectedException('stored_file_creation_exception');
+        $file2 = $fs->create_file_from_string($filerecord, 'text contents');
+    }
+
+    public function test_create_file_from_pathname_contextid_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->contextid = 'invalid';
+
+        $this->setExpectedException('file_exception', 'Invalid contextid');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_component_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->component = 'bad/component';
+
+        $this->setExpectedException('file_exception', 'Invalid component');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_filearea_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filearea = 'bad-filearea';
+
+        $this->setExpectedException('file_exception', 'Invalid filearea');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_itemid_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->itemid = 'bad-itemid';
+
+        $this->setExpectedException('file_exception', 'Invalid itemid');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_filepath_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filepath = 'a-/bad/-filepath';
+
+        $this->setExpectedException('file_exception', 'Invalid file path');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_filename_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->filename = '';
+
+        $this->setExpectedException('file_exception', 'Invalid file name');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_timecreated_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->timecreated = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timecreated');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_timemodified_invalid() {
+        global $CFG;
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $this->resetAfterTest(true);
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $filerecord->timemodified  = 'today';
+
+        $this->setExpectedException('file_exception', 'Invalid file timemodified');
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+    }
+
+    public function test_create_file_from_pathname_duplicate_file() {
+        global $CFG;
+        $this->resetAfterTest(true);
+
+        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
+
+        $filerecord = $this->generate_file_record();
+        $fs = get_file_storage();
+
+        $file1 = $fs->create_file_from_pathname($filerecord, $path);
+        $this->assertInstanceOf('stored_file', $file1);
+
+        // Creating a file validating unique constraint.
+        $this->setExpectedException('stored_file_creation_exception');
+        $file2 = $fs->create_file_from_pathname($filerecord, $path);
+    }
 }
index bf7316a..2e1514c 100644 (file)
@@ -91,6 +91,7 @@ M.form_dndupload.init = function(Y, options) {
             this.maxfiles = options.maxfiles;
             this.maxbytes = options.maxbytes;
             this.itemid = options.itemid;
+            this.author = options.author;
             this.container = this.Y.one('#'+options.containerid);
 
             if (options.filemanager) {
@@ -452,6 +453,9 @@ M.form_dndupload.init = function(Y, options) {
             formdata.append('sesskey', M.cfg.sesskey);
             formdata.append('repo_id', this.repositoryid);
             formdata.append('itemid', this.itemid);
+            if (this.author) {
+                formdata.append('author', this.author);
+            }
             if (this.filemanager) { // Filepickers do not have folders
                 formdata.append('savepath', this.filemanager.currentpath);
             }
index 592dc1c..6627c2a 100644 (file)
@@ -963,6 +963,7 @@ M.form_filemanager.init = function(Y, options) {
         filemanager: manager,
         acceptedtypes: options.accepted_types,
         clientid: options.client_id,
+        author: options.author,
         maxfiles: options.maxfiles,
         maxbytes: options.maxbytes,
         itemid: options.itemid,
index 4b5de33..e372a23 100644 (file)
@@ -47,6 +47,7 @@ M.form_filepicker.init = function(Y, options) {
     var dndoptions = {
         clientid: options.client_id,
         acceptedtypes: options.accepted_types,
+        author: options.author,
         maxfiles: -1,
         maxbytes: options.maxbytes,
         itemid: options.itemid,
index 68d891a..b4e4918 100644 (file)
@@ -119,6 +119,7 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
     $image->height = $imageinfo[1];
     $image->type   = $imageinfo[2];
 
+    $t = null;
     switch ($image->type) {
         case IMAGETYPE_GIF:
             if (function_exists('imagecreatefromgif')) {
@@ -127,6 +128,11 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
                 debugging('GIF not supported on this server');
                 return false;
             }
+            // Guess transparent colour from GIF.
+            $transparent = imagecolortransparent($im);
+            if ($transparent != -1) {
+                $t = imagecolorsforindex($im, $transparent);
+            }
             break;
         case IMAGETYPE_JPEG:
             if (function_exists('imagecreatefromjpeg')) {
@@ -166,19 +172,37 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
     if (function_exists('imagecreatetruecolor') and $CFG->gdversion >= 2) {
         $im1 = imagecreatetruecolor(100, 100);
         $im2 = imagecreatetruecolor(35, 35);
-        if ($image->type == IMAGETYPE_PNG and $imagefnc === 'imagepng') {
+        $im3 = imagecreatetruecolor(512, 512);
+        if ($image->type != IMAGETYPE_JPEG and $imagefnc === 'imagepng') {
+            if ($t) {
+                // Transparent GIF hacking...
+                $transparentcolour = imagecolorallocate($im1 , $t['red'] , $t['green'] , $t['blue']);
+                imagecolortransparent($im1 , $transparentcolour);
+                $transparentcolour = imagecolorallocate($im2 , $t['red'] , $t['green'] , $t['blue']);
+                imagecolortransparent($im2 , $transparentcolour);
+                $transparentcolour = imagecolorallocate($im3 , $t['red'] , $t['green'] , $t['blue']);
+                imagecolortransparent($im3 , $transparentcolour);
+            }
+
             imagealphablending($im1, false);
             $color = imagecolorallocatealpha($im1, 0, 0,  0, 127);
             imagefill($im1, 0, 0,  $color);
             imagesavealpha($im1, true);
+
             imagealphablending($im2, false);
             $color = imagecolorallocatealpha($im2, 0, 0,  0, 127);
             imagefill($im2, 0, 0,  $color);
             imagesavealpha($im2, true);
+
+            imagealphablending($im3, false);
+            $color = imagecolorallocatealpha($im3, 0, 0,  0, 127);
+            imagefill($im3, 0, 0,  $color);
+            imagesavealpha($im3, true);
         }
     } else {
         $im1 = imagecreate(100, 100);
         $im2 = imagecreate(35, 35);
+        $im3 = imagecreate(512, 512);
     }
 
     $cx = $image->width / 2;
@@ -192,6 +216,7 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
 
     imagecopybicubic($im1, $im, 0, 0, $cx - $half, $cy - $half, 100, 100, $half * 2, $half * 2);
     imagecopybicubic($im2, $im, 0, 0, $cx - $half, $cy - $half, 35, 35, $half * 2, $half * 2);
+    imagecopybicubic($im3, $im, 0, 0, $cx - $half, $cy - $half, 512, 512, $half * 2, $half * 2);
 
     $fs = get_file_storage();
 
@@ -220,6 +245,17 @@ function process_new_icon($context, $component, $filearea, $itemid, $originalfil
     $icon['filename'] = 'f2'.$imageext;
     $fs->create_file_from_string($icon, $data);
 
+    ob_start();
+    if (!$imagefnc($im3, NULL, $quality, $filters)) {
+        ob_end_clean();
+        $fs->delete_area_files($context->id, $component, $filearea, $itemid);
+        return false;
+    }
+    $data = ob_get_clean();
+    imagedestroy($im3);
+    $icon['filename'] = 'f3'.$imageext;
+    $fs->create_file_from_string($icon, $data);
+
     return $file1->get_id();
 }
 
index e652a8c..7da6ed6 100644 (file)
@@ -455,6 +455,11 @@ function install_cli_database(array $options, $interactive) {
     set_config('release', $release);
     set_config('branch', $branch);
 
+    if (PHPUNIT_TEST) {
+        // mark as test database as soon as possible
+        set_config('phpunittest', 'na');
+    }
+
     // install all plugins types, local, etc.
     upgrade_noncore(true);
 
index 82bf053..1640f14 100644 (file)
@@ -115,7 +115,7 @@ class calc_formula {
      * @param string $formula
      * @return string localised formula
      */
-    function localize($formula) {
+    public static function localize($formula) {
         $formula = str_replace('.', '$', $formula); // temp placeholder
         $formula = str_replace(',', get_string('listsep', 'langconfig'), $formula);
         $formula = str_replace('$', get_string('decsep', 'langconfig'), $formula);
@@ -127,7 +127,7 @@ class calc_formula {
      * @param string $formula localised formula
      * @return string
      */
-    function unlocalize($formula) {
+    public static function unlocalize($formula) {
         $formula = str_replace(get_string('decsep', 'langconfig'), '$', $formula);
         $formula = str_replace(get_string('listsep', 'langconfig'), ',', $formula);
         $formula = str_replace('$', '.', $formula); // temp placeholder
index 8b0379b..23ee2f5 100644 (file)
@@ -9642,7 +9642,7 @@ function address_in_subnet($addr, $subnetstr) {
  */
 function mtrace($string, $eol="\n", $sleep=0) {
 
-    if (defined('STDOUT')) {
+    if (defined('STDOUT') and !PHPUNIT_TEST) {
         fwrite(STDOUT, $string.$eol);
     } else {
         echo $string . $eol;
index 514eac9..4bd0673 100644 (file)
@@ -321,6 +321,9 @@ class user_picture implements renderable {
         } else if ($this->size === true or $this->size == 1) {
             $filename = 'f1';
             $size = 100;
+        } else if ($this->size > 100) {
+            $filename = 'f3';
+            $size = (int)$this->size;
         } else if ($this->size >= 50) {
             $filename = 'f1';
             $size = (int)$this->size;
index 80f20c6..697ca6f 100644 (file)
@@ -2025,7 +2025,9 @@ class core_renderer extends renderer_base {
 
         $currentfile = $options->currentfile;
         if (empty($currentfile)) {
-            $currentfile = get_string('nofilesattached', 'repository');
+            $currentfile = '';
+        } else {
+            $currentfile .= ' - ';
         }
         if ($options->maxbytes) {
             $size = $options->maxbytes;
@@ -2056,7 +2058,7 @@ EOD;
             $html .= <<<EOD
     <div id="file_info_{$client_id}" class="mdl-left filepicker-filelist" style="position: relative">
     <div class="filepicker-filename">
-        <div class="filepicker-container">$currentfile<span class="dndupload-message"> - $strdndenabled <br/><span class="dndupload-arrow"></span></span></div>
+        <div class="filepicker-container">$currentfile<span class="dndupload-message">$strdndenabled <br/><span class="dndupload-arrow"></span></span></div>
     </div>
     <div><div class="dndupload-target">{$strdroptoupload}<br/><span class="dndupload-arrow"></span></div></div>
     </div>
index df5239c..20f8a34 100644 (file)
@@ -280,25 +280,35 @@ class plugin_manager {
     }
 
     /**
-     * Checks all dependencies for all installed plugins. Used by install and upgrade.
+     * Checks all dependencies for all installed plugins
+     *
+     * This is used by install and upgrade. The array passed by reference as the second
+     * argument is populated with the list of plugins that have failed dependencies (note that
+     * a single plugin can appear multiple times in the $failedplugins).
+     *
      * @param int $moodleversion the version from version.php.
+     * @param array $failedplugins to return the list of plugins with non-satisfied dependencies
      * @return bool true if all the dependencies are satisfied for all plugins.
      */
-    public function all_plugins_ok($moodleversion) {
+    public function all_plugins_ok($moodleversion, &$failedplugins = array()) {
+
+        $return = true;
         foreach ($this->get_plugins() as $type => $plugins) {
             foreach ($plugins as $plugin) {
 
-                if (!empty($plugin->versionrequires) && $plugin->versionrequires > $moodleversion) {
-                    return false;
+                if (!$plugin->is_core_dependency_satisfied($moodleversion)) {
+                    $return = false;
+                    $failedplugins[] = $plugin->component;
                 }
 
                 if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
-                    return false;
+                    $return = false;
+                    $failedplugins[] = $plugin->component;
                 }
             }
         }
 
-        return true;
+        return $return;
     }
 
     /**
@@ -1610,6 +1620,22 @@ abstract class plugininfo_base {
         return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
     }
 
+    /**
+     * Returns true if the the given Moodle version is enough to run this plugin
+     *
+     * @param string|int|double $moodleversion
+     * @return bool
+     */
+    public function is_core_dependency_satisfied($moodleversion) {
+
+        if (empty($this->versionrequires)) {
+            return true;
+
+        } else {
+            return (double)$this->versionrequires <= (double)$moodleversion;
+        }
+    }
+
     /**
      * Returns the status of the plugin
      *
index 81727f5..3baf0b5 100644 (file)
@@ -809,7 +809,7 @@ class portfolio_exporter {
      */
     public function get_tempfiles($skipfile='portfolio-export.zip') {
         $fs = get_file_storage();
-        $files = $fs->get_area_files(SYSCONTEXTID, 'portfolio', 'exporter', $this->id, '', false);
+        $files = $fs->get_area_files(SYSCONTEXTID, 'portfolio', 'exporter', $this->id, 'sortorder, itemid, filepath, filename', false);
         if (empty($files)) {
             return array();
         }
index 1a635f3..23cf9e1 100644 (file)
@@ -478,8 +478,21 @@ setup_DB();
 if (PHPUNIT_TEST and !PHPUNIT_UTIL) {
     // make sure tests do not run in parallel
     phpunit_util::acquire_test_lock();
-    // reset DB tables
-    phpunit_util::reset_database();
+    $dbhash = null;
+    try {
+        if ($dbhash = $DB->get_field('config', 'value', array('name'=>'phpunittest'))) {
+            // reset DB tables
+            phpunit_util::reset_database();
+        }
+    } catch (Exception $e) {
+        if ($dbhash) {
+            // we ned to reinit if reset fails
+            $DB->set_field('config', 'value', 'na', array('name'=>'phpunittest'));
+        } else {
+            throw $e;
+        }
+    }
+    unset($dbhash);
 }
 
 // Disable errors for now - needed for installation when debug enabled in config.php
index f35c916..2315916 100644 (file)
@@ -70,7 +70,8 @@ class conditionlib_testcase extends advanced_testcase {
         $this->assertEquals(
             (object)array('id'=>$id,'showavailability'=>1,
                 'availablefrom'=>17,'availableuntil'=>398,'course'=>64,
-                'conditionsgrade'=>array(), 'conditionscompletion'=>array()),
+                'conditionsgrade'=>array(), 'conditionscompletion'=>array(),
+                'visible' => 1),
             $test->get_full_course_module());
 
         // just the course_modules stuff; check it doesn't request that from db
@@ -78,11 +79,13 @@ class conditionlib_testcase extends advanced_testcase {
         $cm->availablefrom=2;
         $cm->availableuntil=74;
         $cm->course=38;
+        $cm->visible = 1;
         $test=new condition_info($cm,CONDITION_MISSING_EXTRATABLE);
         $this->assertEquals(
             (object)array('id'=>$id,'showavailability'=>0,
                 'availablefrom'=>2,'availableuntil'=>74,'course'=>38,
-                'conditionsgrade'=>array(), 'conditionscompletion'=>array()),
+                'conditionsgrade' => array(), 'conditionscompletion' => array(),
+                'visible' => 1),
             $test->get_full_course_module());
 
         // Now let's add some actual grade/completion conditions
@@ -136,7 +139,8 @@ class conditionlib_testcase extends advanced_testcase {
         $this->assertEquals(
                 (object)array('id' => $id, 'showavailability' => 1, 'groupingid' => 13,
                     'availablefrom' => 17, 'availableuntil' => 398, 'course' => 64,
-                    'conditionsgrade' => array(), 'conditionscompletion' => array()),
+                    'conditionsgrade' => array(), 'conditionscompletion' => array(),
+                    'visible' => 1),
                 $test->get_full_section());
 
         // Just the course_sections stuff; check it doesn't request that from db
@@ -146,11 +150,13 @@ class conditionlib_testcase extends advanced_testcase {
         $section->availableuntil = 74;
         $section->course = 38;
         $section->groupingid = 99;
+        $section->visible = 1;
         $test = new condition_info_section($section, CONDITION_MISSING_EXTRATABLE);
         $this->assertEquals(
                 (object)array('id' => $id, 'showavailability' => 0, 'groupingid' => 99,
                     'availablefrom' => 2, 'availableuntil' => 74, 'course' => 38,
-                    'conditionsgrade' => array(), 'conditionscompletion' => array()),
+                    'conditionsgrade' => array(), 'conditionscompletion' => array(),
+                    'visible' => 1),
                 $test->get_full_section());
 
         // Now let's add some actual grade/completion conditions
index 6e6e029..aa6ee8f 100644 (file)
@@ -1764,3 +1764,78 @@ function admin_mnet_method_profile(Zend_Server_Reflection_Function_Abstract $fun
     }
     return $profile;
 }
+
+
+/**
+ * This function finds duplicate records (based on combinations of fields that should be unique)
+ * and then progamatically generated a "most correct" version of the data, update and removing
+ * records as appropriate
+ *
+ * Thanks to Dan Marsden for help
+ *
+ * @param   string  $table      Table name
+ * @param   array   $uniques    Array of field names that should be unique
+ * @param   array   $fieldstocheck  Array of fields to generate "correct" data from (optional)
+ * @return  void
+ */
+function upgrade_course_completion_remove_duplicates($table, $uniques, $fieldstocheck = array()) {
+    global $DB;
+
+    // Find duplicates
+    $sql_cols = implode(', ', $uniques);
+
+    $sql = "SELECT {$sql_cols} FROM {{$table}} GROUP BY {$sql_cols} HAVING (count(id) > 1)";
+    $duplicates = $DB->get_recordset_sql($sql, array());
+
+    // Loop through duplicates
+    foreach ($duplicates as $duplicate) {
+        $pointer = 0;
+
+        // Generate SQL for finding records with these duplicate uniques
+        $sql_select = implode(' = ? AND ', $uniques).' = ?'; // builds "fieldname = ? AND fieldname = ?"
+        $uniq_values = array();
+        foreach ($uniques as $u) {
+            $uniq_values[] = $duplicate->$u;
+        }
+
+        $sql_order = implode(' DESC, ', $uniques).' DESC'; // builds "fieldname DESC, fieldname DESC"
+
+        // Get records with these duplicate uniques
+        $records = $DB->get_records_select(
+            $table,
+            $sql_select,
+            $uniq_values,
+            $sql_order
+        );
+
+        // Loop through and build a "correct" record, deleting the others
+        $needsupdate = false;
+        $origrecord = null;
+        foreach ($records as $record) {
+            $pointer++;
+            if ($pointer === 1) { // keep 1st record but delete all others.
+                $origrecord = $record;
+            } else {
+                // If we have fields to check, update original record
+                if ($fieldstocheck) {
+                    // we need to keep the "oldest" of all these fields as the valid completion record.
+                    // but we want to ignore null values
+                    foreach ($fieldstocheck as $f) {
+                        if ($record->$f && (($origrecord->$f > $record->$f) || !$origrecord->$f)) {
+                            $origrecord->$f = $record->$f;
+                            $needsupdate = true;
+                        }
+                    }
+                }
+                $DB->delete_records($table, array('id' => $record->id));
+            }
+        }
+        if ($needsupdate || isset($origrecord->reaggregate)) {
+            // If this table has a reaggregate field, update to force recheck on next cron run
+            if (isset($origrecord->reaggregate)) {
+                $origrecord->reaggregate = time();
+            }
+            $DB->update_record($table, $origrecord);
+        }
+    }
+}
index 35fc12e..8555f32 100644 (file)
@@ -167,6 +167,13 @@ YUI.add('moodle-core-blocks', function(Y) {
             this.dragsourceregion = null;
         },
 
+        drag_dropmiss : function(e) {
+            // Missed the target, but we assume the user intended to drop it
+            // on the last last ghost node location, e.drag and e.drop should be
+            // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+            this.drop_hit(e);
+        },
+
         drop_hit : function(e) {
             var drag = e.drag;
             // Get a reference to our drag node
index 930f600..27d0f1d 100644 (file)
@@ -15,6 +15,7 @@ YUI.add('moodle-core-dragdrop', function(Y) {
         samenodeclass : null,
         parentnodeclass : null,
         groups : [],
+        lastdroptarget : null,
         initializer : function(params) {
             // Listen for all drag:start events
             Y.DD.DDM.on('drag:start', this.global_drag_start, this);
@@ -26,6 +27,8 @@ YUI.add('moodle-core-dragdrop', function(Y) {
             Y.DD.DDM.on('drop:over', this.global_drop_over, this);
             // Listen for all drop:hit events
             Y.DD.DDM.on('drop:hit', this.global_drop_hit, this);
+            // Listen for all drop:miss events
+            Y.DD.DDM.on('drag:dropmiss', this.global_drag_dropmiss, this);
         },
 
         get_drag_handle: function(title, classname, iconclass) {
@@ -34,8 +37,7 @@ YUI.add('moodle-core-dragdrop', function(Y) {
                 .setAttrs({
                     'src' : M.util.image_url(MOVEICON.pix, MOVEICON.component),
                     'alt' : title,
-                    'title' : M.str.moodle.move,
-                    'hspace' : '3'
+                    'title' : M.str.moodle.move
                 });
             if (iconclass) {
                 dragicon.addClass(iconclass);
@@ -66,14 +68,23 @@ YUI.add('moodle-core-dragdrop', function(Y) {
             return new M.core.exception(e);
         },
 
+        in_group: function(target) {
+            var ret = false;
+            Y.each(this.groups, function(v, k) {
+                if (target._groups[v]) {
+                    ret = true;
+                }
+            }, this);
+            return ret;
+        },
         /*
          * Drag-dropping related functions
          */
         global_drag_start : function(e) {
             // Get our drag object
             var drag = e.target;
-            // Check that drop object belong to correct group
-            if (!drag.target.inGroup(this.groups)) {
+            // Check that drag object belongs to correct group
+            if (!this.in_group(drag)) {
                 return;
             }
             // Set some general styles here
@@ -89,8 +100,8 @@ YUI.add('moodle-core-dragdrop', function(Y) {
 
         global_drag_end : function(e) {
             var drag = e.target;
-            // Check that drop object belong to correct group
-            if (!drag.target.inGroup(this.groups)) {
+            // Check that drag object belongs to correct group
+            if (!this.in_group(drag)) {
                 return;
             }
             //Put our general styles back
@@ -103,8 +114,8 @@ YUI.add('moodle-core-dragdrop', function(Y) {
 
         global_drag_drag : function(e) {
             var drag = e.target;
-            // Check that drop object belong to correct group
-            if (!drag.target.inGroup(this.groups)) {
+            // Check that drag object belongs to correct group
+            if (!this.in_group(drag)) {
                 return;
             }
             //Get the last y point
@@ -130,6 +141,8 @@ YUI.add('moodle-core-dragdrop', function(Y) {
             //Get a reference to our drag and drop nodes
             var drag = e.drag.get('node');
             var drop = e.drop.get('node');
+            // Save last drop target for the case of missed target processing
+            this.lastdroptarget = e.drop;
             //Are we dropping on the same node?
             if (drop.hasClass(this.samenodeclass)) {
                 //Are we not going up?
@@ -149,6 +162,19 @@ YUI.add('moodle-core-dragdrop', function(Y) {
             this.drop_over(e);
         },
 
+        global_drag_dropmiss : function(e) {
+            // drag:dropmiss does not have e.drag and e.drop properties
+            // we substitute them for the ease of use. For e.drop we use,
+            // this.lastdroptarget (ghost node we use for indicating where to drop)
+            e.drag = e.target;
+            // Check that drop object belong to correct group
+            if (!e.drag.target.inGroup(this.groups)) {
+                return;
+            }
+            e.drop = this.lastdroptarget;
+            this.drag_dropmiss(e);
+        },
+
         global_drop_hit : function(e) {
             // Check that drop object belong to correct group
             if (!e.drop.inGroup(this.groups)) {
@@ -163,6 +189,7 @@ YUI.add('moodle-core-dragdrop', function(Y) {
         drag_start : function(e) {},
         drag_end : function(e) {},
         drag_drag : function(e) {},
+        drag_dropmiss : function(e) {},
         drop_over : function(e) {},
         drop_hit : function(e) {}
     }, {
index 69b6ec4..dcb31e9 100644 (file)
@@ -344,7 +344,7 @@ abstract class assign_plugin {
      * @param bool $showviewlink Modifed to return whether or not to show a link to the full submission/feedback
      * @return string - return a string representation of the submission in full
      */
-    public function view_summary(stdClass $submissionorgrade, $showviewlink) {
+    public function view_summary(stdClass $submissionorgrade, $showviewlink) {
         return '';
     }
 
index 7f97175..dadd2a8 100644 (file)
@@ -105,7 +105,7 @@ class assign_feedback_comments extends assign_feedback_plugin {
      * @param bool $showviewlink Set to true to show a link to view the full feedback
      * @return string
      */
-    public function view_summary(stdClass $grade, $showviewlink) {
+    public function view_summary(stdClass $grade, $showviewlink) {
         $feedbackcomments = $this->get_feedback_comments($grade->id);
         if ($feedbackcomments) {
             $text = format_text($feedbackcomments->commenttext, $feedbackcomments->commentformat, array('context' => $this->assignment->get_context()));
index 20c4be7..9989cf3 100644 (file)
@@ -146,7 +146,7 @@ class assign_feedback_file extends assign_feedback_plugin {
      * @param bool $showviewlink - Set to true to show a link to see the full list of files
      * @return string
      */
-    public function view_summary(stdClass $grade, $showviewlink) {
+    public function view_summary(stdClass $grade, $showviewlink) {
         $count = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA);
         // show a view all link if the number of files is over this limit
         $showviewlink = $count > ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES;
index ff37319..2211bb8 100644 (file)
@@ -118,10 +118,10 @@ class assign {
     private $returnparams = array();
 
     /** @var string modulename prevents excessive calls to get_string */
-    private static $modulename = '';
+    private static $modulename = null;
 
     /** @var string modulenameplural prevents excessive calls to get_string */
-    private static $modulenameplural = '';
+    private static $modulenameplural = null;
 
     /**
      * Constructor for the base assign class
@@ -2368,8 +2368,8 @@ class assign {
         global $CFG;
         $posthtml  = '<p><font face="sans-serif">'.
                      '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.format_string($course->shortname, true, array('context' => $context->get_course_context())).'</a> ->'.
-                     '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$course->id.'">'.$modulename.'</a> ->'.
-                     '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$coursemodule->id.'">'.format_string($assignmentname, true, array('context' => $context)).'</a></font></p>';
+                     '<a href="'.$CFG->wwwroot.'/mod/assign/index.php?id='.$course->id.'">'.$modulename.'</a> ->'.
+                     '<a href="'.$CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id.'">'.format_string($assignmentname, true, array('context' => $context)).'</a></font></p>';
         $posthtml .= '<hr /><font face="sans-serif">';
         $posthtml .= '<p>'.get_string($messagetype . 'html', 'assign', $info).'</p>';
         $posthtml .= '</font><hr />';
index 7860b64..187bc9a 100644 (file)
@@ -36,6 +36,9 @@ $ADMIN->add('assignmentplugins', new admin_category('assignfeedbackplugins',
 $ADMIN->add('assignfeedbackplugins', new assign_admin_page_manage_assign_plugins('assignfeedback'));
 
 
+assign_plugin_manager::add_admin_assign_plugin_settings('assignsubmission', $ADMIN, $settings, $module);
+assign_plugin_manager::add_admin_assign_plugin_settings('assignfeedback', $ADMIN, $settings, $module);
+
 if ($ADMIN->fulltree) {
     $menu = array();
     foreach (get_plugin_list('assignfeedback') as $type => $notused) {
@@ -54,4 +57,4 @@ if ($ADMIN->fulltree) {
                    new lang_string('configshowrecentsubmissions', 'assign'), 0));
     $settings->add(new admin_setting_configcheckbox('assign/submissionreceipts',
                    get_string('sendsubmissionreceipts', 'mod_assign'), get_string('sendsubmissionreceipts_help', 'mod_assign'), 1));
-}
\ No newline at end of file
+}
index 43ee80d..eb87938 100644 (file)
@@ -53,7 +53,7 @@ class assign_submission_comments extends assign_submission_plugin {
     * @param bool $showviewlink - If the comments are long this is set to true so they can be shown in a separate page
     * @return string
     */
-   public function view_summary(stdClass $submission, $showviewlink) {
+   public function view_summary(stdClass $submission, $showviewlink) {
 
         // never show a link to view full submission
         $showviewlink = false;
index b097c39..3f95f34 100644 (file)
@@ -244,7 +244,7 @@ class assign_submission_file extends assign_submission_plugin {
      * @param bool $showviewlink Set this to true if the list of files is long
      * @return string
      */
-    public function view_summary(stdClass $submission, $showviewlink) {
+    public function view_summary(stdClass $submission, $showviewlink) {
         $count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);
 
         // show we show a link to view all files for this plugin?
index dcaf0c5..0ab3777 100644 (file)
@@ -192,7 +192,7 @@ class assign_submission_onlinetext extends assign_submission_plugin {
       * @param bool $showviewlink - If the summary has been truncated set this to true
       * @return string
       */
-    public function view_summary(stdClass $submission, $showviewlink) {
+    public function view_summary(stdClass $submission, $showviewlink) {
 
         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
         // always show the view link
index f737bda..5d29b71 100644 (file)
@@ -38,74 +38,39 @@ $string['pluginname'] = 'Book';
 $string['pluginadministration'] = 'Book administration';
 
 $string['toc'] = 'Table of contents';
-$string['faq'] = 'Book FAQ';
-$string['faq_help'] = '
-*Why only two levels?*
-
-Two levels are generally enough for all books, three levels would lead to poorly structured documents. Book module is designed for
-creation of short multipage study materials. It is usually better to use PDF format for longer documents. The easiest way to create PDFs are
-virtual printers (see
-<a  href="http://sector7g.wurzel6.de/pdfcreator/index_en.htm"  target="_blank">PDFCreator</a>,
-<a  href="http://fineprint.com/products/pdffactory/index.html"  target="_blank">PDFFactory</a>,
-<a  href="http://www.adobe.com/products/acrobatstd/main.html"  target="_blank">Adobe Acrobat</a>,
-etc.).
-
-*Can students edit books?*
-
-Only teachers can create and edit books. There are no plans to implement student editing for books, but somebody may create something
-similar for students (Portfolio?). The main reason is to keep Book module as simple as possible.
-
-*How do I search the books?*
-
-At present there is only one way, use browser\'s search capability in print page. Global searching is now possible only in Moodle forums.
-It would be nice to have global searching for all resources including books, any volunteers?
-
-*My titles do not fit on one line.*
-
-Either rephrase your titles or ask your site admin to change TOC
-width. It is defined globally for all books in module configuration
-page.';
-
 $string['customtitles'] = 'Custom titles';
-$string['customtitles_help'] = 'Chapter titles are displayed automatically only in TOC.';
+$string['customtitles_help'] = 'Normally the chapter title is displayed in the table of contents (TOC) AND as a heading above the content.
 
+If the custom titles checkbox is ticked, the chapter title is NOT displayed as a heading above the content. A different title (perhaps longer than the chapter title) may be entered as part of the content.';
 $string['chapters'] = 'Chapters';
 $string['editingchapter'] = 'Editing chapter';
 $string['chaptertitle'] = 'Chapter title';
 $string['content'] = 'Content';
 $string['subchapter'] = 'Subchapter';
-
-$string['numbering'] = 'Chapter numbering';
-$string['numbering_help'] = '* None - chapter and subchapter titles are not formatted at all, use if you want to define special numbering styles. For example letters: in chapter title type "A First Chapter", "A.1 Some Subchapter",...
-* Numbers - chapters and subchapters are numbered (1, 1.1, 1.2, 2, ...)
-* Bullets - subchapters are indented and displayed with bullets
-* Indented - subchapters are indented';
-
+$string['numbering'] = 'Chapter formatting';
+$string['numbering_help'] = '* None - Chapter and subchapter titles have no formatting
+* Numbers - Chapters and subchapter titles are numbered 1, 1.1, 1.2, 2, ...
+* Bullets - Subchapters are indented and displayed with bullets in the table of contents
+* Indented - Subchapters are indented in the table of contents';
 $string['numbering0'] = 'None';
 $string['numbering1'] = 'Numbers';
 $string['numbering2'] = 'Bullets';
 $string['numbering3'] = 'Indented';
-$string['numberingoptions'] = 'Available numbering options';
-$string['numberingoptions_help'] = 'Select numbering options that should be available when creating new books.';
-
+$string['numberingoptions'] = 'Available options for chapter formatting';
+$string['numberingoptions_desc'] = 'Options for displaying chapters and subchapters in the table of contents';
 $string['chapterscount'] = 'Chapters';
-
 $string['addafter'] = 'Add new chapter';
 $string['confchapterdelete'] = 'Do you really want to delete this chapter?';
 $string['confchapterdeleteall'] = 'Do you really want to delete this chapter and all its subchapters?';
-
 $string['top'] = 'top';
-
 $string['navprev'] = 'Previous';
 $string['navnext'] = 'Next';
 $string['navexit'] = 'Exit book';
-
 $string['book:addinstance'] = 'Add a new book';
 $string['book:read'] = 'Read book';
 $string['book:edit'] = 'Edit book chapters';
 $string['book:viewhiddenchapters'] = 'View hidden book chapters';
-
-$string['errorchapter'] = 'Error reading book chapter.';
+$string['errorchapter'] = 'Error reading chapter of book.';
 
 $string['page-mod-book-x'] = 'Any book module page';
 
index 96f9f45..238913d 100644 (file)
@@ -194,12 +194,6 @@ function book_add_fake_block($chapters, $chapter, $book, $cm, $edit) {
 
     $toc = book_get_toc($chapters, $chapter, $book, $cm, $edit, 0);
 
-    if ($edit) {
-        $toc .= '<div class="book_faq">';
-        $toc .=  $OUTPUT->help_icon('faq', 'mod_book', get_string('faq', 'mod_book'));
-        $toc .=  '</div>';
-    }
-
     $bc = new block_contents();
     $bc->title = get_string('toc', 'mod_book');
     $bc->attributes['class'] = 'block';
index d58cce3..b795875 100644 (file)
@@ -45,7 +45,7 @@ class mod_book_mod_form extends moodleform_mod {
             $mform->setType('name', PARAM_CLEANHTML);
         }
         $mform->addRule('name', null, 'required', null, 'client');
-        $this->add_intro_editor($config->requiremodintro, get_string('summary'));
+        $this->add_intro_editor($config->requiremodintro, get_string('moduleintro'));
 
         $alloptions = book_get_numbering_types();
         $allowed = explode(',', $config->numberingoptions);
index 810ad3c..38f87bb 100644 (file)
@@ -35,7 +35,7 @@ if ($ADMIN->fulltree) {
     $options = book_get_numbering_types();
 
     $settings->add(new admin_setting_configmultiselect('book/numberingoptions',
-        get_string('numberingoptions', 'mod_book'), get_string('numberingoptions_help', 'mod_book'),
+        get_string('numberingoptions', 'mod_book'), get_string('numberingoptions_desc', 'mod_book'),
         array_keys($options), $options));
 
 
@@ -46,4 +46,4 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configselect('book/numbering',
         get_string('numbering', 'mod_book'), '', BOOK_NUM_NUMBERS, $options));
 
-}
\ No newline at end of file
+}
index d09189d..6ddaf09 100644 (file)
 
 /* == Fake toc block == */
 
-.mod_book .book_faq {
-  font-size: 0.7em;
-}
-
 /* toc style NONE */
 .mod_book .book_toc_none {
   font-size: 0.8em;
index 2273e55..5b6a505 100644 (file)
@@ -26,5 +26,5 @@ defined('MOODLE_INTERNAL') || die;
 
 $string['exportimscp:export'] = 'Export book as IMS content package';
 $string['generateimscp'] = 'Generate IMS CP';
-$string['nochapters'] = 'No book chapters found, can not export to IMS CP.';
+$string['nochapters'] = 'No book chapters found, so unable to export to IMS CP.';
 $string['pluginname'] = 'Book IMS CP export';
index c564009..bbd811d 100644 (file)
 defined('MOODLE_INTERNAL') || die;
 
 $string['doimport'] = 'Import';
-$string['errornochapters'] = 'Can not find chapters in selected file';
-$string['import'] = 'Import from HTML';
+$string['errornochapters'] = 'Cannot find chapters in selected file';
+$string['import'] = 'Import chapter';
 $string['importhtml:import'] = 'Import chapters';
 $string['importing'] = 'Importing';
 $string['importingchapters'] = 'Importing chapters into book';
-$string['pluginname'] = 'Book HTML import';
+$string['pluginname'] = 'Book chapter import';
 $string['relinking'] = 'Relinking';
 $string['type'] = 'Type';
 $string['typeonefile'] = 'One HTML file with headings as chapters';
 $string['typezipfiles'] = 'Each HTML file represents one chapter';
-$string['typezipdirs'] = 'Each directory represents one chapter';
-$string['ziparchive'] = 'Zip archive';
-$string['ziparchive_help'] = 'Select a ZIP archive that contains HTML files and other media. File or directory names ending with "_sub" indicate subchapters. You can use copy and paste in text editor for simple HML files without embedded media.';
\ No newline at end of file
+$string['typezipdirs'] = 'Each folder represents one chapter';
+$string['ziparchive'] = 'Zip file';
+$string['ziparchive_help'] = 'Select a zip file containing HTML files and optional multimedia files and folders. To upload subchapters, add "_sub" to the end of HTML file or folder names.';
index fe07130..fbce66e 100644 (file)
@@ -25,6 +25,6 @@
 defined('MOODLE_INTERNAL') || die;
 
 $module->component = 'mod_book'; // Full name of the plugin (used for diagnostics)
-$module->version   = 2012052100; // The current module version (Date: YYYYMMDDXX)
+$module->version   = 2012052700; // The current module version (Date: YYYYMMDDXX)
 $module->requires  = 2012051900; // Requires this Moodle version
 $module->cron      = 0;          // Period for cron to check this module (secs)
index 9185685..0b74e47 100644 (file)
@@ -48,7 +48,7 @@ $PAGE->set_activity_record($folder);
 
 $data = new stdClass();
 $data->id = $cm->id;
-$options = array('mainfile'=>true, 'subdirs'=>1, 'maxbytes'=>$CFG->maxbytes, 'maxfiles'=>-1, 'accepted_types'=>'*');
+$options = array('subdirs'=>1, 'maxbytes'=>$CFG->maxbytes, 'maxfiles'=>-1, 'accepted_types'=>'*');
 file_prepare_standard_filemanager($data, 'files', $options, $context, 'mod_folder', 'content', 0);
 
 $mform = new mod_folder_edit_form(null, array('data'=>$data, 'options'=>$options));
index f1444af..86bf31b 100644 (file)
@@ -31,7 +31,7 @@
  * @return string the full path to the cached RSS feed directory. Null if there is a problem.
  */
 function forum_rss_get_feed($context, $args) {
-    global $CFG, $DB;
+    global $CFG, $DB, $USER;
 
     $status = true;
 
@@ -43,7 +43,7 @@ function forum_rss_get_feed($context, $args) {
 
     $forumid  = clean_param($args[3], PARAM_INT);
     $cm = get_coursemodule_from_instance('forum', $forumid, 0, false, MUST_EXIST);
-    $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $modcontext = context_module::instance($cm->id);
 
     //context id from db should match the submitted one
     if ($context->id != $modcontext->id || !has_capability('mod/forum:viewdiscussion', $modcontext)) {
@@ -58,8 +58,14 @@ function forum_rss_get_feed($context, $args) {
     //the sql that will retreive the data for the feed and be hashed to get the cache filename
     $sql = forum_rss_get_sql($forum, $cm);
 
-    //hash the sql to get the cache file name
-    $filename = rss_get_file_name($forum, $sql);
+    // Hash the sql to get the cache file name.
+    // If the forum is Q and A then we need to cache the files per user. This can
+    // have a large impact on performance, so we want to only do it on this type of forum.
+    if ($forum->type == 'qanda') {
+        $filename = rss_get_file_name($forum, $sql . $USER->id);
+    } else {
+        $filename = rss_get_file_name($forum, $sql);
+    }
     $cachedfilepath = rss_get_file_full_name('mod_forum', $filename);
 
     //Is the cache out of date?
@@ -151,7 +157,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
     $now = round(time(), -2);
     $params = array($cm->instance);
 
-    $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $modcontext = context_module::instance($cm->id);
 
     if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
@@ -205,7 +211,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
  * @return string the SQL query to be used to get the Post details from the forum table of the database
  */
 function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
-    $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $modcontext = context_module::instance($cm->id);
 
     //get group enforcement SQL
     $groupmode    = groups_get_activity_groupmode($cm);
@@ -290,8 +296,10 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
  *
  * @Todo MDL-31129 implement post attachment handling
  */
-function forum_rss_feed_contents($forum, $sql, $context) {
-    global $CFG, $DB;
+
+function forum_rss_feed_contents($forum, $sql) {
+    global $CFG, $DB, $USER;
+
 
     $status = true;
 
@@ -305,23 +313,45 @@ function forum_rss_feed_contents($forum, $sql, $context) {
         $isdiscussion = false;
     }
 
+    if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
+        print_error('invalidcoursemodule');
+    }
+    $context = context_module::instance($cm->id);
+
     $formatoptions = new stdClass();
     $items = array();
     foreach ($recs as $rec) {
             $item = new stdClass();
             $user = new stdClass();
-            if ($isdiscussion && !empty($rec->discussionname)) {
-                $item->title = format_string($rec->discussionname);
-            } else if (!empty($rec->postsubject)) {
-                $item->title = format_string($rec->postsubject);
+
+            if ($isdiscussion && !forum_user_can_see_discussion($forum, $rec->discussionid, $context)) {
+                // This is a discussion which the user has no permission to view
+                $item->title = get_string('forumsubjecthidden', 'forum');
+                $message = get_string('forumbodyhidden', 'forum');
+                $item->author = get_string('forumauthorhidden', 'forum');
+            } else if (!$isdiscussion && !forum_user_can_see_post($forum, $rec->discussionid, $rec->postid, $USER, $cm)) {
+                // This is a post which the user has no permission to view
+                $item->title = get_string('forumsubjecthidden', 'forum');
+                $message = get_string('forumbodyhidden', 'forum');
+                $item->author = get_string('forumauthorhidden', 'forum');
             } else {
-                //we should have an item title by now but if we dont somehow then substitute something somewhat meaningful
-                $item->title = format_string($forum->name.' '.userdate($rec->postcreated,get_string('strftimedatetimeshort', 'langconfig')));
+                // The user must have permission to view
+                if ($isdiscussion && !empty($rec->discussionname)) {
+                    $item->title = format_string($rec->discussionname);
+                } else if (!empty($rec->postsubject)) {
+                    $item->title = format_string($rec->postsubject);
+                } else {
+                    //we should have an item title by now but if we dont somehow then substitute something somewhat meaningful
+                    $item->title = format_string($forum->name.' '.userdate($rec->postcreated,get_string('strftimedatetimeshort', 'langconfig')));
+                }
+                $user->firstname = $rec->userfirstname;
+                $user->lastname = $rec->userlastname;
+                $item->author = fullname($user);
+                $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
+                        'mod_forum', 'post', $rec->postid);
+                $formatoptions->trusted = $rec->posttrust;
             }
-            $user->firstname = $rec->userfirstname;
-            $user->lastname = $rec->userlastname;
-            $item->author = fullname($user);
-            $item->pubdate = $rec->postcreated;
+
             if ($isdiscussion) {
                 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid;
             } else {
@@ -329,8 +359,6 @@ function forum_rss_feed_contents($forum, $sql, $context) {
             }
 
             $formatoptions->trusted = $rec->posttrust;
-            $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
-                'mod_forum', 'post', $rec->postid);
             $item->description = format_text($message, $rec->postformat, $formatoptions, $forum->course);
 
             //TODO: MDL-31129 implement post attachment handling
@@ -342,6 +370,7 @@ function forum_rss_feed_contents($forum, $sql, $context) {
                     $item->attachments = array();
                 }
             }*/
+            $item->pubdate = $rec->postcreated;
 
             $items[] = $item;
         }
index 9066c8e..58a5fd5 100644 (file)
@@ -62,7 +62,7 @@ if ($user) {
     if (!has_capability('mod/forum:managesubscriptions', $context)) {
         print_error('nopermissiontosubscribe', 'forum');
     }
-    $user = $DB->get_record('user', array('id' => $user), MUST_EXIST);
+    $user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST);
 } else {
     $user = $USER;
 }
index fa45821..c1bec7f 100644 (file)
@@ -212,10 +212,13 @@ M.mod_quiz.secure_window = {
             window.location = 'about:blank';
         }
         Y.delegate('contextmenu', M.mod_quiz.secure_window.prevent, document, '*');
-        Y.delegate('mousedown', M.mod_quiz.secure_window.prevent_mouse, document, '*');
-        Y.delegate('mouseup', M.mod_quiz.secure_window.prevent_mouse, document, '*');
-        Y.delegate('dragstart', M.mod_quiz.secure_window.prevent, document, '*');
+        Y.delegate('mousedown',   M.mod_quiz.secure_window.prevent_mouse, document, '*');
+        Y.delegate('mouseup',     M.mod_quiz.secure_window.prevent_mouse, document, '*');
+        Y.delegate('dragstart',   M.mod_quiz.secure_window.prevent, document, '*');
         Y.delegate('selectstart', M.mod_quiz.secure_window.prevent, document, '*');
+        Y.delegate('cut',         M.mod_quiz.secure_window.prevent, document, '*');
+        Y.delegate('copy',        M.mod_quiz.secure_window.prevent, document, '*');
+        Y.delegate('paste',       M.mod_quiz.secure_window.prevent, document, '*');
         M.mod_quiz.secure_window.clear_status;
         Y.on('beforeprint', function() {
             Y.one(document.body).setStyle('display', 'none');
index 179c507..d5f2081 100644 (file)
@@ -500,7 +500,7 @@ function mod_resource_dndupload_handle($uploadinfo) {
     $data = new stdClass();
     $data->course = $uploadinfo->course->id;
     $data->name = $uploadinfo->displayname;
-    $data->intro = '<p>'.$uploadinfo->displayname.'</p>';
+    $data->intro = '';
     $data->introformat = FORMAT_HTML;
     $data->coursemodule = $uploadinfo->coursemodule;
     $data->files = $uploadinfo->draftitemid;
@@ -512,6 +512,8 @@ function mod_resource_dndupload_handle($uploadinfo) {
     $data->popupwidth = $config->popupwidth;
     $data->printheading = $config->printheading;
     $data->printintro = $config->printintro;
+    $data->showsize = $config->showsize;
+    $data->showtype = $config->showtype;
 
     return resource_add_instance($data, null);
 }
index c926c12..43369fe 100644 (file)
@@ -121,7 +121,7 @@ function scorm_parse_aicc($scorm) {
 
     $fs = get_file_storage();
 
-    $files = $fs->get_area_files($context->id, 'mod_scorm', 'content', 0, '', false);
+    $files = $fs->get_area_files($context->id, 'mod_scorm', 'content', 0, 'sortorder, itemid, filepath, filename', false);
 
     $version = 'AICC';
     $ids = array();
diff --git a/pix/u/f3.png b/pix/u/f3.png
new file mode 100644 (file)
index 0000000..27e3516
Binary files /dev/null and b/pix/u/f3.png differ
index 719317d..4a1a127 100644 (file)
@@ -122,6 +122,11 @@ class question_import_form extends moodleform {
             return $errors;
         }
 
+        if (empty($data['format'])) {
+            $errors['format'] = get_string('required');
+            return $errors;
+        }
+
         $formatfile = 'format/' . $data['format'] . '/format.php';
         if (!is_readable($formatfile)) {
             throw new moodle_exception('formatnotfound', 'question', '', $data['format']);
index 7a80f4d..6d60323 100644 (file)
@@ -40,5 +40,85 @@ function xmldb_qtype_essay_upgrade($oldversion) {
     // Moodle v2.2.0 release upgrade line
     // Put any upgrade step following this
 
+    if ($oldversion < 2011102701) {
+        // In Moodle <= 2.0 essay had both question.generalfeedback and question_answers.feedback.
+        // This was silly, and in Moodel >= 2.1 only question.generalfeedback. To avoid
+        // dataloss, we concatenate question_answers.feedback onto the end of question.generalfeedback.
+        $toupdate = $DB->get_recordset_sql("
+                SELECT q.id,
+                       q.generalfeedback,
+                       q.generalfeedbackformat,
+                       qa.feedback,
+                       qa.feedbackformat
+
+                  FROM {question} q
+                  JOIN {question_answers} qa ON qa.question = q.id
+
+                 WHERE q.qtype = 'essay'
+                   AND " . $DB->sql_isnotempty('question_answers', 'feedback', false, true));
+
+        foreach ($toupdate as $data) {
+            upgrade_set_timeout(60);
+            if ($data->generalfeedbackformat == $data->feedbackformat) {
+                $DB->set_field('question', 'generalfeedback',
+                        $data->generalfeedback . $data->feedback,
+                        array('id' => $data->id));
+
+            } else {
+                $newdata = new stdClass();
+                $newdata->id = $data->id;
+                $newdata->generalfeedback =
+                        qtype_essay_convert_to_html($data->generalfeedback, $data->generalfeedbackformat) .
+                        qtype_essay_convert_to_html($data->feedback,        $data->feedbackformat);
+                $newdata->generalfeedbackformat = FORMAT_HTML;
+                $DB->update_record('question', $newdata);
+            }
+        }
+
+        $toupdate->close();
+
+        // Essay savepoint reached.
+        upgrade_plugin_savepoint(true, 2011102701, 'qtype', 'essay');
+    }
+
+    if ($oldversion < 2011102702) {
+        // Then we delete the old question_answers rows for essay questions.
+        $DB->delete_records_select('question_answers',
+                "question IN (SELECT id FROM {question} WHERE qtype = 'essay')");
+
+        // Essay savepoint reached.
+        upgrade_plugin_savepoint(true, 2011102702, 'qtype', 'essay');
+    }
+
     return true;
 }
+
+/**
+ * Convert some content to HTML.
+ * @param string $text the content to convert to HTML
+ * @param int $oldformat One of the FORMAT_... constants.
+ */
+function qtype_essay_convert_to_html($text, $oldformat) {
+    switch ($oldformat) {
+        // Similar to format_text.
+
+        case FORMAT_PLAIN:
+            $text = s($text);
+            $text = str_replace(' ', '&nbsp; ', $text);
+            $text = nl2br($text);
+            return $text;
+
+        case FORMAT_MARKDOWN:
+            return markdown_to_html($text);
+
+        case FORMAT_MOODLE:
+            return text_to_html($text);
+
+        case FORMAT_HTML:
+            return $text;
+
+        default:
+            throw new coding_exception(
+                    'Unexpected text format when upgrading essay questions.');
+    }
+}
index afb52f8..288954f 100644 (file)
@@ -113,7 +113,9 @@ class qtype_essay_renderer extends qtype_renderer {
         $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
                 'attachments', $options->context->id);
 
-        return form_filemanager_render($pickeroptions) . html_writer::empty_tag(
+        $fm = new form_filemanager($pickeroptions);
+        $filesrenderer = $this->page->get_renderer('core', 'files');
+        return $filesrenderer->render($fm). html_writer::empty_tag(
                 'input', array('type' => 'hidden', 'name' => $qa->get_qt_field_name('attachments'),
                 'value' => $pickeroptions->itemid));
     }
index fec2889..0b44d4b 100644 (file)
@@ -26,7 +26,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'qtype_essay';
-$plugin->version   = 2011102700;
+$plugin->version   = 2011102702;
 
 $plugin->requires  = 2011102700;
 
index c0a94fe..a672f2a 100644 (file)
@@ -42,7 +42,7 @@ if (empty($host_course)) {
 
 $group       = optional_param('group', 0, PARAM_INT); // Group to display
 $user        = optional_param('user', 0, PARAM_INT); // User to display
-$date        = optional_param('date', 0, PARAM_FILE); // Date to display - number or some string
+$date        = optional_param('date', 0, PARAM_INT); // Date to display
 $modname     = optional_param('modname', '', PARAM_PLUGIN); // course_module->id
 $modid       = optional_param('modid', 0, PARAM_FILE); // number or 'site_errors'
 $modaction   = optional_param('modaction', '', PARAM_PATH); // an action as recorded in the logs
index 2f1e8fe..16353b8 100644 (file)
@@ -186,7 +186,7 @@ class repository_coursefiles extends repository {
     }
 
     public function supported_returntypes() {
-        return (FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE);
+        return (FILE_INTERNAL | FILE_REFERENCE);
     }
 
     public static function get_type_option_names() {
index f1b5859..b0e7006 100644 (file)
@@ -34,31 +34,31 @@ $THEME->layouts = array(
     'standard' => array(
         'file' => 'default.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // Main course page
     'course' => array(
         'file' => 'default.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'coursecategory' => array(
         'file' => 'default.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // part of course, typical for modules - default page layout if $cm specified in require_login()
     'incourse' => array(
         'file' => 'default.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // The site home page.
     'frontpage' => array(
         'file' => 'default.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     // Server administration scripts.
index 8b6746d..6fcbaee 100644 (file)
@@ -32,33 +32,33 @@ $THEME->layouts = array(
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     // Course page
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     // Course page
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     'frontpage' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     'admin' => array(
@@ -70,13 +70,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu' => true)
     ),
     'login' => array(
index 751523c..4de9416 100644 (file)
@@ -38,8 +38,8 @@ $THEME->name = 'arialist';
 
 
 $THEME->parents = array(
-       'canvas',
-       'base',
+    'canvas',
+    'base',
 );
 
 /////////////////////////////////////////////////////
@@ -55,8 +55,8 @@ $THEME->parents = array(
 
 $THEME->sheets = array(
     'pagelayout',
-       'core',
-       'settings',
+    'core',
+    'settings',
 );
 
 ////////////////////////////////////////////////////
@@ -65,12 +65,12 @@ $THEME->sheets = array(
 ////////////////////////////////////////////////////
 
 $THEME->parents_exclude_sheets = array(
-               'base'=>array(
-                       'pagelayout',
-               ),
-               'canvas'=>array(
-                       'pagelayout',
-               ),
+        'base'=>array(
+            'pagelayout',
+        ),
+        'canvas'=>array(
+            'pagelayout',
+        ),
 );
 
 
@@ -156,7 +156,7 @@ $THEME->layouts = array(
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true),
     ),
     'embedded' => array(
-       'theme' => 'canvas',
+        'theme' => 'canvas',
         'file' => 'embedded.php',
         'regions' => array(),
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true),
@@ -209,14 +209,14 @@ $THEME->csspostprocess = 'arialist_process_css';
 // As above but will be included in the page footer.
 ////////////////////////////////////////////////////
 
-$THEME->larrow = '&lang;';
+$THEME->larrow  = '&lang;';
 
 ////////////////////////////////////////////////////
 // Overrides the left arrow image used throughout
 // Moodle
 ////////////////////////////////////////////////////
 
-$THEME->rarrow = '&rang;';
+$THEME->rarrow  = '&rang;';
 
 ////////////////////////////////////////////////////
 // Overrides the right arrow image used throughout Moodle
index 978396c..7c9aa2d 100644 (file)
@@ -64,31 +64,31 @@ $THEME->layouts = array(
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // Main course page
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // part of course, typical for modules - default page layout if $cm specified in require_login()
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // The site home page.
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // Server administration scripts.
     'admin' => array(
@@ -100,14 +100,14 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     // My public page
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index 7aff519..8c91ce3 100644 (file)
@@ -69,32 +69,32 @@ $THEME->layouts = array(
     'base' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -104,13 +104,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
diff --git a/theme/boxxie/style/boilerplate.css b/theme/boxxie/style/boilerplate.css
deleted file mode 100644 (file)
index 3bf99fc..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/* -------------------------------------------------------------- 
-  
-   Boilerplate reset.css
-   * Resets default browser CSS.
-   
--------------------------------------------------------------- */
-
-html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
-body { line-height: 1.5; background: #fff; margin: 0; }
-table { border-collapse: collapse; border-spacing: 0; }
-caption, th, td { text-align: left; font-weight:400; }
-blockquote:before, blockquote:after, q:before, q:after { content: ""; }
-blockquote, q { quotes: "" ""; }
-a img { border: none; }
-input,textarea { margin: 0; }
-
-/* Removes Firefox imposed outline */
-a { outline: none; }
-
-/* Clearing floats without extra markup  */
-.wrapper { display: inline-block; }
-.wrapper:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
-* html .wrapper { height: 1%; }
-.wrapper { display: block; }
-
-/* -------------------------------------------------------------- 
-   
-   Boilerplate typography.css
-   * Sets up some sensible default typography.
-  
--------------------------------------------------------------- */
-
-/* This is where you set your desired font size. The line-heights 
-   and vertical margins are automatically calculated from this. 
-   The percentage is of 16px (0.75 * 16px = 12px). */
-body { font-size: 85%; }
-
-
-/* Default fonts and colors. */
-body,h1,h2,h3,h4,h5,h6,p,ul,ol,dl,input,textarea { font-family: Helvetica, Arial, sans-serif; }
-
-
-/* Headings
--------------------------------------------------------------- */
-
-h1,h2,h3,h4,h5,h6 { font-weight: bold; }
-
-h1 { font-size: 2.25em; line-height: 1; margin-bottom: 0.5em; }
-h2 { font-size: 1.75em; margin-bottom: 0.5em; }
-h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; }
-h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1em; }
-h5 { font-size: 1em; margin-bottom: 1.5em; }
-h6 { font-size: 1em; }
-
-
-/* Text elements
--------------------------------------------------------------- */
-
-p { margin: 0 0 1em; }
-
-ul, ol { margin: 0 1.5em 1.5em 1.5em; }
-ul { list-style-type: circle; }
-ol { list-style-type: decimal; }
-
-dl { margin: 0 0 1.5em 0; }
-dl dt { font-weight: bold; }
-dl dd { margin-left: 1.5em; }
-
-abbr, acronym { border-bottom: 1px dotted #000; }
-address { margin-top: 1.5em; font-style: italic; }
-del { color: #000; }
-
-a { color: #009; text-decoration: none; }
-a:hover { text-decoration: underline; }
-
-blockquote { margin: 1.5em; }
-strong { font-weight: bold; }
-em, dfn { font-style: italic; }
-dfn { font-weight: bold; }
-pre, code { margin: 1.5em 0; white-space: pre; }
-pre, code, tt { font: 1.2em monospace; line-height: 1.5; } 
-tt { display: block; margin: 1.5em 0; line-height: 1.5; }
-
-
-/* Tables
--------------------------------------------------------------- */
-
-th { border-bottom: 2px solid #ddd; font-weight: bold; }
-th,td { padding: 4px;vertical-align: middle }
-tfoot { font-style: italic; }
-caption { background: #ffc; }
-
-
-/* Some default classes
--------------------------------------------------------------- */
-
-.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; }
-.large { font-size: 1.25em; line-height:1.5em; margin-bottom: 1em; }
-.quiet { color: #999; }
-
-.hide { display: none; }
-.highlight { background: #ffc; }
-
-.top { margin-top: 0; padding-top: 0; }
-.bottom { margin-bottom: 0; padding-bottom: 0; }
-
-/* -------------------------------------------------------------- 
-   
-   Boilerplate forms.css
-   * Sets up some default styling for forms
-   
--------------------------------------------------------------- */
-
-label { font-weight: bold; }
-
-/* Fieldsets */
-fieldset { padding: 1.4em; margin: 0 0 1.5em 0; border: 1px solid #ddd; }
-legend { padding: 0 .4em; font-weight: bold; font-size: 1.2em; }
-
-/* Textareas */
-textarea { margin: 0.5em 0.5em 0 0; }
-textarea { padding: .4em; }
-
-
-/* hForm
--------------------------------------------------------------- */
-form.hform p { margin: 0 0 .5em; }
-form.hform p label { float: left; width: 100px; }
-
-form.hform p input { width: 200px; }
-form.hform p select { width: 200px; }
-
-form.hform p input.button { width: auto; }
-form.hform p input.checkbox { width: auto; }
-form.hform p input.radio { width: auto; }
-
-form.hform p.checkbox { margin-left: 100px; }
-form.hform p.checkbox label { float: none; }
-form.hform p.checkbox input { width: auto; }
-
-
-/* vForm
--------------------------------------------------------------- */
-form.vform p { margin: 0 0 .5em; }
-form.vform p label { display: block; }
-
-form.vform p.checkbox label { display: inline; }
index 1fdd596..925a72b 100644 (file)
@@ -89,32 +89,32 @@ $THEME->layouts = array(
     'base' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -124,13 +124,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index 37888ab..f537c48 100644 (file)
@@ -91,33 +91,33 @@ $THEME->layouts = array(
     'base' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'admin' => array(
@@ -128,13 +128,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index d97fa46..f905a47 100644 (file)
@@ -73,22 +73,13 @@ p {margin:0}
 #page-enrol-instances .select.menujump {margin-left:0.5em;}
 
 /* environmenttable */
-.environmenttable .error {
-    background-color: red;
-}
-
-.environmenttable .warn {
-    background-color: yellow;
-}
-
-.environmenttable .ok {
-    background-color: lime;
-}
+.environmenttable .error {background-color:red;}
+.environmenttable .warn {background-color:yellow;}
+.environmenttable .ok {background-color:lime;}
 
 /* adminsettings */
-#adminsettings .form-overridden {
-    background-color: yellow;
-}
+#adminsettings .form-overridden {background-color:yellow;}
+#adminsettings .form-warning {color:red;}
 
 /* tables */
 .editcourse th,
index 25814bd..ddc32e8 100644 (file)
@@ -76,32 +76,32 @@ $THEME->layouts = array(
     'base' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -111,13 +111,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index 823f923..992a338 100644 (file)
@@ -80,32 +80,32 @@ $THEME->layouts = array(
     'base' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -115,13 +115,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index cc138f2..39e79c4 100644 (file)
@@ -49,35 +49,36 @@ $THEME->editor_sheets = array('editor');
 ////////////////////////////////////////////////////
 
 $THEME->layouts = array(
+    // Most backwards compatible layout without the blocks - this is the layout used by default
     'base' => array(
         'file' => 'general.php',
-        'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'regions' => array(),
     ),
-    'general' => array(
+    // Standard layout with blocks, this is recommended for most pages with general information
+    'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -87,13 +88,13 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
diff --git a/theme/serenity/layout/embedded.php b/theme/serenity/layout/embedded.php
deleted file mode 100644 (file)
index 9d39e02..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php echo $OUTPUT->doctype() ?>
-<html <?php echo $OUTPUT->htmlattributes() ?>>
-<head>
-    <title><?php echo $PAGE->title ?></title>
-    <link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
-    <?php echo $OUTPUT->standard_head_html() ?>
-</head>
-<body id="<?php p($PAGE->bodyid) ?>" class="<?php p($PAGE->bodyclasses) ?>">
-<?php echo $OUTPUT->standard_top_of_body_html() ?>
-
-<div id="page">
-
-<!-- END OF HEADER -->
-
-    <div id="content" class="clearfix">
-        <?php echo $OUTPUT->main_content() ?>
-    </div>
-
-<!-- START OF FOOTER -->
-</div>
-<?php echo $OUTPUT->standard_end_of_body_html() ?>
-</body>
-</html>
\ No newline at end of file
diff --git a/theme/serenity/layout/frontpage.php b/theme/serenity/layout/frontpage.php
deleted file mode 100644 (file)
index 3c21eaa..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-$hassidepre = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-pre', $OUTPUT));
-$hassidepost = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-post', $OUTPUT));
-
-$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
-$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT));
-$custommenu = $OUTPUT->custom_menu();
-$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
-
-$bodyclasses = array();
-if ($showsidepre && !$showsidepost) {
-    $bodyclasses[] = 'side-pre-only';
-} else if ($showsidepost && !$showsidepre) {
-    $bodyclasses[] = 'side-post-only';
-} else if (!$showsidepost && !$showsidepre) {
-    $bodyclasses[] = 'content-only';
-}
-if ($hassidepre || $hassidepost) {
-       $bodyclasses[] = 'background';
-}
-if ($hascustommenu) {
-    $bodyclasses[] = 'has_custom_menu';
-}
-
-echo $OUTPUT->doctype() ?>
-<html <?php echo $OUTPUT->htmlattributes() ?>>
-<head>
-    <title><?php echo $PAGE->title ?></title>
-    <link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
-    <meta name="description" content="<?php p(strip_tags(format_text($SITE->summary, FORMAT_HTML))) ?>" />
-    <?php echo $OUTPUT->standard_head_html() ?>
-</head>
-<body id="<?php p($PAGE->bodyid) ?>" class="<?php p($PAGE->bodyclasses.' '.join(' ', $bodyclasses)) ?>">
-<?php echo $OUTPUT->standard_top_of_body_html() ?>
-
-<div id="page">
-       <div id="wrapper" class="clearfix">
-
-<!-- START OF HEADER -->
-
-    <div id="page-header" class="clearfix">
-               <div id="page-header-wrapper">
-               <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
-           <div class="headermenu">
-                       <?php
-                           echo $OUTPUT->login_info();
-                       echo $OUTPUT->lang_menu();
-                           echo $PAGE->headingmenu;
-                       ?>
-               </div>
-           </div>
-    </div>
-<?php if ($hascustommenu) { ?>
-       <div id="custommenu"><?php echo $custommenu; ?></div>
-<?php } ?>
-<!-- END OF HEADER -->
-
-<!-- START OF CONTENT -->
-
-<div id="page-content-wrapper">
-    <div id="page-content">
-        <div id="region-main-box">
-            <div id="region-post-box">
-
-                <div id="region-main-wrap">
-                    <div id="region-main">
-                        <div class="region-content">
-                            <?php echo $OUTPUT->main_content() ?>
-                        </div>
-                    </div>
-                </div>
-
-                <?php if ($hassidepre) { ?>
-                <div id="region-pre" class="block-region">
-                    <div class="region-content">
-                        <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
-                    </div>
-                </div>
-                <?php } ?>
-
-                <?php if ($hassidepost) { ?>
-                <div id="region-post" class="block-region">
-                    <div class="region-content">
-                        <?php echo $OUTPUT->blocks_for_region('side-post') ?>
-                    </div>
-                </div>
-                <?php } ?>
-
-            </div>
-        </div>
-    </div>
-</div>
-
-<!-- END OF CONTENT -->
-
-       </div>
-
-<!-- START OF FOOTER -->
-
-    <div id="page-footer">
-        <p class="helplink">
-        <?php echo page_doc_link(get_string('moodledocslink')) ?>
-        </p>
-
-        <?php
-        echo $OUTPUT->login_info();
-        echo $OUTPUT->home_link();
-        echo $OUTPUT->standard_footer_html();
-        ?>
-    </div>
-
-<!-- END OF FOOTER -->
-</div>
-<?php echo $OUTPUT->standard_end_of_body_html() ?>
-</body>
-</html>
diff --git a/theme/serenity/layout/general.php b/theme/serenity/layout/general.php
deleted file mode 100644 (file)
index 3c454f3..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-
-$hasheading = ($PAGE->heading);
-$hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
-$hasfooter = (empty($PAGE->layout_options['nofooter']));
-$hassidepre = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-pre', $OUTPUT));
-$hassidepost = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-post', $OUTPUT));
-
-$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
-$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT));
-$custommenu = $OUTPUT->custom_menu();
-$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
-
-$bodyclasses = array();
-if ($showsidepre && !$showsidepost) {
-    $bodyclasses[] = 'side-pre-only';
-} else if ($showsidepost && !$showsidepre) {
-    $bodyclasses[] = 'side-post-only';
-} else if (!$showsidepost && !$showsidepre) {
-    $bodyclasses[] = 'content-only';
-}
-if ($hascustommenu) {
-    $bodyclasses[] = 'has_custom_menu';
-}
-echo $OUTPUT->doctype() ?>
-<html <?php echo $OUTPUT->htmlattributes() ?>>
-<head>
-    <title><?php echo $PAGE->title ?></title>
-    <link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
-    <?php echo $OUTPUT->standard_head_html() ?>
-</head>
-<body id="<?php p($PAGE->bodyid) ?>" class="<?php p($PAGE->bodyclasses.' '.join(' ', $bodyclasses)) ?>">
-<?php echo $OUTPUT->standard_top_of_body_html() ?>
-
-<div id="page">
-       <div id="wrapper" class="clearfix">
-<?php if ($hasheading || $hasnavbar) { ?>
-
-    <div id="page-header" class="clearfix">
-
-               <?php if ($hasheading) { ?>
-                       <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
-                   <div class="headermenu">
-                               <?php
-                               echo $OUTPUT->login_info();
-                                       if (!empty($PAGE->layout_options['langmenu'])) {
-                                       echo $OUTPUT->lang_menu();
-                                   }
-                               echo $PAGE->headingmenu
-                               ?>
-                       </div>
-               <?php } ?>
-
-    </div>
-
-       <?php if ($hascustommenu) { ?>
-       <div id="custommenu"><?php echo $custommenu; ?></div>
-       <?php } ?>
-
-    <?php if ($hasnavbar) { ?>
-           <div class="navbar clearfix">
-           <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
-            <div class="navbutton"> <?php echo $PAGE->button; ?></div>
-      </div>
-    <?php } ?>
-
-<?php } ?>
-
-<!-- END OF HEADER -->
-
-<div id="page-content-wrapper">
-    <div id="page-content">
-        <div id="region-main-box">
-            <div id="region-post-box">
-
-                <div id="region-main-wrap">
-                    <div id="region-main">
-                        <div class="region-content">
-                            <?php echo $OUTPUT->main_content() ?>
-                        </div>
-                    </div>
-                </div>
-
-                <?php if ($hassidepre) { ?>
-                <div id="region-pre" class="block-region">
-                    <div class="region-content">
-                        <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
-                    </div>
-                </div>
-                <?php } ?>
-
-                <?php if ($hassidepost) { ?>
-                <div id="region-post" class="block-region">
-                    <div class="region-content">
-                        <?php echo $OUTPUT->blocks_for_region('side-post') ?>
-                    </div>
-                </div>
-                <?php } ?>
-
-            </div>
-        </div>
-    </div>
-</div>
-
-    </div>
-
-<!-- START OF FOOTER -->
-    <?php if ($hasfooter) { ?>
-    <div id="page-footer" class="clearfix">
-        <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
-        <?php
-        echo $OUTPUT->login_info();
-        echo $OUTPUT->home_link();
-        echo $OUTPUT->standard_footer_html();
-        ?>
-    </div>
-    <?php } ?>
-</div>
-<?php echo $OUTPUT->standard_end_of_body_html() ?>
-</body>
-</html>
index 41ba3bc..f9bca28 100644 (file)
@@ -53,29 +53,29 @@ $THEME->layouts = array(
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     // Course page
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     // Course page
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'frontpage' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'admin' => array(
         'file' => 'general.php',
@@ -85,12 +85,12 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post'
+        'defaultregion' => 'side-pre'
     ),
     'login' => array(
         'file' => 'general.php',
index 30d4319..0d81929 100644 (file)
@@ -54,31 +54,31 @@ $THEME->layouts = array(
     'standard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // Main course page
     'course' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     'coursecategory' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // part of course, typical for modules - default page layout if $cm specified in require_login()
     'incourse' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // The site home page.
     'frontpage' => array(
         'file' => 'frontpage.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     // Server administration scripts.
     'admin' => array(
@@ -90,14 +90,14 @@ $THEME->layouts = array(
     'mydashboard' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
     ),
     // My public page
     'mypublic' => array(
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
-        'defaultregion' => 'side-post',
+        'defaultregion' => 'side-pre',
     ),
     'login' => array(
         'file' => 'general.php',
index 4e9b5f1..4d18a33 100644 (file)
@@ -2,6 +2,11 @@ This files describes API changes in /theme/* themes,
 information provided here is intended especially for theme designer.
 
 
+=== 2.3 ===
+
+optional changes:
+* add new u/f3.png image when theme contains customised f1 and f2 default user images
+
 === 2.2 ===
 
 required changes:
index 5da4d8d..54f7962 100644 (file)
@@ -33,10 +33,8 @@ if (isguestuser()) {
 }
 
 $returnurl = optional_param('returnurl', '', PARAM_URL);
-$returnbutton = true;
 
 if (empty($returnurl)) {
-    $returnbutton = false;
     $returnurl = new moodle_url('/user/files.php');
 }
 
@@ -58,7 +56,7 @@ $data->returnurl = $returnurl;
 $options = array('subdirs'=>1, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>-1, 'accepted_types'=>'*');
 file_prepare_standard_filemanager($data, 'files', $options, $context, 'user', 'private', 0);
 
-$mform = new user_files_form(null, array('data'=>$data, 'options'=>$options, 'cancelbutton'=>$returnbutton));
+$mform = new user_files_form(null, array('data'=>$data, 'options'=>$options));
 
 if ($mform->is_cancelled()) {
     redirect($returnurl);
index 5351056..55451da 100644 (file)
@@ -33,12 +33,11 @@ class user_files_form extends moodleform {
 
         $data           = $this->_customdata['data'];
         $options        = $this->_customdata['options'];
-        $cancelbutton   = isset($this->_customdata['cancelbutton']) ? $this->_customdata['cancelbutton'] : true;
 
         $mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options);
         $mform->addElement('hidden', 'returnurl', $data->returnurl);
 
-        $this->add_action_buttons($cancelbutton, get_string('savechanges'));
+        $this->add_action_buttons(true, get_string('savechanges'));
 
         $this->set_data($data);
     }
index f9992a1..dd7484c 100644 (file)
@@ -30,7 +30,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012052500.00;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012052900.05;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes