Merge branch 'MDL-25883-master' of https://github.com/mackensen/moodle
authorSam Hemelryk <sam@moodle.com>
Mon, 13 Feb 2012 01:35:19 +0000 (14:35 +1300)
committerSam Hemelryk <sam@moodle.com>
Mon, 13 Feb 2012 01:35:19 +0000 (14:35 +1300)
255 files changed:
admin/blocks.php
admin/cli/install.php
admin/cli/mysql_engine.php
admin/enrol.php
admin/index.php
admin/modules.php
admin/qbehaviours.php
admin/qtypes.php
admin/renderer.php
admin/roles/lib.php
admin/settings/subsystems.php
admin/tool/innodb/index.php
admin/tool/timezoneimport/index.php
admin/tool/unittest/ex_reporter.php
admin/user.php
backup/converter/moodle1/handlerlib.php
backup/moodle2/restore_stepslib.php
backup/restorelib.php
backup/util/helper/restore_decode_processor.class.php
backup/util/helper/restore_decode_rule.class.php
backup/util/loggers/file_logger.class.php
blocks/admin_bookmarks/block_admin_bookmarks.php
blocks/comments/block_comments.php
blocks/glossary_random/block_glossary_random.php
blocks/html/edit_form.php
blocks/login/block_login.php
blocks/mentees/block_mentees.php
blocks/navigation/block_navigation.php
blocks/navigation/renderer.php
blocks/private_files/block_private_files.php
blocks/settings/block_settings.php
blocks/tags/block_tags.php
blog/index.php
blog/lib.php
calendar/lib.php
config-dist.php
course/edit_form.php
course/format/topics/format.php
course/format/weeks/format.php
course/index.php
course/lib.php
course/modedit.php
enrol/externallib.php
enrol/imsenterprise/lib.php
grade/grading/lib.php
grade/grading/pick.php
grade/report/grader/styles.css
grade/report/overview/lib.php
group/autogroup.php
group/externallib.php
index.php
install/lang/az/moodle.php
install/lang/de/install.php
install/lang/de_du/install.php
install/lang/de_kids/langconfig.php
install/lang/he/install.php
install/lang/tt/langconfig.php [new file with mode: 0644]
lang/en/admin.php
lang/en/currencies.php
lang/en/error.php
lang/en/moodle.php
lang/en/question.php
lib/accesslib.php
lib/adminlib.php
lib/ajax/ajaxlib.php
lib/ajax/section_classes.js
lib/blocklib.php
lib/csslib.php [new file with mode: 0644]
lib/datalib.php
lib/db/install.xml
lib/db/log.php
lib/db/upgrade.php
lib/enrollib.php
lib/filelib.php
lib/filterlib.php
lib/form/dndupload.js
lib/form/filemanager.js
lib/form/filemanager.php
lib/form/password.php
lib/form/passwordunmask.php
lib/formslib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/pear/HTML/QuickForm/checkbox.php
lib/plagiarismlib.php
lib/portfoliolib.php
lib/simpletest/fixtures/gradetest.php
lib/simpletest/testcompletionlib.php
lib/simpletest/testcsslib.php [new file with mode: 0644]
lib/simpletest/testfilterconfig.php
lib/simpletest/testmoodlelib.php
lib/simpletest/testtextlib.php
lib/simpletest/testweblib.php
lib/textlib.class.php
lib/timezone.txt
lib/upgradelib.php
lib/weblib.php
login/index.php
message/lib.php
message/output/email/lang/en/message_email.php
message/output/email/message_output_email.php
mod/assignment/lib.php
mod/chat/lib.php
mod/choice/report.php
mod/feedback/lib.php
mod/feedback/show_entries.php
mod/forum/discuss.php
mod/forum/index.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/subscribe.php
mod/forum/unsubscribeall.php
mod/glossary/lib.php
mod/lesson/format.php
mod/lesson/lib.php
mod/lesson/pagetypes/essay.php
mod/quiz/attempt.php
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/edit.php
mod/quiz/editlib.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/module.js
mod/quiz/override_form.php
mod/quiz/renderer.php
mod/quiz/report/overview/overview_table.php
mod/quiz/report/reportlib.php
mod/quiz/report/statistics/version.php
mod/quiz/simpletest/testquizobj.php
mod/quiz/startattempt.php
mod/quiz/styles.css
mod/quiz/version.php
mod/quiz/view.php
mod/resource/lang/en/resource.php
mod/resource/lib.php
mod/resource/locallib.php
mod/resource/mod_form.php
mod/resource/settings.php
mod/resource/styles.css
mod/resource/view.php
mod/scorm/datamodels/scorm_13.js.php
mod/scorm/lib.php
mod/scorm/mod_form.php
mod/survey/lib.php
mod/url/lib.php
mod/url/locallib.php
mod/url/view.php
mod/workshop/form/accumulative/backup/moodle1/lib.php
mod/workshop/form/comments/backup/moodle1/lib.php
mod/workshop/form/numerrors/backup/moodle1/lib.php
mod/workshop/form/rubric/backup/moodle1/lib.php
question/behaviour/adaptive/simpletest/testwalkthrough.php
question/behaviour/deferredcbm/renderer.php
question/behaviour/immediatecbm/simpletest/testwalkthrough.php
question/behaviour/interactive/simpletest/testwalkthrough.php
question/editlib.php
question/engine/datalib.php
question/engine/questionattempt.php
question/engine/questionattemptstep.php
question/engine/questionusage.php
question/engine/renderer.php
question/engine/simpletest/helpers.php
question/engine/simpletest/testquestionattempt.php
question/engine/simpletest/testquestionusagebyactivity.php
question/engine/simpletest/testunitofwork.php [new file with mode: 0644]
question/engine/upgrade/upgradelib.php
question/format/blackboard_six/format.php
question/preview.php
question/previewlib.php
question/type/calculated/datasetdefinitions_form.php
question/type/calculated/datasetitems_form.php
question/type/calculated/lang/en/qtype_calculated.php
question/type/calculated/questiontype.php
question/type/calculated/simpletest/helper.php
question/type/calculated/simpletest/testquestion.php
question/type/calculated/simpletest/testquestiontype.php [new file with mode: 0644]
question/type/calculatedsimple/edit_calculatedsimple_form.php
question/type/calculatedsimple/questiontype.php
question/type/edit_question_form.php
question/type/match/backup/moodle1/lib.php
question/type/missingtype/questiontype.php
question/type/numerical/question.php
question/type/numerical/questiontype.php
question/type/numerical/simpletest/helper.php
question/type/numerical/simpletest/testquestion.php
question/type/numerical/simpletest/testquestiontype.php
question/type/questionbase.php
question/type/questiontypebase.php
question/type/random/edit_random_form.php
question/type/random/questiontype.php
question/type/randomsamatch/lang/en/qtype_randomsamatch.php
question/type/randomsamatch/questiontype.php
question/type/shortanswer/questiontype.php
question/type/shortanswer/simpletest/helper.php [new file with mode: 0644]
question/type/shortanswer/simpletest/testquestion.php
question/type/shortanswer/simpletest/testquestiontype.php
question/type/truefalse/simpletest/testquestiontype.php
question/type/upgrade.txt
report/participation/index.php
report/questioninstances/index.php
repository/draftfiles_ajax.php
repository/filepicker.js
repository/lib.php
tag/index.php
tag/user.php
theme/afterburner/config.php
theme/afterburner/layout/default.php
theme/afterburner/style/afterburner_dock.css
theme/afterburner/style/afterburner_settings.css [new file with mode: 0644]
theme/afterburner/style/afterburner_styles.css
theme/arialist/config.php
theme/arialist/style/pagelayout.css
theme/base/style/dock.css
theme/canvas/style/core.css
theme/formal_white/config.php
theme/formal_white/lang/en/theme_formal_white.php
theme/formal_white/layout/embedded.php
theme/formal_white/layout/frontpage.php
theme/formal_white/layout/general.php
theme/formal_white/layout/report.php [new file with mode: 0644]
theme/formal_white/lib.php
theme/formal_white/settings.php
theme/formal_white/style/formal_white.css
theme/formal_white/style/frame.css
theme/formal_white/style/quiz.css
theme/formal_white/version.php
theme/magazine/config.php
theme/magazine/style/colors.css
theme/magazine/style/core.css
theme/mymobile/style/core.css
theme/sky_high/config.php
theme/sky_high/layout/frontpage.php
theme/sky_high/layout/general.php
theme/sky_high/style/admin.css [new file with mode: 0644]
theme/sky_high/style/core.css
theme/sky_high/style/report.css
theme/sky_high/style/settings.css [new file with mode: 0644]
theme/splash/style/sl.css
theme/styles.php
theme/styles_debug.php
theme/yui_combo.php
user/externallib.php
user/index.php
user/selector/lib.php
version.php
webservice/rest/locallib.php
webservice/simpletest/testwebservice.php

index ca9633d..b1c53df 100644 (file)
         } else {
             uninstall_plugin('block', $block->name);
 
+            $a = new stdClass();
             $a->block = $strblockname;
             $a->directory = $CFG->dirroot.'/blocks/'.$block->name;
             notice(get_string('blockdeletefiles', '', $a), 'blocks.php');
index facda35..b812aaf 100644 (file)
@@ -337,12 +337,7 @@ $CFG->httpswwwroot  = $CFG->wwwroot;
 
 
 //We need dataroot before lang download
-$dataroot = clean_param($options['dataroot'], PARAM_PATH);
-if ($dataroot !== $options['dataroot']) {
-    $a = (object)array('option' => 'dataroot', 'value' => $options['dataroot']);
-    cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
-}
-$CFG->dataroot = $dataroot;
+$CFG->dataroot = $options['dataroot'];
 if ($interactive) {
     cli_separator();
     $i=0;
index 53c4b84..c6d7c6e 100644 (file)
@@ -76,7 +76,7 @@ if (!empty($options['engine'])) {
         }
         echo str_pad($table->name, 40). " - ";
 
-        $DB->change_database_structure("ALTER TABLE {$table->name} TYPE = $engine");
+        $DB->change_database_structure("ALTER TABLE {$table->name} ENGINE = $engine");
         echo "DONE\n";
         $converted++;
     }
index 2d53db9..ab14a23 100644 (file)
@@ -31,6 +31,7 @@ $enrol   = required_param('enrol', PARAM_PLUGIN);
 $confirm = optional_param('confirm', 0, PARAM_BOOL);
 
 $PAGE->set_url('/admin/enrol.php');
+$PAGE->set_context(context_system::instance());
 
 require_login();
 require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
@@ -112,6 +113,7 @@ switch ($action) {
             uninstall_plugin('enrol', $enrol);
             $syscontext->mark_dirty(); // resets all enrol caches
 
+            $a = new stdClass();
             $a->plugin = $strplugin;
             $a->directory = "$CFG->dirroot/enrol/$enrol";
             echo $OUTPUT->notification(get_string('uninstalldeletefiles', 'enrol', $a), 'notifysuccess');
index b4bcff7..7e8a6fc 100644 (file)
@@ -190,6 +190,15 @@ if ($version > $CFG->version) {  // upgrade
     $PAGE->set_pagelayout('maintenance');
     $PAGE->set_popup_notification_allowed(false);
 
+    if (upgrade_stale_php_files_present()) {
+        $PAGE->set_title($stradministration);
+        $PAGE->set_cacheable(false);
+
+        $output = $PAGE->get_renderer('core', 'admin');
+        echo $output->upgrade_stale_php_files_page();
+        die();
+    }
+
     if (empty($confirmupgrade)) {
         $a->oldversion = "$CFG->release ($CFG->version)";
         $a->newversion = "$release ($version)";
index 45f7a57..2290612 100644 (file)
@@ -92,6 +92,7 @@
             }
 
             uninstall_plugin('mod', $delete);
+            $a = new stdClass();
             $a->module = $strmodulename;
             $a->directory = "$CFG->dirroot/mod/$delete";
             echo $OUTPUT->notification(get_string("moduledeletefiles", "", $a), 'notifysuccess');
index 2ae5f08..ce81b13 100644 (file)
@@ -191,6 +191,7 @@ if (($delete = optional_param('delete', '', PARAM_PLUGIN)) && confirm_sesskey())
     // Remove event handlers and dequeue pending events
     events_uninstall('qbehaviour_' . $delete);
 
+    $a = new stdClass();
     $a->behaviour = $behaviourname;
     $a->directory = get_plugin_directory('qbehaviour', $delete);
     echo $OUTPUT->box(get_string('qbehaviourdeletefiles', 'question', $a), 'generalbox', 'notice');
index 79bcf68..cbe828f 100644 (file)
@@ -171,6 +171,7 @@ if (($delete = optional_param('delete', '', PARAM_PLUGIN)) && confirm_sesskey())
     // Remove event handlers and dequeue pending events
     events_uninstall('qtype_' . $delete);
 
+    $a = new stdClass();
     $a->qtype = $qtypename;
     $a->directory = $qtypes[$delete]->plugin_dir();
     echo $OUTPUT->box(get_string('qtypedeletefiles', 'question', $a), 'generalbox', 'notice');
index d823e0e..cecd725 100644 (file)
@@ -57,6 +57,26 @@ class core_admin_renderer extends plugin_renderer_base {
         return $output;
     }
 
+    /**
+     * Display page explaining proper upgrade process,
+     * there can not be any PHP file leftovers...
+     *
+     * @return string HTML to output.
+     */
+    public function upgrade_stale_php_files_page() {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading(get_string('upgradestalefiles', 'admin'));
+        $output .= $this->box_start('generalbox', 'notice');
+        $output .= get_string('upgradestalefilesinfo', 'admin', get_docs_url('Upgrading'));
+        $output .= html_writer::empty_tag('br');
+        $output .= html_writer::tag('div', $this->single_button($this->page->url, get_string('reload'), 'get'), array('class' => 'buttons'));
+        $output .= $this->box_end();
+        $output .= $this->footer();
+
+        return $output;
+    }
+
     /**
      * Display the 'environment check' page that is displayed during install.
      * @param int $maturity
@@ -339,8 +359,10 @@ class core_admin_renderer extends plugin_renderer_base {
             return '';
         }
 
-        return $this->warning(get_string('sitemaintenancewarning2', 'admin',
-                new moodle_url('/admin/settings.php', array('section' => 'maintenancemode'))));
+        $url = new moodle_url('/admin/settings.php', array('section' => 'maintenancemode'));
+        $url = $url->out(); // get_string() does not support objects in params
+
+        return $this->warning(get_string('sitemaintenancewarning2', 'admin', $url));
     }
 
     /**
@@ -597,8 +619,7 @@ class core_admin_renderer extends plugin_renderer_base {
 
             if (is_null($otherplugin)) {
                 $ok = false;
-            }
-            if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
+            } else if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
                 $ok = false;
             }
 
index 68a3c8b..d2dbaba 100644 (file)
@@ -1044,10 +1044,9 @@ class potential_assignees_below_course extends role_assign_user_selector_base {
         $sql   = " FROM {user} u
                   WHERE u.id IN ($enrolsql) $wherecondition
                         AND u.id NOT IN (
-                           SELECT u.id
-                             FROM {role_assignments} r, {user} u
+                           SELECT r.userid
+                             FROM {role_assignments} r
                             WHERE r.contextid = :contextid
-                                  AND u.id = r.userid
                                   AND r.roleid = :roleid)";
         $order = ' ORDER BY lastname ASC, firstname ASC';
 
@@ -1096,10 +1095,9 @@ class potential_assignees_course_and_above extends role_assign_user_selector_bas
         $sql = " FROM {user}
                 WHERE $wherecondition
                       AND id NOT IN (
-                         SELECT u.id
-                           FROM {role_assignments} r, {user} u
+                         SELECT r.userid
+                           FROM {role_assignments} r
                           WHERE r.contextid = :contextid
-                                AND u.id = r.userid
                                 AND r.roleid = :roleid)";
         $order = ' ORDER BY lastname ASC, firstname ASC';
 
index 27e9511..7c70af3 100644 (file)
@@ -20,6 +20,8 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $options = array(DAYSECS=>new lang_string('secondstotime86400'), WEEKSECS=>new lang_string('secondstotime604800'), 2620800=>new lang_string('nummonths', 'moodle', 1), 15724800=>new lang_string('nummonths', 'moodle', 6),0=>new lang_string('never'));
     $optionalsubsystems->add(new admin_setting_configselect('messagingdeletereadnotificationsdelay', new lang_string('messagingdeletereadnotificationsdelay', 'admin'), new lang_string('configmessagingdeletereadnotificationsdelay', 'admin'), 604800, $options));
 
+    $optionalsubsystems->add(new admin_setting_configcheckbox('messagingallowemailoverride', new lang_string('messagingallowemailoverride', 'admin'), new lang_string('configmessagingallowemailoverride','admin'), 0));
+
     $optionalsubsystems->add(new admin_setting_configcheckbox('enablestats', new lang_string('enablestats', 'admin'), new lang_string('configenablestats', 'admin'), 0));
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('enablerssfeeds', new lang_string('enablerssfeeds', 'admin'), new lang_string('configenablerssfeeds', 'admin'), 0));
@@ -43,4 +45,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $checkbox->set_affects_modinfo(true);
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('enableplagiarism', new lang_string('enableplagiarism','plagiarism'), new lang_string('configenableplagiarism','plagiarism'), 0));
+
+    $optionalsubsystems->add(new admin_setting_configcheckbox('enablecssoptimiser', new lang_string('enablecssoptimiser','admin'), new lang_string('enablecssoptimiser_desc','admin'), 0));
 }
index faaea89..bbe4d85 100644 (file)
@@ -50,7 +50,7 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
         $DB->set_debug(true);
         foreach ($tables as $table) {
             $fulltable = $DB->get_prefix().$table;
-            $DB->change_database_structure("ALTER TABLE $fulltable TYPE=INNODB");
+            $DB->change_database_structure("ALTER TABLE $fulltable ENGINE=INNODB");
         }
         $DB->set_debug(false);
     }
index 8319f83..478ca4c 100644 (file)
 /// That's it!
 
     if ($importdone) {
-        $a = null;
+        $a = new stdClass();
         $a->count = count($timezones);
         $a->source  = $importdone;
         echo $OUTPUT->heading(get_string('importtimezonescount', 'tool_timezoneimport', $a), 3);
index 995bebd..5d440f3 100644 (file)
@@ -289,4 +289,9 @@ class ExHtmlReporter extends HtmlReporter {
     function get_string($identifier, $a = NULL) {
         return get_string($identifier, 'tool_unittest', $a);
     }
+
+    function _htmlEntities($message) {
+        // Override subclass message that breaks UTF8.
+        return s($message);
+    }
 }
index 1ccb69f..56e457e 100644 (file)
     $users = get_users_listing($sort, $dir, $page*$perpage, $perpage, '', '', '',
             $extrasql, $params, $context);
     $usercount = get_users(false);
-    $usersearchcount = get_users(false, '', true, null, "", '', '', '', '', '*', $extrasql, $params);
+    $usersearchcount = get_users(false, '', false, null, "", '', '', '', '', '*', $extrasql, $params);
 
     if ($extrasql !== '') {
         echo $OUTPUT->heading("$usersearchcount / $usercount ".get_string('users'));
index a6b7788..f0c9754 100644 (file)
@@ -345,6 +345,8 @@ class moodle1_root_handler extends moodle1_xml_handler {
         // {@see backup_general_helper::backup_is_samesite()}
         if (isset($backupinfo['original_site_identifier_hash'])) {
             $this->xmlwriter->full_tag('original_site_identifier_hash', $backupinfo['original_site_identifier_hash']);
+        } else {
+            $this->xmlwriter->full_tag('original_site_identifier_hash', null);
         }
         $this->xmlwriter->full_tag('original_course_id', $originalcourseinfo['original_course_id']);
         $this->xmlwriter->full_tag('original_course_fullname', $originalcourseinfo['original_course_fullname']);
index 14d130a..2aebdcf 100644 (file)
@@ -371,40 +371,35 @@ class restore_gradebook_structure_step extends restore_structure_step {
         }
         $rs->close();
 
-        //need to correct the grade category path and parent
+        // Need to correct the grade category path and parent
         $conditions = array(
             'courseid' => $this->get_courseid()
         );
-        $grade_category = new stdclass();
 
         $rs = $DB->get_recordset('grade_categories', $conditions);
-        if (!empty($rs)) {
-            //get all the parents correct first as grade_category::build_path() loads category parents from the DB
-            foreach($rs as $gc) {
-                if (!empty($gc->parent)) {
-                    $grade_category->id = $gc->id;
-                    $grade_category->parent = $this->get_mappingid('grade_category', $gc->parent);
-                    $DB->update_record('grade_categories', $grade_category);
-                }
+        // Get all the parents correct first as grade_category::build_path() loads category parents from the DB
+        foreach ($rs as $gc) {
+            if (!empty($gc->parent)) {
+                $grade_category = new stdClass();
+                $grade_category->id = $gc->id;
+                $grade_category->parent = $this->get_mappingid('grade_category', $gc->parent);
+                $DB->update_record('grade_categories', $grade_category);
             }
         }
-        if (isset($grade_category->parent)) {
-            unset($grade_category->parent);
-        }
         $rs->close();
 
+        // Now we can rebuild all the paths
         $rs = $DB->get_recordset('grade_categories', $conditions);
-        if (!empty($rs)) {
-            //now we can rebuild all the paths
-            foreach($rs as $gc) {
-                $grade_category->id = $gc->id;
-                $grade_category->path = grade_category::build_path($gc);
-                $DB->update_record('grade_categories', $grade_category);
-            }
+        foreach ($rs as $gc) {
+            $grade_category = new stdClass();
+            $grade_category->id = $gc->id;
+            $grade_category->path = grade_category::build_path($gc);
+            $grade_category->depth = substr_count($grade_category->path, '/') - 1;
+            $DB->update_record('grade_categories', $grade_category);
         }
         $rs->close();
 
-        //Restore marks items as needing update. Update everything now.
+        // Restore marks items as needing update. Update everything now.
         grade_regrade_final_grades($this->get_courseid());
     }
 }
index 654a87f..6eba776 100644 (file)
                         }
 
                         //Now build the EVENT record structure
+                        $eve = new stdClass();
                         $eve->name = backup_todb($info['EVENT']['#']['NAME']['0']['#']);
                         $eve->description = backup_todb($info['EVENT']['#']['DESCRIPTION']['0']['#']);
                         $eve->format = backup_todb($info['EVENT']['#']['FORMAT']['0']['#']);
index 8716029..66dfbef 100644 (file)
@@ -63,7 +63,7 @@ class restore_decode_processor {
 
     public function add_rule($rule) {
         if (!$rule instanceof restore_decode_rule) {
-            throw new restore_decode_processor_exception('incorrect_restore_decode_rule', get_class($content));
+            throw new restore_decode_processor_exception('incorrect_restore_decode_rule', get_class($rule));
         }
         $rule->set_restoreid($this->restoreid);
         $rule->set_wwwroots($this->sourcewwwroot, $this->targetwwwroot);
index 52e6f63..ded4126 100644 (file)
@@ -151,7 +151,7 @@ class restore_decode_rule {
         $countma = count($mappings);
         // Check mappings number matches placeholders
         if ($countph != $countma) {
-            $msg = new stdclass();
+            $a = new stdClass();
             $a->placeholders = $countph;
             $a->mappings     = $countma;
             throw new restore_decode_rule_exception('decode_rule_mappings_incorrect_count', $a);
index b183767..e4ab4b1 100644 (file)
@@ -61,7 +61,7 @@ class file_logger extends base_logger {
     public function __wakeup() {
         if ($this->level > backup::LOG_NONE) { // Only create the file if we are going to log something
             if (! $this->fhandle = fopen($this->fullpath, 'a')) {
-                throw new base_logger_exception('error_opening_file', $fullpath);
+                throw new base_logger_exception('error_opening_file', $this->fullpath);
             }
         }
     }
index 856b9b8..331f353 100644 (file)
@@ -79,6 +79,7 @@ class block_admin_bookmarks extends block_base {
         if ($this->contentgenerated === true) {
             return $this->content;
         }
+        $this->content = new stdClass();
 
         if (get_user_preferences('admin_bookmarks')) {
             require_once($CFG->libdir.'/adminlib.php');
index a00cd09..527d020 100644 (file)
@@ -48,21 +48,23 @@ class block_comments extends block_base {
 
     function get_content() {
         global $CFG, $PAGE;
+        if ($this->content !== NULL) {
+            return $this->content;
+        }
         if (!$CFG->usecomments) {
+            $this->content = new stdClass();
             $this->content->text = '';
             if ($this->page->user_is_editing()) {
                 $this->content->text = get_string('disabledcomments');
             }
             return $this->content;
         }
-        if ($this->content !== NULL) {
-            return $this->content;
-        }
-        if (empty($this->instance)) {
-            return null;
-        }
+        $this->content = new stdClass();
         $this->content->footer = '';
         $this->content->text = '';
+        if (empty($this->instance)) {
+            return $this->content;
+        }
         list($context, $course, $cm) = get_context_info_array($PAGE->context->id);
 
         $args = new stdClass;
index 72fc75b..649cd77 100644 (file)
@@ -119,6 +119,7 @@ class block_glossary_random extends block_base {
         global $USER, $CFG, $DB;
 
         if (empty($this->config->glossary)) {
+            $this->content = new stdClass();
             $this->content->text   = get_string('notyetconfigured','block_glossary_random');
             $this->content->footer = '';
             return $this->content;
@@ -153,7 +154,7 @@ class block_glossary_random extends block_base {
             return $this->content;
         }
 
-        $this->content = new stdClass;
+        $this->content = new stdClass();
         $this->content->text = $this->config->cache;
 
         // place link to glossary in the footer if the glossary is visible
index 30c005d..aa7b466 100644 (file)
@@ -72,6 +72,9 @@ class block_html_edit_form extends block_edit_form {
         unset($this->block->config->text);
         parent::set_data($defaults);
         // restore $text
+        if (!isset($this->block->config)) {
+            $this->block->config = new stdClass();
+        }
         $this->block->config->text = $text;
         if (isset($title)) {
             // Reset the preserved title
index 516a5fa..2d92009 100644 (file)
@@ -43,6 +43,7 @@ class block_login extends block_base {
 
         $username = get_moodle_cookie();
 
+        $this->content = new stdClass();
         $this->content->footer = '';
         $this->content->text = '';
 
index 45d9463..aa340af 100644 (file)
@@ -25,6 +25,8 @@ class block_mentees extends block_base {
             return $this->content;
         }
 
+        $this->content = new stdClass();
+
         // get all the mentees, i.e. users you have a direct assignment to
         if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, u.firstname, u.lastname
                                                     FROM {role_assignments} ra, {context} c, {user} u
index 79b8086..8bfa9b7 100644 (file)
@@ -195,6 +195,7 @@ class block_navigation extends block_base {
         
         // Grab the items to display
         $renderer = $this->page->get_renderer('block_navigation');
+        $this->content = new stdClass();
         $this->content->text = $renderer->navigation_tree($navigation, $expansionlimit, $options);
 
         // Set content generated to true so that we know it has been done
@@ -282,7 +283,7 @@ class block_navigation extends block_base {
      * @return string The truncated string
      */
     protected function trim_left($textlib, $string, $length) {
-        return '...'.$textlib->substr($string, $textlib->strlen($string)-$length);
+        return '...'.$textlib->substr($string, $textlib->strlen($string)-$length, $length);
     }
     /**
      * Truncate a string from the right
index e28ac28..b62d4db 100644 (file)
@@ -30,12 +30,14 @@ class block_navigation_renderer extends plugin_renderer_base {
             $isexpandable = (empty($expansionlimit) || ($item->type > navigation_node::TYPE_ACTIVITY || $item->type < $expansionlimit) || ($item->contains_active_node() && $item->children->count() > 0));
             $isbranch = $isexpandable && ($item->children->count() > 0 || ($item->has_children() && (isloggedin() || $item->type <= navigation_node::TYPE_CATEGORY)));
 
-            $hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY )&& $item->icon instanceof renderable);
+            $hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY || $item->type == navigation_node::TYPE_RESOURCE) && $item->icon instanceof renderable);
 
             if ($hasicon) {
                 $icon = $this->output->render($item->icon);
-                $content = $icon.$content; // use CSS for spacing of icons
+            } else {
+                $icon = '';
             }
+            $content = $icon.$content; // use CSS for spacing of icons
             if ($item->helpbutton !== null) {
                 $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
             }
@@ -57,6 +59,7 @@ class block_navigation_renderer extends plugin_renderer_base {
             } else if ($item->action instanceof action_link) {
                 //TODO: to be replaced with something else
                 $link = $item->action;
+                $link->text = $icon.$link->text;
                 $link->attributes = array_merge($link->attributes, $attributes);
                 $content = $this->output->render($link);
                 $linkrendered = true;
index a552dd9..32e1282 100644 (file)
@@ -51,6 +51,7 @@ class block_private_files extends block_base {
             return null;
         }
 
+        $this->content = new stdClass();
         $this->content->text = '';
         $this->content->footer = '';
         if (isloggedin() && !isguestuser()) {   // Show the block
index 89706f6..3e2aea8 100644 (file)
@@ -129,6 +129,7 @@ class block_settings extends block_base {
         }
 
         $renderer = $this->page->get_renderer('block_settings');
+        $this->content = new stdClass();
         $this->content->text = $renderer->settings_tree($this->page->settingsnav);
 
         // only do search if you have moodle/site:config
index 0a35df1..b7a35fd 100644 (file)
@@ -47,6 +47,10 @@ class block_tags extends block_base {
             return $this->content;
         }
 
+        if (!isset($this->config)) {
+            $this->config = new stdClass();
+        }
+
         if (empty($this->config->numberoftags)) {
             $this->config->numberoftags = 80;
         }
index e1c0c22..8d28dbc 100644 (file)
@@ -197,20 +197,18 @@ if (!empty($userid)) {
 
 $courseid = (empty($courseid)) ? SITEID : $courseid;
 
-if (!empty($courseid)) {
-    $PAGE->set_context(get_context_instance(CONTEXT_COURSE, $courseid));
-}
-
-if (!empty($modid)) {
-    $PAGE->set_context(get_context_instance(CONTEXT_MODULE, $modid));
+if (empty($entryid) && empty($modid) && empty($groupid)) {
+    $PAGE->set_context(context_user::instance($USER->id));
+} else if (!empty($modid)) {
+    $PAGE->set_context(context_module::instance($modid));
+} else if (!empty($courseid)) {
+    $PAGE->set_context(context_course::instance($courseid));
+} else {
+    $PAGE->set_context(context_system::instance());
 }
 
 $blogheaders = blog_get_headers();
 
-if (empty($entryid) && empty($modid) && empty($groupid)) {
-    $PAGE->set_context(get_context_instance(CONTEXT_USER, $USER->id));
-}
-
 if ($CFG->enablerssfeeds) {
     $rsscontext = null;
     $filtertype = null;
index 991069c..5a92fb5 100644 (file)
@@ -314,7 +314,7 @@ function blog_get_context_url($context=null) {
 
     // Change contextlevel to SYSTEM if viewing the site course
     if ($context->contextlevel == CONTEXT_COURSE && $context->instanceid == SITEID) {
-        $context->contextlevel = CONTEXT_SYSTEM;
+        $context = context_system::instance();
     }
 
     $filterparam = '';
index c60e6b6..e4e76cd 100644 (file)
 <?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-/////////////////////////////////////////////////////////////////////////////
-//                                                                         //
-// NOTICE OF COPYRIGHT                                                     //
-//                                                                         //
-// Moodle - Calendar extension                                             //
-//                                                                         //
-// Copyright (C) 2003-2004  Greek School Network            www.sch.gr     //
-//                                                                         //
-// Designed by:                                                            //
-//     Avgoustos Tsinakos (tsinakos@teikav.edu.gr)                         //
-//     Jon Papaioannou (pj@moodle.org)                                     //
-//                                                                         //
-// Programming and development:                                            //
-//     Jon Papaioannou (pj@moodle.org)                                     //
-//                                                                         //
-// For bugs, suggestions, etc contact:                                     //
-//     Jon Papaioannou (pj@moodle.org)                                     //
-//                                                                         //
-// The current module was developed at the University of Macedonia         //
-// (www.uom.gr) under the funding of the Greek School Network (www.sch.gr) //
-// The aim of this project is to provide additional and improved           //
-// functionality to the Asynchronous Distance Education service that the   //
-// Greek School Network deploys.                                           //
-//                                                                         //
-// This program is free software; you can redistribute it and/or modify    //
-// it under the terms of the GNU General Public License as published by    //
-// the Free Software Foundation; either version 2 of the License, or       //
-// (at your option) any later version.                                     //
-//                                                                         //
-// This program is distributed in the hope that it will be useful,         //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of          //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           //
-// GNU General Public License for more details:                            //
-//                                                                         //
-//          http://www.gnu.org/copyleft/gpl.html                           //
-//                                                                         //
-/////////////////////////////////////////////////////////////////////////////
-
-// These are read by the administration component to provide default values
+/**
+ * Calendar extension
+ *
+ * @package    core_calendar
+ * @copyright  2004 Greek School Network (http://www.sch.gr), Jon Papaioannou,
+ *             Avgoustos Tsinakos
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ *  These are read by the administration component to provide default values
+ */
+
+/**
+ * CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD - default value of upcoming event preference
+ */
 define('CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD', 21);
+
+/**
+ * CALENDAR_DEFAULT_UPCOMING_MAXEVENTS - default value to display the maximum number of upcoming event
+ */
 define('CALENDAR_DEFAULT_UPCOMING_MAXEVENTS', 10);
-define('CALENDAR_DEFAULT_STARTING_WEEKDAY',   1);
+
+/**
+ * CALENDAR_DEFAULT_STARTING_WEEKDAY - default value to display the starting weekday
+ */
+define('CALENDAR_DEFAULT_STARTING_WEEKDAY', 1);
+
 // This is a packed bitfield: day X is "weekend" if $field & (1 << X) is true
 // Default value = 65 = 64 + 1 = 2^6 + 2^0 = Saturday & Sunday
-define('CALENDAR_DEFAULT_WEEKEND',            65);
+
+/**
+ * CALENDAR_DEFAULT_WEEKEND - default value for weekend (Saturday & Sunday)
+ */
+define('CALENDAR_DEFAULT_WEEKEND', 65);
+
+/**
+ * CALENDAR_URL - path to calendar's folder
+ */
 define('CALENDAR_URL', $CFG->wwwroot.'/calendar/');
+
+/**
+ * CALENDAR_TF_24 - Calendar time in 24 hours format
+ */
 define('CALENDAR_TF_24', '%H:%M');
+
+/**
+ * CALENDAR_TF_12 - Calendar time in 12 hours format
+ */
 define('CALENDAR_TF_12', '%I:%M %p');
 
+/**
+ * CALENDAR_EVENT_GLOBAL - Global calendar event types
+ */
 define('CALENDAR_EVENT_GLOBAL', 1);
+
+/**
+ * CALENDAR_EVENT_COURSE - Course calendar event types
+ */
 define('CALENDAR_EVENT_COURSE', 2);
+
+/**
+ * CALENDAR_EVENT_GROUP - group calendar event types
+ */
 define('CALENDAR_EVENT_GROUP', 4);
+
+/**
+ * CALENDAR_EVENT_USER - user calendar event types
+ */
 define('CALENDAR_EVENT_USER', 8);
 
 /**
- * CALENDAR_STARTING_WEEKDAY has since been deprecated please call calendar_get_starting_weekday() instead
- * @deprecated
+ * CALENDAR_STARTING_WEEKDAY - has since been deprecated please call calendar_get_starting_weekday() instead
+ *
+ * @deprecated Moodle 2.0 MDL-24284- please do not use this function any more.
+ * @todo MDL-31132 This will be deleted in Moodle 2.3.
+ * @see calendar_get_starting_weekday()
  */
 define('CALENDAR_STARTING_WEEKDAY', CALENDAR_DEFAULT_STARTING_WEEKDAY);
 
 /**
  * Return the days of the week
  *
- * @return array
+ * @return array array of days
  */
 function calendar_get_days() {
     return array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
@@ -95,13 +129,12 @@ function calendar_get_starting_weekday() {
 /**
  * Generates the HTML for a miniature calendar
  *
- * @global core_renderer $OUTPUT
- * @param array $courses
- * @param array $groups
- * @param array $users
- * @param int $cal_month
- * @param int $cal_year
- * @return string
+ * @param array $courses list of course
+ * @param array $groups list of group
+ * @param array $users user's info
+ * @param int $cal_month calendar month in numeric, default is set to false
+ * @param int $cal_year calendar month in numeric, default is set to false
+ * @return string $content return html table for mini calendar
  */
 function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_year = false) {
     global $CFG, $USER, $OUTPUT;
@@ -375,13 +408,15 @@ function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_y
 }
 
 /**
- * calendar_get_popup, called at multiple points in from calendar_get_mini.
- *        Copied and modified from calendar_get_mini.
- * @global moodle_page $PAGE
- * @param $is_today bool, false except when called on the current day.
- * @param $event_timestart mixed, $events[$eventid]->timestart, OR false if there are no events.
- * @param $popupcontent string.
- * @return $popup string, contains onmousover and onmouseout events.
+ * Gets the calendar popup
+ *
+ * It called at multiple points in from calendar_get_mini.
+ * Copied and modified from calendar_get_mini.
+ *
+ * @param bool $is_today false except when called on the current day.
+ * @param mixed $event_timestart $events[$eventid]->timestart, OR false if there are no events.
+ * @param string $popupcontent content for the popup window/layout
+ * @return string of eventid for the calendar_tooltip popup window/layout
  */
 function calendar_get_popup($is_today, $event_timestart, $popupcontent='') {
     global $PAGE;
@@ -407,6 +442,17 @@ function calendar_get_popup($is_today, $event_timestart, $popupcontent='') {
     return 'id="'.$id.'"';
 }
 
+/**
+ * Gets the calendar upcoming event
+ *
+ * @param array $courses array of courses
+ * @param array|int|bool $groups array of groups, group id or boolean for all/no group events
+ * @param array|int|bool $users array of users, user id or boolean for all/no user events
+ * @param int $daysinfuture number of days in the future we 'll look
+ * @param int $maxevents maximum number of events
+ * @param int $fromtime start time
+ * @return array $output array of upcoming events
+ */
 function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) {
     global $CFG, $COURSE, $DB;
 
@@ -512,6 +558,12 @@ function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxeve
     return $output;
 }
 
+/**
+ * Add calendar event metadata
+ *
+ * @param stdClass $event event info
+ * @return stdClass $event metadata
+ */
 function calendar_add_event_metadata($event) {
     global $CFG, $OUTPUT;
 
@@ -571,7 +623,9 @@ function calendar_add_event_metadata($event) {
 /**
  * Prints a calendar event
  *
- * @deprecated 2.0
+ * @deprecated Moodle 2.0 - MDL-22887 please do not use this function any more.
+ * @todo MDL-31133 - will be removed in Moodle 2.3
+ * @see core_calendar_renderer event function
  */
 function calendar_print_event($event, $showactions=true) {
     global $CFG, $USER, $OUTPUT, $PAGE;
@@ -585,15 +639,16 @@ function calendar_print_event($event, $showactions=true) {
 
 /**
  * Get calendar events
+ *
  * @param int $tstart Start time of time range for events
- * @param int $tend   End time of time range for events
- * @param array/int/boolean $users array of users, user id or boolean for all/no user events
- * @param array/int/boolean $groups array of groups, group id or boolean for all/no group events
- * @param array/int/boolean $courses array of courses, course id or boolean for all/no course events
+ * @param int $tend End time of time range for events
+ * @param array|int|boolean $users array of users, user id or boolean for all/no user events
+ * @param array|int|boolean $groups array of groups, group id or boolean for all/no group events
+ * @param array|int|boolean $courses array of courses, course id or boolean for all/no course events
  * @param boolean $withduration whether only events starting within time range selected
  *                              or events in progress/already started selected as well
  * @param boolean $ignorehidden whether to select only visible events or all events
- * @return array of selected events or an empty array if there aren't any (or there was an error)
+ * @return array $events of selected events or an empty array if there aren't any (or there was an error)
  */
 function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration=true, $ignorehidden=true) {
     global $DB;
@@ -684,6 +739,13 @@ function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withdur
     return $events;
 }
 
+/**
+ * Get control options for Calendar
+ *
+ * @param string $type of calendar
+ * @param array $data calendar information
+ * @return string $content return available control for the calender in html
+ */
 function calendar_top_controls($type, $data) {
     global $CFG;
     $content = '';
@@ -835,6 +897,14 @@ function calendar_top_controls($type, $data) {
     return $content;
 }
 
+/**
+ * Get the controls filter for calendar.
+ *
+ * Filter is used to hide calendar info from the display page
+ *
+ * @param moodle_url $returnurl return-url for filter controls
+ * @return string $content return filter controls in html
+ */
 function calendar_filter_controls(moodle_url $returnurl) {
     global $CFG, $USER, $OUTPUT;
 
@@ -897,6 +967,14 @@ function calendar_filter_controls(moodle_url $returnurl) {
     return $content;
 }
 
+/**
+ * Return the representation day
+ *
+ * @param int $tstamp Timestamp in GMT
+ * @param int $now current Unix timestamp
+ * @param bool $usecommonwords
+ * @return string the formatted date/time
+ */
 function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) {
 
     static $shortformat;
@@ -941,6 +1019,12 @@ function calendar_day_representation($tstamp, $now = false, $usecommonwords = tr
     }
 }
 
+/**
+ * return the formatted representation time
+ *
+ * @param int $time the timestamp in UTC, as obtained from the database
+ * @return string the formatted date/time
+ */
 function calendar_time_representation($time) {
     static $langtimeformat = NULL;
     if($langtimeformat === NULL) {
@@ -958,10 +1042,10 @@ function calendar_time_representation($time) {
  * Adds day, month, year arguments to a URL and returns a moodle_url object.
  *
  * @param string|moodle_url $linkbase
- * @param int $d
- * @param int $m
- * @param int $y
- * @return moodle_url
+ * @param int $d The number of the day.
+ * @param int $m The number of the month.
+ * @param int $y The number of the year.
+ * @return moodle_url|null $linkbase
  */
 function calendar_get_link_href($linkbase, $d, $m, $y) {
     if (empty($linkbase)) {
@@ -985,14 +1069,15 @@ function calendar_get_link_href($linkbase, $d, $m, $y) {
 /**
  * This function has been deprecated as of Moodle 2.0... DO NOT USE!!!!!
  *
- * @deprecated
- * @since 2.0
+ * @deprecated Moodle 2.0 - MDL-24284 please do not use this function any more.
+ * @todo MDL-31134 - will be removed in Moodle 2.3
+ * @see calendar_get_link_href()
  *
  * @param string $text
  * @param string|moodle_url $linkbase
- * @param int|null $d
- * @param int|null $m
- * @param int|null $y
+ * @param int|null $d The number of the day.
+ * @param int|null $m The number of the month.
+ * @param int|null $y The number of the year.
  * @return string HTML link
  */
 function calendar_get_link_tag($text, $linkbase, $d, $m, $y) {
@@ -1008,7 +1093,9 @@ function calendar_get_link_tag($text, $linkbase, $d, $m, $y) {
  *
  * @param string $text The text label.
  * @param string|moodle_url $linkbase The URL stub.
- * @param int $d $m $y Day of month, month and year numbers.
+ * @param int $d The number of the date.
+ * @param int $m The number of the month.
+ * @param int $y year The number of the year.
  * @param bool $accesshide Default visible, or hide from all except screenreaders.
  * @return string HTML string.
  */
@@ -1025,7 +1112,9 @@ function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide=fa
  *
  * @param string $text The text label.
  * @param string|moodle_url $linkbase The URL stub.
- * @param int $d $m $y Day of month, month and year numbers.
+ * @param int $d the number of the Day
+ * @param int $m The number of the month.
+ * @param int $y The number of the year.
  * @param bool $accesshide Default visible, or hide from all except screenreaders.
  * @return string HTML string.
  */
@@ -1037,14 +1126,34 @@ function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide=false)
     return link_arrow_right($text, (string)$href, $accesshide, 'next');
 }
 
+/**
+ * Return the name of the weekday
+ *
+ * @param string $englishname
+ * @return string of the weekeday
+ */
 function calendar_wday_name($englishname) {
     return get_string(strtolower($englishname), 'calendar');
 }
 
+/**
+ * Return the number of days in month
+ *
+ * @param int $month the number of the month.
+ * @param int $year the number of the year
+ * @return int
+ */
 function calendar_days_in_month($month, $year) {
    return intval(date('t', mktime(0, 0, 0, $month, 1, $year)));
 }
 
+/**
+ * Get the upcoming event block
+ *
+ * @param array $events list of events
+ * @param moodle_url|string $linkhref link to event referer
+ * @return string|null $content html block content
+ */
 function calendar_get_block_upcoming($events, $linkhref = NULL) {
     $content = '';
     $lines = count($events);
@@ -1080,6 +1189,16 @@ function calendar_get_block_upcoming($events, $linkhref = NULL) {
     return $content;
 }
 
+/**
+ * Get the next following month
+ *
+ * If the current month is December, it will get the first month of the following year.
+ *
+ *
+ * @param int $month the number of the month.
+ * @param int $year the number of the year.
+ * @return array the following month
+ */
 function calendar_add_month($month, $year) {
     if($month == 12) {
         return array(1, $year + 1);
@@ -1089,6 +1208,15 @@ function calendar_add_month($month, $year) {
     }
 }
 
+/**
+ * Get the previous month
+ *
+ * If the current month is January, it will get the last month of the previous year.
+ *
+ * @param int $month the number of the month.
+ * @param int $year the number of the year.
+ * @return array previous month
+ */
 function calendar_sub_month($month, $year) {
     if($month == 1) {
         return array(12, $year - 1);
@@ -1098,6 +1226,18 @@ function calendar_sub_month($month, $year) {
     }
 }
 
+/**
+ * Get per-day basis events
+ *
+ * @param array $events list of events
+ * @param int $month the number of the month
+ * @param int $year the number of the year
+ * @param array $eventsbyday event on specific day
+ * @param array $durationbyday duration of the event in days
+ * @param array $typesbyday event type (eg: global, course, user, or group)
+ * @param array $courses list of courses
+ * @return void
+ */
 function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) {
     $eventsbyday = array();
     $typesbyday = array();
@@ -1184,6 +1324,14 @@ function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$duratio
     return;
 }
 
+/**
+ * Get current module cache
+ *
+ * @param array $coursecache list of course cache
+ * @param string $modulename name of the module
+ * @param int $instance module instance number
+ * @return stdClass|bool $module information
+ */
 function calendar_get_module_cached(&$coursecache, $modulename, $instance) {
     $module = get_coursemodule_from_instance($modulename, $instance);
 
@@ -1194,6 +1342,13 @@ function calendar_get_module_cached(&$coursecache, $modulename, $instance) {
     return $module;
 }
 
+/**
+ * Get current course cache
+ *
+ * @param array $coursecache list of course cache
+ * @param int $courseid id of the course
+ * @return stdClass $coursecache[$courseid] return the specific course cache
+ */
 function calendar_get_course_cached(&$coursecache, $courseid) {
     global $COURSE, $DB;
 
@@ -1210,9 +1365,8 @@ function calendar_get_course_cached(&$coursecache, $courseid) {
 /**
  * Returns the courses to load events for, the
  *
- * @global moodle_database $DB
  * @param array $courseeventsfrom An array of courses to load calendar events for
- * @param bool $ignorefilters
+ * @param bool $ignorefilters specify the use of filters, false is set as default
  * @return array An array of courses, groups, and user to load calendar events for based upon filters
  */
 function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
@@ -1292,6 +1446,12 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
     return array($courses, $group, $user);
 }
 
+/**
+ * Return the capability for editing calendar event
+ *
+ * @param calendar_event $event event object
+ * @return bool capability to edit event
+ */
 function calendar_edit_event_allowed($event) {
     global $USER, $DB;
 
@@ -1338,8 +1498,7 @@ function calendar_edit_event_allowed($event) {
  * Returns the default courses to display on the calendar when there isn't a specific
  * course to display.
  *
- * @global moodle_database $DB
- * @return array Array of courses to display
+ * @return array $courses Array of courses to display
  */
 function calendar_get_default_courses() {
     global $CFG, $DB;
@@ -1367,6 +1526,12 @@ function calendar_get_default_courses() {
     return $courses;
 }
 
+/**
+ * Display calendar preference button
+ *
+ * @param stdClass $course course object
+ * @return string return preference button in html
+ */
 function calendar_preferences_button(stdClass $course) {
     global $OUTPUT;
 
@@ -1378,6 +1543,16 @@ function calendar_preferences_button(stdClass $course) {
     return $OUTPUT->single_button(new moodle_url('/calendar/preferences.php', array('course' => $course->id)), get_string("preferences", "calendar"));
 }
 
+/**
+ * Get event format time
+ *
+ * @param calendar_event $event event object
+ * @param int $now current time in gmt
+ * @param array $linkparams list of params for event link
+ * @param bool $usecommonwords the words as formatted date/time.
+ * @param int $showtime determine the show time GMT timestamp
+ * @return string $eventtime link/string for event time
+ */
 function calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime=0) {
     $startdate = usergetdate($event->timestart);
     $enddate = usergetdate($event->timestart + $event->timeduration);
@@ -1467,6 +1642,12 @@ function calendar_format_event_time($event, $now, $linkparams = null, $usecommon
     return $eventtime;
 }
 
+/**
+ * Display month selector options
+ *
+ * @param string $name for the select element
+ * @param string|array $selected options for select elements
+ */
 function calendar_print_month_selector($name, $selected) {
     $months = array();
     for ($i=1; $i<=12; $i++) {
@@ -1498,13 +1679,14 @@ function calendar_show_event_type($type, $user = null) {
 
 /**
  * Sets the display of the event type given $display.
+ *
  * If $display = true the event type will be shown.
  * If $display = false the event type will NOT be shown.
  * If $display = null the current value will be toggled and saved.
  *
- * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type
- * @param true|false|null $display
- * @param stdClass|int|null $user
+ * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type object of CALENDAR_EVENT_XXX
+ * @param bool $display option to display event type
+ * @param stdClass|int $user moodle user object or id, null means current user
  */
 function calendar_set_event_type_display($type, $display = null, $user = null) {
     $persist = get_user_preferences('calendar_persistflt', 0, $user);
@@ -1538,8 +1720,15 @@ function calendar_set_event_type_display($type, $display = null, $user = null) {
     }
 }
 
+/**
+ * Get calendar's allowed types
+ *
+ * @param stdClass $allowed list of allowed edit for event  type
+ * @param stdClass|int $course object of a course or course id
+ */
 function calendar_get_allowed_types(&$allowed, $course = null) {
     global $USER, $CFG, $DB;
+    $allowed = new stdClass();
     $allowed->user = has_capability('moodle/calendar:manageownentries', get_system_context());
     $allowed->groups = false; // This may change just below
     $allowed->courses = false; // This may change just below
@@ -1568,9 +1757,11 @@ function calendar_get_allowed_types(&$allowed, $course = null) {
 }
 
 /**
- * see if user can add calendar entries at all
+ * See if user can add calendar entries at all
  * used to print the "New Event" button
- * @return bool
+ *
+ * @param stdClass $course object of a course or course id
+ * @return bool has the capability to add at least one event type
  */
 function calendar_user_can_add_event($course) {
     if (!isloggedin() || isguestuser()) {
@@ -1583,8 +1774,8 @@ function calendar_user_can_add_event($course) {
 /**
  * Check wether the current user is permitted to add events
  *
- * @param object $event
- * @return bool
+ * @param stdClass $event object of event
+ * @return bool has the capability to add event
  */
 function calendar_add_event_allowed($event) {
     global $USER, $DB;
@@ -1629,13 +1820,18 @@ function calendar_add_event_allowed($event) {
 }
 
 /**
- * A class to manage calendar events
+ * Manage calendar events
  *
  * This class provides the required functionality in order to manage calendar events.
  * It was introduced as part of Moodle 2.0 and was created in order to provide a
  * better framework for dealing with calendar events in particular regard to file
  * handling through the new file API
  *
+ * @package    core_calendar
+ * @category   calendar
+ * @copyright  2009 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ *
  * @property int $id The id within the event table
  * @property string $name The name of the event
  * @property string $description The description of the event
@@ -1657,32 +1853,22 @@ function calendar_add_event_allowed($event) {
  */
 class calendar_event {
 
-    /**
-     * An object containing the event properties can be accessed via the
-     * magic __get/set methods
-     * @var array
-     */
+    /** @var array An object containing the event properties can be accessed via the magic __get/set methods */
     protected $properties = null;
+
     /**
-     * The converted event discription with file paths resolved
-     * This gets populated when someone requests description for the first time
-     * @var string
-     */
+     * @var string The converted event discription with file paths resolved. This gets populated when someone requests description for the first time */
     protected $_description = null;
-    /**
-     * The options to use with this description editor
-     * @var array
-     */
+
+    /** @var array The options to use with this description editor */
     protected $editoroptions = array(
             'subdirs'=>false,
             'forcehttps'=>false,
             'maxfiles'=>-1,
             'maxbytes'=>null,
             'trusttext'=>false);
-    /**
-     * The context to use with the description editor
-     * @var object
-     */
+
+    /** @var object The context to use with the description editor */
     protected $editorcontext = null;
 
     /**
@@ -1745,8 +1931,8 @@ class calendar_event {
      * Attempts to call a set_$key method if one exists otherwise falls back
      * to simply set the property
      *
-     * @param string $key
-     * @param mixed $value
+     * @param string $key property name
+     * @param mixed $value value of the property
      */
     public function __set($key, $value) {
         if (method_exists($this, 'set_'.$key)) {
@@ -1761,8 +1947,8 @@ class calendar_event {
      * Attempts to call a get_$key method to return the property and ralls over
      * to return the raw property
      *
-     * @param str $key
-     * @return mixed
+     * @param string $key property name
+     * @return mixed property value
      */
     public function __get($key) {
         if (method_exists($this, 'get_'.$key)) {
@@ -1778,8 +1964,8 @@ class calendar_event {
      * Stupid PHP needs an isset magic method if you use the get magic method and
      * still want empty calls to work.... blah ~!
      *
-     * @param string $key
-     * @return bool
+     * @param string $key $key property name
+     * @return bool|mixed property value, false if property is not exist
      */
     public function __isset($key) {
         return !empty($this->properties->{$key});
@@ -1792,10 +1978,11 @@ class calendar_event {
      * the course event.
      * Default value is set to CONTEXT_USER
      *
-     * @return stdClass
+     * @param stdClass $data information about event
+     * @return stdClass The context object.
      */
     protected function calculate_context(stdClass $data) {
-        global $USER;
+        global $USER, $DB;
 
         $context = null;
         if (isset($data->courseid) && $data->courseid > 0) {
@@ -1821,7 +2008,8 @@ class calendar_event {
     /**
      * Returns an array of editoroptions for this event: Called by __get
      * Please use $blah = $event->editoroptions;
-     * @return array
+     *
+     * @return array event editor options
      */
     protected function get_editoroptions() {
         return $this->editoroptions;
@@ -1831,7 +2019,7 @@ class calendar_event {
      * Returns an event description: Called by __get
      * Please use $blah = $event->description;
      *
-     * @return string
+     * @return string event description
      */
     protected function get_description() {
         global $CFG;
@@ -1870,7 +2058,7 @@ class calendar_event {
     /**
      * Return the number of repeat events there are in this events series
      *
-     * @return int
+     * @return int number of event repeated
      */
     public function count_repeats() {
         global $DB;
@@ -1891,8 +2079,9 @@ class calendar_event {
      * @see add_event()
      * @see update_event()
      *
-     * @param stdClass $data
-     * @param boolean $checkcapability if moodle should check calendar managing capability or not
+     * @param stdClass $data object of event
+     * @param bool $checkcapability if moodle should check calendar managing capability or not
+     * @return bool event updated
      */
     public function update($data, $checkcapability=true) {
         global $CFG, $DB, $USER;
@@ -2071,8 +2260,8 @@ class calendar_event {
      *
      * @see delete_event()
      *
-     * @param bool $deleterepeated
-     * @return bool
+     * @param bool $deleterepeated  delete event repeatedly
+     * @return bool succession of deleting event
      */
     public function delete($deleterepeated=false) {
         global $DB;
@@ -2211,6 +2400,7 @@ class calendar_event {
      * @param null|bool $force If it is left null the events visibility is flipped,
      *                   If it is false the event is made hidden, if it is true it
      *                   is made visible.
+     * @return bool if event is successfully updated, toggle will be visible
      */
     public function toggle_visibility($force=null) {
         global $CFG, $DB;
@@ -2240,11 +2430,9 @@ class calendar_event {
      * Attempts to call the hook for the specified action should a calendar type
      * by set $CFG->calendar, and the appopriate function defined
      *
-     * @static
-     * @staticvar bool $extcalendarinc Used to track the inclusion of the calendar lib
      * @param string $action One of `update_event`, `add_event`, `delete_event`, `show_event`, `hide_event`
      * @param array $args The args to pass to the hook, usually the event is the first element
-     * @return bool
+     * @return bool attempts to call event hook
      */
     public static function calendar_event_hook($action, array $args) {
         global $CFG;
@@ -2274,8 +2462,8 @@ class calendar_event {
      * This function makes use of MUST_EXIST, if the event id passed in is invalid
      * it will result in an exception being thrown
      *
-     * @param int|object $param
-     * @return calendar_event|false
+     * @param int|object $param event object or event id
+     * @return calendar_event|false status for loading calendar_event
      */
     public static function load($param) {
         global $DB;
@@ -2315,51 +2503,40 @@ class calendar_event {
  *
  * This class is used simply to organise the information pertaining to a calendar
  * and is used primarily to make information easily available.
+ *
+ * @package core_calendar
+ * @category calendar
+ * @copyright 2010 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class calendar_information {
-    /**
-     * The day
-     * @var int
-     */
+    /** @var int The day */
     public $day;
-    /**
-     * The month
-     * @var int
-     */
+
+    /** @var int The month */
     public $month;
-    /**
-     * The year
-     * @var int
-     */
+
+    /** @var int The year */
     public $year;
 
-    /**
-     * A course id
-     * @var int
-     */
+    /** @var int A course id */
     public $courseid = null;
-    /**
-     * An array of courses
-     * @var array
-     */
+
+    /** @var array An array of courses */
     public $courses = array();
-    /**
-     * An array of groups
-     * @var array
-     */
+
+    /** @var array An array of groups */
     public $groups = array();
-    /**
-     * An array of users
-     * @var array
-     */
+
+    /** @var array An array of users */
     public $users = array();
 
     /**
      * Creates a new instance
      *
-     * @param int $day
-     * @param int $month
-     * @param int $year
+     * @param int $day the number of the day
+     * @param int $month the number of the month
+     * @param int $year the number of the year
      */
     public function __construct($day=0, $month=0, $year=0) {
 
@@ -2383,10 +2560,11 @@ class calendar_information {
     }
 
     /**
+     * Initialize calendar information
      *
-     * @param stdClass $course
+     * @param stdClass $course object
      * @param array $coursestoload An array of courses [$course->id => $course]
-     * @param type $ignorefilters
+     * @param bool $ignorefilters options to use filter
      */
     public function prepare_for_view(stdClass $course, array $coursestoload, $ignorefilters = false) {
         $this->courseid = $course->id;
@@ -2401,8 +2579,9 @@ class calendar_information {
      * Ensures the date for the calendar is correct and either sets it to now
      * or throws a moodle_exception if not
      *
-     * @param bool $defaultonow
-     * @return bool
+     * @param bool $defaultonow use current time
+     * @throws moodle_exception
+     * @return bool validation of checkdate
      */
     public function checkdate($defaultonow = true) {
         if (!checkdate($this->month, $this->day, $this->year)) {
@@ -2420,14 +2599,16 @@ class calendar_information {
     }
     /**
      * Gets todays timestamp for the calendar
-     * @return int
+     *
+     * @return int today timestamp
      */
     public function timestamp_today() {
         return make_timestamp($this->year, $this->month, $this->day);
     }
     /**
      * Gets tomorrows timestamp for the calendar
-     * @return int
+     *
+     * @return int tomorrow timestamp
      */
     public function timestamp_tomorrow() {
         return make_timestamp($this->year, $this->month, $this->day+1);
@@ -2436,8 +2617,8 @@ class calendar_information {
      * Adds the pretend blocks for teh calendar
      *
      * @param core_calendar_renderer $renderer
-     * @param bool $showfilters
-     * @param string|null $view
+     * @param bool $showfilters display filters, false is set as default
+     * @param string|null $view preference view options (eg: day, month, upcoming)
      */
     public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) {
         if ($showfilters) {
index c41b795..dbc732c 100644 (file)
@@ -385,6 +385,32 @@ $CFG->admin = 'admin';
 //
 //     $CFG->extramemorylimit = 1G;
 //
+// The CSS files the Moodle produces can be extremely large and complex, especially
+// if you are using a custom theme that builds upon several other themes.
+// In Moodle 2.3 a CSS optimiser was added as an experimental feature for advanced
+// users. The CSS optimiser organises the CSS in order to reduce the overall number
+// of rules and styles being sent to the client. It does this by collating the
+// CSS before it is cached removing excess styles and rules and stripping out any
+// extraneous content such as comments and empty rules.
+// The following settings are used to enable and control the optimisation.
+//
+// Enable the CSS optimiser. This will only optimise the CSS if themedesignermode
+// is not enabled. This can be set through the UI however it is noted here as well
+// because the other CSS optimiser settings can not be set through the UI.
+//
+//      $CFG->enablecssoptimiser = true;
+//
+// If set the CSS optimiser will add stats about the optimisation to the top of
+// the optimised CSS file. You can then inspect the CSS to see the affect the CSS
+// optimiser is having.
+//
+//      $CFG->cssoptimiserstats = true;
+//
+// If set the CSS that is optimised will still retain a minimalistic formatting
+// so that anyone wanting to can still clearly read it.
+//
+//      $CFG->cssoptimiserpretty = true;
+//
 //=========================================================================
 // 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
 //=========================================================================
index a5bad50..d87c9d1 100644 (file)
@@ -270,7 +270,15 @@ class course_edit_form extends moodleform {
                 }
 
                 $mods = array(0=>get_string('allownone'));
-                $mods += $DB->get_records_menu('modules', array('visible'=>1), 'name', 'id, name');
+                $allmods = $DB->get_records_menu('modules', array('visible' => 1),
+                        'name', 'id, name');
+                foreach ($allmods as $key => $value) {
+                    // Add module to list unless it cannot be added by users anyway
+                    if (plugin_supports('mod', $value, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER) !==
+                            MOD_ARCHETYPE_SYSTEM) {
+                        $mods[$key] = get_string('pluginname', $value);
+                    }
+                }
                 $mform->addElement('select', 'allowedmods', get_string('to'), $mods, array('multiple'=>'multiple', 'size'=>'10'));
                 $mform->disabledIf('allowedmods', 'restrictmodules', 'eq', 0);
                 // defaults are already in $course
index 7e24305..cc7c3c0 100644 (file)
@@ -189,9 +189,9 @@ while ($section <= $course->numsections) {
         if ($PAGE->user_is_editing() && has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id))) {
 
             if ($course->marker == $section) {  // Show the "light globe" on/off
-                echo '<a href="view.php?id='.$course->id.'&amp;marker=0&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkedthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marked') . '" alt="'.$strmarkedthistopic.'" /></a><br />';
+                echo '<a href="view.php?id='.$course->id.'&amp;marker=0&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkedthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marked') . '" alt="'.$strmarkedthistopic.'" class="icon"/></a><br />';
             } else {
-                echo '<a href="view.php?id='.$course->id.'&amp;marker='.$section.'&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marker') . '" alt="'.$strmarkthistopic.'" /></a><br />';
+                echo '<a href="view.php?id='.$course->id.'&amp;marker='.$section.'&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marker') . '" alt="'.$strmarkthistopic.'" class="icon"/></a><br />';
             }
 
             if ($thissection->visible) {        // Show the hide/show eye
index 74fb964..c5c78fa 100644 (file)
@@ -141,7 +141,7 @@ defined('MOODLE_INTERNAL') || die();
             $thissection = $sections[$section];
 
         } else {
-            unset($thissection);
+            $thissection = new stdClass();
             $thissection->course = $course->id;   // Create a new week structure
             $thissection->section = $section;
             $thissection->name    = null;
index f0a0c10..e873128 100644 (file)
@@ -373,6 +373,7 @@ function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $
         echo '</td>';
         echo '</tr>';
     } else {
+        $category = new stdClass();
         $category->id = '0';
     }
 
index c186422..d34d9ec 100644 (file)
@@ -49,6 +49,11 @@ define('MOD_CLASS_RESOURCE', 1);
 function make_log_url($module, $url) {
     switch ($module) {
         case 'course':
+            if (strpos($url, 'report/') === 0) {
+                // there is only one report type, course reports are deprecated
+                $url = "/$url";
+                break;
+            }
         case 'file':
         case 'login':
         case 'lib':
@@ -145,6 +150,7 @@ function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.tim
     $groupid = 0;
 
     $joins = array();
+    $where = '';
 
     $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
               FROM {mnet_log} l
@@ -812,7 +818,7 @@ function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
 
         $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
 
-        $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $context)));
+        $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)));
         $myxls->write_date($row, 1, $log->time);
         $myxls->write_string($row, 2, $log->ip);
         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
@@ -1069,6 +1075,7 @@ function get_array_of_activities($courseid) {
                    if (empty($rawmods[$seq])) {
                        continue;
                    }
+                   $mod[$seq] = new stdClass();
                    $mod[$seq]->id               = $rawmods[$seq]->instance;
                    $mod[$seq]->cm               = $rawmods[$seq]->id;
                    $mod[$seq]->mod              = $rawmods[$seq]->modname;
@@ -1853,6 +1860,8 @@ function print_section_add_menus($course, $section, $modnames, $vertical=false,
             $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
             if ($archetype == MOD_ARCHETYPE_RESOURCE) {
                 $resources[$urlbase.$modname] = $modnamestr;
+            } else if ($archetype === MOD_ARCHETYPE_SYSTEM) {
+                // System modules cannot be added by user, do not add to dropdown
             } else {
                 // all other archetypes are considered activity
                 $activities[$urlbase.$modname] = $modnamestr;
@@ -2120,6 +2129,7 @@ function print_whole_category_list($category=NULL, $displaylist=NULL, $parentsli
         }
 
     } else {
+        $category = new stdClass();
         $category->id = "0";
     }
 
@@ -2493,7 +2503,7 @@ function print_course($course, $highlightterms = '') {
     echo html_writer::end_tag('div'); // End of info div
 
     echo html_writer::start_tag('div', array('class'=>'summary'));
-    $options = NULL;
+    $options = new stdClass();
     $options->noclean = true;
     $options->para = false;
     $options->overflowdiv = true;
@@ -2640,7 +2650,7 @@ function print_remote_course($course, $width="100%") {
         . format_string($course->cat_name) . ' : '
         . format_string($course->shortname). '</div>';
     echo '</div><div class="summary">';
-    $options = NULL;
+    $options = new stdClass();
     $options->noclean = true;
     $options->para = false;
     $options->overflowdiv = true;
@@ -3099,11 +3109,12 @@ function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $movesele
         $str->duplicate      = get_string("duplicate");
         $str->hide           = get_string("hide");
         $str->show           = get_string("show");
-        $str->clicktochange  = get_string("clicktochange");
-        $str->forcedmode     = get_string("forcedmode");
-        $str->groupsnone     = get_string("groupsnone");
-        $str->groupsseparate = get_string("groupsseparate");
-        $str->groupsvisible  = get_string("groupsvisible");
+        $str->groupsnone     = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsnone"));
+        $str->groupsseparate = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsseparate"));
+        $str->groupsvisible  = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsvisible"));
+        $str->forcedgroupsnone     = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsnone"));
+        $str->forcedgroupsseparate = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsseparate"));
+        $str->forcedgroupsvisible  = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsvisible"));
     }
 
     $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
@@ -3220,16 +3231,19 @@ function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $movesele
         if ($mod->groupmode == SEPARATEGROUPS) {
             $groupmode = 0;
             $grouptitle = $str->groupsseparate;
+            $forcedgrouptitle = $str->forcedgroupsseparate;
             $groupclass = 'editing_groupsseparate';
             $groupimage = 't/groups';
         } else if ($mod->groupmode == VISIBLEGROUPS) {
             $groupmode = 1;
             $grouptitle = $str->groupsvisible;
+            $forcedgrouptitle = $str->forcedgroupsvisible;
             $groupclass = 'editing_groupsvisible';
             $groupimage = 't/groupv';
         } else {
             $groupmode = 2;
             $grouptitle = $str->groupsnone;
+            $forcedgrouptitle = $str->forcedgroupsnone;
             $groupclass = 'editing_groupsnone';
             $groupimage = 't/groupn';
         }
@@ -3238,10 +3252,10 @@ function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $movesele
                 new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
                 new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall')),
                 null,
-                array('class' => $groupclass, 'title' => $grouptitle.' ('.$str->clicktochange.')')
+                array('class' => $groupclass, 'title' => $grouptitle)
             );
         } else {
-            $actions[] = new pix_icon($groupimage, $grouptitle, 'moodle', array('title' => $grouptitle.' ('.$str->forcedmode.')', 'class' => 'iconsmall'));
+            $actions[] = new pix_icon($groupimage, $forcedgrouptitle, 'moodle', array('title' => $forcedgrouptitle, 'class' => 'iconsmall'));
         }
     }
 
index 5865e4a..9ed3f5d 100644 (file)
@@ -316,6 +316,9 @@ if ($mform->is_cancelled()) {
         $fromform->completiongradeitemnumber = null;
     }
 
+    // the type of event to trigger (mod_created/mod_updated)
+    $eventname = '';
+
     if (!empty($fromform->update)) {
 
         if (!empty($course->groupmodeforce) or !isset($fromform->groupmode)) {
@@ -380,14 +383,7 @@ if ($mform->is_cancelled()) {
             $completion->reset_all_state($cm);
         }
 
-        // Trigger mod_updated event with information about this module.
-        $eventdata = new stdClass();
-        $eventdata->modulename = $fromform->modulename;
-        $eventdata->name       = $fromform->name;
-        $eventdata->cmid       = $fromform->coursemodule;
-        $eventdata->courseid   = $course->id;
-        $eventdata->userid     = $USER->id;
-        events_trigger('mod_updated', $eventdata);
+        $eventname = 'mod_updated';
 
         add_to_log($course->id, "course", "update mod",
                    "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule",
@@ -492,14 +488,7 @@ if ($mform->is_cancelled()) {
             condition_info::update_cm_from_form((object)array('id'=>$fromform->coursemodule), $fromform, false);
         }
 
-        // Trigger mod_created event with information about this module.
-        $eventdata = new stdClass();
-        $eventdata->modulename = $fromform->modulename;
-        $eventdata->name       = $fromform->name;
-        $eventdata->cmid       = $fromform->coursemodule;
-        $eventdata->courseid   = $course->id;
-        $eventdata->userid     = $USER->id;
-        events_trigger('mod_created', $eventdata);
+        $eventname = 'mod_created';
 
         add_to_log($course->id, "course", "add mod",
                    "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule",
@@ -511,6 +500,15 @@ if ($mform->is_cancelled()) {
         print_error('invaliddata');
     }
 
+    // Trigger mod_created/mod_updated event with information about this module.
+    $eventdata = new stdClass();
+    $eventdata->modulename = $fromform->modulename;
+    $eventdata->name       = $fromform->name;
+    $eventdata->cmid       = $fromform->coursemodule;
+    $eventdata->courseid   = $course->id;
+    $eventdata->userid     = $USER->id;
+    events_trigger($eventname, $eventdata);
+
     // sync idnumber with grade_item
     if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename,
                  'iteminstance'=>$fromform->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) {
index 9e186ce..123f438 100644 (file)
@@ -172,26 +172,8 @@ class core_enrol_external extends external_api {
             }
         }
 
-        // to overwrite this parameter, you need role:review capability
-        if ($withcapability) {
-            require_capability('moodle/role:review', $coursecontext);
-        }
-        // need accessallgroups capability if you want to overwrite this option
-        if (!empty($groupid) && groups_is_member($groupid)) {
-            require_capability('moodle/site:accessallgroups', $context);
-        }
-        // to overwrite this option, you need course:enrolereview permission
-        if ($onlyactive) {
-            require_capability('moodle/course:enrolreview', $coursecontext);
-        }
-
-        list($coursectxselect, $coursectxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
-        $coursesql = "SELECT c.* $coursectxselect
-                        FROM {course} c $coursectxjoin
-                       WHERE c.id = $courseid";
-        $course = $DB->get_record_sql($coursesql);
-        context_instance_preload($course);
-        $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']);
+        $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+        $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
         if ($courseid == SITEID) {
             $context = get_system_context();
         } else {
@@ -206,9 +188,26 @@ class core_enrol_external extends external_api {
             throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
         }
 
+        if ($courseid == SITEID) {
+            require_capability('moodle/site:viewparticipants', $context);
+        } else {
+            require_capability('moodle/course:viewparticipants', $context);
+        }
+        // to overwrite this parameter, you need role:review capability
+        if ($withcapability) {
+            require_capability('moodle/role:review', $coursecontext);
+        }
+        // need accessallgroups capability if you want to overwrite this option
+        if (!empty($groupid) && groups_is_member($groupid)) {
+            require_capability('moodle/site:accessallgroups', $coursecontext);
+        }
+        // to overwrite this option, you need course:enrolereview permission
+        if ($onlyactive) {
+            require_capability('moodle/course:enrolreview', $coursecontext);
+        }
+
         list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
         list($ctxselect, $ctxjoin) = context_instance_preload_sql('u.id', CONTEXT_USER, 'ctx');
-        $records = $DB->get_records_sql($enrolledsql, $enrolledparams);
         $sqlparams['courseid'] = $courseid;
         $sql = "SELECT u.* $ctxselect
                   FROM {user} u $ctxjoin
@@ -217,9 +216,6 @@ class core_enrol_external extends external_api {
         $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams);
         $users = array();
         foreach ($enrolledusers as $user) {
-            if (!empty($user->deleted)) {
-                continue;
-            }
             context_instance_preload($user);
             if ($userdetails = user_get_user_details($user, $course, $userfields)) {
                 $users[] = $userdetails;
index ace7435..284608a 100644 (file)
@@ -590,6 +590,8 @@ function process_membership_tag($tagcontents){
     // In order to reduce the number of db queries required, group name/id associations are cached in this array:
     $groupids = array();
 
+    $ship = new stdClass();
+
     if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)){
         $ship->coursecode = ($truncatecoursecodes > 0)
                                  ? substr(trim($matches[1]), 0, intval($truncatecoursecodes))
@@ -601,8 +603,8 @@ function process_membership_tag($tagcontents){
         $courseobj->id = $ship->courseid;
 
         foreach($membermatches as $mmatch){
-            unset($member);
-            unset($memberstoreobj);
+            $member = new stdClass();
+            $memberstoreobj = new stdClass();
             if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $mmatch[1], $matches)){
                 $member->idnumber = trim($matches[1]);
             }
@@ -622,6 +624,7 @@ function process_membership_tag($tagcontents){
               //echo "<p>process_membership_tag: unenrolling member due to recstatus of 3</p>";
             }
 
+            $timeframe = new stdClass();
             $timeframe->begin = 0;
             $timeframe->end = 0;
             if(preg_match('{<role\b.*?<timeframe>(.+?)</timeframe>.*?</role>}is', $mmatch[1], $matches)){
@@ -680,6 +683,7 @@ function process_membership_tag($tagcontents){
                                 $groupids[$member->groupname] = $groupid; // Store ID in cache
                             } else {
                                 // Attempt to create the group
+                                $group = new stdClass();
                                 $group->name = $member->groupname;
                                 $group->courseid = $ship->courseid;
                                 $group->timecreated = time();
@@ -752,6 +756,7 @@ function log_line($string){
 * Process the INNER contents of a <timeframe> tag, to return beginning/ending dates.
 */
 function decode_timeframe($string){ // Pass me the INNER CONTENTS of a <timeframe> tag - beginning and/or ending is returned, in unix time, zero indicating not specified
+    $ret = new stdClass();
     $ret->begin = $ret->end = 0;
     // Explanatory note: The matching will ONLY match if the attribute restrict="1"
     // because otherwise the time markers should be ignored (participation should be
index a2c2d26..e27a4ec 100644 (file)
@@ -461,7 +461,7 @@ class grading_manager {
      * @return grading_controller
      */
     public function get_controller($method) {
-        global $CFG;
+        global $CFG, $DB;
 
         $this->ensure_isset(array('context', 'component', 'area'));
 
index fd4d6ef..779e95c 100644 (file)
@@ -144,8 +144,7 @@ if ($searchdata = $searchform->get_data()) {
 }
 
 // construct the SQL to find all matching templates
-$sql = "SELECT DISTINCT gd.id, gd.areaid, gd.name, gd.description, gd.descriptionformat, gd.timecreated,
-                        gd.usercreated, gd.timemodified, gd.usermodified
+$sql = "SELECT DISTINCT gd.id, gd.areaid, gd.name, gd.usercreated
           FROM {grading_definitions} gd
           JOIN {grading_areas} ga ON (gd.areaid = ga.id)
           JOIN {context} cx ON (ga.contextid = cx.id)";
index 20c78ed..4730ba5 100644 (file)
@@ -342,10 +342,6 @@ text-align:center;
 clear:both;
 }
 
-.path-grade-report-grader form {
-text-align:center;
-}
-
 .path-grade-report-grader input.center {
 margin:10px auto 0;
 }
index cf8b757..b610817 100644 (file)
@@ -119,7 +119,14 @@ class grade_report_overview extends grade_report {
                 if (!$course->showgrades) {
                     continue;
                 }
+
                 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+
+                if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                    // The course is hidden and the user isn't allowed to see it
+                    continue;
+                }
+
                 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
                 $courselink = html_writer::link(new moodle_url('/grade/report/user/index.php', array('id' => $course->id, 'userid' => $this->user->id)), $courseshortname);
                 $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext);
index a575c33..42fc1dc 100644 (file)
@@ -172,11 +172,10 @@ if ($editform->is_cancelled()) {
 
         // prepare grouping
         if (!empty($data->grouping)) {
-            $groupingname = trim($data->groupingname);
             if ($data->grouping < 0) {
                 $grouping = new stdClass();
                 $grouping->courseid = $COURSE->id;
-                $grouping->name     = $groupingname;
+                $grouping->name     = trim($data->groupingname);
                 $grouping->id = groups_create_grouping($grouping);
                 $createdgrouping = $grouping->id;
             } else {
index 160dcbf..7ff57d5 100644 (file)
@@ -267,7 +267,6 @@ class core_group_external extends external_api {
 
         $transaction = $DB->start_delegated_transaction();
 
-// TODO: this is problematic because the DB rollback does not handle deleting of group images!
         foreach ($params['groupids'] as $groupid) {
             // validate params
             $groupid = validate_param($groupid, PARAM_INTEGER);
index 232abdc..99bae5f 100644 (file)
--- a/index.php
+++ b/index.php
 
     $hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
 
-    $PAGE->set_url('/');
+    $urlparams = array();
+    if ($CFG->defaulthomepage == HOMEPAGE_MY && optional_param('redirect', 1, PARAM_BOOL) === 0) {
+        $urlparams['redirect'] = 0;
+    }
+    $PAGE->set_url('/', $urlparams);
     $PAGE->set_course($SITE);
 
 /// If the site is currently under maintenance, then print a message
index c413982..f503f4d 100644 (file)
@@ -31,3 +31,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['language'] = 'Dil';
+$string['next'] = 'Növbəti';
+$string['previous'] = 'Əvvələ';
+$string['reload'] = 'Yenilə';
index d26007c..c1a2188 100644 (file)
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
 $string['admindirname'] = 'Admin-Verzeichnis';
 $string['availablelangs'] = 'Verfügbare Sprachpakete';
 $string['chooselanguagehead'] = 'Sprache wählen';
-$string['chooselanguagesub'] = 'Wählen Sie eine Sprache, die Sie während der Installation verwenden wollen. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber können Sie die Sprache jederzeit ändern.';
+$string['chooselanguagesub'] = 'Wählen Sie eine Sprache, die Sie während der Installation verwenden wollen. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber Sie dürfen die Sprache jederzeit ändern.';
 $string['clialreadyconfigured'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/install_database.php, wenn sie diese Website installieren möchten.';
 $string['clialreadyinstalled'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/upgrade.php, wenn Sie diese Website aktualisieren möchten.';
 $string['cliinstallheader'] = 'Installation von Moodle {$a} über die Kommandozeile';
@@ -45,7 +45,7 @@ $string['datarootpermission'] = 'Zugriffsrechte zum Datenverzeichnis';
 $string['dbprefix'] = 'Tabellen-Prefix';
 $string['dirroot'] = 'Moodle-Verzeichnis';
 $string['environmenthead'] = 'Installationsvoraussetzungen werden geprüft ...';
-$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für einige verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird immer eine vollständige Prüfung der Serverausstattung durchgeführt. Bitte fragen Sie den Administrator Ihres Servers, wenn Sie mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommen.';
+$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird eine vollständige Prüfung durchgeführt. Bitte fragen Sie den Server-Administrator, wenn Sie mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommen.';
 $string['errorsinenvironment'] = 'Fehler bei der Prüfung der Systemvoraussetzungen!';
 $string['installation'] = 'Installation';
 $string['langdownloaderror'] = 'Leider konnte das Sprachpaket \'{$a}\' nicht heruntergeladen werden. Die Installation wird in englischer Sprache fortgesetzt.';
index 8f803a6..63609e1 100644 (file)
@@ -30,7 +30,8 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['chooselanguagesub'] = 'Wähle eine Sprache, die du während der Installation verwenden möchtest. Nach der Installation kannst du die Sprache für die Oberfläche und die Nutzer/innen festlegen.';
+$string['chooselanguagesub'] = 'Wähle eine Sprache, die du während der Installation verwenden möchtest. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber du darfst die Sprache jederzeit ändern.';
+$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird eine vollständige Prüfung durchgeführt. Bitte frage den Server-Administrator, wenn du mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommst.';
 $string['memorylimithelp'] = '<p>Die PHP-Einstellung memory_limit für deinen Server ist zur Zeit auf {$a} eingestellt. </p>
 <p>Dies wird vermutlich zu Problemen führen, wenn du Moodle mit vielen Aktivitäten oder vielen Nutzer/innen verwendst. </p>
 <p>Wir empfehlen die Einstellung zu erhöhen. Empfohlen werden 40M oder mehr. Dies kannst du auf verschiedene Arten machen:</p>
index 454dc47..6610cff 100644 (file)
@@ -31,5 +31,5 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['parentlanguage'] = 'de_du';
-$string['thisdirection'] = '';
+$string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Deutsch - Kids';
index e279c29..ac6a0d4 100644 (file)
@@ -34,6 +34,9 @@ $string['admindirname'] = 'ספריית מנהל המערכת';
 $string['availablelangs'] = 'חבילות שפה זמינות';
 $string['chooselanguagehead'] = 'בחר שפה';
 $string['chooselanguagesub'] = 'אנא בחר שפה עבור ההתקנה בלבד. תוכל לבחור בשפה שונה לאתר ולמשתמש באחד מהמסכים הבאים.';
+$string['clialreadyconfigured'] = 'קובץ config.php כבר קיים, אנא השתמש ב
+admin/cli/install_database.php
+אם ברצונך להתקין את אתר זה.';
 $string['clialreadyinstalled'] = 'קובץ ה-config.php קיים כבר, אנא השתמש ב- admin/cli/upgrade.php
 אם ברצונך לשדרג את האתר שלך.';
 $string['cliinstallheader'] = 'תוכנית התקנת Moodle {$a} בשורת הפקודה';
@@ -41,6 +44,7 @@ $string['databasehost'] = 'מסד הנתונים המארח (host)';
 $string['databasename'] = 'שם מסד הנתונים';
 $string['databasetypehead'] = 'בחר התקן מסד הנתונים';
 $string['dataroot'] = 'ספריית הנתונים';
+$string['datarootpermission'] = 'הרשאות תיקיות bתונים (data)';
 $string['dbprefix'] = 'Tables prefix';
 $string['dirroot'] = 'ספריית ה-Moodle';
 $string['environmenthead'] = 'בודק את הסביבה שלך...';
@@ -88,8 +92,7 @@ $string['phpversionhelp'] = '<p>גירסת PHP חייבת להיות לפחות
 (במקרים של גרסת 5.0.x תוכל גם לרדת בגירסה ל- 4.4.x)
 </p>';
 $string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
-$string['welcomep20'] = 'הינך רואה את עמוד זה מפני שהתקנת והפעלת בהלכה את <strong> $a-packname {$a->packversion} 
-</strong>
+$string['welcomep20'] = 'הינך רואה את עמוד זה מפני שהתקנת והפעלת בהלכה את <strong>{$a->packname} {$a->packversion}</strong>
 חבילה במחשבך. ברכותינו!';
 $string['welcomep30'] = 'גירסת <strong>{$a->installername}</strong> כוללת את היישומים ליצור סביבה אשר בה <strong> Moodle </strong>
 יפעל דהיינו:';
diff --git a/install/lang/tt/langconfig.php b/install/lang/tt/langconfig.php
new file mode 100644 (file)
index 0000000..813287a
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle 2.3dev installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['thislanguage'] = 'татар теле';
index ba6d8f7..dbf6a26 100644 (file)
@@ -242,6 +242,7 @@ $string['configmaxevents'] = 'Events to Lookahead';
 $string['configmemcachedhosts'] = 'For memcached. Comma-separated list of hosts that are running the memcached daemon. Use IP addresses to avoid DNS latency. memcached does not behave well if you add/remove hosts on a running setup.';
 $string['configmemcachedpconn'] = 'For memcached. Use persistent connections. Use carefully -- it can make Apache/PHP crash after a restart of the memcached daemon.';
 $string['configmessaging'] = 'Should the messaging system between site users be enabled?';
+$string['configmessagingallowemailoverride'] = 'Allow users to have email message notifications sent to an email address other than the email address in their profile';
 $string['configmessaginghidereadnotifications'] = 'Hide read notifications of events like forum posts when viewing messaging history';
 $string['configmessagingdeletereadnotificationsdelay'] = 'Read notifications can be deleted to save space. How long after a notification is read can it be deleted?';
 $string['configminpassworddigits'] = 'Passwords must have at least these many digits.';
@@ -330,7 +331,7 @@ For example:  standard,orangewhite.';
 $string['configtimezone'] = 'You can set the default timezone here.  This is the only the DEFAULT timezone for displaying dates - each user can override this by setting their own in their profile. "Server time" here will make Moodle default to the server\'s operating system setting, but "Server time" in the user profile will make the user default to this timezone setting.  Cronjobs that depend on a time of day to run will use this timezone.';
 $string['configunzip'] = 'Indicate the location of your unzip program (Unix only, optional).  If specified, this will be used to unpack zip archives on the server.  If you leave this blank, then Moodle will use internal routines.';
 $string['configuseblogassociations'] = 'Should users be able to organize their blog by associating entries with courses and course modules?';
-$string['configuseexternalyui'] = 'Instead of using local files, use online files available on Yahoo&#145;s servers. WARNING: This requires an internet connection, or no AJAX will work on your site.';
+$string['configuseexternalyui'] = 'Instead of using local files, use online files available on Yahoo&#145;s servers. WARNING: This requires an internet connection, or no AJAX will work on your site. This setting is not compatible with sites using https.';
 $string['configusesitenameforsitepages'] = 'If enabled the site\'s shortname will be used for the site pages node in the navigation rather than the string \'Site pages\'';
 $string['configusetags'] = 'Should tags functionality across the site be enabled?';
 $string['configvariables'] = 'Variables';
@@ -466,6 +467,8 @@ $string['enablecomments'] = 'Enable comments';
 $string['enablecourseajax'] = 'Enable AJAX course editing';
 $string['enablecourseajax_desc'] = 'Allow AJAX when editing main course pages. Note that the course format and the theme must support AJAX editing and the user has to enable AJAX in their profiles, too.';
 $string['enablecourserequests'] = 'Enable course requests';
+$string['enablecssoptimiser'] = 'Enable CSS optimiser';
+$string['enablecssoptimiser_desc'] = 'When enabled CSS will be run through an optimisation process before being cached. The optimiser processes the CSS removing duplicate rules and styles, as well as white space removeable and reformatting. Please note turning this on at the same time as theme designer mode is aweful for performance but will help theme designers create optimised CSS.';
 $string['enabledevicedetection'] = 'Enable device detection';
 $string['enablegravatar'] = 'Enable Gravatar';
 $string['enablegravatar_help'] = 'When enabled Moodle will attempt to fetch a user profile picture from Gravatar if the user has not uploaded an image.';
@@ -660,6 +663,7 @@ $string['mediapluginyoutube'] = 'Enable YouTube links filter';
 $string['memcachedhosts'] = 'memcached hosts';
 $string['memcachedpconn'] = 'memcached use persistent connections';
 $string['messaging'] = 'Enable messaging system';
+$string['messagingallowemailoverride'] = 'Notification email override';
 $string['messaginghidereadnotifications'] = 'Hide read notifications';
 $string['messagingdeletereadnotificationsdelay'] = 'Delete read notifications';
 $string['minpassworddigits'] = 'Digits';
@@ -971,6 +975,8 @@ $string['upgradelogs'] = 'For full functionality, your old logs need to be upgra
 $string['upgradelogsinfo'] = 'Some changes have recently been made in the way logs are stored.  To be able to view all of your old logs on a per-activity basis, your old logs need to be upgraded.  Depending on your site this can take a long time (eg several hours) and can be quite taxing on the database for large sites.  Once you start this process you should let it finish (by keeping the browser window open).  Don\'t worry - your site will work fine for other people while the logs are being upgraded.<br /><br />Do you want to upgrade your logs now?';
 $string['upgradesettings'] = 'New settings';
 $string['upgradesettingsintro'] = 'The settings shown below were added during your last Moodle upgrade. Make any changes necessary to the defaults and then click the &quot;Save changes&quot; button at the bottom of this page.';
+$string['upgradestalefiles'] = 'Invalid installation files detected, upgrade cannot continue';
+$string['upgradestalefilesinfo'] = 'Some old PHP scripts have been detected which may indicate that you installed this version over an older one. Please fix the installation directory by removing all old scripts (except config.php) before installing the new version and then try the upgrade again. You can find more information in upgrade documentation at <a href="{$a}">{$a}</a>';
 $string['upgradesure'] = 'Your Moodle files have been changed, and you are about to automatically upgrade your server to this version: <br /><br />
 <strong>{$a}</strong> <br /><br />
 Once you do this you can not go back again. <br /><br />
index 1e0245c..eb032e8 100644 (file)
  */
 
 $string['AED'] = 'United Arab Emirates Dirham';
-$string['AFA'] = 'Afghanistan Afghani';
+$string['AFN'] = 'Afghanistan Afghani';
 $string['ALL'] = 'Albanian Lek';
-$string['ANG'] = 'Netherlands Antillian Guilder';
-$string['AON'] = 'Angolan New Kwanza';
-$string['ARA'] = 'Argentinian Nuevo Peso';
+$string['AMD'] = 'Armenian Dram';
+$string['ANG'] = 'Netherlands Antillean Guilder';
+$string['AOA'] = 'Angolan Kwanza';
+$string['ARS'] = 'Argentine Peso';
 $string['AUD'] = 'Australian Dollar';
 $string['AWG'] = 'Aruban Florin';
+$string['AZN'] = 'Azerbaijanian Manat';
+$string['BAM'] = 'Bosnia and Herzegovina Convertible Mark';
 $string['BBD'] = 'Barbados Dollar';
 $string['BDT'] = 'Bangladeshi Taka';
-$string['BGL'] = 'Bulgarian Lev';
+$string['BGN'] = 'Bulgarian Lev';
 $string['BHD'] = 'Bahraini Dinar';
 $string['BIF'] = 'Burundi Franc';
 $string['BMD'] = 'Bermudian Dollar';
 $string['BND'] = 'Brunei Dollar';
 $string['BOB'] = 'Bolivian Boliviano';
-$string['BRR'] = 'Brazilian Cruzeiro Real';
+$string['BRL'] = 'Brazilian Real';
 $string['BSD'] = 'Bahamian Dollar';
-$string['BTN'] = 'Bhutan Ngultrum';
-$string['BUK'] = 'Myanmar Kyat';
-$string['BWP'] = 'Botswanian Pula';
+$string['BTN'] = 'Bhutanese Ngultrum';
+$string['BWP'] = 'Botswana Pula';
+$string['BYR'] = 'Belarusian Ruble';
 $string['BZD'] = 'Belize Dollar';
 $string['CAD'] = 'Canadian Dollar';
-$string['CDZ'] = 'Zaire New Zaire';
+$string['CDF'] = 'Congolese Franc';
+$string['CHF'] = 'Swiss Franc';
 $string['CLF'] = 'Chilean Unidades de Fomento';
 $string['CLP'] = 'Chilean Peso';
-$string['CNY'] = 'Yuan (Chinese) Renminbi';
+$string['CNY'] = 'Chinese Yuan (Renminbi)';
 $string['COP'] = 'Colombian Peso';
 $string['CRC'] = 'Costa Rican Colon';
-$string['CSD'] = 'Serbian Dinar';
+$string['CUC'] = 'Cuban Convertible Peso';
 $string['CUP'] = 'Cuban Peso';
-$string['CVE'] = 'Escudo Caboverdiano';
-$string['CYP'] = 'Cyprus Pound';
+$string['CVE'] = 'Cape Verde Escudo';
 $string['CZK'] = 'Czech Koruna';
 $string['DJF'] = 'Djibouti Franc';
 $string['DKK'] = 'Danish Krone';
 $string['DOP'] = 'Dominican Peso';
 $string['DZD'] = 'Algerian Dinar';
 $string['EGP'] = 'Egyptian Pound';
+$string['ERN'] = 'Eritrean Nakfa';
 $string['ETB'] = 'Ethiopian Birr';
 $string['EUR'] = 'Euro';
 $string['FJD'] = 'Fiji Dollar';
 $string['FKP'] = 'Falkland Islands Pound';
-$string['GBP'] = 'British Pound';
-$string['GHC'] = 'Ghanaian Cedi';
+$string['GBP'] = 'British Pound Sterling';
+$string['GEL'] = 'Georgian Lari';
+$string['GHS'] = 'Ghanaian Cedi';
 $string['GIP'] = 'Gibraltar Pound';
 $string['GMD'] = 'Gambian Dalasi';
 $string['GNF'] = 'Guinea Franc';
 $string['GTQ'] = 'Guatemalan Quetzal';
-$string['GWP'] = 'Guinea-Bissau Peso';
-$string['GYD'] = 'Guyanan Dollar';
+$string['GYD'] = 'Guyanese Dollar';
 $string['HKD'] = 'Hong Kong Dollar';
 $string['HNL'] = 'Honduran Lempira';
+$string['HRK'] = 'Croatian Kuna';
 $string['HTG'] = 'Haitian Gourde';
 $string['HUF'] = 'Hungarian Forint';
-$string['CHF'] = 'Swiss Franc';
 $string['IDR'] = 'Indonesian Rupiah';
-$string['ILS'] = 'Israeli Shekel';
+$string['ILS'] = 'Israeli Sheqel';
 $string['INR'] = 'Indian Rupee';
 $string['IQD'] = 'Iraqi Dinar';
 $string['IRR'] = 'Iranian Rial';
@@ -88,79 +92,94 @@ $string['ISK'] = 'Iceland Krona';
 $string['JMD'] = 'Jamaican Dollar';
 $string['JOD'] = 'Jordanian Dinar';
 $string['JPY'] = 'Japanese Yen';
-$string['KES'] = 'Kenyan Schilling';
+$string['KES'] = 'Kenyan Shilling';
+$string['KGS'] = 'Kyrgyzstani Som';
 $string['KHR'] = 'Cambodian Riel';
 $string['KMF'] = 'Comoros Franc';
 $string['KPW'] = 'North Korean Won';
 $string['KRW'] = 'South Korean Won';
 $string['KWD'] = 'Kuwaiti Dinar';
 $string['KYD'] = 'Cayman Islands Dollar';
+$string['KZT'] = 'Kazakhstani Tenge';
 $string['LAK'] = 'Lao Kip';
 $string['LBP'] = 'Lebanese Pound';
 $string['LKR'] = 'Sri Lanka Rupee';
 $string['LRD'] = 'Liberian Dollar';
 $string['LSL'] = 'Lesotho Loti';
+$string['LTL'] = 'Lithuanian Litas';
+$string['LVL'] = 'Latvian Lats';
 $string['LYD'] = 'Libyan Dinar';
 $string['MAD'] = 'Moroccan Dirham';
-$string['MGF'] = 'Malagasy Franc';
+$string['MDL'] = 'Moldovan Leu';
+$string['MGA'] = 'Malagasy Ariary';
+$string['MKD'] = 'Makedonian Denar';
+$string['MMK'] = 'Myanmar (Burmese) Kyat';
 $string['MNT'] = 'Mongolian Tugrik';
 $string['MOP'] = 'Macau Pataca';
 $string['MRO'] = 'Mauritanian Ouguiya';
-$string['MTL'] = 'Maltese Lira';
 $string['MUR'] = 'Mauritius Rupee';
-$string['MVR'] = 'Maldive Rufiyaa';
+$string['MVR'] = 'Maldivian Rufiyaa';
 $string['MWK'] = 'Malawi Kwacha';
 $string['MXN'] = 'Mexican Peso';
 $string['MYR'] = 'Malaysian Ringgit';
-$string['MZM'] = 'Mozambique Metical';
+$string['MZN'] = 'Mozambican Metical';
+$string['NAD'] = 'Namibian Dollar';
 $string['NGN'] = 'Nigerian Naira';
-$string['NIO'] = 'Nicaraguan Cordoba';
-$string['NOK'] = 'Norwegian Kroner';
+$string['NIO'] = 'Nicaraguan Cordoba Oro';
+$string['NOK'] = 'Norwegian Krone';
 $string['NPR'] = 'Nepalese Rupee';
 $string['NZD'] = 'New Zealand Dollar';
 $string['OMR'] = 'Omani Rial';
 $string['PAB'] = 'Panamanian Balboa';
-$string['PEN'] = 'Nuevo Sol';
+$string['PEN'] = 'Peruvian Nuevo Sol';
 $string['PGK'] = 'Papua New Guinea Kina';
 $string['PHP'] = 'Philippine Peso';
 $string['PKR'] = 'Pakistan Rupee';
-$string['PLN'] = 'Polish New Zloty';
-$string['PYG'] = 'Paraguay Guarani';
+$string['PLN'] = 'Polish Zloty';
+$string['PYG'] = 'Paraguayan Guarani';
 $string['QAR'] = 'Qatari Rial';
-$string['ROL'] = 'Romanian Leu';
+$string['RON'] = 'Romanian Leu';
+$string['RSD'] = 'Serbian Dinar';
+$string['RUB'] = 'Russian Ruble';
 $string['RWF'] = 'Rwanda Franc';
 $string['SAR'] = 'Saudi Arabian Riyal';
 $string['SBD'] = 'Solomon Islands Dollar';
 $string['SCR'] = 'Seychelles Rupee';
-$string['SDP'] = 'Sudanese Pound';
+$string['SDG'] = 'Sudanese Pound';
 $string['SEK'] = 'Swedish Krona';
 $string['SGD'] = 'Singapore Dollar';
 $string['SHP'] = 'St. Helena Pound';
-$string['SKK'] = 'Slovak Koruna';
-$string['SLL'] = 'Sierra Leone Leone';
-$string['SOS'] = 'Somali Schilling';
-$string['SRG'] = 'Suriname Guilder';
+$string['SLL'] = 'Sierra Leonean Leone';
+$string['SOS'] = 'Somali Shilling';
+$string['SRD'] = 'Surinamese Dollar';
+$string['SSP'] = 'South Sudanese Pound';
 $string['STD'] = 'Sao Tome and Principe Dobra';
-$string['SUR'] = 'USSR Rouble';
 $string['SVC'] = 'El Salvador Colon';
-$string['SYP'] = 'Syrian Potmd';
-$string['SZL'] = 'Swaziland Lilangeni';
-$string['THB'] = 'Thai Bhat';
+$string['SYP'] = 'Syrian Pound';
+$string['SZL'] = 'Swazi Lilangeni';
+$string['THB'] = 'Thai Baht';
+$string['TJS'] = 'Tajikistani Somoni';
+$string['TMT'] = 'Turkmenistani Manat';
 $string['TND'] = 'Tunisian Dinar';
 $string['TOP'] = 'Tongan Pa\'anga';
-$string['TPE'] = 'East Timor Escudo';
-$string['TRL'] = 'Turkish Lira';
+$string['TRY'] = 'Turkish Lira';
 $string['TTD'] = 'Trinidad and Tobago Dollar';
 $string['TWD'] = 'Taiwan Dollar';
-$string['TZS'] = 'Tanzanian Schilling';
-$string['UGS'] = 'Uganda Shilling';
+$string['TZS'] = 'Tanzanian Shilling';
+$string['UAH'] = 'Ukrainian Hryvnia';
+$string['UGX'] = 'Ugandan Shilling';
 $string['USD'] = 'US Dollar';
-$string['UYU'] = 'Uruguayan New Peso';
-$string['VEB'] = 'Venezualan Bolivar';
+$string['UYU'] = 'Uruguayan Peso';
+$string['UZS'] = 'Uzbekistani Sum';
+$string['VEF'] = 'Venezuelan Bolivar Fuerte';
 $string['VND'] = 'Vietnamese Dong';
 $string['VUV'] = 'Vanuatu Vatu';
 $string['WST'] = 'Samoan Tala';
-$string['YER'] = 'Yemeni Riyal';
+$string['XAF'] = 'CFA Franc BEAC';
+$string['XCD'] = 'East Caribbean Dollar';
+$string['XOF'] = 'CFA Franc BCEAO';
+$string['XPF'] = 'CFP Franc';
+$string['YER'] = 'Yemeni Rial';
 $string['ZAR'] = 'South African Rand';
 $string['ZMK'] = 'Zambian Kwacha';
-$string['ZWD'] = 'Zimbabwe Dollar';
+$string['ZWL'] = 'Zimbabwe Dollar';
index ee10b9b..bd54f12 100644 (file)
@@ -435,6 +435,7 @@ $string['rpcerror'] = 'Ooops! Your MNET communication has failed! Here\'s that e
 $string['secretalreadyused'] = 'Change password confirmation link was already used, password was not changed';
 $string['sectionnotexist'] = 'This section does not exist';
 $string['sendmessage'] = 'Send message';
+$string['serverconnection'] = 'Error connecting to the server';
 $string['servicedonotexist'] = 'The service does not exist';
 $string['sessionwaiterr'] = 'Timed out while waiting for session lock.<br />Wait for your current requests to finish and try again later.';
 $string['sessioncookiesdisable'] = 'Incorrect use of require_key_login() - session cookies must be disabled!';
index 7f8fb12..0286c52 100644 (file)
@@ -236,7 +236,7 @@ $string['clear'] = 'Clear';
 $string['clickhelpiconformoreinfo'] = '... continues ... Click on the help icon to read the full article';
 $string['clickhere'] = 'Click here ...';
 $string['clicktohideshow'] = 'Click to expand or collapse';
-$string['clicktochange'] = 'Click to change';
+$string['clicktochangeinbrackets'] = '{$a} (Click to change)';
 $string['closewindow'] = 'Close this window';
 $string['collapseall'] = 'Collapse all';
 $string['commentincontext'] = 'Find this comment in context';
@@ -451,9 +451,9 @@ $string['displayingfirst'] = 'Only the first {$a->count} {$a->things} are displa
 $string['displayingrecords'] = 'Displaying {$a} records';
 $string['displayingusers'] = 'Displaying users {$a->start} to {$a->end}';
 $string['displayonpage'] = 'Display on page';
-$string['dndenabled'] = 'You can drag and drop files into this box to upload them';
+$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_single'] = 'you can drag and drop a file into this box to upload it';
+$string['dndenabled_insentence'] = 'drag and drop available';
 $string['documentation'] = 'Moodle documentation';
 $string['down'] = 'Down';
 $string['download'] = 'Download';
@@ -672,7 +672,7 @@ $string['folderopened'] = 'Opened folder';
 $string['followingoptional'] = 'The following items are optional';
 $string['followingrequired'] = 'The following items are required';
 $string['force'] = 'Force';
-$string['forcedmode'] = 'forced mode';
+$string['forcedmodeinbrackets'] = '{$a} (forced mode)';
 $string['forcelanguage'] = 'Force language';
 $string['forceno'] = 'Do not force';
 $string['forcepasswordchange'] = 'Force password change';
index 1d82d23..48a68b7 100644 (file)
@@ -99,6 +99,7 @@ $string['deletequestioncheck'] = 'Are you absolutely sure you want to delete \'{
 $string['deletequestionscheck'] = 'Are you absolutely sure you want to delete the following questions?<br /><br />{$a}';
 $string['deletingbehaviour'] = 'Deleting question behaviour \'{$a}\'';
 $string['deletingqtype'] = 'Deleting question type \'{$a}\'';
+$string['didnotmatchanyanswer'] = '[Did not match any answer]';
 $string['disabled'] = 'Disabled';
 $string['disterror'] = 'The distribution {$a} caused problems';
 $string['donothing'] = 'Don\'t copy or move files or change links.';
index 18edded..9ca574d 100644 (file)
@@ -1002,6 +1002,7 @@ function get_empty_accessdata() {
     $accessdata['rdef_lcc']   = 0;       // rdef_count during the last compression
     $accessdata['loaded']     = array(); // loaded course contexts
     $accessdata['time']       = time();
+    $accessdata['rsw']        = array();
 
     return $accessdata;
 }
@@ -1149,7 +1150,7 @@ function reload_all_capabilities() {
 
     // copy switchroles
     $sw = array();
-    if (isset($USER->access['rsw'])) {
+    if (!empty($USER->access['rsw'])) {
         $sw = $USER->access['rsw'];
     }
 
@@ -3859,22 +3860,21 @@ function get_user_capability_course($capability, $userid = null, $doanything = t
     // Note the result can be used directly as a context (we are going to), the course
     // fields are just appended.
 
+    $contextpreload = context_helper::get_preload_record_columns_sql('x');
+
     $courses = array();
-    $rs = $DB->get_recordset_sql("SELECT x.*, c.id AS courseid $fieldlist
+    $rs = $DB->get_recordset_sql("SELECT c.id $fieldlist, $contextpreload
                                     FROM {course} c
-                                   INNER JOIN {context} x
-                                         ON (c.id=x.instanceid AND x.contextlevel=".CONTEXT_COURSE.")
+                                    JOIN {context} x ON (c.id=x.instanceid AND x.contextlevel=".CONTEXT_COURSE.")
                                 $orderby");
     // Check capability for each course in turn
-    foreach ($rs as $coursecontext) {
-        if (has_capability($capability, $coursecontext, $userid, $doanything)) {
+    foreach ($rs as $course) {
+        context_helper::preload_from_record($course);
+        $context = context_course::instance($course->id);
+        if (has_capability($capability, $context, $userid, $doanything)) {
             // We've got the capability. Make the record look like a course record
             // and store it
-            $coursecontext->id = $coursecontext->courseid;
-            unset($coursecontext->courseid);
-            unset($coursecontext->contextlevel);
-            unset($coursecontext->instanceid);
-            $courses[] = $coursecontext;
+            $courses[] = $course;
         }
     }
     $rs->close();
@@ -3941,16 +3941,14 @@ function role_switch($roleid, context $context) {
     //
     // Note: it is not possible to switch to roles that do not have course:view
 
-    // Add the switch RA
-    if (!isset($USER->access['rsw'])) {
-        $USER->access['rsw'] = array();
+    if (!isset($USER->access)) {
+        load_all_capabilities();
     }
 
+
+    // Add the switch RA
     if ($roleid == 0) {
         unset($USER->access['rsw'][$context->path]);
-        if (empty($USER->access['rsw'])) {
-            unset($USER->access['rsw']);
-        }
         return true;
     }
 
@@ -6382,6 +6380,7 @@ class context_module extends context {
         }
 
         $modfile = "$CFG->dirroot/mod/$module->name/lib.php";
+        $extracaps = array();
         if (file_exists($modfile)) {
             include_once($modfile);
             $modfunction = $module->name.'_get_extra_capabilities';
@@ -6389,16 +6388,14 @@ class context_module extends context {
                 $extracaps = $modfunction();
             }
         }
-        if (empty($extracaps)) {
-            $extracaps = array();
-        }
 
         $extracaps = array_merge($subcaps, $extracaps);
-
+        $extra = '';
         list($extra, $params) = $DB->get_in_or_equal(
-            $extracaps, SQL_PARAMS_NAMED, 'cap0');
-        $extra = "OR name $extra";
-
+            $extracaps, SQL_PARAMS_NAMED, 'cap0', true, '');
+        if (!empty($extra)) {
+            $extra = "OR name $extra";
+        }
         $sql = "SELECT *
                   FROM {capabilities}
                  WHERE (contextlevel = ".CONTEXT_MODULE."
index 4eaaecb..72d6df3 100644 (file)
@@ -3051,7 +3051,7 @@ class admin_setting_bloglevel extends admin_setting_configselect {
      */
     public function write_setting($data) {
         global $DB, $CFG;
-        if ($data['bloglevel'] == 0) {
+        if ($data == 0) {
             $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
             foreach ($blogblocks as $block) {
                 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
@@ -7896,6 +7896,8 @@ class admin_setting_devicedetectregex extends admin_setting {
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
+    private $excludesystem;
+
     /**
      * Calls parent::__construct - note array $choices is not required
      *
@@ -7903,9 +7905,12 @@ class admin_setting_configmultiselect_modules extends admin_setting_configmultis
      * @param string $visiblename localised setting name
      * @param string $description setting description
      * @param array $defaultsetting a plain array of default module ids
+     * @param bool $excludesystem If true, excludes modules with 'system' archetype
      */
-    public function __construct($name, $visiblename, $description, $defaultsetting = array()) {
+    public function __construct($name, $visiblename, $description, $defaultsetting = array(),
+            $excludesystem = true) {
         parent::__construct($name, $visiblename, $description, $defaultsetting, null);
+        $this->excludesystem = $excludesystem;
     }
 
     /**
@@ -7922,8 +7927,14 @@ class admin_setting_configmultiselect_modules extends admin_setting_configmultis
         global $CFG, $DB;
         $records = $DB->get_records('modules', array('visible'=>1), 'name');
         foreach ($records as $record) {
+            // Exclude modules if the code doesn't exist
             if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
-                $this->choices[$record->id] = $record->name;
+                // Also exclude system modules (if specified)
+                if (!($this->excludesystem &&
+                        plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
+                        MOD_ARCHETYPE_SYSTEM)) {
+                    $this->choices[$record->id] = $record->name;
+                }
             }
         }
         return true;
index 37d0f29..f7d378f 100644 (file)
@@ -149,10 +149,9 @@ class jsportal {
         $output .= "    main.portal.strings['moveleft']='".addslashes_js(get_string('moveleft'))."';\n";
         $output .= "    main.portal.strings['moveright']='".addslashes_js(get_string('moveright'))."';\n";
         $output .= "    main.portal.strings['update']='".addslashes_js(get_string('update'))."';\n";
-        $output .= "    main.portal.strings['groupsnone']='".addslashes_js(get_string('groupsnone'))."';\n";
-        $output .= "    main.portal.strings['groupsseparate']='".addslashes_js(get_string('groupsseparate'))."';\n";
-        $output .= "    main.portal.strings['groupsvisible']='".addslashes_js(get_string('groupsvisible'))."';\n";
-        $output .= "    main.portal.strings['clicktochange']='".addslashes_js(get_string('clicktochange'))."';\n";
+        $output .= "    main.portal.strings['groupsnone']='".addslashes_js(get_string('clicktochangeinbrackets', 'moodle', get_string('groupsnone')))."';\n";
+        $output .= "    main.portal.strings['groupsseparate']='".addslashes_js(get_string('clicktochangeinbrackets', 'moodle', get_string('groupsseparate')))."';\n";
+        $output .= "    main.portal.strings['groupsvisible']='".addslashes_js(get_string('clicktochangeinbrackets', 'moodle', get_string('groupsvisible')))."';\n";
         $output .= "    main.portal.strings['deletecheck']='".addslashes_js(get_string('deletecheckfull','','_var_'))."';\n";
         $output .= "    main.portal.strings['resource']='".addslashes_js(get_string('resource'))."';\n";
         $output .= "    main.portal.strings['activity']='".addslashes_js(get_string('activity'))."';\n";
index cac7cec..c787e3f 100644 (file)
@@ -620,9 +620,9 @@ resource_class.prototype.init_buttons = function() {
     }
 
     // Language strings.
-    var strgroupsnone = main.portal.strings['groupsnone']+' ('+main.portal.strings['clicktochange']+')';
-    var strgroupsseparate = main.portal.strings['groupsseparate']+' ('+main.portal.strings['clicktochange']+')';
-    var strgroupsvisible = main.portal.strings['groupsvisible']+' ('+main.portal.strings['clicktochange']+')';
+    var strgroupsnone = main.portal.strings['groupsnone'];
+    var strgroupsseparate = main.portal.strings['groupsseparate'];
+    var strgroupsvisible = main.portal.strings['groupsvisible'];
 
     this.commandContainer = commandContainer;
     var buttons = commandContainer.getElementsByTagName('a');
@@ -864,13 +864,13 @@ resource_class.prototype.toggle_groupmode = function() {
 
     switch (this.groupmode) {
         case 0:
-            newtitle = main.portal.strings['groupsnone']+' ('+main.portal.strings['clicktochange']+')';
+            newtitle = main.portal.strings['groupsnone'];
             break;
         case 1:
-            newtitle = main.portal.strings['groupsseparate']+' ('+main.portal.strings['clicktochange']+')';
+            newtitle = main.portal.strings['groupsseparate'];
             break;
         case 2:
-            newtitle = main.portal.strings['groupsvisible']+' ('+main.portal.strings['clicktochange']+')';
+            newtitle = main.portal.strings['groupsvisible'];
             break;
     }
 
index 508b94c..90d990c 100644 (file)
@@ -1675,7 +1675,7 @@ function generate_page_type_patterns($pagetype, $parentcontext = null, $currentc
 
     // Ensure that the * pattern is always available if editing block 'at distance', so
     // we always can 'bring back' it to the original context. MDL-30340
-    if ($currentcontext->id != $parentcontext->id && !isset($patterns['*'])) {
+    if ((!isset($currentcontext) or !isset($parentcontext) or $currentcontext->id != $parentcontext->id) && !isset($patterns['*'])) {
         // TODO: We could change the string here, showing its 'bring back' meaning
         $patterns['*'] = get_string('page-x', 'pagetype');
     }
diff --git a/lib/csslib.php b/lib/csslib.php
new file mode 100644 (file)
index 0000000..379ce16
--- /dev/null
@@ -0,0 +1,3584 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains CSS related class, and function for the CSS optimiser
+ *
+ * Please see the {@see css_optimiser} class for greater detail.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Stores CSS in a file at the given path.
+ *
+ * This function either succeeds or throws an exception.
+ *
+ * @param theme_config $theme The theme that the CSS belongs to.
+ * @param string $csspath The path to store the CSS at.
+ * @param array $cssfiles The CSS files to store.
+ */
+function css_store_css(theme_config $theme, $csspath, array $cssfiles) {
+    global $CFG;
+
+    if (!empty($CFG->enablecssoptimiser)) {
+        // This is an experimental feature introduced in Moodle 2.3
+        // The CSS optimiser organises the CSS in order to reduce the overall number
+        // of rules and styles being sent to the client. It does this by collating
+        // the CSS before it is cached removing excess styles and rules and stripping
+        // out any extraneous content such as comments and empty rules.
+        $optimiser = new css_optimiser;
+        $css = '';
+        foreach ($cssfiles as $file) {
+            $css .= file_get_contents($file)."\n";
+        }
+        $css = $theme->post_process($css);
+        $css = $optimiser->process($css);
+
+        // If cssoptimisestats is set then stats from the optimisation are collected
+        // and output at the beginning of the CSS
+        if (!empty($CFG->cssoptimiserstats)) {
+            $css = $optimiser->output_stats_css().$css;
+        }
+    } else {
+        // This is the default behaviour.
+        // The cssoptimise setting was introduced in Moodle 2.3 and will hopefully
+        // in the future be changed from an experimental setting to the default.
+        // The css_minify_css will method will use the Minify library remove
+        // comments, additional whitespace and other minor measures to reduce the
+        // the overall CSS being sent.
+        // However it has the distinct disadvantage of having to minify the CSS
+        // before running the post process functions. Potentially things may break
+        // here if theme designers try to push things with CSS post processing.
+        $css = $theme->post_process(css_minify_css($cssfiles));
+    }
+
+    check_dir_exists(dirname($csspath));
+    $fp = fopen($csspath, 'w');
+    fwrite($fp, $css);
+    fclose($fp);
+}
+
+/**
+ * Sends IE specific CSS
+ *
+ * In writing the CSS parser I have a theory that we could optimise the CSS
+ * then split it based upon the number of selectors to ensure we dont' break IE
+ * and that we include only as many sub-stylesheets as we require.
+ * Of course just a theory but may be fun to code.
+ *
+ * @param string $themename The name of the theme we are sending CSS for.
+ * @param string $rev The revision to ensure we utilise the cache.
+ */
+function css_send_ie_css($themename, $rev) {
+    $lifetime = 60*60*24*30; // 30 days
+
+    $css  = "/** Unfortunately IE6/7 does not support more than 4096 selectors in one CSS file, which means we have to use some ugly hacks :-( **/";
+    $css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=plugins);";
+    $css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=parents);";
+    $css .= "\n@import url(styles.php?theme=$themename&rev=$rev&type=theme);";
+
+    header('Etag: '.md5($rev));
+    header('Content-Disposition: inline; filename="styles.php"');
+    header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
+    header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+    header('Pragma: ');
+    header('Cache-Control: max-age='.$lifetime);
+    header('Accept-Ranges: none');
+    header('Content-Type: text/css; charset=utf-8');
+    header('Content-Length: '.strlen($css));
+
+    echo $css;
+    die;
+}
+
+/**
+ * Sends a cached CSS file
+ *
+ * This function sends the cached CSS file. Remember it is generated on the first
+ * request, then optimised/minified, and finally cached for serving.
+ *
+ * @param string $csspath The path to the CSS file we want to serve.
+ * @param string $rev The revision to make sure we utilise any caches.
+ */
+function css_send_cached_css($csspath, $rev) {
+    $lifetime = 60*60*24*30; // 30 days
+
+    header('Content-Disposition: inline; filename="styles.php"');
+    header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($csspath)) .' GMT');
+    header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+    header('Pragma: ');
+    header('Cache-Control: max-age='.$lifetime);
+    header('Accept-Ranges: none');
+    header('Content-Type: text/css; charset=utf-8');
+    if (!min_enable_zlib_compression()) {
+        header('Content-Length: '.filesize($csspath));
+    }
+
+    readfile($csspath);
+    die;
+}
+
+/**
+ * Sends CSS directly without caching it.
+ *
+ * This function takes a raw CSS string, optimises it if required, and then
+ * serves it.
+ * Turning both themedesignermode and CSS optimiser on at the same time is aweful
+ * for performance because of the optimiser running here. However it was done so
+ * that theme designers could utilise the optimised output during development to
+ * help them optimise their CSS... not that they should write lazy CSS.
+ *
+ * @param string CSS
+ */
+function css_send_uncached_css($css) {
+    global $CFG;
+
+    header('Content-Disposition: inline; filename="styles_debug.php"');
+    header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
+    header('Expires: '. gmdate('D, d M Y H:i:s', time() + THEME_DESIGNER_CACHE_LIFETIME) .' GMT');
+    header('Pragma: ');
+    header('Accept-Ranges: none');
+    header('Content-Type: text/css; charset=utf-8');
+
+    if (is_array($css)) {
+        $css = implode("\n\n", $css);
+    }
+
+    if (!empty($CFG->enablecssoptimiser)) {
+        $css = str_replace("\n", "\r\n", $css);
+
+        $optimiser = new css_optimiser;
+        $css = $optimiser->process($css);
+        if (!empty($CFG->cssoptimiserstats)) {
+            $css = $optimiser->output_stats_css().$css;
+        }
+    }
+
+    echo $css;
+
+    die;
+}
+
+/**
+ * Sends a 404 message about CSS not being found.
+ */
+function css_send_css_not_found() {
+    header('HTTP/1.0 404 not found');
+    die('CSS was not found, sorry.');
+}
+
+/**
+ * Uses the minify library to compress CSS.
+ *
+ * This is used if $CFG->enablecssoptimiser has been turned off. This was
+ * the original CSS optimisation library.
+ * It removes whitespace and shrinks things but does no apparent optimisation.
+ * Note the minify library is still being used for JavaScript.
+ *
+ * @param array $files An array of files to minify
+ * @return string The minified CSS
+ */
+function css_minify_css($files) {
+    global $CFG;
+
+    set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
+    require_once('Minify.php');
+
+    if (0 === stripos(PHP_OS, 'win')) {
+        Minify::setDocRoot(); // IIS may need help
+    }
+    // disable all caching, we do it in moodle
+    Minify::setCache(null, false);
+
+    $options = array(
+        'bubbleCssImports' => false,
+        // Don't gzip content we just want text for storage
+        'encodeOutput' => false,
+        // Maximum age to cache, not used but required
+        'maxAge' => (60*60*24*20),
+        // The files to minify
+        'files' => $files,
+        // Turn orr URI rewriting
+        'rewriteCssUris' => false,
+        // This returns the CSS rather than echoing it for display
+        'quiet' => true
+    );
+    $result = Minify::serve('Files', $options);
+    return $result['content'];
+}
+
+/**
+ * Determines if the given value is a valid CSS colour.
+ *
+ * A CSS colour can be one of the following:
+ *    - Hex colour:  #AA66BB
+ *    - RGB colour:  rgb(0-255, 0-255, 0-255)
+ *    - RGBA colour: rgba(0-255, 0-255, 0-255, 0-1)
+ *    - HSL colour:  hsl(0-360, 0-100%, 0-100%)
+ *    - HSLA colour: hsla(0-360, 0-100%, 0-100%, 0-1)
+ *
+ * Or a recognised browser colour mapping {@see css_optimiser::$htmlcolours}
+ *
+ * @param string $value The colour value to check
+ * @return bool
+ */
+function css_is_colour($value) {
+    $value = trim($value);
+    if (in_array(strtolower($value), array('inherit'))) {
+        return true;
+    } else if (preg_match('/^#([a-fA-F0-9]{1,3}|[a-fA-F0-9]{6})$/', $value)) {
+        return true;
+    } else if (in_array(strtolower($value), array_keys(css_optimiser::$htmlcolours))) {
+        return true;
+    } else if (preg_match('#^rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$#i', $value, $m) && $m[1] < 256 && $m[2] < 256 && $m[3] < 256) {
+        // It is an RGB colour
+        return true;
+    } else if (preg_match('#^rgba\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1}(\.\d+)?)\s*\)$#i', $value, $m) && $m[1] < 256 && $m[2] < 256 && $m[3] < 256) {
+        // It is an RGBA colour
+        return true;
+    } else if (preg_match('#^hsl\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\%\s*,\s*(\d{1,3})\%\s*\)$#i', $value, $m) && $m[1] <= 360 && $m[2] <= 100 && $m[3] <= 100) {
+        // It is an HSL colour
+        return true;
+    } else if (preg_match('#^hsla\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\%\s*,\s*(\d{1,3})\%\s*,\s*(\d{1}(\.\d+)?)\s*\)$#i', $value, $m) && $m[1] <= 360 && $m[2] <= 100 && $m[3] <= 100) {
+        // It is an HSLA colour
+        return true;
+    }
+    // Doesn't look like a colour.
+    return false;
+}
+
+/**
+ * Returns true is the passed value looks like a CSS width.
+ * In order to pass this test the value must be purely numerical or end with a
+ * valid CSS unit term.
+ *
+ * @param string|int $value
+ * @return boolean
+ */
+function css_is_width($value) {
+    $value = trim($value);
+    if (in_array(strtolower($value), array('auto', 'inherit'))) {
+        return true;
+    }
+    if (preg_match('#^(\-\s*)?(\d*\.)?(\d+)\s*(em|px|pt|%|in|cm|mm|ex|pc)?$#i', $value)) {
+        return true;
+    }
+    return false;
+}
+
+/**
+ * A simple sorting function to sort two array values on the number of items they contain
+ *
+ * @param array $a
+ * @param array $b
+ * @return int
+ */
+function css_sort_by_count(array $a, array $b) {
+    $a = count($a);
+    $b = count($b);
+    if ($a == $b) {
+        return 0;
+    }
+    return ($a > $b) ? -1 : 1;
+}
+
+/**
+ * A basic CSS optimiser that strips out unwanted things and then processing the
+ * CSS organising styles and moving duplicates and useless CSS.
+ *
+ * This CSS optimiser works by reading through a CSS string one character at a
+ * time and building an object structure of the CSS.
+ * As part of that processing styles are expanded out as much as they can be to
+ * ensure we collect all mappings, at the end of the processing those styles are
+ * then combined into an optimised form to keep them as short as possible.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_optimiser {
+
+    /**
+     * Used when the processor is about to start processing.
+     * Processing states. Used internally.
+     */
+    const PROCESSING_START = 0;
+
+    /**
+     * Used when the processor is currently processing a selector.
+     * Processing states. Used internally.
+     */
+    const PROCESSING_SELECTORS = 0;
+
+    /**
+     * Used when the processor is currently processing a style.
+     * Processing states. Used internally.
+     */
+    const PROCESSING_STYLES = 1;
+
+    /**
+     * Used when the processor is currently processing a comment.
+     * Processing states. Used internally.
+     */
+    const PROCESSING_COMMENT = 2;
+
+    /**
+     * Used when the processor is currently processing an @ rule.
+     * Processing states. Used internally.
+     */
+    const PROCESSING_ATRULE = 3;
+
+    /**
+     * The raw string length before optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $rawstrlen = 0;
+
+    /**
+     * The number of comments that were removed during optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $commentsincss = 0;
+
+    /**
+     * The number of rules in the CSS before optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $rawrules = 0;
+
+    /**
+     * The number of selectors using in CSS rules before optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $rawselectors = 0;
+
+    /**
+     * The string length after optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $optimisedstrlen = 0;
+
+    /**
+     * The number of rules after optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $optimisedrules = 0;
+
+    /**
+     * The number of selectors used in rules after optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $optimisedselectors = 0;
+
+    /**
+     * The start time of the optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $timestart = 0;
+
+    /**
+     * The end time of the optimisation.
+     * Stats variables set during and after processing
+     * @var int
+     */
+    protected $timecomplete = 0;
+
+    /**
+     * Will be set to any errors that may have occured during processing.
+     * This is updated only at the end of processing NOT during.
+     *
+     * @var array
+     */
+    protected $errors = array();
+
+    /**
+     * Processes incoming CSS optimising it and then returning it.
+     *
+     * @param string $css The raw CSS to optimise
+     * @return string The optimised CSS
+     */
+    public function process($css) {
+        global $CFG;
+
+        $this->reset_stats();
+        $this->timestart = microtime(true);
+        $this->rawstrlen = strlen($css);
+
+        // First up we need to remove all line breaks - this allows us to instantly
+        // reduce our processing requirements and as we will process everything
+        // into a new structure there's really nothing lost.
+        $css = preg_replace('#\r?\n#', ' ', $css);
+
+        // Next remove the comments... no need to them in an optimised world and
+        // knowing they're all gone allows us to REALLY make our processing simpler
+        $css = preg_replace('#/\*(.*?)\*/#m', '', $css, -1, $this->commentsincss);
+
+        $medias = array(
+            'all' => new css_media()
+        );
+        $imports = array();
+        $charset = false;
+
+        $currentprocess = self::PROCESSING_START;
+        $currentrule = css_rule::init();
+        $currentselector = css_selector::init();
+        $inquotes = false;      // ' or "
+        $inbraces = false;      // {
+        $inbrackets = false;    // [
+        $inparenthesis = false; // (
+        $currentmedia = $medias['all'];
+        $currentatrule = null;
+        $suspectatrule = false;
+
+        $buffer = '';
+        $char = null;
+
+        // Next we are going to iterate over every single character in $css.
+        // This is why we removed line breaks and comments!
+        for ($i = 0; $i < $this->rawstrlen; $i++) {
+            $lastchar = $char;
+            $char = substr($css, $i, 1);
+            if ($char == '@' && $buffer == '') {
+                $suspectatrule = true;
+            }
+            switch ($currentprocess) {
+                // Start processing an at rule e.g. @media, @page
+                case self::PROCESSING_ATRULE:
+                    switch ($char) {
+                        case ';':
+                            if (!$inbraces) {
+                                $buffer .= $char;
+                                if ($currentatrule == 'import') {
+                                    $imports[] = $buffer;
+                                    $currentprocess = self::PROCESSING_SELECTORS;
+                                } else if ($currentatrule == 'charset') {
+                                    $charset = $buffer;
+                                    $currentprocess = self::PROCESSING_SELECTORS;
+                                }
+                            }
+                            $buffer = '';
+                            $currentatrule = false;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case '{':
+                            if ($currentatrule == 'media' && preg_match('#\s*@media\s*([a-zA-Z0-9]+(\s*,\s*[a-zA-Z0-9]+)*)#', $buffer, $matches)) {
+                                $mediatypes = str_replace(' ', '', $matches[1]);
+                                if (!array_key_exists($mediatypes, $medias)) {
+                                    $medias[$mediatypes] = new css_media($mediatypes);
+                                }
+                                $currentmedia = $medias[$mediatypes];
+                                $currentprocess = self::PROCESSING_SELECTORS;
+                                $buffer = '';
+                            }
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                    }
+                    break;
+                // Start processing selectors
+                case self::PROCESSING_START:
+                case self::PROCESSING_SELECTORS:
+                    switch ($char) {
+                        case '[':
+                            $inbrackets ++;
+                            $buffer .= $char;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case ']':
+                            $inbrackets --;
+                            $buffer .= $char;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case ' ':
+                            if ($inbrackets) {
+                                // continue 1: The switch processing chars
+                                // continue 2: The switch processing the state
+                                // continue 3: The for loop
+                                continue 3;
+                            }
+                            if (!empty($buffer)) {
+                                if ($suspectatrule && preg_match('#@(media|import|charset)\s*#', $buffer, $matches)) {
+                                    $currentatrule = $matches[1];
+                                    $currentprocess = self::PROCESSING_ATRULE;
+                                    $buffer .= $char;
+                                } else {
+                                    $currentselector->add($buffer);
+                                    $buffer = '';
+                                }
+                            }
+                            $suspectatrule = false;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case '{':
+                            if ($inbrackets) {
+                                // continue 1: The switch processing chars
+                                // continue 2: The switch processing the state
+                                // continue 3: The for loop
+                                continue 3;
+                            }
+
+                            $currentselector->add($buffer);
+                            $currentrule->add_selector($currentselector);
+                            $currentselector = css_selector::init();
+                            $currentprocess = self::PROCESSING_STYLES;
+
+                            $buffer = '';
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case '}':
+                            if ($inbrackets) {
+                                // continue 1: The switch processing chars
+                                // continue 2: The switch processing the state
+                                // continue 3: The for loop
+                                continue 3;
+                            }
+                            if ($currentatrule == 'media') {
+                                $currentmedia = $medias['all'];
+                                $currentatrule = false;
+                                $buffer = '';
+                            }
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case ',':
+                            if ($inbrackets) {
+                                // continue 1: The switch processing chars
+                                // continue 2: The switch processing the state
+                                // continue 3: The for loop
+                                continue 3;
+                            }
+                            $currentselector->add($buffer);
+                            $currentrule->add_selector($currentselector);
+                            $currentselector = css_selector::init();
+                            $buffer = '';
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                    }
+                    break;
+                // Start processing styles
+                case self::PROCESSING_STYLES:
+                    if ($char == '"' || $char == "'") {
+                        if ($inquotes === false) {
+                            $inquotes = $char;
+                        }
+                        if ($inquotes === $char && $lastchar !== '\\') {
+                            $inquotes = false;
+                        }
+                    }
+                    if ($inquotes) {
+                        $buffer .= $char;
+                        continue 2;
+                    }
+                    switch ($char) {
+                        case ';':
+                            $currentrule->add_style($buffer);
+                            $buffer = '';
+                            $inquotes = false;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                        case '}':
+                            $currentrule->add_style($buffer);
+                            $this->rawselectors += $currentrule->get_selector_count();
+
+                            $currentmedia->add_rule($currentrule);
+
+                            $currentrule = css_rule::init();
+                            $currentprocess = self::PROCESSING_SELECTORS;
+                            $this->rawrules++;
+                            $buffer = '';
+                            $inquotes = false;
+                            // continue 1: The switch processing chars
+                            // continue 2: The switch processing the state
+                            // continue 3: The for loop
+                            continue 3;
+                    }
+                    break;
+            }
+            $buffer .= $char;
+        }
+
+        $css = '';
+        if (!empty($charset)) {
+            $imports[] = $charset;
+        }
+        if (!empty($imports)) {
+            $css .= implode("\n", $imports);
+            $css .= "\n\n";
+        }
+        foreach ($medias as $media) {
+            $media->organise_rules_by_selectors();
+            $this->optimisedrules += $media->count_rules();
+            $this->optimisedselectors +=  $media->count_selectors();
+            if ($media->has_errors()) {
+                $this->errors[] = $media->get_errors();
+            }
+            $css .= $media->out();
+        }
+        $this->optimisedstrlen = strlen($css);
+
+        $this->timecomplete = microtime(true);
+        return trim($css);
+    }
+
+    /**
+     * Returns an array of stats from the last processing run
+     * @return string
+     */
+    public function get_stats() {
+        $stats = array(
+            'timestart'             => $this->timestart,
+            'timecomplete'          => $this->timecomplete,
+            'timetaken'             => round($this->timecomplete - $this->timestart, 4),
+            'commentsincss'         => $this->commentsincss,
+            'rawstrlen'             => $this->rawstrlen,
+            'rawselectors'          => $this->rawselectors,
+            'rawrules'              => $this->rawrules,
+            'optimisedstrlen'       => $this->optimisedstrlen,
+            'optimisedrules'        => $this->optimisedrules,
+            'optimisedselectors'     => $this->optimisedselectors,
+            'improvementstrlen'     => round(100 - ($this->optimisedstrlen / $this->rawstrlen) * 100, 1).'%',
+            'improvementrules'      => round(100 - ($this->optimisedrules / $this->rawrules) * 100, 1).'%',
+            'improvementselectors'  => round(100 - ($this->optimisedselectors / $this->rawselectors) * 100, 1).'%',
+        );
+        return $stats;
+    }
+
+    /**
+     * Returns true if any errors have occured during processing
+     *
+     * @return bool
+     */
+    public function has_errors() {
+        return !empty($this->errors);
+    }
+
+    /**
+     * Returns an array of errors that have occured
+     *
+     * @return array
+     */
+    public function get_errors() {
+        return $this->errors;
+    }
+
+    /**
+     * Returns any errors as a string that can be included in CSS.
+     *
+     * @return string
+     */
+    public function output_errors_css() {
+        $computedcss  = "/****************************************\n";
+        $computedcss .= " *--- Errors found during processing ----\n";
+        foreach ($this->errors as $error) {
+            $computedcss .= preg_replace('#^#m', '* ', $error);
+        }
+        $computedcss .= " ****************************************/\n\n";
+        return $computedcss;
+    }
+
+    /**
+     * Returns a string to display stats about the last generation within CSS output
+     *
+     * @return string
+     */
+    public function output_stats_css() {
+        $stats = $this->get_stats();
+
+        $strlenimprovement = round(100 - ($this->optimisedstrlen / $this->rawstrlen) * 100, 1);
+        $ruleimprovement = round(100 - ($this->optimisedrules / $this->rawrules) * 100, 1);
+        $selectorimprovement = round(100 - ($this->optimisedselectors / $this->rawselectors) * 100, 1);
+        $timetaken = round($this->timecomplete - $this->timestart, 4);
+
+        $computedcss  = "/****************************************\n";
+        $computedcss .= " *------- CSS Optimisation stats --------\n";
+        $computedcss .= " *  ".date('r')."\n";
+        $computedcss .= " *  {$stats['commentsincss']}  \t comments removed\n";
+        $computedcss .= " *  Optimisation took {$stats['timetaken']} seconds\n";
+        $computedcss .= " *--------------- before ----------------\n";
+        $computedcss .= " *  {$stats['rawstrlen']}  \t chars read in\n";
+        $computedcss .= " *  {$stats['rawrules']}  \t rules read in\n";
+        $computedcss .= " *  {$stats['rawselectors']}  \t total selectors\n";
+        $computedcss .= " *---------------- after ----------------\n";
+        $computedcss .= " *  {$stats['optimisedstrlen']}  \t chars once optimized\n";
+        $computedcss .= " *  {$stats['optimisedrules']}  \t optimized rules\n";
+        $computedcss .= " *  {$stats['optimisedselectors']}  \t total selectors once optimized\n";
+        $computedcss .= " *---------------- stats ----------------\n";
+        $computedcss .= " *  {$stats['improvementstrlen']}  \t reduction in chars\n";
+        $computedcss .= " *  {$stats['improvementrules']}  \t reduction in rules\n";
+        $computedcss .= " *  {$stats['improvementselectors']}  \t reduction in selectors\n";
+        $computedcss .= " ****************************************/\n\n";
+
+        return $computedcss;
+    }
+
+    /**
+     * Resets the stats ready for another fresh processing
+     */
+    public function reset_stats() {
+        $this->commentsincss = 0;
+        $this->optimisedrules = 0;
+        $this->optimisedselectors = 0;
+        $this->optimisedstrlen = 0;
+        $this->rawrules = 0;
+        $this->rawselectors = 0;
+        $this->rawstrlen = 0;
+        $this->timecomplete = 0;
+        $this->timestart = 0;
+    }
+
+    /**
+     * An array of the common HTML colours that are supported by most browsers.
+     *
+     * This reference table is used to allow us to unify colours, and will aid
+     * us in identifying buggy CSS using unsupported colours.
+     *
+     * @staticvar array
+     * @var array
+     */
+    public static $htmlcolours = array(
+        'aliceblue' => '#F0F8FF',
+        'antiquewhite' => '#FAEBD7',
+        'aqua' => '#00FFFF',
+        'aquamarine' => '#7FFFD4',
+        'azure' => '#F0FFFF',
+        'beige' => '#F5F5DC',
+        'bisque' => '#FFE4C4',
+        'black' => '#000000',
+        'blanchedalmond' => '#FFEBCD',
+        'blue' => '#0000FF',
+        'blueviolet' => '#8A2BE2',
+        'brown' => '#A52A2A',
+        'burlywood' => '#DEB887',
+        'cadetblue' => '#5F9EA0',
+        'chartreuse' => '#7FFF00',
+        'chocolate' => '#D2691E',
+        'coral' => '#FF7F50',
+        'cornflowerblue' => '#6495ED',
+        'cornsilk' => '#FFF8DC',
+        'crimson' => '#DC143C',
+        'cyan' => '#00FFFF',
+        'darkblue' => '#00008B',
+        'darkcyan' => '#008B8B',
+        'darkgoldenrod' => '#B8860B',
+        'darkgray' => '#A9A9A9',
+        'darkgrey' => '#A9A9A9',
+        'darkgreen' => '#006400',
+        'darkKhaki' => '#BDB76B',
+        'darkmagenta' => '#8B008B',
+        'darkolivegreen' => '#556B2F',
+        'arkorange' => '#FF8C00',
+        'darkorchid' => '#9932CC',
+        'darkred' => '#8B0000',
+        'darksalmon' => '#E9967A',
+        'darkseagreen' => '#8FBC8F',
+        'darkslateblue' => '#483D8B',
+        'darkslategray' => '#2F4F4F',
+        'darkslategrey' => '#2F4F4F',
+        'darkturquoise' => '#00CED1',
+        'darkviolet' => '#9400D3',
+        'deeppink' => '#FF1493',
+        'deepskyblue' => '#00BFFF',
+        'dimgray' => '#696969',
+        'dimgrey' => '#696969',
+        'dodgerblue' => '#1E90FF',
+        'firebrick' => '#B22222',
+        'floralwhite' => '#FFFAF0',
+        'forestgreen' => '#228B22',
+        'fuchsia' => '#FF00FF',
+        'gainsboro' => '#DCDCDC',
+        'ghostwhite' => '#F8F8FF',
+        'gold' => '#FFD700',
+        'goldenrod' => '#DAA520',
+        'gray' => '#808080',
+        'grey' => '#808080',
+        'green' => '#008000',
+        'greenyellow' => '#ADFF2F',
+        'honeydew' => '#F0FFF0',
+        'hotpink' => '#FF69B4',
+        'indianred ' => '#CD5C5C',
+        'indigo ' => '#4B0082',
+        'ivory' => '#FFFFF0',
+        'khaki' => '#F0E68C',
+        'lavender' => '#E6E6FA',
+        'lavenderblush' => '#FFF0F5',
+        'lawngreen' => '#7CFC00',
+        'lemonchiffon' => '#FFFACD',
+        'lightblue' => '#ADD8E6',
+        'lightcoral' => '#F08080',
+        'lightcyan' => '#E0FFFF',
+        'lightgoldenrodyellow' => '#FAFAD2',
+        'lightgray' => '#D3D3D3',
+        'lightgrey' => '#D3D3D3',
+        'lightgreen' => '#90EE90',
+        'lightpink' => '#FFB6C1',
+        'lightsalmon' => '#FFA07A',
+        'lightseagreen' => '#20B2AA',
+        'lightskyblue' => '#87CEFA',
+        'lightslategray' => '#778899',
+        'lightslategrey' => '#778899',
+        'lightsteelblue' => '#B0C4DE',
+        'lightyellow' => '#FFFFE0',
+        'lime' => '#00FF00',
+        'limegreen' => '#32CD32',
+        'linen' => '#FAF0E6',
+        'magenta' => '#FF00FF',
+        'maroon' => '#800000',
+        'mediumaquamarine' => '#66CDAA',
+        'mediumblue' => '#0000CD',
+        'mediumorchid' => '#BA55D3',
+        'mediumpurple' => '#9370D8',
+        'mediumseagreen' => '#3CB371',
+        'mediumslateblue' => '#7B68EE',
+        'mediumspringgreen' => '#00FA9A',
+        'mediumturquoise' => '#48D1CC',
+        'mediumvioletred' => '#C71585',
+        'midnightblue' => '#191970',
+        'mintcream' => '#F5FFFA',
+        'mistyrose' => '#FFE4E1',
+        'moccasin' => '#FFE4B5',
+        'navajowhite' => '#FFDEAD',
+        'navy' => '#000080',
+        'oldlace' => '#FDF5E6',
+        'olive' => '#808000',
+        'olivedrab' => '#6B8E23',
+        'orange' => '#FFA500',
+        'orangered' => '#FF4500',
+        'orchid' => '#DA70D6',
+        'palegoldenrod' => '#EEE8AA',
+        'palegreen' => '#98FB98',
+        'paleturquoise' => '#AFEEEE',
+        'palevioletred' => '#D87093',
+        'papayawhip' => '#FFEFD5',
+        'peachpuff' => '#FFDAB9',
+        'peru' => '#CD853F',
+        'pink' => '#FFC0CB',
+        'plum' => '#DDA0DD',
+        'powderblue' => '#B0E0E6',
+        'purple' => '#800080',
+        'red' => '#FF0000',
+        'rosybrown' => '#BC8F8F',
+        'royalblue' => '#4169E1',
+        'saddlebrown' => '#8B4513',
+        'salmon' => '#FA8072',
+        'sandybrown' => '#F4A460',
+        'seagreen' => '#2E8B57',
+        'seashell' => '#FFF5EE',
+        'sienna' => '#A0522D',
+        'silver' => '#C0C0C0',
+        'skyblue' => '#87CEEB',
+        'slateblue' => '#6A5ACD',
+        'slategray' => '#708090',
+        'slategrey' => '#708090',
+        'snow' => '#FFFAFA',
+        'springgreen' => '#00FF7F',
+        'steelblue' => '#4682B4',
+        'tan' => '#D2B48C',
+        'teal' => '#008080',
+        'thistle' => '#D8BFD8',
+        'tomato' => '#FF6347',
+        'transparent' => 'transparent',
+        'turquoise' => '#40E0D0',
+        'violet' => '#EE82EE',
+        'wheat' => '#F5DEB3',
+        'white' => '#FFFFFF',
+        'whitesmoke' => '#F5F5F5',
+        'yellow' => '#FFFF00',
+        'yellowgreen' => '#9ACD32'
+    );
+}
+
+/**
+ * Used to prepare CSS strings
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class css_writer {
+
+    /**
+     * The current indent level
+     * @var int
+     */
+    protected static $indent = 0;
+
+    /**
+     * Returns true if the output should still maintain minimum formatting.
+     * @return bool
+     */
+    protected static function is_pretty() {
+        global $CFG;
+        return (!empty($CFG->cssoptimiserpretty));
+    }
+
+    /**
+     * Returns the indenting char to use for indenting things nicely.
+     * @return string
+     */
+    protected static function get_indent() {
+        if (self::is_pretty()) {
+            return str_repeat("  ", self::$indent);
+        }
+        return '';
+    }
+
+    /**
+     * Increases the current indent
+     */
+    protected static function increase_indent() {
+        self::$indent++;
+    }
+
+    /**
+     * Decreases the current indent
+     */
+    protected static function decrease_indent() {
+        self::$indent--;
+    }
+
+    /**
+     * Returns the string to use as a separator
+     * @return string
+     */
+    protected static function get_separator() {
+        return (self::is_pretty())?"\n":' ';
+    }
+
+    /**
+     * Returns CSS for media
+     *
+     * @param string $typestring
+     * @param array $rules An array of css_rule objects
+     * @return string
+     */
+    public static function media($typestring, array &$rules) {
+        $nl = self::get_separator();
+
+        $output = '';
+        if ($typestring !== 'all') {
+            $output .= $nl.$nl."@media {$typestring} {".$nl;
+            self::increase_indent();
+        }
+        foreach ($rules as $rule) {
+            $output .= $rule->out().$nl;
+        }
+        if ($typestring !== 'all') {
+            self::decrease_indent();
+            $output .= '}';
+        }
+        return $output;
+    }
+
+    /**
+     * Returns CSS for a rule
+     *
+     * @param string $selector
+     * @param string $styles
+     * @return string
+     */
+    public static function rule($selector, $styles) {
+        $css = self::get_indent()."{$selector}{{$styles}}";
+        return $css;
+    }
+
+    /**
+     * Returns CSS for the selectors of a rule
+     *
+     * @param array $selectors Array of css_selector objects
+     * @return string
+     */
+    public static function selectors(array $selectors) {
+        $nl = self::get_separator();
+        $selectorstrings = array();
+        foreach ($selectors as $selector) {
+            $selectorstrings[] = $selector->out();
+        }
+        return join(','.$nl, $selectorstrings);
+    }
+
+    /**
+     * Returns a selector given the components that make it up.
+     *
+     * @param array $components
+     * @return string
+     */
+    public static function selector(array $components) {
+        return trim(join(' ', $components));
+    }
+
+    /**
+     * Returns a CSS string for the provided styles
+     *
+     * @param array $styles Array of css_style objects
+     * @return string
+     */
+    public static function styles(array $styles) {
+        $bits = array();
+        foreach ($styles as $style) {
+            $bits[] = $style->out();
+        }
+        return join('', $bits);
+    }
+
+    /**
+     * Returns a style CSS
+     *
+     * @param string $name
+     * @param string $value
+     * @param bool $important
+     * @return string
+     */
+    public static function style($name, $value, $important = false) {
+        if ($important && strpos($value, '!important') === false) {
+            $value .= ' !important';
+        }
+        return "{$name}:{$value};";
+    }
+}
+
+/**
+ * A structure to represent a CSS selector.
+ *
+ * The selector is the classes, id, elements, and psuedo bits that make up a CSS
+ * rule.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_selector {
+
+    /**
+     * An array of selector bits
+     * @var array
+     */
+    protected $selectors = array();
+
+    /**
+     * The number of selectors.
+     * @var int
+     */
+    protected $count = 0;
+
+    /**
+     * Initialises a new CSS selector
+     * @return css_selector
+     */
+    public static function init() {
+        return new css_selector();
+    }
+
+    /**
+     * CSS selectors can only be created through the init method above.
+     */
+    protected function __construct() {}
+
+    /**
+     * Adds a selector to the end of the current selector
+     * @param string $selector
+     */
+    public function add($selector) {
+        $selector = trim($selector);
+        $count = 0;
+        $count += preg_match_all('/(\.|#)/', $selector, $matchesarray);
+        if (strpos($selector, '.') !== 0 && strpos($selector, '#') !== 0) {
+            $count ++;
+        }
+        $this->count = $count;
+        $this->selectors[] = $selector;
+    }
+    /**
+     * Returns the number of individual components that make up this selector
+     * @return int
+     */
+    public function get_selector_count() {
+        return $this->count;
+    }
+
+    /**
+     * Returns the selector for use in a CSS rule
+     * @return string
+     */
+    public function out() {
+        return css_writer::selector($this->selectors);
+    }
+}
+
+/**
+ * A structure to represent a CSS rule.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_rule {
+
+    /**
+     * An array of CSS selectors {@see css_selector}
+     * @var array
+     */
+    protected $selectors = array();
+
+    /**
+     * An array of CSS styles {@see css_style}
+     * @var array
+     */
+    protected $styles = array();
+
+    /**
+     * Created a new CSS rule. This is the only way to create a new CSS rule externally.
+     * @return css_rule
+     */
+    public static function init() {
+        return new css_rule();
+    }
+
+    /**
+     * Constructs a new css rule.
+     *
+     * @param string $selector The selector or array of selectors that make up this rule.
+     * @param array $styles An array of styles that belong to this rule.
+     */
+    protected function __construct($selector = null, array $styles = array()) {
+        if ($selector != null) {
+            if (is_array($selector)) {
+                $this->selectors = $selector;
+            } else {
+                $this->selectors = array($selector);
+            }
+            $this->add_styles($styles);
+        }
+    }
+
+    /**
+     * Adds a new CSS selector to this rule
+     *
+     * e.g. $rule->add_selector('.one #two.two');
+     *
+     * @param css_selector $selector Adds a CSS selector to this rule.
+     */
+    public function add_selector(css_selector $selector) {
+        $this->selectors[] = $selector;
+    }
+
+    /**
+     * Adds a new CSS style to this rule.
+     *
+     * @param css_style|string $style Adds a new style to this rule
+     */
+    public function add_style($style) {
+        if (is_string($style)) {
+            $style = trim($style);
+            if (empty($style)) {
+                return;
+            }
+            $bits = explode(':', $style, 2);
+            if (count($bits) == 2) {
+                list($name, $value) = array_map('trim', $bits);
+            }
+            if (isset($name) && isset($value) && $name !== '' && $value !== '') {
+                $style = css_style::init($name, $value);
+            }
+        } else if ($style instanceof css_style) {
+            // Clone the style as it may be coming from another rule and we don't
+            // want references as it will likely be overwritten by proceeding
+            // rules
+            $style = clone($style);
+        }
+        if ($style instanceof css_style) {
+            $name = $style->get_name();
+            if (array_key_exists($name, $this->styles)) {
+                $this->styles[$name]->set_value($style->get_value());
+            } else {
+                $this->styles[$name] = $style;
+            }
+        } else if (is_array($style)) {
+            // We probably shouldn't worry about processing styles here but to
+            // be truthful it doesn't hurt.
+            foreach ($style as $astyle) {
+                $this->add_style($astyle);
+            }
+        }
+    }
+
+    /**
+     * An easy method of adding several styles at once. Just calls add_style.
+     *
+     * This method simply iterates over the array and calls {@see css_rule::add_style()}
+     * with each.
+     *
+     * @param array $styles Adds an array of styles
+     */
+    public function add_styles(array $styles) {
+        foreach ($styles as $style) {
+            $this->add_style($style);
+        }
+    }
+
+    /**
+     * Returns the array of selectors
+     *
+     * @return array
+     */
+    public function get_selectors() {
+        return $this->selectors;
+    }
+
+    /**
+     * Returns the array of styles
+     *
+     * @return array
+     */
+    public function get_styles() {
+        return $this->styles;
+    }
+
+    /**
+     * Outputs this rule as a fragment of CSS
+     *
+     * @return string
+     */
+    public function out() {
+        $selectors = css_writer::selectors($this->selectors);
+        $styles = css_writer::styles($this->get_consolidated_styles());
+        return css_writer::rule($selectors, $styles);
+    }
+
+    /**
+     * Consolidates all styles associated with this rule
+     *
+     * @return array An array of consolidated styles
+     */
+    public function get_consolidated_styles() {
+        $finalstyles = array();
+        $consolidate = array();
+        foreach ($this->styles as $style) {
+            $consolidatetoclass = $style->consolidate_to();
+            if ($style->is_valid() && !empty($consolidatetoclass) && class_exists('css_style_'.$consolidatetoclass)) {
+                $class = 'css_style_'.$consolidatetoclass;
+                if (!array_key_exists($class, $consolidate)) {
+                    $consolidate[$class] = array();
+                }
+                $consolidate[$class][] = $style;
+            } else {
+                $finalstyles[] = $style;
+            }
+        }
+
+        foreach ($consolidate as $class => $styles) {
+            $styles = $class::consolidate($styles);
+            foreach ($styles as $style) {
+                $finalstyles[] = $style;
+            }
+        }
+        return $finalstyles;
+    }
+
+    /**
+     * Splits this rules into an array of CSS rules. One for each of the selectors
+     * that make up this rule.
+     *
+     * @return array(css_rule)
+     */
+    public function split_by_selector() {
+        $return = array();
+        foreach ($this->selectors as $selector) {
+            $return[] = new css_rule($selector, $this->styles);
+        }
+        return $return;
+    }
+
+    /**
+     * Splits this rule into an array of rules. One for each of the styles that
+     * make up this rule
+     *
+     * @return array Array of css_rule objects
+     */
+    public function split_by_style() {
+        $return = array();
+        foreach ($this->styles as $style) {
+            $return[] = new css_rule($this->selectors, array($style));
+        }
+        return $return;
+    }
+
+    /**
+     * Gets a hash for the styles of this rule
+     *
+     * @return string
+     */
+    public function get_style_hash() {
+        return md5(css_writer::styles($this->styles));
+    }
+
+    /**
+     * Gets a hash for the selectors of this rule
+     *
+     * @return string
+     */
+    public function get_selector_hash() {
+        return md5(css_writer::selectors($this->selectors));
+    }
+
+    /**
+     * Gets the number of selectors that make up this rule.
+     *
+     * @return int
+     */
+    public function get_selector_count() {
+        $count = 0;
+        foreach ($this->selectors as $selector) {
+            $count += $selector->get_selector_count();
+        }
+        return $count;
+    }
+
+    /**
+     * Returns true if there are any errors with this rule.
+     *
+     * @return bool
+     */
+    public function has_errors() {
+        foreach ($this->styles as $style) {
+            if ($style->has_error()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the error strings that were recorded when processing this rule.
+     *
+     * Before calling this function you should first call {@see css_rule::has_errors()}
+     * to make sure there are errors (hopefully there arn't).
+     *
+     * @return string
+     */
+    public function get_error_string() {
+        $css = $this->out();
+        $errors = array();
+        foreach ($this->styles as $style) {
+            if ($style->has_error()) {
+                $errors[] = "  * ".$style->get_last_error();
+            }
+        }
+        return $css." has the following errors:\n".join("\n", $errors);
+
+    }
+}
+
+/**
+ * A media class to organise rules by the media they apply to.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_media {
+
+    /**
+     * An array of the different media types this instance applies to.
+     * @var array
+     */
+    protected $types = array();
+
+    /**
+     * An array of rules within this media instance
+     * @var array
+     */
+    protected $rules = array();
+
+    /**
+     * Initalises a new media instance
+     *
+     * @param string $for The media that the contained rules are destined for.
+     */
+    public function __construct($for = 'all') {
+        $types = explode(',', $for);
+        $this->types = array_map('trim', $types);
+    }
+
+    /**
+     * Adds a new CSS rule to this media instance
+     *
+     * @param css_rule $newrule
+     */
+    public function add_rule(css_rule $newrule) {
+        foreach ($newrule->split_by_selector() as $rule) {
+            $hash = $rule->get_selector_hash();
+            if (!array_key_exists($hash, $this->rules)) {
+                $this->rules[$hash] = $rule;
+            } else {
+                $this->rules[$hash]->add_styles($rule->get_styles());
+            }
+        }
+    }
+
+    /**
+     * Returns the rules used by this
+     *
+     * @return array
+     */
+    public function get_rules() {
+        return $this->rules;
+    }
+
+    /**
+     * Organises rules by gropuing selectors based upon the styles and consolidating
+     * those selectors into single rules.
+     *
+     * @return bool True if the CSS was optimised by this method
+     */
+    public function organise_rules_by_selectors() {
+        $optimised = array();
+        $beforecount = count($this->rules);
+        foreach ($this->rules as $rule) {
+            $hash = $rule->get_style_hash();
+            if (!array_key_exists($hash, $optimised)) {
+                $optimised[$hash] = clone($rule);
+            } else {
+                foreach ($rule->get_selectors() as $selector) {
+                    $optimised[$hash]->add_selector($selector);
+                }
+            }
+        }
+        $this->rules = $optimised;
+        $aftercount = count($this->rules);
+        return ($beforecount < $aftercount);
+    }
+
+    /**
+     * Returns the total number of rules that exist within this media set
+     *
+     * @return int
+     */
+    public function count_rules() {
+        return count($this->rules);
+    }
+
+    /**
+     * Returns the total number of selectors that exist within this media set
+     *
+     * @return int
+     */
+    public function count_selectors() {
+        $count = 0;
+        foreach ($this->rules as $rule) {
+            $count += $rule->get_selector_count();
+        }
+        return $count;
+    }
+
+    /**
+     * Returns the CSS for this media and all of its rules.
+     *
+     * @return string
+     */
+    public function out() {
+        return css_writer::media(join(',', $this->types), $this->rules);
+    }
+
+    /**
+     * Returns an array of media that this media instance applies to
+     *
+     * @return array
+     */
+    public function get_types() {
+        return $this->types;
+    }
+
+    /**
+     * Returns true if the media has any rules that have errors
+     *
+     * @return boolean
+     */
+    public function has_errors() {
+        foreach ($this->rules as $rule) {
+            if ($rule->has_errors()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns any errors that have happened within rules in this media set.
+     *
+     * @return string
+     */
+    public function get_errors() {
+        $errors = array();
+        foreach ($this->rules as $rule) {
+            if ($rule->has_errors()) {
+                $errors[] = $rule->get_error_string();
+            }
+        }
+        return join("\n", $errors);
+    }
+}
+
+/**
+ * An absract class to represent CSS styles
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class css_style {
+
+    /**
+     * The name of the style
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * The value for the style
+     * @var mixed
+     */
+    protected $value;
+
+    /**
+     * If set to true this style was defined with the !important rule.
+     * Only trolls use !important.
+     * Don't hide under bridges.. its not good for your skin. Do the proper thing
+     * and fix the issue don't just force a fix that will undoubtedly one day
+     * lead to further frustration.
+     * @var bool
+     */
+    protected $important = false;
+
+    /**
+     * Gets set to true if this style has an error
+     * @var bool
+     */
+    protected $error = false;
+
+    /**
+     * The last error message that occured
+     * @var string
+     */
+    protected $errormessage = null;
+
+
+    /**
+     * Initialises a new style.
+     *
+     * This is the only public way to create a style to ensure they that appropriate
+     * style class is used if it exists.
+     *
+     * @param string $name The name of the style.
+     * @param string $value The value of the style.
+     * @return css_style_generic
+     */
+    public static function init($name, $value) {
+        $specificclass = 'css_style_'.preg_replace('#[^a-zA-Z0-9]+#', '', $name);
+        if (class_exists($specificclass)) {
+            return $specificclass::init($value);
+        }
+        return new css_style_generic($name, $value);
+    }
+
+    /**
+     * Creates a new style when given its name and value
+     *
+     * @param string $name The name of the style.
+     * @param string $value The value of the style.
+     */
+    protected function __construct($name, $value) {
+        $this->name = $name;
+        $this->set_value($value);
+    }
+
+    /**
+     * Sets the value for the style
+     *
+     * @param string $value
+     */
+    final public function set_value($value) {
+        $value = trim($value);
+        $important = preg_match('#(\!important\s*;?\s*)$#', $value, $matches);
+        if ($important) {
+            $value = substr($value, 0, -(strlen($matches[1])));
+        }
+        if (!$this->important || $important) {
+            $this->value = $this->clean_value($value);
+            $this->important = $important;
+        }
+        if (!$this->is_valid()) {
+            $this->set_error('Invalid value for '.$this->name);
+        }
+    }
+
+    /**
+     * Returns true if the value associated with this style is valid
+     *
+     * @return bool
+     */
+    public function is_valid() {
+        return true;
+    }
+
+    /**
+     * Returns the name for the style
+     *
+     * @return string
+     */
+    public function get_name() {
+        return $this->name;
+    }
+
+    /**
+     * Returns the value for the style
+     *
+     * @return string
+     */
+    public function get_value() {
+        $value = $this->value;
+        if ($this->important) {
+            $value .= ' !important';
+        }
+        return $value;
+    }
+
+    /**
+     * Returns the style ready for use in CSS
+     *
+     * @param string|null $value A value to use to override the value for this style.
+     * @return string
+     */
+    public function out($value = null) {
+        if (is_null($value)) {
+            $value = $this->get_value();
+        }
+        return css_writer::style($this->name, $value, $this->important);
+    }
+
+    /**
+     * This can be overridden by a specific style allowing it to clean its values
+     * consistently.
+     *
+     * @param mixed $value
+     * @return mixed
+     */
+    protected function clean_value($value) {
+        return $value;
+    }
+
+    /**
+     * If this particular style can be consolidated into another style this function
+     * should return the style that it can be consolidated into.
+     *
+     * @return string|null
+     */
+    public function consolidate_to() {
+        return null;
+    }
+
+    /**
+     * Sets the last error message.
+     *
+     * @param string $message
+     */
+    protected function set_error($message) {
+        $this->error = true;
+        $this->errormessage = $message;
+    }
+
+    /**
+     * Returns true if an error has occured
+     *
+     * @return bool
+     */
+    public function has_error() {
+        return $this->error;
+    }
+
+    /**
+     * Returns the last error that occured or null if no errors have happened.
+     *
+     * @return string
+     */
+    public function get_last_error() {
+        return $this->errormessage;
+    }
+}
+
+/**
+ * A generic CSS style class to use when a more specific class does not exist.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_generic extends css_style {
+
+    /**
+     * Cleans incoming values for typical things that can be optimised.
+     *
+     * @param mixed $value Cleans the provided value optimising it if possible
+     * @return string
+     */
+    protected function clean_value($value) {
+        if (trim($value) == '0px') {
+            $value = 0;
+        } else if (preg_match('/^#([a-fA-F0-9]{3,6})/', $value, $matches)) {
+            $value = '#'.strtoupper($matches[1]);
+        }
+        return $value;
+    }
+}
+
+/**
+ * A colour CSS style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_color extends css_style {
+
+    /**
+     * Creates a new colour style
+     *
+     * @param mixed $value Initialises a new colour style
+     * @return css_style_color
+     */
+    public static function init($value) {
+        return new css_style_color('color', $value);
+    }
+
+    /**
+     * Cleans the colour unifing it to a 6 char hash colour if possible
+     * Doing this allows us to associate identical colours being specified in
+     * different ways. e.g. Red, red, #F00, and #F00000
+     *
+     * @param mixed $value Cleans the provided value optimising it if possible
+     * @return string
+     */
+    protected function clean_value($value) {
+        $value = trim($value);
+        if (css_is_colour($value)) {
+            if (preg_match('/#([a-fA-F0-9]{6})/', $value, $matches)) {
+                $value = '#'.strtoupper($matches[1]);
+            } else if (preg_match('/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/', $value, $matches)) {
+                $value = $matches[1] . $matches[1] . $matches[2] . $matches[2] . $matches[3] . $matches[3];
+                $value = '#'.strtoupper($value);
+            } else if (array_key_exists(strtolower($value), css_optimiser::$htmlcolours)) {
+                $value = css_optimiser::$htmlcolours[strtolower($value)];
+            }
+        }
+        return $value;
+    }
+
+    /**
+     * Returns the colour style for use within CSS.
+     * Will return an optimised hash colour.
+     *
+     * e.g #123456
+     *     #123 instead of #112233
+     *     #F00 instead of red
+     *
+     * @param string $overridevalue If provided then this value will be used instead
+     *     of the styles current value.
+     * @return string
+     */
+    public function out($overridevalue = null) {
+        if ($overridevalue === null) {
+            $overridevalue = $this->value;
+        }
+        return parent::out(self::shrink_value($overridevalue));
+    }
+
+    /**
+     * Shrinks the colour value is possible.
+     *
+     * @param string $value Shrinks the current value to an optimial form if possible
+     * @return string
+     */
+    public static function shrink_value($value) {
+        if (preg_match('/#([a-fA-F0-9])\1([a-fA-F0-9])\2([a-fA-F0-9])\3/', $value, $matches)) {
+            return '#'.$matches[1].$matches[2].$matches[3];
+        }
+        return $value;
+    }
+
+    /**
+     * Returns true if the value is a valid colour.
+     *
+     * @return bool
+     */
+    public function is_valid() {
+        return css_is_colour($this->value);
+    }
+}
+
+/**
+ * A width style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_width extends css_style {
+
+    /**
+     * Checks if the width is valid
+     * @return bool
+     */
+    public function is_valid() {
+        return css_is_width($this->value);
+    }
+
+    /**
+     * Cleans the provided value
+     *
+     * @param mixed $value Cleans the provided value optimising it if possible
+     * @return string
+     */
+    protected function clean_value($value) {
+        if (!css_is_width($value)) {
+            // Note we don't actually change the value to something valid. That
+            // would be bad for futureproofing.
+            $this->set_error('Invalid width specified for '.$this->name);
+        } else if (preg_match('#^0\D+$#', $value)) {
+            $value = 0;
+        }
+        return trim($value);
+    }
+
+    /**
+     * Initialises a new width style
+     *
+     * @param mixed $value The value this style has
+     * @return css_style_width
+     */
+    public static function init($value) {
+        return new css_style_width('width', $value);
+    }
+}
+
+/**
+ * A margin style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_margin extends css_style_width {
+
+    /**
+     * Initialises a margin style.
+     *
+     * In this case we split the margin into several other margin styles so that
+     * we can properly condense overrides and then reconsolidate them later into
+     * an optimal form.
+     *
+     * @param string $value The value the style has
+     * @return array An array of margin values that can later be consolidated
+     */
+    public static function init($value) {
+        $important = '';
+        if (strpos($value, '!important') !== false) {
+            $important = ' !important';
+            $value = str_replace('!important', '', $value);
+        }
+
+        $value = preg_replace('#\s+#', ' ', trim($value));
+        $bits = explode(' ', $value, 4);
+
+        $top = $right = $bottom = $left = null;
+        if (count($bits) > 0) {
+            $top = $right = $bottom = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $right = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $bottom = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $left = array_shift($bits);
+        }
+        return array(
+            new css_style_margintop('margin-top', $top.$important),
+            new css_style_marginright('margin-right', $right.$important),
+            new css_style_marginbottom('margin-bottom', $bottom.$important),
+            new css_style_marginleft('margin-left', $left.$important)
+        );
+    }
+
+    /**
+     * Consolidates individual margin styles into a single margin style
+     *
+     * @param array $styles
+     * @return array An array of consolidated styles
+     */
+    public static function consolidate(array $styles) {
+        if (count($styles) != 4) {
+            return $styles;
+        }
+        $top = $right = $bottom = $left = null;
+        foreach ($styles as $style) {
+            switch ($style->get_name()) {
+                case 'margin-top' : $top = $style->get_value();break;
+                case 'margin-right' : $right = $style->get_value();break;
+                case 'margin-bottom' : $bottom = $style->get_value();break;
+                case 'margin-left' : $left = $style->get_value();break;
+            }
+        }
+        if ($top == $bottom && $left == $right) {
+            if ($top == $left) {
+                return array(new css_style_margin('margin', $top));
+            } else {
+                return array(new css_style_margin('margin', "{$top} {$left}"));
+            }
+        } else if ($left == $right) {
+            return array(new css_style_margin('margin', "{$top} {$right} {$bottom}"));
+        } else {
+            return array(new css_style_margin('margin', "{$top} {$right} {$bottom} {$left}"));
+        }
+    }
+}
+
+/**
+ * A margin top style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_margintop extends css_style_margin {
+
+    /**
+     * A simple init, just a single style
+     *
+     * @param string $value The value the style has
+     * @return css_style_margintop
+     */
+    public static function init($value) {
+        return new css_style_margintop('margin-top', $value);
+    }
+
+    /**
+     * This style can be consolidated into a single margin style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'margin';
+    }
+}
+
+/**
+ * A margin right style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_marginright extends css_style_margin {
+
+    /**
+     * A simple init, just a single style
+     *
+     * @param string $value The value the style has
+     * @return css_style_margintop
+     */
+    public static function init($value) {
+        return new css_style_marginright('margin-right', $value);
+    }
+
+    /**
+     * This style can be consolidated into a single margin style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'margin';
+    }
+}
+
+/**
+ * A margin bottom style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_marginbottom extends css_style_margin {
+
+    /**
+     * A simple init, just a single style
+     *
+     * @param string $value The value the style has
+     * @return css_style_margintop
+     */
+    public static function init($value) {
+        return new css_style_marginbottom('margin-bottom', $value);
+    }
+
+    /**
+     * This style can be consolidated into a single margin style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'margin';
+    }
+}
+
+/**
+ * A margin left style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_marginleft extends css_style_margin {
+
+    /**
+     * A simple init, just a single style
+     *
+     * @param string $value The value the style has
+     * @return css_style_margintop
+     */
+    public static function init($value) {
+        return new css_style_marginleft('margin-left', $value);
+    }
+
+    /**
+     * This style can be consolidated into a single margin style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'margin';
+    }
+}
+
+/**
+ * A border style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_border extends css_style {
+
+    /**
+     * Initalises the border style into an array of individual style compontents
+     *
+     * @param string $value The value the style has
+     * @return css_style_bordercolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 3);
+
+        $return = array();
+        if (count($bits) > 0) {
+            $width = array_shift($bits);
+            if (!css_is_width($width)) {
+                $width = '0';
+            }
+            $return[] = new css_style_borderwidth('border-top-width', $width);
+            $return[] = new css_style_borderwidth('border-right-width', $width);
+            $return[] = new css_style_borderwidth('border-bottom-width', $width);
+            $return[] = new css_style_borderwidth('border-left-width', $width);
+        }
+        if (count($bits) > 0) {
+            $style = array_shift($bits);
+            $return[] = new css_style_borderstyle('border-top-style', $style);
+            $return[] = new css_style_borderstyle('border-right-style', $style);
+            $return[] = new css_style_borderstyle('border-bottom-style', $style);
+            $return[] = new css_style_borderstyle('border-left-style', $style);
+        }
+        if (count($bits) > 0) {
+            $colour = array_shift($bits);
+            $return[] = new css_style_bordercolor('border-top-color', $colour);
+            $return[] = new css_style_bordercolor('border-right-color', $colour);
+            $return[] = new css_style_bordercolor('border-bottom-color', $colour);
+            $return[] = new css_style_bordercolor('border-left-color', $colour);
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidates all border styles into a single style
+     *
+     * @param array $styles An array of border styles
+     * @return array An optimised array of border styles
+     */
+    public static function consolidate(array $styles) {
+
+        $borderwidths = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
+        $borderstyles = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
+        $bordercolors = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
+
+        foreach ($styles as $style) {
+            switch ($style->get_name()) {
+                case 'border-top-width': $borderwidths['top'] = $style->get_value(); break;
+                case 'border-right-width': $borderwidths['right'] = $style->get_value(); break;
+                case 'border-bottom-width': $borderwidths['bottom'] = $style->get_value(); break;
+                case 'border-left-width': $borderwidths['left'] = $style->get_value(); break;
+
+                case 'border-top-style': $borderstyles['top'] = $style->get_value(); break;
+                case 'border-right-style': $borderstyles['right'] = $style->get_value(); break;
+                case 'border-bottom-style': $borderstyles['bottom'] = $style->get_value(); break;
+                case 'border-left-style': $borderstyles['left'] = $style->get_value(); break;
+
+                case 'border-top-color': $bordercolors['top'] = $style->get_value(); break;
+                case 'border-right-color': $bordercolors['right'] = $style->get_value(); break;
+                case 'border-bottom-color': $bordercolors['bottom'] = $style->get_value(); break;
+                case 'border-left-color': $bordercolors['left'] = $style->get_value(); break;
+            }
+        }
+
+        $uniquewidths = count(array_unique($borderwidths));
+        $uniquestyles = count(array_unique($borderstyles));
+        $uniquecolors = count(array_unique($bordercolors));
+
+        $nullwidths = in_array(null, $borderwidths, true);
+        $nullstyles = in_array(null, $borderstyles, true);
+        $nullcolors = in_array(null, $bordercolors, true);
+
+        $allwidthsthesame = ($uniquewidths === 1)?1:0;
+        $allstylesthesame = ($uniquestyles === 1)?1:0;
+        $allcolorsthesame = ($uniquecolors === 1)?1:0;
+
+        $allwidthsnull = $allwidthsthesame && $nullwidths;
+        $allstylesnull = $allstylesthesame && $nullstyles;
+        $allcolorsnull = $allcolorsthesame && $nullcolors;
+
+        $return = array();
+        if ($allwidthsnull && $allstylesnull && $allcolorsnull) {
+            // Everything is null still... boo
+            return array(new css_style_border('border', ''));
+
+        } else if ($allwidthsnull && $allstylesnull) {
+
+            self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
+            return $return;
+
+        } else if ($allwidthsnull && $allcolorsnull) {
+
+            self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
+            return $return;
+
+        } else if ($allcolorsnull && $allstylesnull) {
+
+            self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
+            return $return;
+
+        }
+
+        if ($allwidthsthesame + $allstylesthesame + $allcolorsthesame == 3) {
+
+            $return[] = new css_style_border('border', $borderwidths['top'].' '.$borderstyles['top'].' '.$bordercolors['top']);
+
+        } else if ($allwidthsthesame + $allstylesthesame + $allcolorsthesame == 2) {
+
+            if ($allwidthsthesame && $allstylesthesame && !$nullwidths && !$nullstyles) {
+
+                $return[] = new css_style_border('border', $borderwidths['top'].' '.$borderstyles['top']);
+                self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
+
+            } else if ($allwidthsthesame && $allcolorsthesame && !$nullwidths && !$nullcolors) {
+
+                $return[] = new css_style_border('border', $borderwidths['top'].' solid '.$bordercolors['top']);
+                self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
+
+            } else if ($allstylesthesame && $allcolorsthesame && !$nullstyles && !$nullcolors) {
+
+                $return[] = new css_style_border('border', '1px '.$borderstyles['top'].' '.$bordercolors['top']);
+                self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
+
+            } else {
+                self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
+                self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
+                self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
+            }
+
+        } else if (!$nullwidths && !$nullcolors && !$nullstyles && max(array_count_values($borderwidths)) == 3 && max(array_count_values($borderstyles)) == 3 && max(array_count_values($bordercolors)) == 3) {
+            $widthkeys = array();
+            $stylekeys = array();
+            $colorkeys = array();
+
+            foreach ($borderwidths as $key => $value) {
+                if (!array_key_exists($value, $widthkeys)) {
+                    $widthkeys[$value] = array();
+                }
+                $widthkeys[$value][] = $key;
+            }
+            usort($widthkeys, 'css_sort_by_count');
+            $widthkeys = array_values($widthkeys);
+
+            foreach ($borderstyles as $key => $value) {
+                if (!array_key_exists($value, $stylekeys)) {
+                    $stylekeys[$value] = array();
+                }
+                $stylekeys[$value][] = $key;
+            }
+            usort($stylekeys, 'css_sort_by_count');
+            $stylekeys = array_values($stylekeys);
+
+            foreach ($bordercolors as $key => $value) {
+                if (!array_key_exists($value, $colorkeys)) {
+                    $colorkeys[$value] = array();
+                }
+                $colorkeys[$value][] = $key;
+            }
+            usort($colorkeys, 'css_sort_by_count');
+            $colorkeys = array_values($colorkeys);
+
+            if ($widthkeys == $stylekeys && $stylekeys == $colorkeys) {
+                $key = $widthkeys[0][0];
+                self::build_style_string($return, 'css_style_border', 'border',  $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
+                $key = $widthkeys[1][0];
+                self::build_style_string($return, 'css_style_border'.$key, 'border-'.$key,  $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
+            } else {
+                self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
+                self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
+                self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
+                self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
+            }
+        } else {
+            self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
+            self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
+            self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
+            self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
+        }
+        foreach ($return as $key => $style) {
+            if ($style->get_value() == '') {
+                unset($return[$key]);
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Border styles get consolidated to a single border style.
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+
+    /**
+     * Consolidates a series of border styles into an optimised array of border
+     * styles by looking at the direction of the border and prioritising that
+     * during the optimisation.
+     *
+     * @param array $array An array to add styles into during consolidation. Passed by reference.
+     * @param string $class The class type to initalise
+     * @param string $style The style to create
+     * @param string|array $top The top value
+     * @param string $right The right value
+     * @param string $bottom The bottom value
+     * @param string $left The left value
+     * @return bool
+     */
+    public static function consolidate_styles_by_direction(&$array, $class, $style, $top, $right = null, $bottom = null, $left = null) {
+        if (is_array($top)) {
+            $right = $top['right'];
+            $bottom = $top['bottom'];
+            $left = $top['left'];
+            $top = $top['top'];
+        }
+
+        if ($top == $bottom && $left == $right && $top == $left) {
+            if (is_null($top)) {
+                $array[] = new $class($style, '');
+            } else {
+                $array[] =  new $class($style, $top);
+            }
+        } else if ($top == null || $right == null || $bottom == null || $left == null) {
+            if ($top !== null) {
+                $array[] = new $class(str_replace('border-', 'border-top-', $style), $top);
+            }
+            if ($right !== null) {
+                $array[] = new $class(str_replace('border-', 'border-right-', $style), $right);
+            }
+            if ($bottom !== null) {
+                $array[] = new $class(str_replace('border-', 'border-bottom-', $style), $bottom);
+            }
+            if ($left !== null) {
+                $array[] = new $class(str_replace('border-', 'border-left-', $style), $left);
+            }
+        } else if ($top == $bottom && $left == $right) {
+            $array[] = new $class($style, $top.' '.$right);
+        } else if ($left == $right) {
+            $array[] = new $class($style, $top.' '.$right.' '.$bottom);
+        } else {
+            $array[] = new $class($style, $top.' '.$right.' '.$bottom.' '.$left);
+        }
+        return true;
+    }
+
+    /**
+     * Builds a border style for a set of width, style, and colour values
+     *
+     * @param array $array An array into which the generated style is added
+     * @param string $class The class type to initialise
+     * @param string $cssstyle The style to use
+     * @param string $width The width of the border
+     * @param string $style The style of the border
+     * @param string $color The colour of the border
+     * @return bool
+     */
+    public static function build_style_string(&$array, $class, $cssstyle, $width = null, $style = null, $color = null) {
+        if (!is_null($width) && !is_null($style) && !is_null($color)) {
+            $array[] = new $class($cssstyle, $width.' '.$style.' '.$color);
+        } else if (!is_null($width) && !is_null($style) && is_null($color)) {
+            $array[] = new $class($cssstyle, $width.' '.$style);
+        } else if (!is_null($width) && is_null($style) && is_null($color)) {
+            $array[] = new $class($cssstyle, $width);
+        } else {
+            if (!is_null($width)) $array[] = new $class($cssstyle, $width);
+            if (!is_null($style)) $array[] = new $class($cssstyle, $style);
+            if (!is_null($color)) $array[] = new $class($cssstyle, $color);
+        }
+        return true;
+    }
+}
+
+/**
+ * A border colour style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_bordercolor extends css_style_color {
+
+    /**
+     * Creates a new border colour style
+     *
+     * Based upon the colour style
+     *
+     * @param mixed $value
+     * @return Array of css_style_bordercolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 4);
+
+        $top = $right = $bottom = $left = null;
+        if (count($bits) > 0) {
+            $top = $right = $bottom = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $right = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $bottom = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $left = array_shift($bits);
+        }
+        return array(
+            css_style_bordertopcolor::init($top),
+            css_style_borderrightcolor::init($right),
+            css_style_borderbottomcolor::init($bottom),
+            css_style_borderleftcolor::init($left)
+        );
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+
+    /**
+     * Cleans the value
+     *
+     * @param string $value Cleans the provided value optimising it if possible
+     * @return string
+     */
+    protected function clean_value($value) {
+        $values = explode(' ', $value);
+        $values = array_map('parent::clean_value', $values);
+        return join (' ', $values);
+    }
+
+    /**
+     * Outputs this style
+     *
+     * @param string $overridevalue
+     * @return string
+     */
+    public function out($overridevalue = null) {
+        if ($overridevalue === null) {
+            $overridevalue = $this->value;
+        }
+        $values = explode(' ', $overridevalue);
+        $values = array_map('css_style_color::shrink_value', $values);
+        return parent::out(join (' ', $values));
+    }
+}
+
+/**
+ * A border left style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderleft extends css_style_generic {
+
+    /**
+     * Initialises the border left style into individual components
+     *
+     * @param string $value
+     * @return array Array of css_style_borderleftwidth|css_style_borderleftstyle|css_style_borderleftcolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 3);
+
+        $return = array();
+        if (count($bits) > 0) {
+            $return[] = css_style_borderleftwidth::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderleftstyle::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderleftcolor::init(array_shift($bits));
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border right style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderright extends css_style_generic {
+
+    /**
+     * Initialises the border right style into individual components
+     *
+     * @param string $value The value of the style
+     * @return array Array of css_style_borderrightwidth|css_style_borderrightstyle|css_style_borderrightcolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 3);
+
+        $return = array();
+        if (count($bits) > 0) {
+            $return[] = css_style_borderrightwidth::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderrightstyle::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderrightcolor::init(array_shift($bits));
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border top style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_bordertop extends css_style_generic {
+
+    /**
+     * Initialises the border top style into individual components
+     *
+     * @param string $value The value of the style
+     * @return array Array of css_style_bordertopwidth|css_style_bordertopstyle|css_style_bordertopcolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 3);
+
+        $return = array();
+        if (count($bits) > 0) {
+            $return[] = css_style_bordertopwidth::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_bordertopstyle::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_bordertopcolor::init(array_shift($bits));
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border bottom style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderbottom extends css_style_generic {
+
+    /**
+     * Initialises the border bottom style into individual components
+     *
+     * @param string $value The value of the style
+     * @return array Array of css_style_borderbottomwidth|css_style_borderbottomstyle|css_style_borderbottomcolor
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 3);
+
+        $return = array();
+        if (count($bits) > 0) {
+            $return[] = css_style_borderbottomwidth::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderbottomstyle::init(array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $return[] = css_style_borderbottomcolor::init(array_shift($bits));
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border width style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderwidth extends css_style_width {
+
+    /**
+     * Creates a new border colour style
+     *
+     * Based upon the colour style
+     *
+     * @param string $value The value of the style
+     * @return array Array of css_style_border*width
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 4);
+
+        $top = $right = $bottom = $left = null;
+        if (count($bits) > 0) {
+            $top = $right = $bottom = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $right = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $bottom = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $left = array_shift($bits);
+        }
+        return array(
+            css_style_bordertopwidth::init($top),
+            css_style_borderrightwidth::init($right),
+            css_style_borderbottomwidth::init($bottom),
+            css_style_borderleftwidth::init($left)
+        );
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border style style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderstyle extends css_style_generic {
+
+    /**
+     * Creates a new border colour style
+     *
+     * Based upon the colour style
+     *
+     * @param string $value The value of the style
+     * @return array Array of css_style_border*style
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value, 4);
+
+        $top = $right = $bottom = $left = null;
+        if (count($bits) > 0) {
+            $top = $right = $bottom = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $right = $left = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $bottom = array_shift($bits);
+        }
+        if (count($bits) > 0) {
+            $left = array_shift($bits);
+        }
+        return array(
+            css_style_bordertopstyle::init($top),
+            css_style_borderrightstyle::init($right),
+            css_style_borderbottomstyle::init($bottom),
+            css_style_borderleftstyle::init($left)
+        );
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border top colour style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_bordertopcolor extends css_style_bordercolor {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_bordertopcolor
+     */
+    public static function init($value) {
+        return new css_style_bordertopcolor('border-top-color', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border left colour style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderleftcolor extends css_style_bordercolor {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderleftcolor
+     */
+    public static function init($value) {
+        return new css_style_borderleftcolor('border-left-color', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border right colour style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderrightcolor extends css_style_bordercolor {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderrightcolor
+     */
+    public static function init($value) {
+        return new css_style_borderrightcolor('border-right-color', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border bottom colour style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderbottomcolor extends css_style_bordercolor {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderbottomcolor
+     */
+    public static function init($value) {
+        return new css_style_borderbottomcolor('border-bottom-color', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border width top style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_bordertopwidth extends css_style_borderwidth {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_bordertopwidth
+     */
+    public static function init($value) {
+        return new css_style_bordertopwidth('border-top-width', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border width left style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderleftwidth extends css_style_borderwidth {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderleftwidth
+     */
+    public static function init($value) {
+        return new css_style_borderleftwidth('border-left-width', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border width right style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderrightwidth extends css_style_borderwidth {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderrightwidth
+     */
+    public static function init($value) {
+        return new css_style_borderrightwidth('border-right-width', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border width bottom style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderbottomwidth extends css_style_borderwidth {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderbottomwidth
+     */
+    public static function init($value) {
+        return new css_style_borderbottomwidth('border-bottom-width', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border top style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_bordertopstyle extends css_style_borderstyle {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_bordertopstyle
+     */
+    public static function init($value) {
+        return new css_style_bordertopstyle('border-top-style', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border left style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderleftstyle extends css_style_borderstyle {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderleftstyle
+     */
+    public static function init($value) {
+        return new css_style_borderleftstyle('border-left-style', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border right style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderrightstyle extends css_style_borderstyle {
+
+    /**
+     * Initialises this style object
+     *
+     * @param string $value The value of the style
+     * @return css_style_borderrightstyle
+     */
+    public static function init($value) {
+        return new css_style_borderrightstyle('border-right-style', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A border bottom style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_borderbottomstyle extends css_style_borderstyle {
+
+    /**
+     * Initialises this style object
+     *
+     * @return css_style_borderbottomstyle
+     */
+    public static function init($value) {
+        return new css_style_borderbottomstyle('border-bottom-style', $value);
+    }
+
+    /**
+     * Consolidate this to a single border style
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'border';
+    }
+}
+
+/**
+ * A background style
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_background extends css_style {
+
+    /**
+     * Initialises a background style
+     *
+     * @param type $value The value of the style
+     * @return array An array of background component.
+     */
+    public static function init($value) {
+        // colour - image - repeat - attachment - position
+
+        $imageurl = null;
+        if (preg_match('#url\(([^\)]+)\)#', $value, $matches)) {
+            $imageurl = trim($matches[1]);
+            $value = str_replace($matches[1], '', $value);
+        }
+
+        $value = preg_replace('#\s+#', ' ', $value);
+        $bits = explode(' ', $value);
+
+        $repeats = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit');
+        $attachments = array('scroll' , 'fixed', 'inherit');
+
+        $return = array();
+        $unknownbits = array();
+
+        if (count($bits) > 0 && css_is_colour(reset($bits))) {
+            $return[] = new css_style_backgroundcolor('background-color', array_shift($bits));
+        }
+        if (count($bits) > 0 && preg_match('#(none|inherit|url\(\))#', reset($bits))) {
+            $image = array_shift($bits);
+            if ($image == 'url()') {
+                $image = "url({$imageurl})";
+            }
+            $return[] = new css_style_backgroundimage('background-image', $image);
+        }
+        if (count($bits) > 0 && in_array(reset($bits), $repeats)) {
+            $return[] = new css_style_backgroundrepeat('background-repeat', array_shift($bits));
+        }
+        if (count($bits) > 0 && in_array(reset($bits), $attachments)) {
+            // scroll , fixed, inherit
+            $return[] = new css_style_backgroundattachment('background-attachment', array_shift($bits));
+        }
+        if (count($bits) > 0) {
+            $widthbits = array();
+            foreach ($bits as $bit) {
+                if (in_array($bit, array('top', 'left', 'bottom', 'right', 'center')) || css_is_width($bit)) {
+                    $widthbits[] = $bit;
+                } else {
+                    $unknownbits[] = $bit;
+                }
+            }
+            $return[] = new css_style_backgroundposition('background-position', join(' ',$widthbits));
+        }
+        if (count($unknownbits)) {
+            foreach ($unknownbits as $bit) {
+                if (css_is_colour($bit)) {
+                    $return[] = new css_style_backgroundcolor('background-color', $bit);
+                } else if (in_array($bit, $repeats)) {
+                    $return[] = new css_style_backgroundrepeat('background-repeat', $bit);
+                } else if (in_array($bit, $attachments)) {
+                    $return[] = new css_style_backgroundattachment('background-attachment', $bit);
+                }
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Consolidates background styles into a single background style
+     *
+     * @param array $styles Consolidates the provided array of background styles
+     * @return array Consolidated optimised background styles
+     */
+    public static function consolidate(array $styles) {
+
+        if (count($styles) < 1) {
+            return $styles;
+        }
+
+        $color = $image = $repeat = $attachment = $position = null;
+        foreach ($styles as $style) {
+            switch ($style->get_name()) {
+                case 'background-color' : $color = css_style_color::shrink_value($style->get_value()); break;
+                case 'background-image' : $image = $style->get_value(); break;
+                case 'background-repeat' : $repeat = $style->get_value(); break;
+                case 'background-attachment' : $attachment = $style->get_value(); break;
+                case 'background-position' : $position = $style->get_value(); break;
+            }
+        }
+
+        if ((is_null($image) || is_null($position) || is_null($repeat)) && ($image!= null || $position != null || $repeat != null)) {
+            return $styles;
+        }
+
+        $value = array();
+        if (!is_null($color)) $value[] .= $color;
+        if (!is_null($image)) $value[] .= $image;
+        if (!is_null($repeat)) $value[] .= $repeat;
+        if (!is_null($attachment)) $value[] .= $attachment;
+        if (!is_null($position)) $value[] .= $position;
+        return array(new css_style_background('background', join(' ', $value)));
+    }
+}
+
+/**
+ * A background colour style.
+ *
+ * Based upon the colour style.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_backgroundcolor extends css_style_color {
+
+    /**
+     * Creates a new background colour style
+     *
+     * @param string $value The value of the style
+     * @return css_style_backgroundcolor
+     */
+    public static function init($value) {
+        return new css_style_backgroundcolor('background-color', $value);
+    }
+
+    /**
+     * css_style_backgroundcolor consolidates to css_style_background
+     *
+     * @return string
+     */
+    public function consolidate_to() {
+        return 'background';
+    }
+}
+
+/**
+ * A background image style.
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_backgroundimage extends css_style_generic {
+
+    /**
+     * Creates a new background colour style
+     *
+     * @param string $value The value of the style
+     * @return css_style_backgroundimage
+     */
+    public static function init($value) {
+        return new css_style_backgroundimage('background-image', $value);
+    }
+
+    /**
+     * Consolidates this style into a single background style
+     *
+     * @return