Merge branch 'MDL-40902_master' of https://github.com/nadavkav/moodle
authorSam Hemelryk <sam@moodle.com>
Mon, 29 Jul 2013 22:36:49 +0000 (10:36 +1200)
committerSam Hemelryk <sam@moodle.com>
Mon, 29 Jul 2013 22:36:49 +0000 (10:36 +1200)
193 files changed:
backup/moodle2/restore_stepslib.php
backup/upgrade.txt
backup/util/dbops/backup_controller_dbops.class.php
backup/util/dbops/restore_dbops.class.php
backup/util/dbops/tests/backup_dbops_test.php
badges/lib/backpacklib.php
cache/stores/file/lib.php
cache/tests/cache_test.php
enrol/meta/locallib.php
enrol/meta/settings.php
enrol/meta/tests/plugin_test.php [new file with mode: 0644]
grade/report/user/lib.php
lib/behat/lib.php
lib/csslib.php
lib/db/upgrade.php
lib/db/upgradelib.php
lib/ddl/mssql_sql_generator.php
lib/dml/mssql_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/editor/tinymce/plugins/pdw/readme_moodle.txt
lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin_src.js [deleted file]
lib/filestorage/zip_archive.php
lib/grouplib.php
lib/jslib.php
lib/minify/config.php [changed mode: 0644->0755]
lib/minify/lib/CSSmin.php [new file with mode: 0644]
lib/minify/lib/DooDigestAuth.php [new file with mode: 0644]
lib/minify/lib/Minify.php
lib/minify/lib/Minify/Build.php
lib/minify/lib/Minify/CSS.php
lib/minify/lib/Minify/Cache/File.php
lib/minify/lib/Minify/Cache/XCache.php [new file with mode: 0644]
lib/minify/lib/Minify/ClosureCompiler.php [new file with mode: 0644]
lib/minify/lib/Minify/Controller/Base.php
lib/minify/lib/Minify/Controller/Files.php
lib/minify/lib/Minify/Controller/Groups.php
lib/minify/lib/Minify/Controller/MinApp.php
lib/minify/lib/Minify/Controller/Page.php
lib/minify/lib/Minify/Controller/Version1.php
lib/minify/lib/Minify/HTML.php
lib/minify/lib/Minify/HTML/Helper.php
lib/minify/lib/Minify/JS/ClosureCompiler.php
lib/minify/lib/Minify/Lines.php
lib/minify/lib/Minify/Loader.php [new file with mode: 0644]
lib/minify/lib/Minify/YUICompressor.php
lib/minify/lib/MrClay/Cli.php
lib/minify/lib/MrClay/Cli/Arg.php
lib/minify/readme_moodle.txt
lib/minify/utils.php
lib/modinfolib.php
lib/moodlelib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/questionlib.php
lib/setuplib.php
lib/tests/accesslib_test.php
lib/tests/admintree_test.php
lib/tests/authlib_test.php
lib/tests/behat/behat_hooks.php
lib/tests/blocklib_test.php
lib/tests/code_test.php
lib/tests/collator_test.php
lib/tests/completionlib_advanced_test.php [deleted file]
lib/tests/completionlib_test.php
lib/tests/component_test.php
lib/tests/componentlib_test.php
lib/tests/conditionlib_test.php
lib/tests/configonlylib_test.php
lib/tests/coursecatlib_test.php
lib/tests/csslib_test.php
lib/tests/csvclass_test.php
lib/tests/datalib_test.php
lib/tests/environment_test.php
lib/tests/event_test.php
lib/tests/eventslib_test.php
lib/tests/externallib_test.php
lib/tests/filelib_test.php
lib/tests/filterlib_test.php [moved from lib/tests/filter_test.php with 74% similarity]
lib/tests/formslib_test.php
lib/tests/gradelib_test.php
lib/tests/grouplib_test.php
lib/tests/html2text_test.php
lib/tests/html_writer_test.php [moved from lib/tests/htmlwriter_test.php with 69% similarity]
lib/tests/htmlpurifier_test.php
lib/tests/markdown_test.php
lib/tests/mathslib_test.php
lib/tests/medialib_test.php
lib/tests/messagelib_test.php
lib/tests/modinfolib_test.php
lib/tests/moodle_page_test.php [moved from lib/tests/pagelib_test.php with 71% similarity]
lib/tests/moodlelib_test.php
lib/tests/navigationlib_test.php
lib/tests/outputcomponents_test.php
lib/tests/outputrequirementslib_test.php
lib/tests/pluginlib_test.php
lib/tests/questionlib_test.php
lib/tests/rsslib_test.php
lib/tests/setuplib_test.php
lib/tests/statslib_test.php
lib/tests/string_manager_test.php [moved from lib/tests/string_test.php with 94% similarity]
lib/tests/text_test.php
lib/tests/theme_config_test.php [moved from lib/tests/outputlib_test.php with 53% similarity]
lib/tests/upgradelib_test.php
lib/tests/weblib_test.php
lib/tests/xhtml_container_stack_test.php [new file with mode: 0644]
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/weblib.php
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-alert/moodle-core-notification-alert-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-alert/moodle-core-notification-alert-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-alert/moodle-core-notification-alert.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception.js [new file with mode: 0644]
lib/yui/build/moodle-core-notification/moodle-core-notification-debug.js
lib/yui/build/moodle-core-notification/moodle-core-notification-min.js
lib/yui/build/moodle-core-notification/moodle-core-notification.js
lib/yui/src/notification/build.json
lib/yui/src/notification/js/ajaxexception.js [new file with mode: 0644]
lib/yui/src/notification/js/alert.js [new file with mode: 0644]
lib/yui/src/notification/js/confirm.js [new file with mode: 0644]
lib/yui/src/notification/js/dialogue.js [new file with mode: 0644]
lib/yui/src/notification/js/exception.js [new file with mode: 0644]
lib/yui/src/notification/js/notification.js
lib/yui/src/notification/js/shared.js [new file with mode: 0644]
lib/yui/src/notification/meta/notification.json
mod/assign/externallib.php
mod/assign/feedback/file/importziplib.php
mod/assignment/lib.php
mod/choice/styles.css
mod/feedback/import_form.php
mod/feedback/lib.php
mod/feedback/styles.css
mod/forum/lib.php
mod/label/lib.php
mod/lti/db/access.php
mod/lti/lang/en/lti.php
mod/lti/lib.php
mod/lti/locallib.php
mod/lti/mod_form.php
mod/lti/service.php
mod/page/lib.php
mod/quiz/report/statistics/lib.php
mod/quiz/report/statistics/report.php
mod/resource/lib.php
mod/url/lib.php
question/editlib.php
question/format/aiken/format.php
question/format/aiken/lang/en/qformat_aiken.php
question/format/aiken/version.php
question/format/blackboard_six/format.php
question/format/blackboard_six/formatqti.php
question/format/examview/format.php
question/format/gift/format.php
question/format/gift/lang/en/qformat_gift.php
question/format/gift/tests/giftformat_test.php
question/format/gift/version.php
question/format/learnwise/format.php
question/format/learnwise/lang/en/qformat_learnwise.php
question/format/learnwise/version.php
question/format/missingword/format.php
question/format/missingword/lang/en/qformat_missingword.php
question/format/missingword/version.php
question/format/multianswer/lang/en/qformat_multianswer.php
question/format/multianswer/version.php
question/format/upgrade.txt
question/format/webct/TODO.txt
question/format/webct/format.php
question/format/webct/lang/en/qformat_webct.php
question/format/webct/version.php
question/format/xhtml/format.php
question/format/xhtml/lang/en/qformat_xhtml.php
question/format/xhtml/version.php
question/format/xhtml/xhtml.css
question/format/xml/format.php
question/format/xml/lang/en/qformat_xml.php
question/format/xml/tests/xmlformat_test.php
question/format/xml/version.php
question/upgrade.txt [new file with mode: 0644]
report/log/locallib.php
repository/s3/README_MOODLE.txt
repository/s3/S3.php
version.php

index ed36b0a..2b6cdab 100644 (file)
@@ -510,11 +510,11 @@ class restore_review_pending_block_positions extends restore_execution_step {
 
         // Get all the block_position objects pending to match
         $params = array('backupid' => $this->get_restoreid(), 'itemname' => 'block_position');
-        $rs = $DB->get_recordset('backup_ids_temp', $params, '', 'itemid');
+        $rs = $DB->get_recordset('backup_ids_temp', $params, '', 'itemid, info');
         // Process block positions, creating them or accumulating for final step
         foreach($rs as $posrec) {
-            // Get the complete position object (stored as info)
-            $position = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'block_position', $posrec->itemid)->info;
+            // Get the complete position object out of the info field.
+            $position = backup_controller_dbops::decode_backup_temp_info($posrec->info);
             // If position is for one already mapped (known) contextid
             // process it now, creating the position, else nothing to
             // do, position finally discarded
@@ -546,12 +546,12 @@ class restore_process_course_modules_availability extends restore_execution_step
 
         // Get all the module_availability objects to process
         $params = array('backupid' => $this->get_restoreid(), 'itemname' => 'module_availability');
-        $rs = $DB->get_recordset('backup_ids_temp', $params, '', 'itemid');
+        $rs = $DB->get_recordset('backup_ids_temp', $params, '', 'itemid, info');
         // Process availabilities, creating them if everything matches ok
         foreach($rs as $availrec) {
             $allmatchesok = true;
             // Get the complete availabilityobject
-            $availability = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'module_availability', $availrec->itemid)->info;
+            $availability = backup_controller_dbops::decode_backup_temp_info($availrec->info);
             // Map the sourcecmid if needed and possible
             if (!empty($availability->sourcecmid)) {
                 $newcm = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'course_module', $availability->sourcecmid);
@@ -3624,7 +3624,7 @@ class restore_process_file_aliases_queue extends restore_execution_step {
 
         // Iterate over aliases in the queue.
         foreach ($rs as $record) {
-            $info = unserialize(base64_decode($record->info));
+            $info = restore_dbops::decode_backup_temp_info($record->info);
 
             // Try to pick a repository instance that should serve the alias.
             $repository = $this->choose_repository($info);
@@ -3657,7 +3657,7 @@ class restore_process_file_aliases_queue extends restore_execution_step {
                 $source = null;
 
                 foreach ($candidates as $candidate) {
-                    $candidateinfo = unserialize(base64_decode($candidate->info));
+                    $candidateinfo = backup_controller_dbops::decode_backup_temp_info($candidate->info);
                     if ($candidateinfo->filename === $reference['filename']
                             and $candidateinfo->filepath === $reference['filepath']
                             and !is_null($candidate->newcontextid)
index 586ee70..26e2e00 100644 (file)
@@ -7,6 +7,12 @@ information provided here is intended especially for developers.
   method is not available anymore. Temp tables must be created
   inline always.
 
+* Using the info field from backup_ids_temp or backup_files_temp
+  must now go via backup_controller_dbops::decode_backup_temp_info() and
+  backup_controller_dbops::encode_backup_temp_info(). The implementation
+  of the encoding has changed.  These new functions encapsulate any future
+  changes to the encoding.
+
 === 2.5 ===
 
 * New optional param $sortby in backup set_source_table() allows to
index 085fe22..668ff61 100644 (file)
@@ -154,6 +154,44 @@ abstract class backup_controller_dbops extends backup_dbops {
         $dbman->drop_table($table); // And drop it
     }
 
+    /**
+     * Decode the info field from backup_ids_temp or backup_files_temp.
+     *
+     * @param mixed $info The info field data to decode, may be an object or a simple integer.
+     * @return mixed The decoded information.  For simple types it returns, for complex ones we decode.
+     */
+    public static function decode_backup_temp_info($info) {
+        // We encode all data except null.
+        if ($info != null) {
+            if (extension_loaded('zlib')) {
+                return unserialize(gzuncompress(base64_decode($info)));
+            } else {
+                return unserialize(base64_decode($info));
+            }
+        }
+        return $info;
+    }
+
+    /**
+     * Encode the info field for backup_ids_temp or backup_files_temp.
+     *
+     * @param mixed $info string The info field data to encode.
+     * @return string An encoded string of data or null if the input is null.
+     */
+    public static function encode_backup_temp_info($info) {
+        // We encode if there is any information to keep the translations simpler.
+        if ($info != null) {
+            // We compress if possible. It reduces db, network and memory storage. The saving is greater than CPU compression cost.
+            // Compression level 1 is chosen has it produces good compression with the smallest possible overhead, see MDL-40618.
+            if (extension_loaded('zlib')) {
+                return base64_encode(gzcompress(serialize($info), 1));
+            } else {
+                return base64_encode(serialize($info));
+            }
+        }
+        return $info;
+    }
+
     /**
      * Given one type and id from controller, return the corresponding courseid
      */
index 12f2601..0e5fabc 100644 (file)
@@ -152,7 +152,7 @@ abstract class restore_dbops {
         $problems = array(); // To store warnings/errors
 
         // Get loaded roles from backup_ids
-        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'role'), '', 'itemid');
+        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'role'), '', 'itemid, info');
         foreach ($rs as $recrole) {
             // If the rolemappings->modified flag is set, that means that we are coming from
             // manually modified mappings (by UI), so accept those mappings an put them to backup_ids
@@ -163,14 +163,13 @@ abstract class restore_dbops {
             // Else, we haven't any info coming from UI, let's calculate the mappings, matching
             // in multiple ways and checking permissions. Note mapping to 0 means "skip"
             } else {
-                $role = (object)self::get_backup_ids_record($restoreid, 'role', $recrole->itemid)->info;
+                $role = (object)backup_controller_dbops::decode_backup_temp_info($recrole->info);
                 $match = self::get_best_assignable_role($role, $courseid, $userid, $samesite);
                 // Send match to backup_ids
                 self::set_backup_ids_record($restoreid, 'role', $recrole->itemid, $match);
                 // Build the rolemappings element for controller
                 unset($role->id);
                 unset($role->nameincourse);
-                unset($role->nameincourse);
                 $role->targetroleid = $match;
                 $rolemappings->mappings[$recrole->itemid] = $role;
                 // Prepare warning if no match found
@@ -673,20 +672,21 @@ abstract class restore_dbops {
         global $DB;
 
         $results = array();
-        $qcats = $DB->get_records_sql("SELECT itemid, parentitemid AS contextid
+        $qcats = $DB->get_recordset_sql("SELECT itemid, parentitemid AS contextid, info
                                          FROM {backup_ids_temp}
                                        WHERE backupid = ?
                                          AND itemname = 'question_category'", array($restoreid));
         foreach ($qcats as $qcat) {
             // If this qcat context haven't been acummulated yet, do that
             if (!isset($results[$qcat->contextid])) {
-                $temprec = self::get_backup_ids_record($restoreid, 'question_category', $qcat->itemid);
+                $info = backup_controller_dbops::decode_backup_temp_info($qcat->info);
                 // Filter by contextlevel if necessary
-                if (is_null($contextlevel) || $contextlevel == $temprec->info->contextlevel) {
-                    $results[$qcat->contextid] = $temprec->info->contextlevel;
+                if (is_null($contextlevel) || $contextlevel == $info->contextlevel) {
+                    $results[$qcat->contextid] = $info->contextlevel;
                 }
             }
         }
+        $qcats->close();
         // Sort by value (contextlevel from CONTEXT_SYSTEM downto CONTEXT_MODULE)
         asort($results);
         return $results;
@@ -700,15 +700,16 @@ abstract class restore_dbops {
         global $DB;
 
         $results = array();
-        $qcats = $DB->get_records_sql("SELECT itemid
+        $qcats = $DB->get_recordset_sql("SELECT itemid, info
                                          FROM {backup_ids_temp}
                                         WHERE backupid = ?
                                           AND itemname = 'question_category'
                                           AND parentitemid = ?", array($restoreid, $contextid));
         foreach ($qcats as $qcat) {
-            $temprec = self::get_backup_ids_record($restoreid, 'question_category', $qcat->itemid);
-            $results[$qcat->itemid] = $temprec->info;
+            $results[$qcat->itemid] = backup_controller_dbops::decode_backup_temp_info($qcat->info);
         }
+        $qcats->close();
+
         return $results;
     }
 
@@ -798,15 +799,15 @@ abstract class restore_dbops {
         global $DB;
 
         $results = array();
-        $qs = $DB->get_records_sql("SELECT itemid
+        $qs = $DB->get_recordset_sql("SELECT itemid, info
                                       FROM {backup_ids_temp}
                                      WHERE backupid = ?
                                        AND itemname = 'question'
                                        AND parentitemid = ?", array($restoreid, $qcatid));
         foreach ($qs as $q) {
-            $temprec = self::get_backup_ids_record($restoreid, 'question', $q->itemid);
-            $results[$q->itemid] = $temprec->info;
+            $results[$q->itemid] = backup_controller_dbops::decode_backup_temp_info($q->info);
         }
+        $qs->close();
         return $results;
     }
 
@@ -893,7 +894,7 @@ abstract class restore_dbops {
         $basepath = $basepath . '/files/';// Get backup file pool base
         $rs = $DB->get_recordset_sql($sql, $params);
         foreach ($rs as $rec) {
-            $file = (object)unserialize(base64_decode($rec->info));
+            $file = (object)backup_controller_dbops::decode_backup_temp_info($rec->info);
 
             // ignore root dirs (they are created automatically)
             if ($file->filepath == '/' && $file->filename == '.') {
@@ -1018,9 +1019,9 @@ abstract class restore_dbops {
         $themes    = get_list_of_themes(); // Get themes for quick search later
 
         // Iterate over all the included users with newitemid = 0, have to create them
-        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'user', 'newitemid' => 0), '', 'itemid, parentitemid');
+        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'user', 'newitemid' => 0), '', 'itemid, parentitemid, info');
         foreach ($rs as $recuser) {
-            $user = (object)self::get_backup_ids_record($restoreid, 'user', $recuser->itemid)->info;
+            $user = (object)backup_controller_dbops::decode_backup_temp_info($recuser->info);
 
             // if user lang doesn't exist here, use site default
             if (!array_key_exists($user->lang, $languages)) {
@@ -1409,9 +1410,9 @@ abstract class restore_dbops {
         }
 
         // Iterate over all the included users
-        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'user'), '', 'itemid');
+        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'user'), '', 'itemid, info');
         foreach ($rs as $recuser) {
-            $user = (object)self::get_backup_ids_record($restoreid, 'user', $recuser->itemid)->info;
+            $user = (object)backup_controller_dbops::decode_backup_temp_info($recuser->info);
 
             // Find the correct mnethostid for user before performing any further check
             if (empty($user->mnethosturl) || $user->mnethosturl === $CFG->wwwroot) {
@@ -1496,7 +1497,7 @@ abstract class restore_dbops {
         global $DB;
 
         // Store external files info in `info` field
-        $filerec->info     = base64_encode(serialize($filerec)); // Serialize the whole rec in info
+        $filerec->info     = backup_controller_dbops::encode_backup_temp_info($filerec); // Encode the whole record into info.
         $filerec->backupid = $restoreid;
         $DB->insert_record('backup_files_temp', $filerec);
     }
@@ -1511,7 +1512,7 @@ abstract class restore_dbops {
             $extrarecord['parentitemid'] = $parentitemid;
         }
         if ($info != null) {
-            $extrarecord['info'] = base64_encode(serialize($info));
+            $extrarecord['info'] = backup_controller_dbops::encode_backup_temp_info($info);
         }
 
         self::set_backup_ids_cached($restoreid, $itemname, $itemid, $extrarecord);
@@ -1520,8 +1521,9 @@ abstract class restore_dbops {
     public static function get_backup_ids_record($restoreid, $itemname, $itemid) {
         $dbrec = self::get_backup_ids_cached($restoreid, $itemname, $itemid);
 
+        // We must test if info is a string, as the cache stores info in object form.
         if ($dbrec && isset($dbrec->info) && is_string($dbrec->info)) {
-            $dbrec->info = unserialize(base64_decode($dbrec->info));
+            $dbrec->info = backup_controller_dbops::decode_backup_temp_info($dbrec->info);
         }
 
         return $dbrec;
@@ -1566,18 +1568,17 @@ abstract class restore_dbops {
         // Get the course context
         $coursectx = context_course::instance($courseid);
         // Get all the mapped roles we have
-        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'role'), '', 'itemid');
+        $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'role'), '', 'itemid, info, newitemid');
         foreach ($rs as $recrole) {
-            // Get the complete temp_ids record
-            $role = (object)self::get_backup_ids_record($restoreid, 'role', $recrole->itemid);
+            $info = backup_controller_dbops::decode_backup_temp_info($recrole->info);
             // If it's one mapped role and we have one name for it
-            if (!empty($role->newitemid) && !empty($role->info['nameincourse'])) {
+            if (!empty($recrole->newitemid) && !empty($info['nameincourse'])) {
                 // If role name doesn't exist, add it
                 $rolename = new stdclass();
-                $rolename->roleid = $role->newitemid;
+                $rolename->roleid = $recrole->newitemid;
                 $rolename->contextid = $coursectx->id;
                 if (!$DB->record_exists('role_names', (array)$rolename)) {
-                    $rolename->name = $role->info['nameincourse'];
+                    $rolename->name = $info['nameincourse'];
                     $DB->insert_record('role_names', $rolename);
                 }
             }
index 55d57ba..e57dc7e 100644 (file)
@@ -147,6 +147,18 @@ class backup_dbops_testcase extends advanced_testcase {
         // Drop and check it doesn't exists anymore
         backup_controller_dbops::drop_backup_ids_temp_table('testingid');
         $this->assertFalse($dbman->table_exists('backup_ids_temp'));
+
+        // Test encoding/decoding of backup_ids_temp,backup_files_temp encode/decode functions.
+        // We need to handle both objects and data elements.
+        $object = new stdClass();
+        $object->item1 = 10;
+        $object->item2 = 'a String';
+        $testarray = array($object, 10, null, 'string', array('a' => 'b', 1 => 1));
+        foreach ($testarray as $item) {
+            $encoded = backup_controller_dbops::encode_backup_temp_info($item);
+            $decoded = backup_controller_dbops::decode_backup_temp_info($encoded);
+            $this->assertEquals($item, $decoded);
+        }
     }
 
     /**
index 58a9b74..4ecf084 100644 (file)
@@ -60,10 +60,11 @@ class OpenBadgesBackpackHandler {
         }
 
         $options = array(
-            'FRESH_CONNECT' => true,
+            'FRESH_CONNECT'  => true,
             'RETURNTRANSFER' => true,
-            'FORBID_REUSE' => true,
-            'HEADER' => 0,
+            'FORBID_REUSE'   => true,
+            'HEADER'         => 0,
+            'HTTPHEADER'     => array('Expect:'),
             'CONNECTTIMEOUT' => 3,
         );
 
index 96d11b2..adc1a9b 100644 (file)
@@ -266,7 +266,7 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
         $this->definition = $definition;
         $hash = preg_replace('#[^a-zA-Z0-9]+#', '_', $this->definition->get_id());
         $this->path = $this->filestorepath.'/'.$hash;
-        make_writable_directory($this->path);
+        make_writable_directory($this->path, false);
         if ($this->prescan && $definition->get_mode() !== self::MODE_REQUEST) {
             $this->prescan = false;
         }
@@ -314,11 +314,13 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
             return $this->path . '/' . $key . '.cache';
         } else {
             // We are using a single subdirectory to achieve 1 level.
-            $subdir = substr($key, 0, 3);
+           // We suffix the subdir so it does not clash with any windows
+           // reserved filenames like 'con'.
+            $subdir = substr($key, 0, 3) . '-cache';
             $dir = $this->path . '/' . $subdir;
             if ($create) {
                 // Create the directory. This function does it recursivily!
-                make_writable_directory($dir);
+                make_writable_directory($dir, false);
             }
             return $dir . '/' . $key . '.cache';
         }
index 5121875..1388004 100644 (file)
@@ -119,6 +119,23 @@ class cache_phpunit_tests extends advanced_testcase {
         }
     }
 
+    /**
+     * Tests for cache keys that would break on windows.
+     */
+    public function test_windows_nasty_keys() {
+        $instance = cache_config_phpunittest::instance();
+        $instance->phpunit_add_definition('phpunit/windowskeytest', array(
+            'mode' => cache_store::MODE_APPLICATION,
+            'component' => 'phpunit',
+            'area' => 'windowskeytest',
+            'simplekeys' => true,
+            'simpledata' => true
+        ));
+        $cache = cache::make('phpunit', 'windowskeytest');
+        $this->assertTrue($cache->set('contest', 'test data 1'));
+        $this->assertEquals('test data 1', $cache->get('contest'));
+    }
+
     /**
      * Tests the default application cache
      */
@@ -191,9 +208,9 @@ class cache_phpunit_tests extends advanced_testcase {
      * @param cache_loader $cache
      */
     protected function run_on_cache(cache_loader $cache) {
-        $key = 'testkey';
+        $key = 'contestkey';
         $datascalars = array('test data', null);
-        $dataarray = array('test' => 'data', 'part' => 'two');
+        $dataarray = array('contest' => 'data', 'part' => 'two');
         $dataobject = (object)$dataarray;
 
         foreach ($datascalars as $datascalar) {
@@ -850,7 +867,7 @@ class cache_phpunit_tests extends advanced_testcase {
         // OK data added, data invalidated, and invalidation time has been set.
         // Now we need to manually add back the data and adjust the invalidation time.
         $hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit');
-        $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las/lastinvalidation-$hash.cache";
+        $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache";
         // Make sure the file is correct.
         $this->assertTrue(file_exists($timefile));
         $timecont = serialize(cache::now() - 60); // Back 60sec in the past to force it to re-invalidate.
@@ -858,7 +875,7 @@ class cache_phpunit_tests extends advanced_testcase {
         file_put_contents($timefile, $timecont);
         $this->assertTrue(file_exists($timefile));
 
-        $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes/testkey1-$hash.cache";
+        $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache";
         $datacont = serialize("test data 1");
         make_writable_directory(dirname($datafile));
         file_put_contents($datafile, $datacont);
index a23f9b5..3a3c26b 100644 (file)
@@ -90,12 +90,6 @@ class enrol_meta_handler {
 
         $context = context_course::instance($instance->courseid);
 
-        if (!$parentcontext = context_course::instance($instance->customint1, IGNORE_MISSING)) {
-            // linking to missing course is not possible
-            role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
-            return;
-        }
-
         // list of enrolments in parent course (we ignore meta enrols in parents completely)
         list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
         $params['userid'] = $userid;
@@ -114,10 +108,8 @@ class enrol_meta_handler {
             return;
         }
 
-        if (!enrol_is_enabled('meta')) {
-            if ($ue) {
-                role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
-            }
+        if (!$parentcontext = context_course::instance($instance->customint1, IGNORE_MISSING)) {
+            // Weird, we should not get here.
             return;
         }
 
@@ -172,9 +164,13 @@ class enrol_meta_handler {
             $ue->status = $parentstatus;
         }
 
+        $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
+
         // only active users in enabled instances are supposed to have roles (we can reassign the roles any time later)
         if ($ue->status != ENROL_USER_ACTIVE or $instance->status != ENROL_INSTANCE_ENABLED) {
-            if ($roles) {
+            if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
+                // Always keep the roles.
+            } else if ($roles) {
                 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
             }
             return;
@@ -187,6 +183,11 @@ class enrol_meta_handler {
             }
         }
 
+        if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
+            // Always keep the roles.
+            return;
+        }
+
         // remove roles
         foreach ($roles as $rid) {
             if (!isset($parentroles[$rid])) {
@@ -206,25 +207,30 @@ class enrol_meta_handler {
      */
     protected static function user_not_supposed_to_be_here($instance, $ue, context_course $context, $plugin) {
         if (!$ue) {
-            // not enrolled yet - simple!
+            // Not enrolled yet - simple!
             return;
         }
 
         $userid = $ue->userid;
-        $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
+        $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
 
         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
-            // purges grades, group membership, preferences, etc. - admins were warned!
+            // Purges grades, group membership, preferences, etc. - admins were warned!
             $plugin->unenrol_user($instance, $userid);
-            return;
 
-        } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
-            // just suspend users and remove all roles (we can reassign the roles any time later)
+        } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
             if ($ue->status != ENROL_USER_SUSPENDED) {
                 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
-                role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
             }
-            return;
+
+        } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+            if ($ue->status != ENROL_USER_SUSPENDED) {
+                $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
+            }
+            role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
+
+        } else {
+            debugging('Unknown unenrol action '.$unenrolaction);
         }
     }
 
@@ -316,8 +322,10 @@ class enrol_meta_handler {
      * @return bool success
      */
     public static function user_unenrolled($ue) {
-
-        // keep unenrolling even if plugin disabled
+        if (!enrol_is_enabled('meta')) {
+            // This is slow, let enrol_meta_sync() deal with disabled plugin.
+            return true;
+        }
 
         if ($ue->enrol === 'meta') {
             // prevent circular dependencies - we can not sync meta enrolments recursively
@@ -360,7 +368,10 @@ class enrol_meta_handler {
     public static function course_deleted($course) {
         global $DB;
 
-        // NOTE: do not test if plugin enabled, we want to keep disabling instances with invalid course links
+        if (!enrol_is_enabled('meta')) {
+            // This is slow, let enrol_meta_sync() deal with disabled plugin.
+            return true;
+        }
 
         // does anything want to sync with this parent?
         if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'courseid ASC, id ASC')) {
@@ -368,19 +379,29 @@ class enrol_meta_handler {
         }
 
         $plugin = enrol_get_plugin('meta');
+        $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
 
-        // hack the DB info for all courses first
-        foreach ($enrols as $enrol) {
-            $enrol->customint1 = 0;
-            $enrol->status = ENROL_INSTANCE_DISABLED;
-            $DB->update_record('enrol', $enrol);
-            $context = context_course::instance($enrol->courseid);
-            role_unassign_all(array('contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id));
+        if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
+            // Simple, just delete this instance which purges all enrolments,
+            // admins were warned that this is risky setting!
+            foreach ($enrols as $enrol) {
+                $plugin->delete_instance($enrol);
+            }
+            return true;
         }
 
-        // now trigger sync for each instance and purge caches
         foreach ($enrols as $enrol) {
-            $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED);
+            $enrol->customint = 0;
+            $DB->update_record('enrol', $enrol);
+
+            if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+                // This makes all enrolments suspended very quickly.
+                $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED);
+            }
+            if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+                $context = context_course::instance($enrol->courseid);
+                role_unassign_all(array('contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id));
+            }
         }
 
         return true;
@@ -419,7 +440,7 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
 
     $meta = enrol_get_plugin('meta');
 
-    $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
+    $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
     $skiproles     = $meta->get_config('nosyncroleids', '');
     $skiproles     = empty($skiproles) ? array() : explode(',', $skiproles);
     $syncall       = $meta->get_config('syncall', 1);
@@ -493,19 +514,24 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
             if ($verbose) {
                 mtrace("  unenrolling: $ue->userid ==> $instance->courseid");
             }
-            continue;
 
-        } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
-            // just disable and ignore any changes
+        } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
+            if ($ue->status != ENROL_USER_SUSPENDED) {
+                $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
+                if ($verbose) {
+                    mtrace("  suspending: $ue->userid ==> $instance->courseid");
+                }
+            }
+
+        } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
             if ($ue->status != ENROL_USER_SUSPENDED) {
                 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
                 $context = context_course::instance($instance->courseid);
-                role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
+                role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
                 if ($verbose) {
                     mtrace("  suspending and removing all roles: $ue->userid ==> $instance->courseid");
                 }
             }
-            continue;
         }
     }
     $rs->close();
@@ -617,6 +643,7 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
     } else {
         $notignored = "";
     }
+
     $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
               FROM {role_assignments} ra
               JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
@@ -625,14 +652,16 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
          LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :activeuser)
              WHERE pra.id IS NULL OR ue.id IS NULL OR e.status <> :enabledinstance";
 
-    $rs = $DB->get_recordset_sql($sql, $params);
-    foreach($rs as $ra) {
-        role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
-        if ($verbose) {
-            mtrace("  unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
+    if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach($rs as $ra) {
+            role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
+            if ($verbose) {
+                mtrace("  unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
+            }
         }
+        $rs->close();
     }
-    $rs->close();
 
 
     // kick out or suspend users without synced roles if syncall disabled
index d2f5d93..3cbc4e1 100644 (file)
@@ -36,8 +36,10 @@ if ($ADMIN->fulltree) {
         $settings->add(new admin_setting_configcheckbox('enrol_meta/syncall', get_string('syncall', 'enrol_meta'), get_string('syncall_desc', 'enrol_meta'), 1));
 
         $options = array(
-            ENROL_EXT_REMOVED_UNENROL        => get_string('extremovedunenrol', 'enrol'),
-            ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'));
+            ENROL_EXT_REMOVED_UNENROL        => get_string('extremovedunenrol', 'core_enrol'),
+            ENROL_EXT_REMOVED_SUSPEND        => get_string('extremovedsuspend', 'core_enrol'),
+            ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'core_enrol'),
+        );
         $settings->add(new admin_setting_configselect('enrol_meta/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_SUSPENDNOROLES, $options));
     }
 }
diff --git a/enrol/meta/tests/plugin_test.php b/enrol/meta/tests/plugin_test.php
new file mode 100644 (file)
index 0000000..fdfec33
--- /dev/null
@@ -0,0 +1,442 @@
+<?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/>.
+
+/**
+ * Meta enrolment sync functional test.
+ *
+ * @package    enrol_meta
+ * @category   phpunit
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+class enrol_meta_plugin_testcase extends advanced_testcase {
+
+    protected function enable_plugin() {
+        $enabled = enrol_get_plugins(true);
+        $enabled['meta'] = true;
+        $enabled = array_keys($enabled);
+        set_config('enrol_plugins_enabled', implode(',', $enabled));
+    }
+
+    protected function disable_plugin() {
+        $enabled = enrol_get_plugins(true);
+        unset($enabled['meta']);
+        $enabled = array_keys($enabled);
+        set_config('enrol_plugins_enabled', implode(',', $enabled));
+    }
+
+    protected function is_meta_enrolled($user, $enrol, $role = null) {
+        global $DB;
+
+        if (!$DB->record_exists('user_enrolments', array('enrolid'=>$enrol->id, 'userid'=>$user->id))) {
+            return false;
+        }
+
+        if ($role === null) {
+            return true;
+        }
+
+        return $this->has_role($user, $enrol, $role);
+    }
+
+    protected function has_role($user, $enrol, $role) {
+        global $DB;
+
+        $context = context_course::instance($enrol->courseid);
+
+        if ($role === false) {
+            if ($DB->record_exists('role_assignments', array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id))) {
+                return false;
+            }
+        } else if (!$DB->record_exists('role_assignments', array('contextid'=>$context->id, 'userid'=>$user->id, 'roleid'=>$role->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public function test_sync() {
+        global $CFG, $DB;
+
+        $this->resetAfterTest(true);
+
+        $metalplugin = enrol_get_plugin('meta');
+        $manplugin = enrol_get_plugin('manual');
+
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+        $user5 = $this->getDataGenerator()->create_user();
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $course3 = $this->getDataGenerator()->create_course();
+        $course4 = $this->getDataGenerator()->create_course();
+        $manual1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $manual2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $manual3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $manual4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+
+        $student = $DB->get_record('role', array('shortname'=>'student'));
+        $teacher = $DB->get_record('role', array('shortname'=>'teacher'));
+        $manager = $DB->get_record('role', array('shortname'=>'manager'));
+
+        $this->disable_plugin();
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course1->id, $student->id);
+        $this->getDataGenerator()->enrol_user($user3->id, $course1->id, 0);
+        $this->getDataGenerator()->enrol_user($user4->id, $course1->id, $teacher->id);
+        $this->getDataGenerator()->enrol_user($user5->id, $course1->id, $manager->id);
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $student->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course2->id, $teacher->id);
+
+        $this->assertEquals(7, $DB->count_records('user_enrolments'));
+        $this->assertEquals(6, $DB->count_records('role_assignments'));
+
+        set_config('syncall', 0, 'enrol_meta');
+        set_config('nosyncroleids', $manager->id, 'enrol_meta');
+
+        require_once($CFG->dirroot.'/enrol/meta/locallib.php');
+
+        enrol_meta_sync(null, false);
+        $this->assertEquals(7, $DB->count_records('user_enrolments'));
+        $this->assertEquals(6, $DB->count_records('role_assignments'));
+
+        $this->enable_plugin();
+        enrol_meta_sync(null, false);
+        $this->assertEquals(7, $DB->count_records('user_enrolments'));
+        $this->assertEquals(6, $DB->count_records('role_assignments'));
+
+        $e1 = $metalplugin->add_instance($course3, array('customint1'=>$course1->id));
+        $e2 = $metalplugin->add_instance($course3, array('customint1'=>$course2->id));
+        $e3 = $metalplugin->add_instance($course4, array('customint1'=>$course2->id));
+        $enrol1 = $DB->get_record('enrol', array('id'=>$e1));
+        $enrol2 = $DB->get_record('enrol', array('id'=>$e2));
+        $enrol3 = $DB->get_record('enrol', array('id'=>$e3));
+
+        enrol_meta_sync($course4->id, false);
+        $this->assertEquals(9, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol3, $student));
+        $this->assertTrue($this->is_meta_enrolled($user2, $enrol3, $teacher));
+
+        enrol_meta_sync(null, false);
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(13, $DB->count_records('role_assignments'));
+
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        $this->assertTrue($this->is_meta_enrolled($user2, $enrol1, $student));
+        $this->assertFalse($this->is_meta_enrolled($user3, $enrol1));
+        $this->assertTrue($this->is_meta_enrolled($user4, $enrol1, $teacher));
+        $this->assertFalse($this->is_meta_enrolled($user5, $enrol1));
+
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol2, $student));
+        $this->assertTrue($this->is_meta_enrolled($user2, $enrol2, $teacher));
+
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol3, $student));
+        $this->assertTrue($this->is_meta_enrolled($user2, $enrol3, $teacher));
+
+        set_config('syncall', 1, 'enrol_meta');
+        enrol_meta_sync(null, false);
+        $this->assertEquals(16, $DB->count_records('user_enrolments'));
+        $this->assertEquals(13, $DB->count_records('role_assignments'));
+
+        $this->assertTrue($this->is_meta_enrolled($user3, $enrol1, false));
+        $this->assertTrue($this->is_meta_enrolled($user5, $enrol1, false));
+
+        $this->assertEquals(16, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->disable_plugin();
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $manplugin->unenrol_user($manual2, $user1->id);
+
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(11, $DB->count_records('role_assignments'));
+        $this->assertEquals(14, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        $this->enable_plugin();
+
+        set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND, 'enrol_meta');
+        enrol_meta_sync($course4->id, false);
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(11, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol3, $student));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol3->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+
+        enrol_meta_sync(null, false);
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(11, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol1->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol2, $student));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol2->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+
+        set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES, 'enrol_meta');
+        enrol_meta_sync($course4->id, false);
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol3, false));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol3->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+
+        enrol_meta_sync(null, false);
+        $this->assertEquals(14, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, false));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol1->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol2, false));
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$enrol2->id, 'status'=>ENROL_USER_SUSPENDED, 'userid'=>$user1->id)));
+
+        set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL, 'enrol_meta');
+        enrol_meta_sync($course4->id, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol3));
+
+        enrol_meta_sync(null, false);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol2));
+
+
+        // Now try sync triggered by events.
+
+        set_config('syncall', 1, 'enrol_meta');
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 0);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, false));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, false));
+
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+
+        set_config('syncall', 0, 'enrol_meta');
+        enrol_meta_sync(null, false);
+        $this->assertEquals(9, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(9, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 0);
+        $this->assertEquals(10, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(10, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(10, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(10, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        role_assign($teacher->id, $user1->id, context_course::instance($course1->id)->id);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $teacher));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $teacher));
+
+        role_unassign($teacher->id, $user1->id, context_course::instance($course1->id)->id);
+        $this->assertEquals(10, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(10, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(10, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(10, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $this->assertEquals(9, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(9, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertFalse($this->is_meta_enrolled($user1, $enrol1));
+
+        set_config('syncall', 1, 'enrol_meta');
+        set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND, 'enrol_meta');
+        enrol_meta_sync(null, false);
+        $this->assertEquals(11, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $manplugin->update_user_enrol($manual1, $user1->id, ENROL_USER_SUSPENDED);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $this->assertEquals(12, $DB->count_records('user_enrolments'));
+        $this->assertEquals(9, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(12, $DB->count_records('user_enrolments'));
+        $this->assertEquals(9, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        set_config('syncall', 1, 'enrol_meta');
+        set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES, 'enrol_meta');
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+        $manplugin->unenrol_user($manual1, $user1->id);
+        $this->assertEquals(12, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, false));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(12, $DB->count_records('user_enrolments'));
+        $this->assertEquals(8, $DB->count_records('role_assignments'));
+        $this->assertEquals(11, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, false));
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, $student->id);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        $this->assertTrue($this->is_meta_enrolled($user1, $enrol1, $student));
+
+
+        set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL, 'enrol_meta');
+        enrol_meta_sync(null, false);
+        $this->assertEquals(13, $DB->count_records('user_enrolments'));
+        $this->assertEquals(10, $DB->count_records('role_assignments'));
+        $this->assertEquals(13, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        delete_course($course1, false);
+        $this->assertEquals(3, $DB->count_records('user_enrolments'));
+        $this->assertEquals(3, $DB->count_records('role_assignments'));
+        $this->assertEquals(3, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(3, $DB->count_records('user_enrolments'));
+        $this->assertEquals(3, $DB->count_records('role_assignments'));
+        $this->assertEquals(3, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        delete_course($course2, false);
+        $this->assertEquals(0, $DB->count_records('user_enrolments'));
+        $this->assertEquals(0, $DB->count_records('role_assignments'));
+        $this->assertEquals(0, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+        enrol_meta_sync(null, false);
+        $this->assertEquals(0, $DB->count_records('user_enrolments'));
+        $this->assertEquals(0, $DB->count_records('role_assignments'));
+        $this->assertEquals(0, $DB->count_records('user_enrolments', array('status'=>ENROL_USER_ACTIVE)));
+
+        delete_course($course3, false);
+        delete_course($course4, false);
+
+    }
+}
index c5920ff..3526c58 100644 (file)
@@ -365,7 +365,8 @@ class grade_report_user extends grade_report {
                     $cm = $instances[$grade_object->iteminstance];
                     if (!$cm->uservisible) {
                         // Further checks are required to determine whether the activity is entirely hidden or just greyed out.
-                        if ($cm->is_user_access_restricted_by_group() || $cm->is_user_access_restricted_by_conditional_access()) {
+                        if ($cm->is_user_access_restricted_by_group() || $cm->is_user_access_restricted_by_conditional_access() ||
+                                $cm->is_user_access_restricted_by_capability()) {
                             $hide = true;
                         }
                     }
index 559821e..7374550 100644 (file)
@@ -134,7 +134,7 @@ function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
     }
 
     // Wrapping the output.
-    echo '<div class="phpdebugmessage">' . PHP_EOL;
+    echo '<div class="phpdebugmessage" data-rel="phpdebugmessage">' . PHP_EOL;
     echo "$errnostr: $errstr in $errfile on line $errline" . PHP_EOL;
     echo '</div>';
 
index 37c5f3f..574d246 100644 (file)
@@ -317,8 +317,8 @@ function css_minify_css($files) {
     unset($_SERVER['HTTP_IF_NONE_MATCH']);
     unset($_SERVER['HTTP_IF_MODIFIED_SINCE']);
 
-    set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
-    require_once('Minify.php');
+    require_once("$CFG->libdir/minify/lib/Minify/Loader.php");
+    Minify_Loader::register();
 
     if (0 === stripos(PHP_OS, 'win')) {
         Minify::setDocRoot(); // IIS may need help
index a580d0a..ee19a96 100644 (file)
@@ -2306,5 +2306,12 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2013071500.02);
     }
 
+    if ($oldversion < 2013072600.01) {
+        upgrade_mssql_nvarcharmax();
+        upgrade_mssql_varbinarymax();
+
+        upgrade_main_savepoint(true, 2013072600.01);
+    }
+
     return true;
 }
index 6edc5d8..3d89eb9 100644 (file)
@@ -164,3 +164,109 @@ function upgrade_mysql_fix_unsigned_and_lob_columns() {
         $pbar->update($i, $tablecount, "Converted unsigned/lob columns in MySQL database - $i/$tablecount.");
     }
 }
+
+/**
+ * Migrate NTEXT to NVARCHAR(MAX).
+ */
+function upgrade_mssql_nvarcharmax() {
+    global $DB;
+
+    if ($DB->get_dbfamily() !== 'mssql') {
+        return;
+    }
+
+    $pbar = new progress_bar('mssqlconvertntext', 500, true);
+
+    $prefix = $DB->get_prefix();
+    $tables = $DB->get_tables(false);
+
+    $tablecount = count($tables);
+    $i = 0;
+    foreach ($tables as $table) {
+        $i++;
+
+        $columns = array();
+
+        $sql = "SELECT column_name
+                  FROM INFORMATION_SCHEMA.COLUMNS
+                 WHERE table_name = '{{$table}}' AND UPPER(data_type) = 'NTEXT'";
+        $rs = $DB->get_recordset_sql($sql);
+        foreach ($rs as $column) {
+            $columns[] = $column->column_name;
+        }
+        $rs->close();
+
+        if ($columns) {
+            // Set appropriate timeout - 1 minute per thousand of records should be enough, min 60 minutes just in case.
+            $count = $DB->count_records($table, array());
+            $timeout = ($count/1000)*60;
+            $timeout = ($timeout < 60*60) ? 60*60 : (int)$timeout;
+            upgrade_set_timeout($timeout);
+
+            $updates = array();
+            foreach ($columns as $column) {
+                // Change the definition.
+                $sql = "ALTER TABLE {$prefix}$table ALTER COLUMN $column NVARCHAR(MAX)";
+                $DB->change_database_structure($sql);
+                $updates[] = "$column = $column";
+            }
+
+            // Now force the migration of text data to new optimised storage.
+            $sql = "UPDATE {{$table}} SET ".implode(', ', $updates);
+            $DB->execute($sql);
+        }
+
+        $pbar->update($i, $tablecount, "Converted NTEXT to NVARCHAR(MAX) columns in MS SQL Server database - $i/$tablecount.");
+    }
+}
+
+/**
+ * Migrate IMAGE to VARBINARY(MAX).
+ */
+function upgrade_mssql_varbinarymax() {
+    global $DB;
+
+    if ($DB->get_dbfamily() !== 'mssql') {
+        return;
+    }
+
+    $pbar = new progress_bar('mssqlconvertimage', 500, true);
+
+    $prefix = $DB->get_prefix();
+    $tables = $DB->get_tables(false);
+
+    $tablecount = count($tables);
+    $i = 0;
+    foreach ($tables as $table) {
+        $i++;
+
+        $columns = array();
+
+        $sql = "SELECT column_name
+                  FROM INFORMATION_SCHEMA.COLUMNS
+                 WHERE table_name = '{{$table}}' AND UPPER(data_type) = 'IMAGE'";
+        $rs = $DB->get_recordset_sql($sql);
+        foreach ($rs as $column) {
+            $columns[] = $column->column_name;
+        }
+        $rs->close();
+
+        if ($columns) {
+            // Set appropriate timeout - 1 minute per thousand of records should be enough, min 60 minutes just in case.
+            $count = $DB->count_records($table, array());
+            $timeout = ($count/1000)*60;
+            $timeout = ($timeout < 60*60) ? 60*60 : (int)$timeout;
+            upgrade_set_timeout($timeout);
+
+            foreach ($columns as $column) {
+                // Change the definition.
+                $sql = "ALTER TABLE {$prefix}$table ALTER COLUMN $column VARBINARY(MAX)";
+                $DB->change_database_structure($sql);
+            }
+
+            // Binary columns should not be used, do not waste time optimising the storage.
+        }
+
+        $pbar->update($i, $tablecount, "Converted IMAGE to VARBINARY(MAX) columns in MS SQL Server database - $i/$tablecount.");
+    }
+}
index bbf769d..ccd2d81 100644 (file)
@@ -221,10 +221,10 @@ class mssql_sql_generator extends sql_generator {
                 $dbtype .= '(' . $xmldb_length . ')';
                 break;
             case XMLDB_TYPE_TEXT:
-                $dbtype = 'NTEXT';
+                $dbtype = 'NVARCHAR(MAX)';
                 break;
             case XMLDB_TYPE_BINARY:
-                $dbtype = 'IMAGE';
+                $dbtype = 'VARBINARY(MAX)';
                 break;
             case XMLDB_TYPE_DATETIME:
                 $dbtype = 'DATETIME';
index 862b055..2191b89 100644 (file)
@@ -464,9 +464,15 @@ class mssql_native_moodle_database extends moodle_database {
             // id columns being auto_incremnt are PK by definition
             $info->primary_key = ($info->name == 'id' && $info->meta_type == 'R' && $info->auto_increment);
 
-            // Put correct length for character and LOB types
-            $info->max_length = $info->meta_type == 'C' ? $rawcolumn->char_max_length : $rawcolumn->max_length;
-            $info->max_length = ($info->meta_type == 'X' || $info->meta_type == 'B') ? -1 : $info->max_length;
+            if ($info->meta_type === 'C' and $rawcolumn->char_max_length == -1) {
+                // This is NVARCHAR(MAX), not a normal NVARCHAR.
+                $info->max_length = -1;
+                $info->meta_type = 'X';
+            } else {
+                // Put correct length for character and LOB types
+                $info->max_length = $info->meta_type == 'C' ? $rawcolumn->char_max_length : $rawcolumn->max_length;
+                $info->max_length = ($info->meta_type == 'X' || $info->meta_type == 'B') ? -1 : $info->max_length;
+            }
 
             // Scale
             $info->scale = $rawcolumn->scale ? $rawcolumn->scale : false;
@@ -573,6 +579,7 @@ class mssql_native_moodle_database extends moodle_database {
                 $type = 'X';
                 break;
             case 'IMAGE':
+            case 'VARBINARY':
             case 'VARBINARY(MAX)':
                 $type = 'B';
                 break;
index 13c46e4..b5062b1 100644 (file)
@@ -528,9 +528,15 @@ class sqlsrv_native_moodle_database extends moodle_database {
             // id columns being auto_incremnt are PK by definition
             $info->primary_key = ($info->name == 'id' && $info->meta_type == 'R' && $info->auto_increment);
 
-            // Put correct length for character and LOB types
-            $info->max_length = $info->meta_type == 'C' ? $rawcolumn->char_max_length : $rawcolumn->max_length;
-            $info->max_length = ($info->meta_type == 'X' || $info->meta_type == 'B') ? -1 : $info->max_length;
+            if ($info->meta_type === 'C' and $rawcolumn->char_max_length == -1) {
+                // This is NVARCHAR(MAX), not a normal NVARCHAR.
+                $info->max_length = -1;
+                $info->meta_type = 'X';
+            } else {
+                // Put correct length for character and LOB types
+                $info->max_length = $info->meta_type == 'C' ? $rawcolumn->char_max_length : $rawcolumn->max_length;
+                $info->max_length = ($info->meta_type == 'X' || $info->meta_type == 'B') ? -1 : $info->max_length;
+            }
 
             // Scale
             $info->scale = $rawcolumn->scale ? $rawcolumn->scale : false;
@@ -645,6 +651,7 @@ class sqlsrv_native_moodle_database extends moodle_database {
            break;
 
           case 'IMAGE':
+          case 'VARBINARY':
           case 'VARBINARY(MAX)':
            $type = 'B';
            break;
index 0f510ff..9175492 100644 (file)
@@ -12,4 +12,6 @@ Upgrade procedure:
 2/ bump up version.php
 3/ update lib/thirdpartylibs.xml
 4/ reimplement patch in MDL-23646
+5/ add in "DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe" (without quotes)
+   after "DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe"
 5/ reminify the js manually (I used uglifyjs)
index 3646b35..13a65a4 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
  * THE SOFTWARE.\r
  * Based on TinyMCE Wordpress plugin (Kitchen Sink)\r
+ * \r
+ * Changes V1.1.1 --> V1.2\r
+ * \r
+ * heeae made some modifications and posted his work on Sourceforge. I thought the cookie support \r
+ * was really handy so I updated the script.\r
+ * http://sourceforge.net/tracker/?func=detail&atid=738747&aid=2904683&group_id=103281\r
+ *\r
+ * 1. Added cookie support.\r
+ * 2. Some optimization\r
+ * 3. Bug fix of fire ifr.clientHeight in FF 3 ( to DOM.getStyles)\r
+ *\r
+ * Thanks heeae!\r
+ * \r
+ * Changes V1.1 --> V1.1.1\r
+ *\r
+ * Bugfix for Firefox 3.6. Caused error while loading script.\r
+ *\r
+ * Added lines 72 - 76:\r
+ *\r
+ * obj = ed.controlManager.get(tbIds[j]);\r
+ * if(typeof obj =="undefined") {\r
+ *             continue;\r
+ * }\r
+ * id = obj.id;\r
+ *\r
+ * instead of:\r
+ *\r
+ * try {\r
+ *             id = ed.controlManager.get(tbIds[j]).id;\r
+ * }\r
+ * catch(e) {\r
+ * //if(typeof id == "undefined") continue;\r
+ *             continue;\r
+ * }\r
+ *\r
+ * Thanks Anton for fixing this bug\r
+ * \r
  */\r
-!function(){var DOM=tinymce.DOM;tinymce.PluginManager.requireLangPack("pdw");tinymce.create("tinymce.plugins.pdw",{init:function(ed,url){var t=this,tbIds=new Array,toolbars=new Array,i;toolbars=ed.settings.pdw_toggle_toolbars.split(",");for(i=0;i<toolbars.length;i++){tbIds[i]=ed.getParam("","toolbar"+toolbars[i].replace(" ",""))}ed.addCommand("mcePDWToggleToolbars",function(){var cm=ed.controlManager,id,j,Cookie=tinymce.util.Cookie,Toggle_PDW,Toggle=Cookie.getHash("TinyMCE_toggle")||new Object;for(j=0;j<tbIds.length;j++){obj=ed.controlManager.get(tbIds[j]);if(typeof obj=="undefined"){continue}id=obj.id;if(DOM.isHidden(id)){Toggle_PDW=0;var e=document.getElementById(id);if(e){e.style.display="table";t._resizeIframe(ed,tbIds[j],-26)}}else{Toggle_PDW=1;var e=document.getElementById(id);if(e){e.style.display="none"}t._resizeIframe(ed,tbIds[j],26)}}cm.setActive("pdw_toggle",Toggle_PDW);ed.settings.pdw_toggle_on=Toggle_PDW;Toggle[ed.id]=Toggle_PDW;Cookie.setHash("TinyMCE_toggle",Toggle)});ed.addButton("pdw_toggle",{title:ed.getLang("pdw.desc",0),cmd:"mcePDWToggleToolbars",image:url+"/img/toolbars.gif"});ed.onPostRender.add(function(){var toggle=tinymce.util.Cookie.getHash("TinyMCE_toggle")||new Object;var run=false;if(toggle[ed.id]==null){run=ed.settings.pdw_toggle_on==1?true:false}else if(toggle[ed.id]==1){run=true}if(run){var cm=ed.controlManager,tdId,id;for(i=0;i<toolbars.length;i++){tbId=ed.getParam("","toolbar"+toolbars[i].replace(" ",""));id=ed.controlManager.get(tbId).id;cm.setActive("pdw_toggle",1);DOM.hide(id);t._resizeIframe(ed,tbId,26)}}})},_resizeIframe:function(ed,tb_id,dy){var ifr=ed.getContentAreaContainer().firstChild;DOM.setStyle(ifr,"height",DOM.getSize(ifr).h+dy);ed.theme.deltaHeight+=dy},getInfo:function(){return{longname:"PDW Toggle Toolbars",author:"Guido Neele",authorurl:"http://www.neele.name/",infourl:"http://www.neele.name/pdw_toggle_toolbars",version:"1.2"}}});tinymce.PluginManager.add("pdw",tinymce.plugins.pdw)}();
\ No newline at end of file
+\r
+(function() {\r
+       var DOM = tinymce.DOM;\r
+       tinymce.PluginManager.requireLangPack('pdw');\r
+       \r
+       tinymce.create('tinymce.plugins.pdw', {\r
+               /**\r
+                * Initializes the plugin, this will be executed after the plugin has been created.\r
+                * This call is done before the editor instance has finished it's initialization so use the onInit event\r
+                * of the editor instance to intercept that event.\r
+                *\r
+                * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.\r
+                * @param {string} url Absolute URL to where the plugin is located.\r
+                */\r
+               init : function(ed, url) {\r
+                       var t = this, tbIds = new Array(), toolbars = new Array(), i;\r
+                       \r
+                       // Split toolbars\r
+                       toolbars = (ed.settings.pdw_toggle_toolbars).split(',');\r
+                       \r
+                       for(i = 0; i < toolbars.length; i++){\r
+                               tbIds[i] = ed.getParam('', 'toolbar' + (toolbars[i]).replace(' ',''));\r
+                       }\r
+                       \r
+                       // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');\r
+                       ed.addCommand('mcePDWToggleToolbars', function() {\r
+                       \r
+                               var cm = ed.controlManager, id, j, Cookie = tinymce.util.Cookie, Toggle_PDW, Toggle = Cookie.getHash("TinyMCE_toggle") || new Object();\r
+                               for(j = 0; j < tbIds.length; j++){\r
+                                       \r
+                                       obj = ed.controlManager.get(tbIds[j]);\r
+                    if(typeof obj =="undefined") {\r
+                        continue;\r
+                    }\r
+                    id = obj.id;\r
+                                       \r
+                                       if (DOM.isHidden(id)) {\r
+                                               Toggle_PDW = 0;\r
+                        var e = document.getElementById(id);\r
+                        if (e) {\r
+                            e.style.display = 'table';\r
+                            t._resizeIframe(ed, tbIds[j], -26);\r
+                        }\r
+                                               \r
+                                       } else {\r
+                                               Toggle_PDW = 1;\r
+                        var e = document.getElementById(id);\r
+                        if (e) {\r
+                            e.style.display = 'none';\r
+                        }\r
+                                               t._resizeIframe(ed, tbIds[j], 26);\r
+                                       }\r
+                               }\r
+                               cm.setActive('pdw_toggle', Toggle_PDW);\r
+                               ed.settings.pdw_toggle_on = Toggle_PDW;\r
+                               Toggle[ed.id] = Toggle_PDW;\r
+                               Cookie.setHash("TinyMCE_toggle", Toggle);\r
+                       });\r
+                       \r
+                       // Register pdw_toggle button\r
+                       ed.addButton('pdw_toggle', {\r
+                               title : ed.getLang('pdw.desc', 0),\r
+                               cmd : 'mcePDWToggleToolbars',\r
+                               image : url + '/img/toolbars.gif'\r
+                       });\r
+                       \r
+                       ed.onPostRender.add(function(){\r
+                               var toggle = tinymce.util.Cookie.getHash("TinyMCE_toggle") || new Object();\r
+                               var run = false;\r
+                               \r
+                               // Check if value is stored in cookie\r
+                               if(toggle[ed.id] == null){\r
+                                       // No cookie so check if the setting pdw_toggle_on is set to 1 then hide toolbars and set button active\r
+                                       run = ed.settings.pdw_toggle_on == 1 ? true : false;\r
+                               } else if(toggle[ed.id] == 1){\r
+                                       run = true;\r
+                               }\r
+                       \r
+                               if (run) {\r
+\r
+                                       var cm = ed.controlManager, tdId, id;\r
+                                       \r
+                                       for(i = 0; i < toolbars.length; i++){\r
+                                               tbId = ed.getParam('', 'toolbar' + (toolbars[i]).replace(' ',''));\r
+                                               id = ed.controlManager.get(tbId).id;\r
+                                               cm.setActive('pdw_toggle', 1);\r
+                                               DOM.hide(id);\r
+                                               t._resizeIframe(ed, tbId, 26);\r
+                                       }\r
+                               }\r
+                       });\r
+               },\r
+               \r
+               // Resizes the iframe by a relative height value\r
+               _resizeIframe : function(ed, tb_id, dy) {\r
+                   var ifr = ed.getContentAreaContainer().firstChild;\r
+                   var ifrcon = ed.getContentAreaContainer();\r
+                       \r
+                   DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe\r
+                   DOM.setStyle(ifr, 'width',DOM.getSize(ifrcon).w); // Resize iframe\r
+                       ed.theme.deltaHeight += dy; // For resize cookie\r
+               },\r
+\r
+               /**\r
+                * Returns information about the plugin as a name/value array.\r
+                * The current keys are longname, author, authorurl, infourl and version.\r
+                *\r
+                * @return {Object} Name/value array containing information about the plugin.\r
+                */\r
+               getInfo : function() {\r
+                       return {\r
+                               longname : 'PDW Toggle Toolbars',\r
+                               author : 'Guido Neele',\r
+                               authorurl : 'http://www.neele.name/',\r
+                               infourl : 'http://www.neele.name/pdw_toggle_toolbars',\r
+                               version : "1.2"\r
+                       };\r
+               }\r
+       });\r
+\r
+       // Register plugin\r
+       tinymce.PluginManager.add('pdw', tinymce.plugins.pdw);\r
+})();\r
diff --git a/lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin_src.js b/lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin_src.js
deleted file mode 100644 (file)
index 6a1c707..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/**\r
- * PDW Toggle Toolbars v1.2\r
- * Url: http://www.neele.name\r
- * Author: Guido Neele\r
- * \r
- * Permission is hereby granted, free of charge, to any person obtaining a copy\r
- * of this software and associated documentation files (the "Software"), to deal\r
- * in the Software without restriction, including without limitation the rights\r
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
- * copies of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- * \r
- * The above copyright notice and this permission notice shall be included in\r
- * all copies or substantial portions of the Software.\r
- * \r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
- * THE SOFTWARE.\r
- * Based on TinyMCE Wordpress plugin (Kitchen Sink)\r
- * \r
- * Changes V1.1.1 --> V1.2\r
- * \r
- * heeae made some modifications and posted his work on Sourceforge. I thought the cookie support \r
- * was really handy so I updated the script.\r
- * http://sourceforge.net/tracker/?func=detail&atid=738747&aid=2904683&group_id=103281\r
- *\r
- * 1. Added cookie support.\r
- * 2. Some optimization\r
- * 3. Bug fix of fire ifr.clientHeight in FF 3 ( to DOM.getStyles)\r
- *\r
- * Thanks heeae!\r
- * \r
- * Changes V1.1 --> V1.1.1\r
- *\r
- * Bugfix for Firefox 3.6. Caused error while loading script.\r
- *\r
- * Added lines 72 - 76:\r
- *\r
- * obj = ed.controlManager.get(tbIds[j]);\r
- * if(typeof obj =="undefined") {\r
- *             continue;\r
- * }\r
- * id = obj.id;\r
- *\r
- * instead of:\r
- *\r
- * try {\r
- *             id = ed.controlManager.get(tbIds[j]).id;\r
- * }\r
- * catch(e) {\r
- * //if(typeof id == "undefined") continue;\r
- *             continue;\r
- * }\r
- *\r
- * Thanks Anton for fixing this bug\r
- * \r
- */\r
-\r
-(function() {\r
-       var DOM = tinymce.DOM;\r
-       tinymce.PluginManager.requireLangPack('pdw');\r
-       \r
-       tinymce.create('tinymce.plugins.pdw', {\r
-               /**\r
-                * Initializes the plugin, this will be executed after the plugin has been created.\r
-                * This call is done before the editor instance has finished it's initialization so use the onInit event\r
-                * of the editor instance to intercept that event.\r
-                *\r
-                * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.\r
-                * @param {string} url Absolute URL to where the plugin is located.\r
-                */\r
-               init : function(ed, url) {\r
-                       var t = this, tbIds = new Array(), toolbars = new Array(), i;\r
-                       \r
-                       // Split toolbars\r
-                       toolbars = (ed.settings.pdw_toggle_toolbars).split(',');\r
-                       \r
-                       for(i = 0; i < toolbars.length; i++){\r
-                               tbIds[i] = ed.getParam('', 'toolbar' + (toolbars[i]).replace(' ',''));\r
-                       }\r
-                       \r
-                       // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');\r
-                       ed.addCommand('mcePDWToggleToolbars', function() {\r
-                       \r
-                               var cm = ed.controlManager, id, j, Cookie = tinymce.util.Cookie, Toggle_PDW, Toggle = Cookie.getHash("TinyMCE_toggle") || new Object();\r
-                               for(j = 0; j < tbIds.length; j++){\r
-                                       \r
-                                       obj = ed.controlManager.get(tbIds[j]);\r
-                    if(typeof obj =="undefined") {\r
-                        continue;\r
-                    }\r
-                    id = obj.id;\r
-                                       \r
-                                       if (DOM.isHidden(id)) {\r
-                                               Toggle_PDW = 0;\r
-                        var e = document.getElementById(id);\r
-                        if (e) {\r
-                            e.style.display = 'table';\r
-                            t._resizeIframe(ed, tbIds[j], -26);\r
-                        }\r
-                                               \r
-                                       } else {\r
-                                               Toggle_PDW = 1;\r
-                        var e = document.getElementById(id);\r
-                        if (e) {\r
-                            e.style.display = 'none';\r
-                        }\r
-                                               t._resizeIframe(ed, tbIds[j], 26);\r
-                                       }\r
-                               }\r
-                               cm.setActive('pdw_toggle', Toggle_PDW);\r
-                               ed.settings.pdw_toggle_on = Toggle_PDW;\r
-                               Toggle[ed.id] = Toggle_PDW;\r
-                               Cookie.setHash("TinyMCE_toggle", Toggle);\r
-                       });\r
-                       \r
-                       // Register pdw_toggle button\r
-                       ed.addButton('pdw_toggle', {\r
-                               title : ed.getLang('pdw.desc', 0),\r
-                               cmd : 'mcePDWToggleToolbars',\r
-                               image : url + '/img/toolbars.gif'\r
-                       });\r
-                       \r
-                       ed.onPostRender.add(function(){\r
-                               var toggle = tinymce.util.Cookie.getHash("TinyMCE_toggle") || new Object();\r
-                               var run = false;\r
-                               \r
-                               // Check if value is stored in cookie\r
-                               if(toggle[ed.id] == null){\r
-                                       // No cookie so check if the setting pdw_toggle_on is set to 1 then hide toolbars and set button active\r
-                                       run = ed.settings.pdw_toggle_on == 1 ? true : false;\r
-                               } else if(toggle[ed.id] == 1){\r
-                                       run = true;\r
-                               }\r
-                       \r
-                               if (run) {\r
-\r
-                                       var cm = ed.controlManager, tdId, id;\r
-                                       \r
-                                       for(i = 0; i < toolbars.length; i++){\r
-                                               tbId = ed.getParam('', 'toolbar' + (toolbars[i]).replace(' ',''));\r
-                                               id = ed.controlManager.get(tbId).id;\r
-                                               cm.setActive('pdw_toggle', 1);\r
-                                               DOM.hide(id);\r
-                                               t._resizeIframe(ed, tbId, 26);\r
-                                       }\r
-                               }\r
-                       });\r
-               },\r
-               \r
-               // Resizes the iframe by a relative height value\r
-               _resizeIframe : function(ed, tb_id, dy) {\r
-                       var ifr = ed.getContentAreaContainer().firstChild;\r
-                       \r
-                       DOM.setStyle(ifr, 'height',DOM.getSize(ifr).h + dy); // Resize iframe\r
-                       ed.theme.deltaHeight += dy; // For resize cookie\r
-               },\r
-\r
-               /**\r
-                * Returns information about the plugin as a name/value array.\r
-                * The current keys are longname, author, authorurl, infourl and version.\r
-                *\r
-                * @return {Object} Name/value array containing information about the plugin.\r
-                */\r
-               getInfo : function() {\r
-                       return {\r
-                               longname : 'PDW Toggle Toolbars',\r
-                               author : 'Guido Neele',\r
-                               authorurl : 'http://www.neele.name/',\r
-                               infourl : 'http://www.neele.name/pdw_toggle_toolbars',\r
-                               version : "1.2"\r
-                       };\r
-               }\r
-       });\r
-\r
-       // Register plugin\r
-       tinymce.PluginManager.add('pdw', tinymce.plugins.pdw);\r
-})();\r
index 6a5fa6e..4304c6e 100644 (file)
@@ -318,10 +318,9 @@ class zip_archive extends file_archive {
         }
         if (substr($fileinfo->pathname, -9) === 'Thumbs.db') {
             $stream = $this->za->getStream($fileinfo->pathname);
-            $info = unpack('Nsiga/Nsigb', fread($stream, 8));
-            $signature = fread($stream, 8);
+            $info = base64_encode(fread($stream, 8));
             fclose($stream);
-            if ($info['siga'] === 0xd0cf11e0 && $info['sigb'] === 0xa1b11ae1) {
+            if ($info === '0M8R4KGxGuE=') {
                 // It's an OLE Compound File - so it's almost certainly a Windows thumbnail cache.
                 return true;
             }
index 528bf58..0c71af1 100644 (file)
@@ -464,22 +464,19 @@ function groups_get_course_groupmode($course) {
  * overrides activity setting if groupmodeforce enabled.
  *
  * @category group
- * @param cm_info $cm the course module object. Only the ->course and ->groupmode need to be set.
+ * @param cm_info|stdClass $cm the course module object. Only the ->course and ->groupmode need to be set.
  * @param stdClass $course object optional course object to improve perf
  * @return int group mode
  */
 function groups_get_activity_groupmode($cm, $course=null) {
-    global $COURSE, $DB;
-
-    // get course object (reuse COURSE if possible)
     if (isset($course->id) and $course->id == $cm->course) {
         //ok
-    } else if ($cm->course == $COURSE->id) {
-        $course = $COURSE;
+    } else if (isset($cm->coursegroupmode) && isset($cm->coursegroupmodeforce)) {
+        // This is an instance of cm_info (or clone) and already has the necessary course fields in it.
+        return empty($cm->coursegroupmodeforce) ? $cm->groupmode : $cm->coursegroupmode;
     } else {
-        if (!$course = $DB->get_record('course', array('id'=>$cm->course))) {
-            print_error('invalidcourseid');
-        }
+        // Get course object (reuse $COURSE if possible).
+        $course = get_course($cm->course, false);
     }
 
     return empty($course->groupmodeforce) ? $cm->groupmode : $course->groupmode;
index bbfb895..ecfa35d 100644 (file)
@@ -99,14 +99,14 @@ function js_send_unmodified($lastmodified, $etag) {
  * @return string
  */
 function js_minify($files) {
-    // setup include path
-    set_include_path(__DIR__ . '/minify/lib' . PATH_SEPARATOR . get_include_path());
-    require_once('Minify.php');
+    global $CFG;
 
     if (empty($files)) {
         return '';
     }
 
+    require_once("$CFG->libdir/minify/lib/Minify/Loader.php");
+    Minify_Loader::register();
     if (0 === stripos(PHP_OS, 'win')) {
         Minify::setDocRoot(); // IIS may need help
     }
old mode 100644 (file)
new mode 100755 (executable)
index 90e557f..bf8fc20
@@ -26,15 +26,19 @@ $min_uploaderHoursBehind = 0;
 $min_libPath = dirname(__FILE__) . '/lib';
 // do not change zlib compression or buffering here
 
-// TODO: locking setting, caching setting
-
 return; // end of moodle modification
 
 
 /**
  * Allow use of the Minify URI Builder app. Only set this to true while you need it.
- **/
-$min_enableBuilder = true;
+ */
+$min_enableBuilder = false;
+
+/**
+ * If non-empty, the Builder will be protected with HTTP Digest auth.
+ * The username is "admin".
+ */
+$min_builderPassword = 'admin';
 
 
 /**
@@ -124,15 +128,10 @@ $min_serveOptions['maxAge'] = 1800;
 
 
 /**
- * To use Google's Closure Compiler API (falling back to JSMin on failure),
- * uncomment the following lines:
+ * To use Google's Closure Compiler API to minify Javascript (falling back to JSMin
+ * on failure), uncomment the following line:
  */
-/*function closureCompiler($js) {
-    require_once 'Minify/JS/ClosureCompiler.php';
-    return Minify_JS_ClosureCompiler::minify($js);
-}
-$min_serveOptions['minifiers']['application/x-javascript'] = 'closureCompiler';
-//*/
+//$min_serveOptions['minifiers']['application/x-javascript'] = array('Minify_JS_ClosureCompiler', 'minify');
 
 
 /**
diff --git a/lib/minify/lib/CSSmin.php b/lib/minify/lib/CSSmin.php
new file mode 100644 (file)
index 0000000..a60b4ac
--- /dev/null
@@ -0,0 +1,758 @@
+<?php
+
+/*!
+ * cssmin.php rev ebaf67b 12/06/2013
+ * Author: Tubal Martin - http://tubalmartin.me/
+ * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
+ *
+ * This is a PHP port of the CSS minification tool distributed with YUICompressor, 
+ * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
+ * Permission is hereby granted to use the PHP version under the same
+ * conditions as the YUICompressor.
+ */
+
+/*!
+ * YUI Compressor
+ * http://developer.yahoo.com/yui/compressor/
+ * Author: Julien Lecomte - http://www.julienlecomte.net/
+ * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
+ * The copyrights embodied in the content of this file are licensed
+ * by Yahoo! Inc. under the BSD (revised) open source license.
+ */
+
+class CSSmin
+{
+    const NL = '___YUICSSMIN_PRESERVED_NL___';
+    const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
+    const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
+    const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
+    const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
+
+    private $comments;
+    private $preserved_tokens;
+    private $memory_limit;
+    private $max_execution_time;
+    private $pcre_backtrack_limit;
+    private $pcre_recursion_limit;
+    private $raise_php_limits;
+
+    /**
+     * @param bool|int $raise_php_limits
+     * If true, PHP settings will be raised if needed
+     */
+    public function __construct($raise_php_limits = TRUE)
+    {
+        // Set suggested PHP limits
+        $this->memory_limit = 128 * 1048576; // 128MB in bytes
+        $this->max_execution_time = 60; // 1 min
+        $this->pcre_backtrack_limit = 1000 * 1000;
+        $this->pcre_recursion_limit =  500 * 1000;
+
+        $this->raise_php_limits = (bool) $raise_php_limits;
+    }
+
+    /**
+     * Minify a string of CSS
+     * @param string $css
+     * @param int|bool $linebreak_pos
+     * @return string
+     */
+    public function run($css = '', $linebreak_pos = FALSE)
+    {
+        if (empty($css)) {
+            return '';
+        }
+
+        if ($this->raise_php_limits) {
+            $this->do_raise_php_limits();
+        }
+
+        $this->comments = array();
+        $this->preserved_tokens = array();
+
+        $start_index = 0;
+        $length = strlen($css);
+
+        $css = $this->extract_data_urls($css);
+
+        // collect all comment blocks...
+        while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
+            $end_index = $this->index_of($css, '*/', $start_index + 2);
+            if ($end_index < 0) {
+                $end_index = $length;
+            }
+            $comment_found = $this->str_slice($css, $start_index + 2, $end_index);
+            $this->comments[] = $comment_found;
+            $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
+            $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
+            // Set correct start_index: Fixes issue #2528130
+            $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
+        }
+
+        // preserve strings so their content doesn't get accidentally minified
+        $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
+
+        // Let's divide css code in chunks of 25.000 chars aprox.
+        // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
+        // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
+        // long strings and a (sub)pattern matches a number of chars greater than
+        // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
+        // returning NULL and $css would be empty.
+        $charset = '';
+        $charset_regexp = '/(@charset)( [^;]+;)/i';
+        $css_chunks = array();
+        $css_chunk_length = 25000; // aprox size, not exact
+        $start_index = 0;
+        $i = $css_chunk_length; // save initial iterations
+        $l = strlen($css);
+
+
+        // if the number of characters is 25000 or less, do not chunk
+        if ($l <= $css_chunk_length) {
+            $css_chunks[] = $css;
+        } else {
+            // chunk css code securely
+            while ($i < $l) {
+                $i += 50; // save iterations. 500 checks for a closing curly brace }
+                if ($l - $start_index <= $css_chunk_length || $i >= $l) {
+                    $css_chunks[] = $this->str_slice($css, $start_index);
+                    break;
+                }
+                if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
+                    // If there are two ending curly braces }} separated or not by spaces,
+                    // join them in the same chunk (i.e. @media blocks)
+                    $next_chunk = substr($css, $i);
+                    if (preg_match('/^\s*\}/', $next_chunk)) {
+                        $i = $i + $this->index_of($next_chunk, '}') + 1;
+                    }
+
+                    $css_chunks[] = $this->str_slice($css, $start_index, $i);
+                    $start_index = $i;
+                }
+            }
+        }
+
+        // Minify each chunk
+        for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
+            $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
+            // Keep the first @charset at-rule found
+            if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
+                $charset = strtolower($matches[1]) . $matches[2];
+            }
+            // Delete all @charset at-rules
+            $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
+        }
+
+        // Update the first chunk and push the charset to the top of the file.
+        $css_chunks[0] = $charset . $css_chunks[0];
+
+        return implode('', $css_chunks);
+    }
+
+    /**
+     * Sets the memory limit for this script
+     * @param int|string $limit
+     */
+    public function set_memory_limit($limit)
+    {
+        $this->memory_limit = $this->normalize_int($limit);
+    }
+
+    /**
+     * Sets the maximum execution time for this script
+     * @param int|string $seconds
+     */
+    public function set_max_execution_time($seconds)
+    {
+        $this->max_execution_time = (int) $seconds;
+    }
+
+    /**
+     * Sets the PCRE backtrack limit for this script
+     * @param int $limit
+     */
+    public function set_pcre_backtrack_limit($limit)
+    {
+        $this->pcre_backtrack_limit = (int) $limit;
+    }
+
+    /**
+     * Sets the PCRE recursion limit for this script
+     * @param int $limit
+     */
+    public function set_pcre_recursion_limit($limit)
+    {
+        $this->pcre_recursion_limit = (int) $limit;
+    }
+
+    /**
+     * Try to configure PHP to use at least the suggested minimum settings
+     */
+    private function do_raise_php_limits()
+    {
+        $php_limits = array(
+            'memory_limit' => $this->memory_limit,
+            'max_execution_time' => $this->max_execution_time,
+            'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
+            'pcre.recursion_limit' =>  $this->pcre_recursion_limit
+        );
+
+        // If current settings are higher respect them.
+        foreach ($php_limits as $name => $suggested) {
+            $current = $this->normalize_int(ini_get($name));
+            // memory_limit exception: allow -1 for "no memory limit".
+            if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
+                ini_set($name, $suggested);
+            }
+        }
+    }
+
+    /**
+     * Does bulk of the minification
+     * @param string $css
+     * @param int|bool $linebreak_pos
+     * @return string
+     */
+    private function minify($css, $linebreak_pos)
+    {
+        // strings are safe, now wrestle the comments
+        for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
+
+            $token = $this->comments[$i];
+            $placeholder = '/' . self::COMMENT . $i . '___/';
+
+            // ! in the first position of the comment means preserve
+            // so push to the preserved tokens keeping the !
+            if (substr($token, 0, 1) === '!') {
+                $this->preserved_tokens[] = $token;
+                $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
+                $css = preg_replace($placeholder, $token_tring, $css, 1);
+                // Preserve new lines for /*! important comments
+                $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
+                $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
+                continue;
+            }
+
+            // \ in the last position looks like hack for Mac/IE5
+            // shorten that to /*\*/ and the next one to /**/
+            if (substr($token, (strlen($token) - 1), 1) === '\\') {
+                $this->preserved_tokens[] = '\\';
+                $css = preg_replace($placeholder,  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
+                $i = $i + 1; // attn: advancing the loop
+                $this->preserved_tokens[] = '';
+                $css = preg_replace('/' . self::COMMENT . $i . '___/',  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
+                continue;
+            }
+
+            // keep empty comments after child selectors (IE7 hack)
+            // e.g. html >/**/ body
+            if (strlen($token) === 0) {
+                $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
+                if ($start_index > 2) {
+                    if (substr($css, $start_index - 3, 1) === '>') {
+                        $this->preserved_tokens[] = '';
+                        $css = preg_replace($placeholder,  self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
+                    }
+                }
+            }
+
+            // in all other cases kill the comment
+            $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
+        }
+
+
+        // Normalize all whitespace strings to single spaces. Easier to work with that way.
+        $css = preg_replace('/\s+/', ' ', $css);
+
+        // Shorten & preserve calculations calc(...) since spaces are important
+        $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
+
+        // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
+        // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
+        $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
+
+        // Remove leading zeros from integer and float numbers preceded by : or a white-space
+        // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
+        $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
+
+        // Remove trailing zeros from float numbers preceded by : or a white-space
+        // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
+        $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
+
+        // Remove trailing .0 -> -9.0 to -9
+        $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
+
+        // Replace 0 length numbers with 0
+        $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
+
+        // Remove the spaces before the things that should not have spaces before them.
+        // But, be careful not to turn "p :link {...}" into "p:link{...}"
+        // Swap out any pseudo-class colons with the token, and then swap back.
+        $css = preg_replace_callback('/(?:^|\})(?:(?:[^\{\:])+\:)+(?:[^\{]*\{)/', array($this, 'replace_colon'), $css);
+        
+        // Remove spaces before the things that should not have spaces before them.
+        $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
+
+        // Restore spaces for !important
+        $css = preg_replace('/\!important/i', ' !important', $css);
+
+        // bring back the colon
+        $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
+
+        // retain space for special IE6 cases
+        $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
+
+        // no space after the end of a preserved comment
+        $css = preg_replace('/\*\/ /', '*/', $css);
+
+        // lowercase some popular @directives
+        $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
+
+        // lowercase some more common pseudo-elements
+        $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
+
+        // lowercase some more common functions
+        $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
+
+        // lower case some common function that can be values
+        // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
+        $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
+        
+        // Put the space back in some cases, to support stuff like
+        // @media screen and (-webkit-min-device-pixel-ratio:0){
+        $css = preg_replace('/\band\(/i', 'and (', $css);
+
+        // Remove the spaces after the things that should not have spaces after them.
+        $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
+
+        // remove unnecessary semicolons
+        $css = preg_replace('/;+\}/', '}', $css);
+
+        // Fix for issue: #2528146
+        // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
+        // to avoid issues on Symbian S60 3.x browsers.
+        $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
+
+        // Replace 0 length units 0(px,em,%) with 0.
+        $css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css);
+
+        // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
+        $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
+
+        // Fix for issue: #2528142
+        // Replace text-shadow:0; with text-shadow:0 0 0;
+        $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
+
+        // Replace background-position:0; with background-position:0 0;
+        // same for transform-origin
+        // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
+        $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
+
+        // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
+        // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
+        // This makes it more likely that it'll get further compressed in the next step.
+        $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
+        $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
+
+        // Shorten colors from #AABBCC to #ABC or short color name.
+        $css = $this->compress_hex_colors($css);
+
+        // border: none to border:0, outline: none to outline:0
+        $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
+
+        // shorter opacity IE filter
+        $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
+
+        // Find a fraction that is used for Opera's -o-device-pixel-ratio query
+        // Add token to add the "\" back in later
+        $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
+
+        // Remove empty rules.
+        $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
+
+        // Add "/" back to fix Opera -o-device-pixel-ratio query
+        $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
+
+        // Some source control tools don't like it when files containing lines longer
+        // than, say 8000 characters, are checked in. The linebreak option is used in
+        // that case to split long lines after a specific column.
+        if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
+            $linebreak_pos = (int) $linebreak_pos;
+            $start_index = $i = 0;
+            while ($i < strlen($css)) {
+                $i++;
+                if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
+                    $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
+                    $start_index = $i;
+                }
+            }
+        }
+
+        // Replace multiple semi-colons in a row by a single one
+        // See SF bug #1980989
+        $css = preg_replace('/;;+/', ';', $css);
+
+        // Restore new lines for /*! important comments
+        $css = preg_replace('/'. self::NL .'/', "\n", $css);
+
+        // Lowercase all uppercase properties
+        $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
+
+        // restore preserved comments and strings
+        for ($i = 0, $max = count($this->preserved_tokens); $i < $max; $i++) {
+            $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
+        }
+
+        // Trim the final string (for any leading or trailing white spaces)
+        return trim($css);
+    }
+
+    /**
+     * Utility method to replace all data urls with tokens before we start
+     * compressing, to avoid performance issues running some of the subsequent
+     * regexes against large strings chunks.
+     *
+     * @param string $css
+     * @return string
+     */
+    private function extract_data_urls($css)
+    {
+        // Leave data urls alone to increase parse performance.
+        $max_index = strlen($css) - 1;
+        $append_index = $index = $last_index = $offset = 0;
+        $sb = array();
+        $pattern = '/url\(\s*(["\']?)data\:/i';
+
+        // Since we need to account for non-base64 data urls, we need to handle
+        // ' and ) being part of the data string. Hence switching to indexOf,
+        // to determine whether or not we have matching string terminators and
+        // handling sb appends directly, instead of using matcher.append* methods.
+
+        while (preg_match($pattern, $css, $m, 0, $offset)) {
+            $index = $this->index_of($css, $m[0], $offset);
+            $last_index = $index + strlen($m[0]);
+            $start_index = $index + 4; // "url(".length()
+            $end_index = $last_index - 1;
+            $terminator = $m[1]; // ', " or empty (not quoted)
+            $found_terminator = FALSE;
+
+            if (strlen($terminator) === 0) {
+                $terminator = ')';
+            }
+
+            while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
+                $end_index = $this->index_of($css, $terminator, $end_index + 1);
+
+                // endIndex == 0 doesn't really apply here
+                if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
+                    $found_terminator = TRUE;
+                    if (')' != $terminator) {
+                        $end_index = $this->index_of($css, ')', $end_index);
+                    }
+                }
+            }
+
+            // Enough searching, start moving stuff over to the buffer
+            $sb[] = $this->str_slice($css, $append_index, $index);
+
+            if ($found_terminator) {
+                $token = $this->str_slice($css, $start_index, $end_index);
+                $token = preg_replace('/\s+/', '', $token);
+                $this->preserved_tokens[] = $token;
+
+                $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
+                $sb[] = $preserver;
+
+                $append_index = $end_index + 1;
+            } else {
+                // No end terminator found, re-add the whole match. Should we throw/warn here?
+                $sb[] = $this->str_slice($css, $index, $last_index);
+                $append_index = $last_index;
+            }
+
+            $offset = $last_index;
+        }
+
+        $sb[] = $this->str_slice($css, $append_index);
+
+        return implode('', $sb);
+    }
+
+    /**
+     * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
+     *
+     * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
+     * e.g. #AddressForm { ... }
+     *
+     * DOES NOT compress IE filters, which have hex color values (which would break things).
+     * e.g. filter: chroma(color="#FFFFFF");
+     *
+     * DOES NOT compress invalid hex values.
+     * e.g. background-color: #aabbccdd
+     *
+     * @param string $css
+     * @return string
+     */
+    private function compress_hex_colors($css)
+    {
+        // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
+        $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
+        $_index = $index = $last_index = $offset = 0;
+        $sb = array();
+        // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
+        $short_safe = array(
+            '#808080' => 'gray',
+            '#008000' => 'green',
+            '#800000' => 'maroon',
+            '#000080' => 'navy',
+            '#808000' => 'olive',
+            '#ffa500' => 'orange',
+            '#800080' => 'purple',
+            '#c0c0c0' => 'silver',
+            '#008080' => 'teal',
+            '#f00' => 'red'
+        );
+
+        while (preg_match($pattern, $css, $m, 0, $offset)) {
+            $index = $this->index_of($css, $m[0], $offset);
+            $last_index = $index + strlen($m[0]);
+            $is_filter = $m[1] !== null && $m[1] !== '';
+
+            $sb[] = $this->str_slice($css, $_index, $index);
+
+            if ($is_filter) {
+                // Restore, maintain case, otherwise filter will break
+                $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
+            } else {
+                if (strtolower($m[2]) == strtolower($m[3]) &&
+                    strtolower($m[4]) == strtolower($m[5]) &&
+                    strtolower($m[6]) == strtolower($m[7])) {
+                    // Compress.
+                    $hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
+                } else {
+                    // Non compressible color, restore but lower case.
+                    $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
+                }
+                // replace Hex colors to short safe color names
+                $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
+            }
+
+            $_index = $offset = $last_index - strlen($m[8]);
+        }
+
+        $sb[] = $this->str_slice($css, $_index);
+
+        return implode('', $sb);
+    }
+
+    /* CALLBACKS
+     * ---------------------------------------------------------------------------------------------
+     */
+
+    private function replace_string($matches)
+    {
+        $match = $matches[0];
+        $quote = substr($match, 0, 1);
+        // Must use addcslashes in PHP to avoid parsing of backslashes
+        $match = addcslashes($this->str_slice($match, 1, -1), '\\');
+
+        // maybe the string contains a comment-like substring?
+        // one, maybe more? put'em back then
+        if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
+            for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
+                $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
+            }
+        }
+
+        // minify alpha opacity in filter strings
+        $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
+
+        $this->preserved_tokens[] = $match;
+        return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
+    }
+
+    private function replace_colon($matches)
+    {
+        return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
+    }
+
+    private function replace_calc($matches)
+    {
+        $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
+        return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
+    }
+
+    private function rgb_to_hex($matches)
+    {
+        // Support for percentage values rgb(100%, 0%, 45%);
+        if ($this->index_of($matches[1], '%') >= 0){
+            $rgbcolors = explode(',', str_replace('%', '', $matches[1]));
+            for ($i = 0; $i < count($rgbcolors); $i++) {
+                $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
+            }
+        } else {
+            $rgbcolors = explode(',', $matches[1]);
+        }
+
+        // Values outside the sRGB color space should be clipped (0-255)
+        for ($i = 0; $i < count($rgbcolors); $i++) {
+            $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
+            $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
+        }
+
+        // Fix for issue #2528093
+        if (!preg_match('/[\s\,\);\}]/', $matches[2])){
+            $matches[2] = ' ' . $matches[2];
+        }
+
+        return '#' . implode('', $rgbcolors) . $matches[2];
+    }
+
+    private function hsl_to_hex($matches)
+    {
+        $values = explode(',', str_replace('%', '', $matches[1]));
+        $h = floatval($values[0]);
+        $s = floatval($values[1]);
+        $l = floatval($values[2]);
+
+        // Wrap and clamp, then fraction!
+        $h = ((($h % 360) + 360) % 360) / 360;
+        $s = $this->clamp_number($s, 0, 100) / 100;
+        $l = $this->clamp_number($l, 0, 100) / 100;
+
+        if ($s == 0) {
+            $r = $g = $b = $this->round_number(255 * $l);
+        } else {
+            $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
+            $v1 = (2 * $l) - $v2;
+            $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
+            $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
+            $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
+        }
+
+        return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
+    }
+
+    private function lowercase_pseudo_first($matches)
+    {
+        return ':first-'. strtolower($matches[1]) .' '. $matches[2];
+    }
+
+    private function lowercase_directives($matches) 
+    {
+        return '@'. strtolower($matches[1]);
+    }
+
+    private function lowercase_pseudo_elements($matches) 
+    {
+        return ':'. strtolower($matches[1]);
+    }
+
+    private function lowercase_common_functions($matches) 
+    {
+        return ':'. strtolower($matches[1]) .'(';
+    }
+
+    private function lowercase_common_functions_values($matches) 
+    {
+        return $matches[1] . strtolower($matches[2]);
+    }
+
+    private function lowercase_properties($matches)
+    {
+        return $matches[1].strtolower($matches[2]).$matches[3];
+    }
+
+    /* HELPERS
+     * ---------------------------------------------------------------------------------------------
+     */
+
+    private function hue_to_rgb($v1, $v2, $vh)
+    {
+        $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
+        if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
+        if ($vh * 2 < 1) return $v2;
+        if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
+        return $v1;
+    }
+
+    private function round_number($n)
+    {
+        return intval(floor(floatval($n) + 0.5), 10);
+    }
+
+    private function clamp_number($n, $min, $max)
+    {
+        return min(max($n, $min), $max);
+    }
+
+    /**
+     * PHP port of Javascript's "indexOf" function for strings only
+     * Author: Tubal Martin http://blog.margenn.com
+     *
+     * @param string $haystack
+     * @param string $needle
+     * @param int    $offset index (optional)
+     * @return int
+     */
+    private function index_of($haystack, $needle, $offset = 0)
+    {
+        $index = strpos($haystack, $needle, $offset);
+
+        return ($index !== FALSE) ? $index : -1;
+    }
+
+    /**
+     * PHP port of Javascript's "slice" function for strings only
+     * Author: Tubal Martin http://blog.margenn.com
+     * Tests: http://margenn.com/tubal/str_slice/
+     *
+     * @param string   $str
+     * @param int      $start index
+     * @param int|bool $end index (optional)
+     * @return string
+     */
+    private function str_slice($str, $start = 0, $end = FALSE)
+    {
+        if ($end !== FALSE && ($start < 0 || $end <= 0)) {
+            $max = strlen($str);
+
+            if ($start < 0) {
+                if (($start = $max + $start) < 0) {
+                    return '';
+                }
+            }
+
+            if ($end < 0) {
+                if (($end = $max + $end) < 0) {
+                    return '';
+                }
+            }
+
+            if ($end <= $start) {
+                return '';
+            }
+        }
+
+        $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
+        return ($slice === FALSE) ? '' : $slice;
+    }
+
+    /**
+     * Convert strings like "64M" or "30" to int values
+     * @param mixed $size
+     * @return int
+     */
+    private function normalize_int($size)
+    {
+        if (is_string($size)) {
+            switch (substr($size, -1)) {
+                case 'M': case 'm': return $size * 1048576;
+                case 'K': case 'k': return $size * 1024;
+                case 'G': case 'g': return $size * 1073741824;
+            }
+        }
+
+        return (int) $size;
+    }
+}
\ No newline at end of file
diff --git a/lib/minify/lib/DooDigestAuth.php b/lib/minify/lib/DooDigestAuth.php
new file mode 100644 (file)
index 0000000..69bc4ed
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * DooDigestAuth class file.
+ *
+ * @author Leng Sheng Hong <darkredz@gmail.com>
+ * @link http://www.doophp.com/
+ * @copyright Copyright &copy; 2009 Leng Sheng Hong
+ * @license http://www.doophp.com/license
+ */
+
+/**
+ * Handles HTTP digest authentication
+ *
+ * <p>HTTP digest authentication can be used with the URI router.
+ * HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
+ * If you are running PHP on Apache in CGI/FastCGI mode, you would need to
+ * add the following line to your .htaccess for digest auth to work correctly.</p>
+ * <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
+ *
+ * <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
+ *
+ * @author Leng Sheng Hong <darkredz@gmail.com>
+ * @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
+ * @package doo.auth
+ * @since 1.0
+ */
+class DooDigestAuth{
+
+    /**
+     * Authenticate against a list of username and passwords.
+     *
+     * <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
+     * you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
+     *
+     * @param string $realm Name of the authentication session
+     * @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
+     * @param string $fail_msg Message to be displayed if the User cancel the login
+     * @param string $fail_url URL to be redirect if the User cancel the login
+     * @return string The username if login success.
+     */
+    public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
+        $realm = "Restricted area - $realm";
+
+        //user => password
+        //$users = array('admin' => '1234', 'guest' => 'guest');
+        if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
+            $_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+        }
+
+        if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
+            header('WWW-Authenticate: Digest realm="'.$realm.
+                   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+            header('HTTP/1.1 401 Unauthorized');
+            if($fail_msg!=NULL)
+                die($fail_msg);
+            if($fail_url!=NULL)
+                die("<script>window.location.href = '$fail_url'</script>");
+            exit;
+        }
+
+        // analyze the PHP_AUTH_DIGEST variable
+        if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
+            header('WWW-Authenticate: Digest realm="'.$realm.
+                   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+            header('HTTP/1.1 401 Unauthorized');
+            if($fail_msg!=NULL)
+                die($fail_msg);
+            if($fail_url!=NULL)
+                die("<script>window.location.href = '$fail_url'</script>");
+            exit;
+        }
+
+        // generate the valid response
+        $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
+        $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
+        $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
+
+        if ($data['response'] != $valid_response){
+            header('HTTP/1.1 401 Unauthorized');
+            header('WWW-Authenticate: Digest realm="'.$realm.
+                   '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+            if($fail_msg!=NULL)
+                die($fail_msg);
+            if($fail_url!=NULL)
+                die("<script>window.location.href = '$fail_url'</script>");
+            exit;
+        }
+
+        // ok, valid username & password
+        return $data['username'];
+    }
+
+    /**
+     * Method to parse the http auth header, works with IE.
+     *
+     * Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
+     *
+     * @param string $txt header string to parse
+     * @return array An assoc array of the digest auth session
+     */
+    private static function http_digest_parse($txt)
+    {
+        $res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
+        $data['username'] = (isset($match[1]))?$match[1]:null;
+        $res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
+        $data['nonce'] = $match[1];
+        $res = preg_match('/nc=([0-9]+)/i', $txt, $match);
+        $data['nc'] = $match[1];
+        $res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
+        $data['cnonce'] = $match[1];
+        $res = preg_match('/qop=([^,]+)/i', $txt, $match);
+        $data['qop'] = str_replace('"','',$match[1]);
+        $res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
+        $data['uri'] = $match[1];
+        $res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
+        $data['response'] = $match[1];
+        return $data;
+    }
+
+
+}
index 9634f22..cf05af9 100644 (file)
@@ -3,11 +3,6 @@
  * Class Minify  
  * @package Minify
  */
-
-/**
- * Minify_Source
- */
-require_once 'Minify/Source.php';
  
 /**
  * Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
@@ -29,7 +24,7 @@ require_once 'Minify/Source.php';
  */
 class Minify {
     
-    const VERSION = '2.1.5';
+    const VERSION = '2.1.7';
     const TYPE_CSS = 'text/css';
     const TYPE_HTML = 'text/html';
     // there is some debate over the ideal JS Content-Type, but this is the
@@ -85,7 +80,6 @@ class Minify {
     public static function setCache($cache = '', $fileLocking = true)
     {
         if (is_string($cache)) {
-            require_once 'Minify/Cache/File.php';
             self::$_cache = new Minify_Cache_File($cache, $fileLocking);
         } else {
             self::$_cache = $cache;
@@ -161,9 +155,11 @@ class Minify {
      * 
      * @param array $options controller/serve options
      * 
-     * @return mixed null, or, if the 'quiet' option is set to true, an array
+     * @return null|array if the 'quiet' option is set to true, an array
      * with keys "success" (bool), "statusCode" (int), "content" (string), and
      * "headers" (array).
+     *
+     * @throws Exception
      */
     public static function serve($controller, $options = array())
     {
@@ -174,10 +170,6 @@ class Minify {
         if (is_string($controller)) {
             // make $controller into object
             $class = 'Minify_Controller_' . $controller;
-            if (! class_exists($class, false)) {
-                require_once "Minify/Controller/" 
-                    . str_replace('_', '/', $controller) . ".php";    
-            }
             $controller = new $class();
             /* @var Minify_Controller_Base $controller */
         }
@@ -219,8 +211,7 @@ class Minify {
                 $contentEncoding = self::$_options['encodeMethod'];
             } else {
                 // sniff request header
-                require_once 'HTTP/Encoder.php';
-                // depending on what the client accepts, $contentEncoding may be 
+                // depending on what the client accepts, $contentEncoding may be
                 // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
                 // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
                 list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
@@ -231,7 +222,6 @@ class Minify {
         }
         
         // check client cache
-        require_once 'HTTP/ConditionalGet.php';
         $cgOptions = array(
             'lastModifiedTime' => self::$_options['lastModifiedTime']
             ,'isPublic' => self::$_options['isPublic']
@@ -300,7 +290,7 @@ class Minify {
                     throw $e;
                 }
                 self::$_cache->store($cacheId, $content);
-                if (function_exists('gzencode')) {
+                if (function_exists('gzencode') && self::$_options['encodeMethod']) {
                     self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
                 }
             }
@@ -451,7 +441,7 @@ class Minify {
     /**
      * Set up sources to use Minify_Lines
      *
-     * @param array $sources Minify_Source instances
+     * @param Minify_Source[] $sources Minify_Source instances
      */
     protected static function _setupDebug($sources)
     {
@@ -468,6 +458,8 @@ class Minify {
      * Combines sources and minifies the result.
      *
      * @return string
+     *
+     * @throws Exception
      */
     protected static function _combineMinify()
     {
@@ -526,7 +518,6 @@ class Minify {
                 $imploded = implode($implodeSeparator, $groupToProcessTogether);
                 $groupToProcessTogether = array();
                 if ($lastMinifier) {
-                    self::$_controller->loadMinifier($lastMinifier);
                     try {
                         $content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
                     } catch (Exception $e) {
index e625165..1185fbe 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Source.php';
-
 /**
  * Maintain a single last modification time for a group of Minify sources to
  * allow use of far off Expires headers in Minify.
index 49882e9..3241455 100644 (file)
@@ -56,6 +56,7 @@ class Minify_CSS {
     public static function minify($css, $options = array()) \r
     {\r
         $options = array_merge(array(\r
+            'compress' => true,\r
             'removeCharsets' => true,\r
             'preserveComments' => true,\r
             'currentDir' => null,\r
@@ -67,21 +68,20 @@ class Minify_CSS {
         if ($options['removeCharsets']) {\r
             $css = preg_replace('/@charset[^;]+;\\s*/', '', $css);\r
         }\r
-        require_once 'Minify/CSS/Compressor.php';\r
-        if (! $options['preserveComments']) {\r
-            $css = Minify_CSS_Compressor::process($css, $options);\r
-        } else {\r
-            require_once 'Minify/CommentPreserver.php';\r
-            $css = Minify_CommentPreserver::process(\r
-                $css\r
-                ,array('Minify_CSS_Compressor', 'process')\r
-                ,array($options)\r
-            );\r
+        if ($options['compress']) {\r
+            if (! $options['preserveComments']) {\r
+                $css = Minify_CSS_Compressor::process($css, $options);\r
+            } else {\r
+                $css = Minify_CommentPreserver::process(\r
+                    $css\r
+                    ,array('Minify_CSS_Compressor', 'process')\r
+                    ,array($options)\r
+                );\r
+            }\r
         }\r
         if (! $options['currentDir'] && ! $options['prependRelativePath']) {\r
             return $css;\r
         }\r
-        require_once 'Minify/CSS/UriRewriter.php';\r
         if ($options['currentDir']) {\r
             return Minify_CSS_UriRewriter::rewrite(\r
                 $css\r
index 3fa6b18..27b424a 100644 (file)
@@ -186,7 +186,6 @@ class Minify_Cache_File {
      */
     protected function _log($msg)
     {
-        require_once 'Minify/Logger.php';
         Minify_Logger::log($msg);
     }
     
diff --git a/lib/minify/lib/Minify/Cache/XCache.php b/lib/minify/lib/Minify/Cache/XCache.php
new file mode 100644 (file)
index 0000000..3039ded
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Class Minify_Cache_XCache
+ *
+ * @link http://xcache.lighttpd.net/
+ * @package Minify
+ */
+
+/**
+ * XCache-based cache class for Minify
+ * {@see http://xcache.lighttpd.net/wiki/XcacheApi XCache API}
+ *
+ * <code>
+ * Minify::setCache(new Minify_Cache_XCache());
+ * </code>
+ *
+ * @package Minify
+ * @author Elan Ruusamäe <glen@delfi.ee>
+ **/
+class Minify_Cache_XCache {
+
+    /**
+     * Create a Minify_Cache_XCache object, to be passed to
+     * Minify::setCache().
+     *
+     * @param int $expire seconds until expiration (default = 0
+     * meaning the item will not get an expiration date)
+     */
+    public function __construct($expire = 0)
+    {
+        $this->_exp = $expire;
+    }
+
+    /**
+     * Write data to cache.
+     *
+     * @param string $id cache id
+     * @param string $data
+     * @return bool success
+     */
+    public function store($id, $data)
+    {
+        return xcache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
+    }
+
+    /**
+     * Get the size of a cache entry
+     *
+     * @param string $id cache id
+     * @return int size in bytes
+     */
+    public function getSize($id)
+    {
+        if (! $this->_fetch($id)) {
+            return false;
+        }
+        return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
+            ? mb_strlen($this->_data, '8bit')
+            : strlen($this->_data);
+    }
+
+    /**
+     * Does a valid cache entry exist?
+     *
+     * @param string $id cache id
+     * @param int $srcMtime mtime of the original source file(s)
+     * @return bool exists
+     */
+    public function isValid($id, $srcMtime)
+    {
+        return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
+    }
+
+    /**
+     * Send the cached content to output
+     *
+     * @param string $id cache id
+     */
+    public function display($id)
+    {
+        echo $this->_fetch($id)
+            ? $this->_data
+            : '';
+    }
+
+    /**
+     * Fetch the cached content
+     *
+     * @param string $id cache id
+     * @return string
+     */
+    public function fetch($id)
+    {
+        return $this->_fetch($id)
+            ? $this->_data
+            : '';
+    }
+
+    private $_exp = null;
+
+    // cache of most recently fetched id
+    private $_lm = null;
+    private $_data = null;
+    private $_id = null;
+
+    /**
+     * Fetch data and timestamp from xcache, store in instance
+     *
+     * @param string $id
+     * @return bool success
+     */
+    private function _fetch($id)
+    {
+        if ($this->_id === $id) {
+            return true;
+        }
+        $ret = xcache_get($id);
+        if (false === $ret) {
+            $this->_id = null;
+            return false;
+        }
+        list($this->_lm, $this->_data) = explode('|', $ret, 2);
+        $this->_id = $id;
+        return true;
+    }
+}
diff --git a/lib/minify/lib/Minify/ClosureCompiler.php b/lib/minify/lib/Minify/ClosureCompiler.php
new file mode 100644 (file)
index 0000000..7856097
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+/**
+ * Class Minify_ClosureCompiler
+ * @package Minify
+ */
+
+/**
+ * Compress Javascript using the Closure Compiler
+ *
+ * You must set $jarFile and $tempDir before calling the minify functions.
+ * Also, depending on your shell's environment, you may need to specify
+ * the full path to java in $javaExecutable or use putenv() to setup the
+ * Java environment.
+ *
+ * <code>
+ * Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar';
+ * Minify_ClosureCompiler::$tempDir = '/tmp';
+ * $code = Minify_ClosureCompiler::minify(
+ *   $code,
+ *   array('compilation_level' => 'SIMPLE_OPTIMIZATIONS')
+ * );
+ *
+ * --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
+ *
+ * </code>
+ *
+ * @todo unit tests, $options docs
+ * @todo more options support (or should just passthru them all?)
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ * @author Elan Ruusamäe <glen@delfi.ee>
+ */
+class Minify_ClosureCompiler {
+
+    /**
+     * Filepath of the Closure Compiler jar file. This must be set before
+     * calling minifyJs().
+     *
+     * @var string
+     */
+    public static $jarFile = null;
+
+    /**
+     * Writable temp directory. This must be set before calling minifyJs().
+     *
+     * @var string
+     */
+    public static $tempDir = null;
+
+    /**
+     * Filepath of "java" executable (may be needed if not in shell's PATH)
+     *
+     * @var string
+     */
+    public static $javaExecutable = 'java';
+
+    /**
+     * Minify a Javascript string
+     *
+     * @param string $js
+     *
+     * @param array $options (verbose is ignored)
+     *
+     * @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
+     *
+     * @return string
+     */
+    public static function minify($js, $options = array())
+    {
+        self::_prepare();
+        if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
+            throw new Exception('Minify_ClosureCompiler : could not create temp file.');
+        }
+        file_put_contents($tmpFile, $js);
+        exec(self::_getCmd($options, $tmpFile), $output, $result_code);
+        unlink($tmpFile);
+        if ($result_code != 0) {
+            throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
+        }
+        return implode("\n", $output);
+    }
+
+    private static function _getCmd($userOptions, $tmpFile)
+    {
+        $o = array_merge(
+            array(
+                'charset' => 'utf-8',
+                'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
+            ),
+            $userOptions
+        );
+        $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
+             . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
+                ? " --charset {$o['charset']}"
+                : '');
+
+        foreach (array('compilation_level') as $opt) {
+            if ($o[$opt]) {
+                $cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
+            }
+        }
+        return $cmd . ' ' . escapeshellarg($tmpFile);
+    }
+
+    private static function _prepare()
+    {
+        if (! is_file(self::$jarFile)) {
+            throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
+        }
+        if (! is_readable(self::$jarFile)) {
+            throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
+        }
+        if (! is_dir(self::$tempDir)) {
+            throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
+        }
+        if (! is_writable(self::$tempDir)) {
+            throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
+        }
+    }
+}
+
+/* vim:ts=4:sw=4:et */
index 240b544..5a86329 100644 (file)
@@ -78,33 +78,6 @@ abstract class Minify_Controller_Base {
         return $ret;
     }
     
-    /**
-     * Load any code necessary to execute the given minifier callback.
-     * 
-     * The controller is responsible for loading minification code on demand
-     * via this method. This built-in function will only load classes for
-     * static method callbacks where the class isn't already defined. It uses
-     * the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this 
-     * function will include 'Jimmy/Minifier.php'.
-     * 
-     * If you need code loaded on demand and this doesn't suit you, you'll need
-     * to override this function in your subclass. 
-     * @see Minify_Controller_Page::loadMinifier()
-     * 
-     * @param callback $minifierCallback callback of minifier function
-     * 
-     * @return null
-     */
-    public function loadMinifier($minifierCallback)
-    {
-        if (is_array($minifierCallback)
-            && is_string($minifierCallback[0])
-            && !class_exists($minifierCallback[0], false)) {
-            
-            require str_replace('_', '/', $minifierCallback[0]) . '.php';
-        }
-    }
-    
     /**
      * Is a user-given file within an allowable directory, existing,
      * and having an extension js/css/html/txt ?
@@ -244,7 +217,6 @@ abstract class Minify_Controller_Base {
      * @return null
      */
     public function log($msg) {
-        require_once 'Minify/Logger.php';
         Minify_Logger::log($msg);
     }
 }
index 83f028a..f084cd0 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Controller/Base.php';
-
 /**
  * Controller class for minifying a set of files
  * 
index 2d4e43b..c4c25db 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Controller/Base.php';
-
 /**
  * Controller class for serving predetermined groups of minimized sets, selected
  * by PATH_INFO
index d47c60d..6943ee6 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Controller/Base.php';
-
 /**
  * Controller class for requests to /min/index.php
  * 
@@ -22,6 +20,13 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
      * @return array Minify options
      */
     public function setupSources($options) {
+        // PHP insecure by default: realpath() and other FS functions can't handle null bytes.
+        foreach (array('g', 'b', 'f') as $key) {
+            if (isset($_GET[$key])) {
+                $_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]);
+            }
+        }
+
         // filter controller options
         $cOptions = array_merge(
             array(
@@ -36,7 +41,6 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
         $sources = array();
         $this->selectionId = '';
         $firstMissingResource = null;
-        
         if (isset($_GET['g'])) {
             // add group(s)
             $this->selectionId .= 'g=' . $_GET['g'];
@@ -195,9 +199,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
     protected function _getFileSource($file, $cOptions)
     {
         $spec['filepath'] = $file;
-        if ($cOptions['noMinPattern']
-            && preg_match($cOptions['noMinPattern'], basename($file))) {
-            $spec['minifier'] = '';
+        if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) {
+            if (preg_match('~\.css$~i', $file)) {
+                $spec['minifyOptions']['compress'] = false;
+            } else {
+                $spec['minifier'] = '';
+            }
         }
         return new Minify_Source($spec);
     }
index de471e1..1095fb4 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Controller/Base.php';
-
 /**
  * Controller class for serving a single HTML page
  * 
@@ -59,7 +57,6 @@ class Minify_Controller_Page extends Minify_Controller_Base {
                 'cssMinifier' => array('Minify_CSS', 'minify')
                 ,'jsMinifier' => array('JSMin', 'minify')
             );
-            $this->_loadCssJsMinifiers = true;
             unset($options['minifyAll']);
         }
         $this->sources[] = new Minify_Source($sourceSpec);
@@ -67,21 +64,5 @@ class Minify_Controller_Page extends Minify_Controller_Base {
         $options['contentType'] = Minify::TYPE_HTML;
         return $options;
     }
-    
-    protected $_loadCssJsMinifiers = false;
-    
-    /**
-     * @see Minify_Controller_Base::loadMinifier()
-     */
-    public function loadMinifier($minifierCallback)
-    {
-        if ($this->_loadCssJsMinifiers) {
-            // Minify will not call for these so we must manually load
-            // them when Minify/HTML.php is called for.
-            require_once 'Minify/CSS.php';
-            require_once 'JSMin.php';
-        }
-        parent::loadMinifier($minifierCallback); // load Minify/HTML.php
-    }
 }
 
index 5279d36..91fcf61 100644 (file)
@@ -4,8 +4,6 @@
  * @package Minify
  */
 
-require_once 'Minify/Controller/Base.php';
-
 /**
  * Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
  * 
@@ -26,6 +24,11 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
      * 
      */
     public function setupSources($options) {
+        // PHP insecure by default: realpath() and other FS functions can't handle null bytes.
+        if (isset($_GET['files'])) {
+            $_GET['files'] = str_replace("\x00", '', (string)$_GET['files']);
+        }
+
         self::_setupDefines();
         if (MINIFY_USE_CACHE) {
             $cacheDir = defined('MINIFY_CACHE_DIR')
@@ -51,8 +54,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
         ) {
             return $options;
         }
-        $extension = $m[1];
-        
+
         $files = explode(',', $_GET['files']);
         if (count($files) > MINIFY_MAX_FILES) {
             return $options;
@@ -63,7 +65,6 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
             . DIRECTORY_SEPARATOR;
         $prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
         
-        $sources = array();
         $goodFiles = array();
         $hasBadSource = false;
         
index e9453ff..da356dd 100644 (file)
@@ -1,22 +1,26 @@
 <?php\r
 /**\r
- * Class Minify_HTML  \r
+ * Class Minify_HTML\r
  * @package Minify\r
  */\r
 \r
 /**\r
  * Compress HTML\r
  *\r
- * This is a heavy regex-based removal of whitespace, unnecessary comments and \r
+ * This is a heavy regex-based removal of whitespace, unnecessary comments and\r
  * tokens. IE conditional comments are preserved. There are also options to have\r
- * STYLE and SCRIPT blocks compressed by callback functions. \r
- * \r
+ * STYLE and SCRIPT blocks compressed by callback functions.\r
+ *\r
  * A test suite is available.\r
- * \r
+ *\r
  * @package Minify\r
  * @author Stephen Clay <steve@mrclay.org>\r
  */\r
 class Minify_HTML {\r
+    /**\r
+     * @var boolean\r
+     */\r
+    protected $_jsCleanComments = true;\r
 \r
     /**\r
      * "Minify" an HTML page\r
@@ -27,21 +31,21 @@ class Minify_HTML {
      *\r
      * 'cssMinifier' : (optional) callback function to process content of STYLE\r
      * elements.\r
-     * \r
+     *\r
      * 'jsMinifier' : (optional) callback function to process content of SCRIPT\r
      * elements. Note: the type attribute is ignored.\r
-     * \r
+     *\r
      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If\r
      * unset, minify will sniff for an XHTML doctype.\r
-     * \r
+     *\r
      * @return string\r
      */\r
     public static function minify($html, $options = array()) {\r
-        $min = new Minify_HTML($html, $options);\r
+        $min = new self($html, $options);\r
         return $min->process();\r
     }\r
-    \r
-    \r
+\r
+\r
     /**\r
      * Create a minifier object\r
      *\r
@@ -51,13 +55,15 @@ class Minify_HTML {
      *\r
      * 'cssMinifier' : (optional) callback function to process content of STYLE\r
      * elements.\r
-     * \r
+     *\r
      * 'jsMinifier' : (optional) callback function to process content of SCRIPT\r
      * elements. Note: the type attribute is ignored.\r
-     * \r
+     *\r
+     * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block\r
+     *\r
      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If\r
      * unset, minify will sniff for an XHTML doctype.\r
-     * \r
+     *\r
      * @return null\r
      */\r
     public function __construct($html, $options = array())\r
@@ -72,9 +78,12 @@ class Minify_HTML {
         if (isset($options['jsMinifier'])) {\r
             $this->_jsMinifier = $options['jsMinifier'];\r
         }\r
+        if (isset($options['jsCleanComments'])) {\r
+            $this->_jsCleanComments = (bool)$options['jsCleanComments'];\r
+        }\r
     }\r
-    \r
-    \r
+\r
+\r
     /**\r
      * Minify the markeup given in the constructor\r
      * \r
@@ -213,17 +222,19 @@ class Minify_HTML {
         // whitespace surrounding? preserve at least one space\r
         $ws1 = ($m[1] === '') ? '' : ' ';\r
         $ws2 = ($m[4] === '') ? '' : ' ';\r
\r
+\r
         // remove HTML comments (and ending "//" if present)\r
-        $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);\r
-            \r
+        if ($this->_jsCleanComments) {\r
+            $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);\r
+        }\r
+\r
         // remove CDATA section markers\r
         $js = $this->_removeCdata($js);\r
         \r
         // minify\r
         $minifier = $this->_jsMinifier\r
             ? $this->_jsMinifier\r
-            : 'trim'; \r
+            : 'trim';\r
         $js = call_user_func($minifier, $js);\r
         \r
         return $this->_reservePlace($this->_needsCdata($js)\r
index 807fdc9..f92ab85 100644 (file)
@@ -15,10 +15,10 @@ class Minify_HTML_Helper {
     public $minAppUri = '/min';
     public $groupsConfigFile = '';
 
-    /*
+    /**
      * Get an HTML-escaped Minify URI for a group or set of files
      * 
-     * @param mixed $keyOrFiles a group key or array of filepaths/URIs
+     * @param string|array $keyOrFiles a group key or array of filepaths/URIs
      * @param array $opts options:
      *   'farExpires' : (default true) append a modified timestamp for cache revving
      *   'debug' : (default false) append debug flag
@@ -51,8 +51,12 @@ class Minify_HTML_Helper {
         return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
     }
 
-    /*
+    /**
      * Get non-HTML-escaped URI to minify the specified files
+     *
+     * @param bool $farExpires
+     * @param bool $debug
+     * @return string
      */
     public function getRawUri($farExpires = true, $debug = false)
     {
@@ -74,6 +78,12 @@ class Minify_HTML_Helper {
         return $path;
     }
 
+    /**
+     * Set the files that will comprise the URI we're building
+     *
+     * @param array $files
+     * @param bool $checkLastModified
+     */
     public function setFiles($files, $checkLastModified = true)
     {
         $this->_groupKey = null;
@@ -94,6 +104,12 @@ class Minify_HTML_Helper {
         $this->_filePaths = $files;
     }
 
+    /**
+     * Set the group of files that will comprise the URI we're building
+     *
+     * @param string $key
+     * @param bool $checkLastModified
+     */
     public function setGroup($key, $checkLastModified = true)
     {
         $this->_groupKey = $key;
@@ -103,13 +119,23 @@ class Minify_HTML_Helper {
             }
             if (is_file($this->groupsConfigFile)) {
                 $gc = (require $this->groupsConfigFile);
-                if (isset($gc[$key])) {
-                    $this->_lastModified = self::getLastModified($gc[$key]);
+                $keys = explode(',', $key);
+                foreach ($keys as $key) {
+                    if (isset($gc[$key])) {
+                        $this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
+                    }
                 }
             }
         }
     }
-    
+
+    /**
+     * Get the max(lastModified) of all files
+     *
+     * @param array|string $sources
+     * @param int $lastModified
+     * @return int
+     */
     public static function getLastModified($sources, $lastModified = 0)
     {
         $max = $lastModified;
@@ -142,13 +168,19 @@ class Minify_HTML_Helper {
      * @return mixed a common char or '' if any do not match
      */
     protected static function _getCommonCharAtPos($arr, $pos) {
-        $l = count($arr);
+        if (!isset($arr[0][$pos])) {
+            return '';
+        }
         $c = $arr[0][$pos];
-        if ($c === '' || $l === 1)
+        $l = count($arr);
+        if ($l === 1) {
             return $c;
-        for ($i = 1; $i < $l; ++$i)
-            if ($arr[$i][$pos] !== $c)
+        }
+        for ($i = 1; $i < $l; ++$i) {
+            if ($arr[$i][$pos] !== $c) {
                 return '';
+            }
+        }
         return $c;
     }
 
@@ -157,11 +189,11 @@ class Minify_HTML_Helper {
      *
      * @param array $paths root-relative URIs of files
      * @param string $minRoot root-relative URI of the "min" application
+     * @return string
      */
     protected static function _getShortestUri($paths, $minRoot = '/min/') {
         $pos = 0;
         $base = '';
-        $c;
         while (true) {
             $c = self::_getCommonCharAtPos($paths, $pos);
             if ($c === '') {
index f4ed5bd..51f7cd1 100644 (file)
@@ -69,7 +69,7 @@ class Minify_JS_ClosureCompiler {
         }
         return $response;
     }
-    
+
     protected $_fallbackFunc = null;
 
     protected function _getResponse($postBody)
@@ -79,7 +79,7 @@ class Minify_JS_ClosureCompiler {
             $contents = file_get_contents(self::URL, false, stream_context_create(array(
                 'http' => array(
                     'method' => 'POST',
-                    'header' => 'Content-type: application/x-www-form-urlencoded',
+                    'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
                     'content' => $postBody,
                     'max_redirects' => 0,
                     'timeout' => 15,
@@ -125,7 +125,6 @@ class Minify_JS_ClosureCompiler {
      */
     protected function _fallback($js)
     {
-        require_once 'JSMin.php';
         return JSMin::minify($js);
     }
 }
index ca8afa1..e999a6c 100644 (file)
@@ -55,7 +55,11 @@ class Minify_Lines {
         $newLines = array();
         while (null !== ($line = array_shift($lines))) {
             if (('' !== $id) && (0 == $i % 50)) {
-                array_push($newLines, '', "/* {$id} */", '');
+                if ($inComment) {
+                    array_push($newLines, '', "/* {$id} *|", '');
+                } else {
+                    array_push($newLines, '', "/* {$id} */", '');
+                }
             }
             ++$i;
             $newLines[] = self::_addNote($line, $i, $inComment, $padTo);
@@ -65,7 +69,6 @@ class Minify_Lines {
         
         // check for desired URI rewriting
         if (isset($options['currentDir'])) {
-            require_once 'Minify/CSS/UriRewriter.php';
             Minify_CSS_UriRewriter::$debugText = '';
             $content = Minify_CSS_UriRewriter::rewrite(
                  $content
@@ -93,6 +96,9 @@ class Minify_Lines {
      */
     private static function _eolInComment($line, $inComment)
     {
+        // crude way to avoid things like // */
+        $line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line);
+
         while (strlen($line)) {
             $search = $inComment
                 ? '*/'
diff --git a/lib/minify/lib/Minify/Loader.php b/lib/minify/lib/Minify/Loader.php
new file mode 100644 (file)
index 0000000..0a225c0
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Class Minify_Loader
+ * @package Minify
+ */
+
+/**
+ * Class autoloader
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class Minify_Loader {
+    public function loadClass($class)
+    {
+        $file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
+        $file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php';
+        if (is_readable($file)) {
+            require $file;
+        }
+    }
+
+    static public function register()
+    {
+        $inst = new self();
+        spl_autoload_register(array($inst, 'loadClass'));
+    }
+}
index c5bd8a1..8e6bfcb 100644 (file)
  * Java environment.
  * 
  * <code>
- * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.3.5.jar';
+ * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar';
  * Minify_YUICompressor::$tempDir = '/tmp';
  * $code = Minify_YUICompressor::minifyJs(
  *   $code
  *   ,array('nomunge' => true, 'line-break' => 1000)
  * );
  * </code>
- * 
+ *
+ * Note: In case you run out stack (default is 512k), you may increase stack size in $options:
+ *   array('stack-size' => '2048k')
+ *
  * @todo unit tests, $options docs
  * 
  * @package Minify
@@ -108,10 +111,15 @@ class Minify_YUICompressor {
                 ,'nomunge' => false
                 ,'preserve-semi' => false
                 ,'disable-optimizations' => false
+                   ,'stack-size' => ''
             )
             ,$userOptions
         );
-        $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
+        $cmd = self::$javaExecutable
+                . (!empty($o['stack-size'])
+                   ? ' -Xss' . $o['stack-size']
+                   : '')
+                . ' -jar ' . escapeshellarg(self::$jarFile)
              . " --type {$type}"
              . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
                 ? " --charset {$o['charset']}" 
@@ -134,8 +142,8 @@ class Minify_YUICompressor {
         if (! is_file(self::$jarFile)) {
             throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
         }
-        if (! is_executable(self::$jarFile)) {
-            throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.');
+        if (! is_readable(self::$jarFile)) {
+            throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
         }
         if (! is_dir(self::$tempDir)) {
             throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
index ac20158..9aa8e06 100644 (file)
@@ -2,6 +2,9 @@
 
 namespace MrClay;
 
+use MrClay\Cli\Arg;
+use InvalidArgumentException;
+
 /**
  * Forms a front controller for a console app, handling and validating arguments (options)
  *
@@ -51,7 +54,7 @@ class Cli {
     public $isHelpRequest = false;
 
     /**
-     * @var array of Cli\Arg
+     * @var Arg[]
      */
     protected $_args = array();
 
@@ -80,8 +83,8 @@ class Cli {
     }
 
     /**
-     * @param Cli\Arg|string $letter
-     * @return Cli\Arg
+     * @param Arg|string $letter
+     * @return Arg
      */
     public function addOptionalArg($letter)
     {
@@ -89,8 +92,8 @@ class Cli {
     }
 
     /**
-     * @param Cli\Arg|string $letter
-     * @return Cli\Arg
+     * @param Arg|string $letter
+     * @return Arg
      */
     public function addRequiredArg($letter)
     {
@@ -100,17 +103,17 @@ class Cli {
     /**
      * @param string $letter
      * @param bool $required
-     * @param Cli\Arg|null $arg
-     * @return Cli\Arg
-     * @throws \InvalidArgumentException
+     * @param Arg|null $arg
+     * @return Arg
+     * @throws InvalidArgumentException
      */
-    public function addArgument($letter, $required, Cli\Arg $arg = null)
+    public function addArgument($letter, $required, Arg $arg = null)
     {
         if (! preg_match('/^[a-zA-Z]$/', $letter)) {
-            throw new \InvalidArgumentException('$letter must be in [a-zA-z]');
+            throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
         }
         if (! $arg) {
-            $arg = new Cli\Arg($required);
+            $arg = new Arg($required);
         }
         $this->_args[$letter] = $arg;
         return $arg;
@@ -118,7 +121,7 @@ class Cli {
 
     /**
      * @param string $letter
-     * @return Cli\Arg|null
+     * @return Arg|null
      */
     public function getArgument($letter)
     {
@@ -143,7 +146,7 @@ class Cli {
         
         $lettersUsed = '';
         foreach ($this->_args as $letter => $arg) {
-            /* @var Cli\Arg $arg  */
+            /* @var Arg $arg  */
             $options .= $letter;
             $lettersUsed .= $letter;
             
@@ -159,7 +162,7 @@ class Cli {
         $this->debug['getopt_return'] = $o;
 
         foreach ($this->_args as $letter => $arg) {
-            /* @var Cli\Arg $arg  */
+            /* @var Arg $arg  */
             $this->values[$letter] = false;
             if (isset($o[$letter])) {
                 if (is_bool($o[$letter])) {
@@ -295,7 +298,7 @@ class Cli {
     {
         $r = "\n";
         foreach ($this->_args as $letter => $arg) {
-            /* @var Cli\Arg $arg  */
+            /* @var Arg $arg  */
             $desc = $arg->getDescription();
             $flag = " -$letter ";
             if ($arg->mayHaveValue) {
index 81146a7..5fa5932 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace MrClay\Cli;
 
+use BadMethodCallException;
+
 /**
  * An argument for a CLI app. This specifies the argument, what values it expects and
  * how it's treated during validation.
@@ -150,7 +152,7 @@ class Arg {
      * @param string $name
      * @param array $args
      * @return Arg
-     * @throws \BadMethodCallException
+     * @throws BadMethodCallException
      */
     public function __call($name, array $args = array())
     {
@@ -160,7 +162,7 @@ class Arg {
                 $this->spec['mustHaveValue'] = true;
             }
         } else {
-            throw new \BadMethodCallException('Method does not exist');
+            throw new BadMethodCallException('Method does not exist');
         }
         return $this;
     }
index 9ba344d..05c03f1 100644 (file)
@@ -1,13 +1,11 @@
-Description of Minify 2.1.5 import into Moodle
+Description of Minify 2.1.7 import into Moodle
 
 Notes:
- * Uses are required to add minify/lib to the include path
- * We ever actually use things within minify/lib/*
+ * Do not use things within minify/lib/*
 
 Usage:
- * /lib/javascript.php
- * /theme/javascript.php
- * /theme/styles.php
+ * js_minify() from /lib/jslib.php
+ * css_minify_css() from /lib/csslib.php
 
 Changes:
  * Removed index.php - Is an unused entry point program and could potentially
index a18d7ad..7cf930a 100644 (file)
@@ -2,11 +2,18 @@
 /**
  * Utility functions for generating URIs in HTML files
  *
+ * @warning These functions execute min/groupsConfig.php, sometimes multiple times.
+ * You must make sure that functions are not redefined, and if your use custom sources,
+ * you must require_once dirname(__FILE__) . '/lib/Minify/Source.php' so that
+ * class is available.
+ *
  * @package Minify
  */
 
-require_once dirname(__FILE__) . '/lib/Minify/HTML/Helper.php';
-
+if (! class_exists('Minify_Loader', false)) {
+    require dirname(__FILE__) . '/lib/Minify/Loader.php';
+    Minify_Loader::register();
+}
 
 /*
  * Get an HTML-escaped Minify URI for a group or set of files. By default, URIs
index 1355b08..ac79c12 100644 (file)
@@ -1203,7 +1203,8 @@ class cm_info extends stdClass {
         }
 
         // Check group membership.
-        if ($this->is_user_access_restricted_by_group()) {
+        if ($this->is_user_access_restricted_by_group() ||
+                $this->is_user_access_restricted_by_capability()) {
 
              $this->uservisible = false;
             // Ensure activity is completely hidden from the user.
@@ -1234,6 +1235,23 @@ class cm_info extends stdClass {
         return false;
     }
 
+    /**
+     * Checks whether mod/...:view capability restricts the current user's access.
+     *
+     * @return bool True if the user access is restricted.
+     */
+    public function is_user_access_restricted_by_capability() {
+        $capability = 'mod/' . $this->modname . ':view';
+        $capabilityinfo = get_capability_info($capability);
+        if (!$capabilityinfo) {
+            // Capability does not exist, no one is prevented from seeing the activity.
+            return false;
+        }
+
+        // You are blocked if you don't have the capability.
+        return !has_capability($capability, context_module::instance($this->id));
+    }
+
     /**
      * Checks whether the module's conditional access settings mean that the user cannot see the activity at all
      *
index ac38fc8..c190a82 100644 (file)
@@ -30,9 +30,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-/// CONSTANTS (Encased in phpdoc proper comments)/////////////////////////
+// CONSTANTS (Encased in phpdoc proper comments).
 
-/// Date and time constants ///
+// Date and time constants.
 /**
  * Time constant - the number of seconds in a year
  */
@@ -68,10 +68,8 @@ define('DAYMINS', 1440);
  */
 define('HOURMINS', 60);
 
-/// Parameter constants - every call to optional_param(), required_param()  ///
-/// or clean_param() should have a specified type of parameter.  //////////////
-
-
+// Parameter constants - every call to optional_param(), required_param()
+// or clean_param() should have a specified type of parameter.
 
 /**
  * PARAM_ALPHA - contains only english ascii letters a-zA-Z.
@@ -163,7 +161,8 @@ define('PARAM_INT',      'int');
 define('PARAM_LANG',  'lang');
 
 /**
- * PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the others! Implies PARAM_URL!)
+ * PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the
+ * others! Implies PARAM_URL!)
  */
 define('PARAM_LOCALURL', 'localurl');
 
@@ -173,8 +172,8 @@ define('PARAM_LOCALURL', 'localurl');
 define('PARAM_NOTAGS',   'notags');
 
 /**
- * PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
- * note: the leading slash is not removed, window drive letter is not allowed
+ * PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory
+ * traversals note: the leading slash is not removed, window drive letter is not allowed
  */
 define('PARAM_PATH',     'path');
 
@@ -234,12 +233,14 @@ define('PARAM_TEXT',  'text');
 define('PARAM_THEME',  'theme');
 
 /**
- * PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not accepted but http://localhost.localdomain/ is ok.
+ * PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not accepted but
+ * http://localhost.localdomain/ is ok.
  */
 define('PARAM_URL',      'url');
 
 /**
- * PARAM_USERNAME - Clean username to only contains allowed characters. This is to be used ONLY when manually creating user accounts, do NOT use when syncing with external systems!!
+ * PARAM_USERNAME - Clean username to only contains allowed characters. This is to be used ONLY when manually creating user
+ * accounts, do NOT use when syncing with external systems!!
  */
 define('PARAM_USERNAME',    'username');
 
@@ -248,7 +249,7 @@ define('PARAM_USERNAME',    'username');
  */
 define('PARAM_STRINGID',    'stringid');
 
-///// DEPRECATED PARAM TYPES OR ALIASES - DO NOT USE FOR NEW CODE  /////
+// DEPRECATED PARAM TYPES OR ALIASES - DO NOT USE FOR NEW CODE.
 /**
  * PARAM_CLEAN - obsoleted, please use a more specific type of parameter.
  * It was one of the first types, that is why it is abused so much ;-)
@@ -290,7 +291,7 @@ define('PARAM_MULTILANG',  'text');
 
 /**
  * PARAM_TIMEZONE - expected timezone. Timezone can be int +-(0-13) or float +-(0.5-12.5) or
- * string seperated by '/' and can have '-' &/ '_' (eg. America/North_Dakota/New_Salem
+ * string separated by '/' and can have '-' &/ '_' (eg. America/North_Dakota/New_Salem
  * America/Port-au-Prince)
  */
 define('PARAM_TIMEZONE', 'timezone');
@@ -323,7 +324,7 @@ define('PARAM_AREA', 'area');
 define('PARAM_PLUGIN', 'plugin');
 
 
-/// Web Services ///
+// Web Services.
 
 /**
  * VALUE_REQUIRED - if the parameter is not supplied, there is an error
@@ -350,7 +351,8 @@ define('NULL_NOT_ALLOWED', false);
  */
 define('NULL_ALLOWED', true);
 
-/// Page types ///
+// Page types.
+
 /**
  * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php.
  */
@@ -361,7 +363,7 @@ define('GETREMOTEADDR_SKIP_HTTP_CLIENT_IP', '1');
 /** Get remote addr constant */
 define('GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR', '2');
 
-/// Blog access level constant declaration ///
+// Blog access level constant declaration.
 define ('BLOG_USER_LEVEL', 1);
 define ('BLOG_GROUP_LEVEL', 2);
 define ('BLOG_COURSE_LEVEL', 3);
@@ -369,7 +371,7 @@ define ('BLOG_SITE_LEVEL', 4);
 define ('BLOG_GLOBAL_LEVEL', 5);
 
 
-///Tag constants///
+// Tag constants.
 /**
  * To prevent problems with multibytes strings,Flag updating in nav not working on the review page. this should not exceed the
  * length of "varchar(255) / 3 (bytes / utf-8 character) = 85".
@@ -379,13 +381,13 @@ define ('BLOG_GLOBAL_LEVEL', 5);
  */
 define('TAG_MAX_LENGTH', 50);
 
-/// Password policy constants ///
+// Password policy constants.
 define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz');
 define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
 define ('PASSWORD_DIGITS', '0123456789');
 define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$');
 
-/// Feature constants ///
+// Feature constants.
 // Used for plugin_supports() to report features that are, or are not, supported by a module.
 
 /** True if module can provide a grade */
@@ -492,17 +494,20 @@ define('MOODLE_OFFICIAL_MOBILE_SERVICE', 'moodle_mobile_app');
 define('USER_CAN_IGNORE_FILE_SIZE_LIMITS', -1);
 
 /**
- * Course display settings
+ * Course display settings: display all sections on one page.
+ */
+define('COURSE_DISPLAY_SINGLEPAGE', 0);
+/**
+ * Course display settings: split pages into a page per section.
  */
-define('COURSE_DISPLAY_SINGLEPAGE', 0); // display all sections on one page
-define('COURSE_DISPLAY_MULTIPAGE', 1); // split pages into a page per section
+define('COURSE_DISPLAY_MULTIPAGE', 1);
 
 /**
- * Authentication constants.
+ * Authentication constant: String used in password field when password is not stored.
  */
-define('AUTH_PASSWORD_NOT_CACHED', 'not cached'); // String used in password field when password is not stored.
+define('AUTH_PASSWORD_NOT_CACHED', 'not cached');
 
-/// PARAMETER HANDLING ////////////////////////////////////////////////////
+// PARAMETER HANDLING.
 
 /**
  * Returns a particular value for the named variable, taken from
@@ -519,12 +524,14 @@ define('AUTH_PASSWORD_NOT_CACHED', 'not cached'); // String used in password fie
  * @param string $parname the name of the page parameter we want
  * @param string $type expected type of parameter
  * @return mixed
+ * @throws coding_exception
  */
 function required_param($parname, $type) {
     if (func_num_args() != 2 or empty($parname) or empty($type)) {
         throw new coding_exception('required_param() requires $parname and $type to be specified (parameter: '.$parname.')');
     }
-    if (isset($_POST[$parname])) {       // POST has precedence
+    // POST has precedence.
+    if (isset($_POST[$parname])) {
         $param = $_POST[$parname];
     } else if (isset($_GET[$parname])) {
         $param = $_GET[$parname];
@@ -534,8 +541,7 @@ function required_param($parname, $type) {
 
     if (is_array($param)) {
         debugging('Invalid array parameter detected in required_param(): '.$parname);
-        // TODO: switch to fatal error in Moodle 2.3
-        //print_error('missingparam', '', '', $parname);
+        // TODO: switch to fatal error in Moodle 2.3.
         return required_param_array($parname, $type);
     }
 
@@ -557,12 +563,14 @@ function required_param($parname, $type) {
  * @param string $parname the name of the page parameter we want
  * @param string $type expected type of parameter
  * @return array
+ * @throws coding_exception
  */
 function required_param_array($parname, $type) {
     if (func_num_args() != 2 or empty($parname) or empty($type)) {
         throw new coding_exception('required_param_array() requires $parname and $type to be specified (parameter: '.$parname.')');
     }
-    if (isset($_POST[$parname])) {       // POST has precedence
+    // POST has precedence.
+    if (isset($_POST[$parname])) {
         $param = $_POST[$parname];
     } else if (isset($_GET[$parname])) {
         $param = $_GET[$parname];
@@ -574,7 +582,7 @@ function required_param_array($parname, $type) {
     }
 
     $result = array();
-    foreach($param as $key=>$value) {
+    foreach ($param as $key => $value) {
         if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
             debugging('Invalid key name in required_param_array() detected: '.$key.', parameter: '.$parname);
             continue;
@@ -600,16 +608,18 @@ function required_param_array($parname, $type) {
  * @param mixed  $default the default value to return if nothing is found
  * @param string $type expected type of parameter
  * @return mixed
+ * @throws coding_exception
  */
 function optional_param($parname, $default, $type) {
     if (func_num_args() != 3 or empty($parname) or empty($type)) {
-        throw new coding_exception('optional_param() requires $parname, $default and $type to be specified (parameter: '.$parname.')');
+        throw new coding_exception('optional_param requires $parname, $default + $type to be specified (parameter: '.$parname.')');
     }
     if (!isset($default)) {
         $default = null;
     }
 
-    if (isset($_POST[$parname])) {       // POST has precedence
+    // POST has precedence.
+    if (isset($_POST[$parname])) {
         $param = $_POST[$parname];
     } else if (isset($_GET[$parname])) {
         $param = $_GET[$parname];
@@ -619,8 +629,7 @@ function optional_param($parname, $default, $type) {
 
     if (is_array($param)) {
         debugging('Invalid array parameter detected in required_param(): '.$parname);
-        // TODO: switch to $default in Moodle 2.3
-        //return $default;
+        // TODO: switch to $default in Moodle 2.3.
         return optional_param_array($parname, $default, $type);
     }
 
@@ -636,19 +645,21 @@ function optional_param($parname, $default, $type) {
  * used like this:
  *    $ids = optional_param('id', array(), PARAM_INT);
  *
- *  Note: arrays of arrays are not supported, only alphanumeric keys with _ and - are supported
+ * Note: arrays of arrays are not supported, only alphanumeric keys with _ and - are supported
  *
  * @param string $parname the name of the page parameter we want
- * @param mixed  $default the default value to return if nothing is found
+ * @param mixed $default the default value to return if nothing is found
  * @param string $type expected type of parameter
  * @return array
+ * @throws coding_exception
  */
 function optional_param_array($parname, $default, $type) {
     if (func_num_args() != 3 or empty($parname) or empty($type)) {
-        throw new coding_exception('optional_param_array() requires $parname, $default and $type to be specified (parameter: '.$parname.')');
+        throw new coding_exception('optional_param_array requires $parname, $default + $type to be specified (parameter: '.$parname.')');
     }
 
-    if (isset($_POST[$parname])) {       // POST has precedence
+    // POST has precedence.
+    if (isset($_POST[$parname])) {
         $param = $_POST[$parname];
     } else if (isset($_GET[$parname])) {
         $param = $_GET[$parname];
@@ -661,7 +672,7 @@ function optional_param_array($parname, $default, $type) {
     }
 
     $result = array();
-    foreach($param as $key=>$value) {
+    foreach ($param as $key => $value) {
         if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
             debugging('Invalid key name in optional_param_array() detected: '.$key.', parameter: '.$parname);
             continue;
@@ -708,7 +719,7 @@ function validate_param($param, $type, $allownull=NULL_NOT_ALLOWED, $debuginfo='
             throw new invalid_parameter_exception($debuginfo);
         }
     } else if ((string)$param !== (string)$cleaned) {
-        // conversion to string is usually lossless
+        // Conversion to string is usually lossless.
         throw new invalid_parameter_exception($debuginfo);
     }
 
@@ -716,8 +727,8 @@ function validate_param($param, $type, $allownull=NULL_NOT_ALLOWED, $debuginfo='
 }
 
 /**
- * Makes sure array contains only the allowed types,
- * this function does not validate array key names!
+ * Makes sure array contains only the allowed types, this function does not validate array key names!
+ *
  * <code>
  * $options = clean_param($options, PARAM_INT);
  * </code>
@@ -726,15 +737,17 @@ function validate_param($param, $type, $allownull=NULL_NOT_ALLOWED, $debuginfo='
  * @param string $type expected format of param after cleaning.
  * @param bool $recursive clean recursive arrays
  * @return array
+ * @throws coding_exception
  */
 function clean_param_array(array $param = null, $type, $recursive = false) {
-    $param = (array)$param; // convert null to empty array
+    // Convert null to empty array.
+    $param = (array)$param;
     foreach ($param as $key => $value) {
         if (is_array($value)) {
             if ($recursive) {
                 $param[$key] = clean_param_array($value, $type, true);
             } else {
-                throw new coding_exception('clean_param_array() can not process multidimensional arrays when $recursive is false.');
+                throw new coding_exception('clean_param_array can not process multidimensional arrays when $recursive is false.');
             }
         } else {
             $param[$key] = clean_param($value, $type);
@@ -749,15 +762,15 @@ function clean_param_array(array $param = null, $type, $recursive = false) {
  * an options field.
  * <code>
  * $course->format = clean_param($course->format, PARAM_ALPHA);
- * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_INT);
+ * $selectedgradeitem = clean_param($selectedgradeitem, PARAM_INT);
  * </code>
  *
  * @param mixed $param the variable we are cleaning
  * @param string $type expected format of param after cleaning.
  * @return mixed
+ * @throws coding_exception
  */
 function clean_param($param, $type) {
-
     global $CFG;
 
     if (is_array($param)) {
@@ -771,49 +784,63 @@ function clean_param($param, $type) {
     }
 
     switch ($type) {
-        case PARAM_RAW:          // no cleaning at all
+        case PARAM_RAW:
+            // No cleaning at all.
             $param = fix_utf8($param);
             return $param;
 
-        case PARAM_RAW_TRIMMED:         // no cleaning, but strip leading and trailing whitespace.
+        case PARAM_RAW_TRIMMED:
+            // No cleaning, but strip leading and trailing whitespace.
             $param = fix_utf8($param);
             return trim($param);
 
-        case PARAM_CLEAN:        // General HTML cleaning, try to use more specific type if possible
-            // this is deprecated!, please use more specific type instead
+        case PARAM_CLEAN:
+            // General HTML cleaning, try to use more specific type if possible this is deprecated!
+            // Please use more specific type instead.
             if (is_numeric($param)) {
                 return $param;
             }
             $param = fix_utf8($param);
-            return clean_text($param);     // Sweep for scripts, etc
+            // Sweep for scripts, etc.
+            return clean_text($param);
 
-        case PARAM_CLEANHTML:    // clean html fragment
+        case PARAM_CLEANHTML:
+            // Clean html fragment.
             $param = fix_utf8($param);
-            $param = clean_text($param, FORMAT_HTML);     // Sweep for scripts, etc
+            // Sweep for scripts, etc.
+            $param = clean_text($param, FORMAT_HTML);
             return trim($param);
 
         case PARAM_INT:
-            return (int)$param;  // Convert to integer
+            // Convert to integer.
+            return (int)$param;
 
         case PARAM_FLOAT:
-            return (float)$param;  // Convert to float
+            // Convert to float.
+            return (float)$param;
 
-        case PARAM_ALPHA:        // Remove everything not a-z
+        case PARAM_ALPHA:
+            // Remove everything not `a-z`.
             return preg_replace('/[^a-zA-Z]/i', '', $param);
 
-        case PARAM_ALPHAEXT:     // Remove everything not a-zA-Z_- (originally allowed "/" too)
+        case PARAM_ALPHAEXT:
+            // Remove everything not `a-zA-Z_-` (originally allowed "/" too).
             return preg_replace('/[^a-zA-Z_-]/i', '', $param);
 
-        case PARAM_ALPHANUM:     // Remove everything not a-zA-Z0-9
+        case PARAM_ALPHANUM:
+            // Remove everything not `a-zA-Z0-9`.
             return preg_replace('/[^A-Za-z0-9]/i', '', $param);
 
-        case PARAM_ALPHANUMEXT:     // Remove everything not a-zA-Z0-9_-
+        case PARAM_ALPHANUMEXT:
+            // Remove everything not `a-zA-Z0-9_-`.
             return preg_replace('/[^A-Za-z0-9_-]/i', '', $param);
 
-        case PARAM_SEQUENCE:     // Remove everything not 0-9,
+        case PARAM_SEQUENCE:
+            // Remove everything not `0-9,`.
             return preg_replace('/[^0-9,]/i', '', $param);
 
-        case PARAM_BOOL:         // Convert to 1 or 0
+        case PARAM_BOOL:
+            // Convert to 1 or 0.
             $tempstr = strtolower($param);
             if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') {
                 $param = 1;
@@ -824,18 +851,19 @@ function clean_param($param, $type) {
             }
             return $param;
 
-        case PARAM_NOTAGS:       // Strip all tags
+        case PARAM_NOTAGS:
+            // Strip all tags.
             $param = fix_utf8($param);
             return strip_tags($param);
 
-        case PARAM_TEXT:    // leave only tags needed for multilang
+        case PARAM_TEXT:
+            // Leave only tags needed for multilang.
             $param = fix_utf8($param);
-            // if the multilang syntax is not correct we strip all tags
-            // because it would break xhtml strict which is required for accessibility standards
-            // please note this cleaning does not strip unbalanced '>' for BC compatibility reasons
+            // If the multilang syntax is not correct we strip all tags because it would break xhtml strict which is required
+            // for accessibility standards please note this cleaning does not strip unbalanced '>' for BC compatibility reasons.
             do {
                 if (strpos($param, '</lang>') !== false) {
-                    // old and future mutilang syntax
+                    // Old and future mutilang syntax.
                     $param = strip_tags($param, '<lang>');
                     if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                         break;
@@ -862,7 +890,7 @@ function clean_param($param, $type) {
                     return $param;
 
                 } else if (strpos($param, '</span>') !== false) {
-                    // current problematic multilang syntax
+                    // Current problematic multilang syntax.
                     $param = strip_tags($param, '<span>');
                     if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                         break;
@@ -889,12 +917,12 @@ function clean_param($param, $type) {
                     return $param;
                 }
             } while (false);
-            // easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string()
+            // Easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string().
             return strip_tags($param);
 
         case PARAM_COMPONENT:
-            // we do not want any guessing here, either the name is correct or not
-            // please note only normalised component names are accepted
+            // We do not want any guessing here, either the name is correct or not
+            // please note only normalised component names are accepted.
             if (!preg_match('/^[a-z]+(_[a-z][a-z0-9_]*)?[a-z0-9]$/', $param)) {
                 return '';
             }
@@ -902,7 +930,7 @@ function clean_param($param, $type) {
                 return '';
             }
             if (strpos($param, 'mod_') === 0) {
-                // module names must not contain underscores because we need to differentiate them from invalid plugin types
+                // Module names must not contain underscores because we need to differentiate them from invalid plugin types.
                 if (substr_count($param, '_') != 1) {
                     return '';
                 }
@@ -911,19 +939,22 @@ function clean_param($param, $type) {
 
         case PARAM_PLUGIN:
         case PARAM_AREA:
-            // we do not want any guessing here, either the name is correct or not
+            // We do not want any guessing here, either the name is correct or not.
             if (!is_valid_plugin_name($param)) {
                 return '';
             }
             return $param;
 
-        case PARAM_SAFEDIR:      // Remove everything not a-zA-Z0-9_-
+        case PARAM_SAFEDIR:
+            // Remove everything not a-zA-Z0-9_- .
             return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param);
 
-        case PARAM_SAFEPATH:     // Remove everything not a-zA-Z0-9/_-
+        case PARAM_SAFEPATH:
+            // Remove everything not a-zA-Z0-9/_- .
             return preg_replace('/[^a-zA-Z0-9\/_-]/i', '', $param);
 
-        case PARAM_FILE:         // Strip all suspicious characters from filename
+        case PARAM_FILE:
+            // Strip all suspicious characters from filename.
             $param = fix_utf8($param);
             $param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':\\\\/]~u', '', $param);
             if ($param === '.' || $param === '..') {
@@ -931,7 +962,8 @@ function clean_param($param, $type) {
             }
             return $param;
 
-        case PARAM_PATH:         // Strip all suspicious characters from file path
+        case PARAM_PATH:
+            // Strip all suspicious characters from file path.
             $param = fix_utf8($param);
             $param = str_replace('\\', '/', $param);
 
@@ -952,50 +984,53 @@ function clean_param($param, $type) {
             $param = preg_replace('~/(\./)+~', '/', $param);
             return $param;
 
-        case PARAM_HOST:         // allow FQDN or IPv4 dotted quad
-            $param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
-            // match ipv4 dotted quad
-            if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
-                // confirm values are ok
+        case PARAM_HOST:
+            // Allow FQDN or IPv4 dotted quad.
+            $param = preg_replace('/[^\.\d\w-]/', '', $param );
+            // Match ipv4 dotted quad.
+            if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/', $param, $match)) {
+                // Confirm values are ok.
                 if ( $match[0] > 255
                      || $match[1] > 255
                      || $match[3] > 255
                      || $match[4] > 255 ) {
-                    // hmmm, what kind of dotted quad is this?
+                    // Hmmm, what kind of dotted quad is this?
                     $param = '';
                 }
-            } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
-                       && !preg_match('/^[\.-]/',  $param) // no leading dots/hyphens
-                       && !preg_match('/[\.-]$/',  $param) // no trailing dots/hyphens
+            } else if ( preg_match('/^[\w\d\.-]+$/', $param) // Dots, hyphens, numbers.
+                       && !preg_match('/^[\.-]/',  $param) // No leading dots/hyphens.
+                       && !preg_match('/[\.-]$/',  $param) // No trailing dots/hyphens.
                        ) {
-                // all is ok - $param is respected
+                // All is ok - $param is respected.
             } else {
-                // all is not ok...
+                // All is not ok...
                 $param='';
             }
             return $param;
 
-        case PARAM_URL:          // allow safe ftp, http, mailto urls
+        case PARAM_URL:          // Allow safe ftp, http, mailto urls.
             $param = fix_utf8($param);
             include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
             if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) {
-                // all is ok, param is respected
+                // All is ok, param is respected.
             } else {
-                $param =''; // not really ok
+                // Not really ok.
+                $param ='';
             }
             return $param;
 
-        case PARAM_LOCALURL:     // allow http absolute, root relative and relative URLs within wwwroot
+        case PARAM_LOCALURL:
+            // Allow http absolute, root relative and relative URLs within wwwroot.
             $param = clean_param($param, PARAM_URL);
             if (!empty($param)) {
                 if (preg_match(':^/:', $param)) {
-                    // root-relative, ok!
-                } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
-                    // absolute, and matches our wwwroot
+                    // Root-relative, ok!
+                } else if (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i', $param)) {
+                    // Absolute, and matches our wwwroot.
                 } else {
-                    // relative - let's make sure there are no tricks
+                    // Relative - let's make sure there are no tricks.
                     if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?')) {
-                        // looks ok.
+                        // Looks ok.
                     } else {
                         $param = '';
                     }
@@ -1005,11 +1040,11 @@ function clean_param($param, $type) {
 
         case PARAM_PEM:
             $param = trim($param);
-            // PEM formatted strings may contain letters/numbers and the symbols
-            // forward slash: /
-            // plus sign:     +
-            // equal sign:    =
-            // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes
+            // PEM formatted strings may contain letters/numbers and the symbols:
+            //   forward slash: /
+            //   plus sign:     +
+            //   equal sign:    =
+            //   , surrounded by BEGIN and END CERTIFICATE prefix and suffixes.
             if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
                 list($wholething, $body) = $matches;
                 unset($wholething, $matches);
@@ -1025,16 +1060,15 @@ function clean_param($param, $type) {
         case PARAM_BASE64:
             if (!empty($param)) {
                 // PEM formatted strings may contain letters/numbers and the symbols
-                // forward slash: /
-                // plus sign:     +
-                // equal sign:    =
+                //   forward slash: /
+                //   plus sign:     +
+                //   equal sign:    =.
                 if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) {
                     return '';
                 }
                 $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
-                // Each line of base64 encoded data must be 64 characters in
-                // length, except for the last line which may be less than (or
-                // equal to) 64 characters long.
+                // Each line of base64 encoded data must be 64 characters in length, except for the last line which may be less
+                // than (or equal to) 64 characters long.
                 for ($i=0, $j=count($lines); $i < $j; $i++) {
                     if ($i + 1 == $j) {
                         if (64 < strlen($lines[$i])) {
@@ -1047,7 +1081,7 @@ function clean_param($param, $type) {
                         return '';
                     }
                 }
-                return implode("\n",$lines);
+                return implode("\n", $lines);
             } else {
                 return '';
             }
@@ -1056,9 +1090,9 @@ function clean_param($param, $type) {
             $param = fix_utf8($param);
             // Please note it is not safe to use the tag name directly anywhere,
             // it must be processed with s(), urlencode() before embedding anywhere.
-            // remove some nasties
+            // Remove some nasties.
             $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
-            //convert many whitespace chars into one
+            // Convert many whitespace chars into one.
             $param = preg_replace('/\s+/', ' ', $param);
             $param = textlib::substr(trim($param), 0, TAG_MAX_LENGTH);
             return $param;
@@ -1109,7 +1143,8 @@ function clean_param($param, $type) {
             if (get_string_manager()->translation_exists($param)) {
                 return $param;
             } else {
-                return ''; // Specified language is not installed or param malformed
+                // Specified language is not installed or param malformed.
+                return '';
             }
 
         case PARAM_THEME:
@@ -1121,15 +1156,17 @@ function clean_param($param, $type) {
             } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$param/config.php")) {
                 return $param;
             } else {
-                return '';  // Specified theme is not installed
+                // Specified theme is not installed.
+                return '';
             }
 
         case PARAM_USERNAME:
             $param = fix_utf8($param);
             $param = str_replace(" " , "", $param);
-            $param = textlib::strtolower($param);  // Convert uppercase to lowercase MDL-16919
+            // Convert uppercase to lowercase MDL-16919.
+            $param = textlib::strtolower($param);
             if (empty($CFG->extendedusernamechars)) {
-                // regular expression, eliminate all chars EXCEPT:
+                // Regular expression, eliminate all chars EXCEPT:
                 // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
                 $param = preg_replace('/[^-\.@_a-z0-9]/', '', $param);
             }
@@ -1150,7 +1187,8 @@ function clean_param($param, $type) {
                 return '';
             }
 
-        case PARAM_TIMEZONE:    //can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'
+        case PARAM_TIMEZONE:
+            // Can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'.
             $param = fix_utf8($param);
             $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3](\.0)?|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
             if (preg_match($timezonepattern, $param)) {
@@ -1159,7 +1197,8 @@ function clean_param($param, $type) {
                 return '';
             }
 
-        default:                 // throw error, switched parameters in optional_param or another serious problem
+        default:
+            // Doh! throw error, switched parameters in optional_param or another serious problem.
             print_error("unknownparamtype", '', '', $type);
     }
 }
@@ -1178,7 +1217,7 @@ function fix_utf8($value) {
 
     } else if (is_string($value)) {
         if ((string)(int)$value === $value) {
-            // shortcut
+            // Shortcut.
             return $value;
         }
 
@@ -1217,20 +1256,21 @@ function fix_utf8($value) {
         return $result;
 
     } else if (is_array($value)) {
-        foreach ($value as $k=>$v) {
+        foreach ($value as $k => $v) {
             $value[$k] = fix_utf8($v);
         }
         return $value;
 
     } else if (is_object($value)) {
-        $value = clone($value); // do not modify original
-        foreach ($value as $k=>$v) {
+        // Do not modify original.
+        $value = clone($value);
+        foreach ($value as $k => $v) {
             $value->$k = fix_utf8($v);
         }
         return $value;
 
     } else {
-        // this is some other type, no utf-8 here
+        // This is some other type, no utf-8 here.
         return $value;
     }
 }
@@ -1252,7 +1292,8 @@ function is_number($value) {
 }
 
 /**
- * Returns host part from url
+ * Returns host part from url.
+ *
  * @param string $url full url
  * @return string host, null if not found
  */
@@ -1290,31 +1331,30 @@ function html_is_blank($string) {
  *
  * A NULL value will delete the entry.
  *
- * @global object
- * @global object
  * @param string $name the key to set
  * @param string $value the value to set (without magic quotes)
- * @param string $plugin (optional) the plugin scope, default NULL
+ * @param string $plugin (optional) the plugin scope, default null
  * @return bool true or exception
  */
-function set_config($name, $value, $plugin=NULL) {
+function set_config($name, $value, $plugin=null) {
     global $CFG, $DB;
 
     if (empty($plugin)) {
         if (!array_key_exists($name, $CFG->config_php_settings)) {
-            // So it's defined for this invocation at least
+            // So it's defined for this invocation at least.
             if (is_null($value)) {
                 unset($CFG->$name);
             } else {
-                $CFG->$name = (string)$value; // settings from db are always strings
+                // Settings from db are always strings.
+                $CFG->$name = (string)$value;
             }
         }
 
-        if ($DB->get_field('config', 'name', array('name'=>$name))) {
+        if ($DB->get_field('config', 'name', array('name' => $name))) {
             if ($value === null) {
-                $DB->delete_records('config', array('name'=>$name));
+                $DB->delete_records('config', array('name' => $name));
             } else {
-                $DB->set_field('config', 'value', $value, array('name'=>$name));
+                $DB->set_field('config', 'value', $value, array('name' => $name));
             }
         } else {
             if ($value !== null) {
@@ -1328,12 +1368,13 @@ function set_config($name, $value, $plugin=NULL) {
             cache_helper::update_site_identifier($value);
         }
         cache_helper::invalidate_by_definition('core', 'config', array(), 'core');
-    } else { // plugin scope
-        if ($id = $DB->get_field('config_plugins', 'id', array('name'=>$name, 'plugin'=>$plugin))) {
+    } else {
+        // Plugin scope.
+        if ($id = $DB->get_field('config_plugins', 'id', array('name' => $name, 'plugin' => $plugin))) {
             if ($value===null) {
-                $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin));
+                $DB->delete_records('config_plugins', array('name' => $name, 'plugin' => $plugin));
             } else {
-                $DB->set_field('config_plugins', 'value', $value, array('id'=>$id));
+                $DB->set_field('config_plugins', 'value', $value, array('id' => $id));
             }
         } else {
             if ($value !== null) {
@@ -1360,13 +1401,14 @@ function set_config($name, $value, $plugin=NULL) {
  * If called with 2 parameters it will return a string single
  * value or false if the value is not found.
  *
- * @static $siteidentifier The site identifier is not cached. We use this static cache so
+ * @static string|false $siteidentifier The site identifier is not cached. We use this static cache so
  *     that we need only fetch it once per request.
  * @param string $plugin full component name
- * @param string $name default NULL
+ * @param string $name default null
  * @return mixed hash-like object or single value, return false no config found
+ * @throws dml_exception
  */
-function get_config($plugin, $name = NULL) {
+function get_config($plugin, $name = null) {
     global $CFG, $DB;
 
     static $siteidentifier = null;
@@ -1408,12 +1450,11 @@ function get_config($plugin, $name = NULL) {
     $cache = cache::make('core', 'config');
     $result = $cache->get($plugin);
     if ($result === false) {
-        // the user is after a recordset
-        $result = new stdClass;
+        // The user is after a recordset.
         if (!$iscore) {
-            $result = $DB->get_records_menu('config_plugins', array('plugin'=>$plugin), '', 'name,value');
+            $result = $DB->get_records_menu('config_plugins', array('plugin' => $plugin), '', 'name,value');
         } else {
-            // this part is not really used any more, but anyway...
+            // This part is not really used any more, but anyway...
             $result = $DB->get_records_menu('config', array(), '', 'name,value');;
         }
         $cache->set($plugin, $result);
@@ -1432,10 +1473,10 @@ function get_config($plugin, $name = NULL) {
 
     foreach ($forced as $key => $value) {
         if (is_null($value) or is_array($value) or is_object($value)) {
-            // we do not want any extra mess here, just real settings that could be saved in db
+            // We do not want any extra mess here, just real settings that could be saved in db.
             unset($result[$key]);
         } else {
-            //convert to string as if it went through the DB
+            // Convert to string as if it went through the DB.
             $result[$key] = (string)$value;
         }
     }
@@ -1444,22 +1485,21 @@ function get_config($plugin, $name = NULL) {
 }
 
 /**
- * Removes a key from global configuration
+ * Removes a key from global configuration.
  *
  * @param string $name the key to set
  * @param string $plugin (optional) the plugin scope
- * @global object
  * @return boolean whether the operation succeeded.
  */
-function unset_config($name, $plugin=NULL) {
+function unset_config($name, $plugin=null) {
     global $CFG, $DB;
 
     if (empty($plugin)) {
         unset($CFG->$name);
-        $DB->delete_records('config', array('name'=>$name));
+        $DB->delete_records('config', array('name' => $name));
         cache_helper::invalidate_by_definition('core', 'config', array(), 'core');
     } else {
-        $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin));
+        $DB->delete_records('config_plugins', array('name' => $name, 'plugin' => $plugin));
         cache_helper::invalidate_by_definition('core', 'config', array(), $plugin);
     }
 
@@ -1468,6 +1508,7 @@ function unset_config($name, $plugin=NULL) {
 
 /**
  * Remove all the config variables for a given plugin.
+ *
  * NOTE: this function is called from lib/db/upgrade.php
  *
  * @param string $plugin a plugin, for example 'quiz' or 'qtype_multichoice';
@@ -1475,9 +1516,9 @@ function unset_config($name, $plugin=NULL) {
  */
 function unset_all_config_for_plugin($plugin) {
     global $DB;
-    // Delete from the obvious config_plugins first
+    // Delete from the obvious config_plugins first.
     $DB->delete_records('config_plugins', array('plugin' => $plugin));
-    // Next delete any suspect settings from config
+    // Next delete any suspect settings from config.
     $like = $DB->sql_like('name', '?', true, true, false, '|');
     $params = array($DB->sql_like_escape($plugin.'_', '|') . '%');
     $DB->delete_records_select('config', $like, $params);
@@ -1494,19 +1535,17 @@ function unset_all_config_for_plugin($plugin) {
  *
  * @param string $value the value of the config setting.
  * @param string $capability the capability - must match the one passed to the admin_setting_users_with_capability constructor.
- * @param bool $include admins, include administrators
+ * @param bool $includeadmins include administrators.
  * @return array of user objects.
  */
 function get_users_from_config($value, $capability, $includeadmins = true) {
-    global $CFG, $DB;
-
     if (empty($value) or $value === '$@NONE@$') {
         return array();
     }
 
-    // we have to make sure that users still have the necessary capability,
+    // We have to make sure that users still have the necessary capability,
     // it should be faster to fetch them all first and then test if they are present
-    // instead of validating them one-by-one
+    // instead of validating them one-by-one.
     $users = get_users_by_capability(context_system::instance(), $capability);
     if ($includeadmins) {
         $admins = get_admins();
@@ -1519,7 +1558,7 @@ function get_users_from_config($value, $capability, $includeadmins = true) {
         return $users;
     }
 
-    $result = array(); // result in correct order
+    $result = array(); // Result in correct order.
     $allowed = explode(',', $value);
     foreach ($allowed as $uid) {
         if (isset($users[$uid])) {
@@ -1533,10 +1572,10 @@ function get_users_from_config($value, $capability, $includeadmins = true) {
 
 
 /**
- * Invalidates browser caches and cached data in temp
+ * Invalidates browser caches and cached data in temp.
  *
  * IMPORTANT - If you are adding anything here to do with the cache directory you should also have a look at
- * {@see phpunit_util::reset_dataroot()}
+ * {@link phpunit_util::reset_dataroot()}
  *
  * @return void
  */
@@ -1551,14 +1590,14 @@ function purge_all_caches() {
 
     cache_helper::purge_all();
 
-    // purge all other caches: rss, simplepie, etc.
+    // Purge all other caches: rss, simplepie, etc.
     remove_dir($CFG->cachedir.'', true);
 
-    // make sure cache dir is writable, throws exception if not
+    // Make sure cache dir is writable, throws exception if not.
     make_cache_directory('');
 
-    // hack: this script may get called after the purifier was initialised,
-    // but we do not want to verify repeatedly this exists in each call
+    // Hack: this script may get called after the purifier was initialised,
+    // but we do not want to verify repeatedly this exists in each call.
     make_cache_directory('htmlpurifier');
 
     // This is the only place where we purge local caches, we are only adding files there.
@@ -1572,20 +1611,19 @@ function purge_all_caches() {
  * Get volatile flags
  *
  * @param string $type
- * @param int    $changedsince default null
- * @return records array
+ * @param int $changedsince default null
+ * @return array records array
  */
-function get_cache_flags($type, $changedsince=NULL) {
+function get_cache_flags($type, $changedsince = null) {
     global $DB;
 
-    $params = array('type'=>$type, 'expiry'=>time());
+    $params = array('type' => $type, 'expiry' => time());
     $sqlwhere = "flagtype = :type AND expiry >= :expiry";
-    if ($changedsince !== NULL) {
+    if ($changedsince !== null) {
         $params['changedsince'] = $changedsince;
         $sqlwhere .= " AND timemodified > :changedsince";
     }
     $cf = array();
-
     if ($flags = $DB->get_records_select('cache_flags', $sqlwhere, $params, '', 'name,value')) {
         foreach ($flags as $flag) {
             $cf[$flag->name] = $flag->value;
@@ -1599,16 +1637,16 @@ function get_cache_flags($type, $changedsince=NULL) {
  *
  * @param string $type
  * @param string $name
- * @param int    $changedsince default null
- * @return records array
+ * @param int $changedsince default null
+ * @return string|false The cache flag value or false
  */
-function get_cache_flag($type, $name, $changedsince=NULL) {
+function get_cache_flag($type, $name, $changedsince=null) {
     global $DB;
 
-    $params = array('type'=>$type, 'name'=>$name, 'expiry'=>time());
+    $params = array('type' => $type, 'name' => $name, 'expiry' => time());
 
     $sqlwhere = "flagtype = :type AND name = :name AND expiry >= :expiry";
-    if ($changedsince !== NULL) {
+    if ($changedsince !== null) {
         $params['changedsince'] = $changedsince;
         $sqlwhere .= " AND timemodified > :changedsince";
     }
@@ -1621,28 +1659,29 @@ function get_cache_flag($type, $name, $changedsince=NULL) {
  *
  * @param string $type the "type" namespace for the key
  * @param string $name the key to set
- * @param string $value the value to set (without magic quotes) - NULL will remove the flag
+ * @param string $value the value to set (without magic quotes) - null will remove the flag
  * @param int $expiry (optional) epoch indicating expiry - defaults to now()+ 24hs
  * @return bool Always returns true
  */
-function set_cache_flag($type, $name, $value, $expiry=NULL) {
+function set_cache_flag($type, $name, $value, $expiry = null) {
     global $DB;
 
     $timemodified = time();
-    if ($expiry===NULL || $expiry < $timemodified) {
+    if ($expiry === null || $expiry < $timemodified) {
         $expiry = $timemodified + 24 * 60 * 60;
     } else {
         $expiry = (int)$expiry;
     }
 
-    if ($value === NULL) {
-        unset_cache_flag($type,$name);
+    if ($value === null) {
+        unset_cache_flag($type, $name);
         return true;
     }
 
-    if ($f = $DB->get_record('cache_flags', array('name'=>$name, 'flagtype'=>$type), '*', IGNORE_MULTIPLE)) { // this is a potential problem in DEBUG_DEVELOPER
+    if ($f = $DB->get_record('cache_flags', array('name' => $name, 'flagtype' => $type), '*', IGNORE_MULTIPLE)) {
+        // This is a potential problem in DEBUG_DEVELOPER.
         if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) {
-            return true; //no need to update
+            return true; // No need to update.
         }
         $f->value        = $value;
         $f->expiry       = $expiry;
@@ -1663,14 +1702,13 @@ function set_cache_flag($type, $name, $value, $expiry=NULL) {
 /**
  * Removes a single volatile flag
  *
- * @global object
  * @param string $type the "type" namespace for the key
  * @param string $name the key to set
  * @return bool
  */
 function unset_cache_flag($type, $name) {
     global $DB;
-    $DB->delete_records('cache_flags', array('name'=>$name, 'flagtype'=>$type));
+    $DB->delete_records('cache_flags', array('name' => $name, 'flagtype' => $type));
     return true;
 }
 
@@ -1685,7 +1723,7 @@ function gc_cache_flags() {
     return true;
 }
 
-// USER PREFERENCE API
+// USER PREFERENCE API.
 
 /**
  * Refresh user preference cache. This is used most often for $USER
@@ -1703,14 +1741,15 @@ function gc_cache_flags() {
  */
 function check_user_preferences_loaded(stdClass $user, $cachelifetime = 120) {
     global $DB;
-    static $loadedusers = array(); // Static cache, we need to check on each page load, not only every 2 minutes.
+    // Static cache, we need to check on each page load, not only every 2 minutes.
+    static $loadedusers = array();
 
     if (!isset($user->id)) {
         throw new coding_exception('Invalid $user parameter in check_user_preferences_loaded() call, missing id field');
     }
 
     if (empty($user->id) or isguestuser($user->id)) {
-        // No permanent storage for not-logged-in users and guest
+        // No permanent storage for not-logged-in users and guest.
         if (!isset($user->preference)) {
             $user->preference = array();
         }
@@ -1722,37 +1761,36 @@ function check_user_preferences_loaded(stdClass $user, $cachelifetime = 120) {
     if (isset($loadedusers[$user->id]) and isset($user->preference) and isset($user->preference['_lastloaded'])) {
         // Already loaded at least once on this page. Are we up to date?
         if ($user->preference['_lastloaded'] + $cachelifetime > $timenow) {
-            // no need to reload - we are on the same page and we loaded prefs just a moment ago
+            // No need to reload - we are on the same page and we loaded prefs just a moment ago.
             return;
 
         } else if (!get_cache_flag('userpreferenceschanged', $user->id, $user->preference['_lastloaded'])) {
-            // no change since the lastcheck on this page
+            // No change since the lastcheck on this page.
             $user->preference['_lastloaded'] = $timenow;
             return;
         }
     }
 
-    // OK, so we have to reload all preferences
+    // OK, so we have to reload all preferences.
     $loadedusers[$user->id] = true;
-    $user->preference = $DB->get_records_menu('user_preferences', array('userid'=>$user->id), '', 'name,value'); // All values
+    $user->preference = $DB->get_records_menu('user_preferences', array('userid' => $user->id), '', 'name,value'); // All values.
     $user->preference['_lastloaded'] = $timenow;
 }
 
 /**
- * Called from set/unset_user_preferences, so that the prefs can
- * be correctly reloaded in different sessions.
+ * Called from set/unset_user_preferences, so that the prefs can be correctly reloaded in different sessions.
  *
  * NOTE: internal function, do not call from other code.
  *
  * @package core
- * @access  private
- * @param   integer         $userid the user whose prefs were changed.
+ * @access private
+ * @param integer $userid the user whose prefs were changed.
  */
 function mark_user_preferences_changed($userid) {
     global $CFG;
 
     if (empty($userid) or isguestuser($userid)) {
-        // no cache flags for guest and not-logged-in users
+        // No cache flags for guest and not-logged-in users.
         return;
     }
 
@@ -1782,24 +1820,25 @@ function set_user_preference($name, $value, $user = null) {
     }
 
     if (is_null($value)) {
-        // null means delete current
+        // Null means delete current.
         return unset_user_preference($name, $user);
     } else if (is_object($value)) {
         throw new coding_exception('Invalid value in set_user_preference() call, objects are not allowed');
     } else if (is_array($value)) {
         throw new coding_exception('Invalid value in set_user_preference() call, arrays are not allowed');
     }
+    // Value column maximum length is 1333 characters.
     $value = (string)$value;
-    if (textlib::strlen($value) > 1333) { //value column maximum length is 1333 characters
+    if (textlib::strlen($value) > 1333) {
         throw new coding_exception('Invalid value in set_user_preference() call, value is is too long for the value column');
     }
 
     if (is_null($user)) {
         $user = $USER;
     } else if (isset($user->id)) {
-        // $user is valid object
+        // It is a valid object.
     } else if (is_numeric($user)) {
-        $user = (object)array('id'=>(int)$user);
+        $user = (object)array('id' => (int)$user);
     } else {
         throw new coding_exception('Invalid $user parameter in set_user_preference() call');
     }
@@ -1807,17 +1846,17 @@ function set_user_preference($name, $value, $user = null) {
     check_user_preferences_loaded($user);
 
     if (empty($user->id) or isguestuser($user->id)) {
-        // no permanent storage for not-logged-in users and guest
+        // No permanent storage for not-logged-in users and guest.
         $user->preference[$name] = $value;
         return true;
     }
 
-    if ($preference = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>$name))) {
+    if ($preference = $DB->get_record('user_preferences', array('userid' => $user->id, 'name' => $name))) {
         if ($preference->value === $value and isset($user->preference[$name]) and $user->preference[$name] === $value) {
-            // preference already set to this value
+            // Preference already set to this value.
             return true;
         }
-        $DB->set_field('user_preferences', 'value', $value, array('id'=>$preference->id));
+        $DB->set_field('user_preferences', 'value', $value, array('id' => $preference->id));
 
     } else {
         $preference = new stdClass();
@@ -1827,10 +1866,10 @@ function set_user_preference($name, $value, $user = null) {
         $DB->insert_record('user_preferences', $preference);
     }
 
-    // update value in cache
+    // Update value in cache.
     $user->preference[$name] = $value;
 
-    // set reload flag for other sessions
+    // Set reload flag for other sessions.
     mark_user_preferences_changed($user->id);
 
     return true;
@@ -1878,9 +1917,9 @@ function unset_user_preference($name, $user = null) {
     if (is_null($user)) {
         $user = $USER;
     } else if (isset($user->id)) {
-        // $user is valid object
+        // It is a valid object.
     } else if (is_numeric($user)) {
-        $user = (object)array('id'=>(int)$user);
+        $user = (object)array('id' => (int)$user);
     } else {
         throw new coding_exception('Invalid $user parameter in unset_user_preference() call');
     }
@@ -1888,18 +1927,18 @@ function unset_user_preference($name, $user = null) {
     check_user_preferences_loaded($user);
 
     if (empty($user->id) or isguestuser($user->id)) {
-        // no permanent storage for not-logged-in user and guest
+        // No permanent storage for not-logged-in user and guest.
         unset($user->preference[$name]);
         return true;
     }
 
-    // delete from DB
-    $DB->delete_records('user_preferences', array('userid'=>$user->id, 'name'=>$name));
+    // Delete from DB.
+    $DB->delete_records('user_preferences', array('userid' => $user->id, 'name' => $name));
 
-    // delete the preference from cache
+    // Delete the preference from cache.
     unset($user->preference[$name]);
 
-    // set reload flag for other sessions
+    // Set reload flag for other sessions.
     mark_user_preferences_changed($user->id);
 
     return true;
@@ -1914,7 +1953,7 @@ function unset_user_preference($name, $user = null) {
  * If a name is specified then this function
  * attempts to return that particular preference value.  If
  * none is found, then the optional value $default is returned,
- * otherwise NULL.
+ * otherwise null.
  *
  * If a $user object is submitted it's 'preference' property is used for the preferences cache.
  *
@@ -1932,7 +1971,7 @@ function get_user_preferences($name = null, $default = null, $user = null) {
     global $USER;
 
     if (is_null($name)) {
-        // all prefs
+        // All prefs.
     } else if (is_numeric($name) or $name === '_lastloaded') {
         throw new coding_exception('Invalid preference name in get_user_preferences() call');
     }
@@ -1940,9 +1979,9 @@ function get_user_preferences($name = null, $default = null, $user = null) {
     if (is_null($user)) {
         $user = $USER;
     } else if (isset($user->id)) {
-        // $user is valid object
+        // Is a valid object.
     } else if (is_numeric($user)) {
-        $user = (object)array('id'=>(int)$user);
+        $user = (object)array('id' => (int)$user);
     } else {
         throw new coding_exception('Invalid $user parameter in get_user_preferences() call');
     }
@@ -1950,15 +1989,18 @@ function get_user_preferences($name = null, $default = null, $user = null) {
     check_user_preferences_loaded($user);
 
     if (empty($name)) {
-        return $user->preference; // All values
+        // All values.
+        return $user->preference;
     } else if (isset($user->preference[$name])) {
-        return $user->preference[$name]; // The single string value
+        // The single string value.
+        return $user->preference[$name];
     } else {
-        return $default; // Default value (null if not specified)
+        // Default value (null if not specified).
+        return $default;
     }
 }
 
-/// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
+// FUNCTIONS FOR HANDLING TIME.
 
 /**
  * Given date parts in user time produce a GMT timestamp.
@@ -1979,18 +2021,19 @@ function get_user_preferences($name = null, $default = null, $user = null) {
  */
 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
 
-    //save input timezone, required for dst offset check.
+    // Save input timezone, required for dst offset check.
     $passedtimezone = $timezone;
 
     $timezone = get_user_timezone_offset($timezone);
 
-    if (abs($timezone) > 13) {  //server time
+    if (abs($timezone) > 13) {
+        // Server time.
         $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
     } else {
         $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
         $time = usertime($time, $timezone);
 
-        //Apply dst for string timezones or if 99 then try dst offset with user's default timezone
+        // Apply dst for string timezones or if 99 then try dst offset with user's default timezone.
         if ($applydst && ((99 == $passedtimezone) || !is_numeric($passedtimezone))) {
             $time -= dst_offset_on($time, $passedtimezone);
         }
@@ -2013,14 +2056,15 @@ function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0,
  * @uses DAYSECS
  * @uses YEARSECS
  * @param int $totalsecs Time in seconds
- * @param object $str Should be a time object
+ * @param stdClass $str Should be a time object
  * @return string A nicely formatted date/time string
  */
- function format_time($totalsecs, $str=NULL) {
+function format_time($totalsecs, $str = null) {
 
     $totalsecs = abs($totalsecs);
 
-    if (!$str) {  // Create the str structure the slow way
+    if (!$str) {
+        // Create the str structure the slow way.
         $str = new stdClass();
         $str->day   = get_string('day');
         $str->days  = get_string('days');
@@ -2034,7 +2078,6 @@ function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0,
         $str->years = get_string('years');
     }
 
-
     $years     = floor($totalsecs/YEARSECS);
     $remainder = $totalsecs - ($years*YEARSECS);
     $days      = floor($remainder/DAYSECS);
@@ -2056,17 +2099,37 @@ function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0,
     $omins = '';
     $osecs = '';
 
-    if ($years)  $oyears  = $years .' '. $sy;
-    if ($days)  $odays  = $days .' '. $sd;
-    if ($hours) $ohours = $hours .' '. $sh;
-    if ($mins)  $omins  = $mins .' '. $sm;
-    if ($secs)  $osecs  = $secs .' '. $ss;
-
-    if ($years) return trim($oyears .' '. $odays);
-    if ($days)  return trim($odays .' '. $ohours);
-    if ($hours) return trim($ohours .' '. $omins);
-    if ($mins)  return trim($omins .' '. $osecs);
-    if ($secs)  return $osecs;
+    if ($years) {
+        $oyears  = $years .' '. $sy;
+    }
+    if ($days) {
+        $odays  = $days .' '. $sd;
+    }
+    if ($hours) {
+        $ohours = $hours .' '. $sh;
+    }
+    if ($mins) {
+        $omins  = $mins .' '. $sm;
+    }
+    if ($secs) {
+        $osecs  = $secs .' '. $ss;
+    }
+
+    if ($years) {
+        return trim($oyears .' '. $odays);
+    }
+    if ($days) {
+        return trim($odays .' '. $ohours);
+    }
+    if ($hours) {
+        return trim($ohours .' '. $omins);
+    }
+    if ($mins) {
+        return trim($omins .' '. $osecs);
+    }
+    if ($secs) {
+        return $osecs;
+    }
     return get_string('now');
 }
 
@@ -2104,7 +2167,8 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
         $format = get_string('strftimedaydatetime', 'langconfig');
     }
 
-    if (!empty($CFG->nofixday)) {  // Config.php can force %d not to be fixed.
+    if (!empty($CFG->nofixday)) {
+        // Config.php can force %d not to be fixed.
         $fixday = false;
     } else if ($fixday) {
         $formatnoday = str_replace('%d', 'DD', $format);
@@ -2124,8 +2188,8 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
         $format = $formatnohour;
     }
 
-    //add daylight saving offset for string timezones only, as we can't get dst for
-    //float values. if timezone is 99 (user default timezone), then try update dst.
+    // Add daylight saving offset for string timezones only, as we can't get dst for
+    // float values. if timezone is 99 (user default timezone), then try update dst.
     if ((99 == $timezone) || !is_numeric($timezone)) {
         $date += dst_offset_on($date, $timezone);
     }
@@ -2133,9 +2197,10 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
     $timezone = get_user_timezone_offset($timezone);
 
     // If we are running under Windows convert to windows encoding and then back to UTF-8
-    // (because it's impossible to specify UTF-8 to fetch locale info in Win32)
+    // (because it's impossible to specify UTF-8 to fetch locale info in Win32).
 
-    if (abs($timezone) > 13) {   /// Server time
+    if (abs($timezone) > 13) {
+        // Server time.
         $datestring = date_format_string($date, $format, $timezone);
         if ($fixday) {
             $daystring  = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date)));
@@ -2170,11 +2235,11 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
  *
  * This function does not do any calculation regarding the user preferences and should
  * therefore receive the final date timestamp, format and timezone. Timezone being only used
- * to differenciate the use of server time or not (strftime() against gmstrftime()).
+ * to differentiate the use of server time or not (strftime() against gmstrftime()).
  *
  * @param int $date the timestamp.
  * @param string $format strftime format.
- * @param int|float $timezone the numerical timezone, typically returned by {@link get_user_timezone_offset()}.
+ * @param int|float $tz the numerical timezone, typically returned by {@link get_user_timezone_offset()}.
  * @return string the formatted date/time.
  * @since 2.3.3
  */
@@ -2209,22 +2274,23 @@ function date_format_string($date, $format, $tz = 99) {
  * @uses HOURSECS
  * @param int $time Timestamp in GMT
  * @param float|int|string $timezone offset's time with timezone, if float and not 99, then no
- *        dst offset is applyed {@link http://docs.moodle.org/dev/Time_API#Timezone}
+ *        dst offset is applied {@link http://docs.moodle.org/dev/Time_API#Timezone}
  * @return array An array that represents the date in user time
  */
 function usergetdate($time, $timezone=99) {
 
-    //save input timezone, required for dst offset check.
+    // Save input timezone, required for dst offset check.
     $passedtimezone = $timezone;
 
     $timezone = get_user_timezone_offset($timezone);
 
-    if (abs($timezone) > 13) {    // Server time
+    if (abs($timezone) > 13) {
+        // Server time.
         return getdate($time);
     }
 
-    //add daylight saving offset for string timezones only, as we can't get dst for
-    //float values. if timezone is 99 (user default timezone), then try update dst.
+    // Add daylight saving offset for string timezones only, as we can't get dst for
+    // float values. if timezone is 99 (user default timezone), then try update dst.
     if ($passedtimezone == 99 || !is_numeric($passedtimezone)) {
         $time += dst_offset_on($time, $passedtimezone);
     }
@@ -2233,7 +2299,7 @@ function usergetdate($time, $timezone=99) {
 
     $datestring = gmstrftime('%B_%A_%j_%Y_%m_%w_%d_%H_%M_%S', $time);
 
-    //be careful to ensure the returned array matches that produced by getdate() above
+    // Be careful to ensure the returned array matches that produced by getdate() above.
     list(
         $getdate['month'],
         $getdate['weekday'],
@@ -2247,9 +2313,9 @@ function usergetdate($time, $timezone=99) {
         $getdate['seconds']
     ) = explode('_', $datestring);
 
-    // set correct datatype to match with getdate()
+    // Set correct datatype to match with getdate().
     $getdate['seconds'] = (int)$getdate['seconds'];
-    $getdate['yday'] = (int)$getdate['yday'] - 1; // gettime returns 0 through 365
+    $getdate['yday'] = (int)$getdate['yday'] - 1; // The function gmstrftime returns 0 through 365.
     $getdate['year'] = (int)$getdate['year'];
     $getdate['mon'] = (int)$getdate['mon'];
     $getdate['wday'] = (int)$getdate['wday'];
@@ -2298,7 +2364,7 @@ function usergetmidnight($date, $timezone=99) {
 
     $userdate = usergetdate($date, $timezone);
 
-    // Time of midnight of this user's day, in GMT
+    // Time of midnight of this user's day, in GMT.
     return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone);
 
 }
@@ -2321,22 +2387,21 @@ function usertimezone($timezone=99) {
         return $tz;
     }
 
-    if(abs($tz) > 13) { // Server time
+    if (abs($tz) > 13) {
+        // Server time.
         return get_string('serverlocaltime');
     }
 
-    if($tz == intval($tz)) {
-        // Don't show .0 for whole hours
+    if ($tz == intval($tz)) {
+        // Don't show .0 for whole hours.
         $tz = intval($tz);
     }
 
-    if($tz == 0) {
+    if ($tz == 0) {
         return 'UTC';
-    }
-    else if($tz > 0) {
+    } else if ($tz > 0) {
         return 'UTC+'.$tz;
-    }
-    else {
+    } else {
         return 'UTC'.$tz;
     }
 
@@ -2354,9 +2419,6 @@ function usertimezone($timezone=99) {
  * @return float
  */
 function get_user_timezone_offset($tz = 99) {
-
-    global $USER, $CFG;
-
     $tz = get_user_timezone($tz);
 
     if (is_float($tz)) {
@@ -2380,8 +2442,6 @@ function get_user_timezone_offset($tz = 99) {
  * @return int|bool if found, false is timezone 99 or error
  */
 function get_timezone_offset($tz) {
-    global $CFG;
-
     if ($tz == 99) {
         return false;
     }
@@ -2421,8 +2481,8 @@ function get_user_timezone($tz = 99) {
 
     $tz = 99;
 
-    // Loop while $tz is, empty but not zero, or 99, and there is another timezone is the array
-    while(((empty($tz) && !is_numeric($tz)) || $tz == 99) && $next = each($timezones)) {
+    // Loop while $tz is, empty but not zero, or 99, and there is another timezone is the array.
+    while (((empty($tz) && !is_numeric($tz)) || $tz == 99) && $next = each($timezones)) {
         $tz = $next['value'];
     }
     return is_numeric($tz) ? (float) $tz : $tz;
@@ -2436,10 +2496,10 @@ function get_user_timezone($tz = 99) {
  * @return stdClass|bool timezonerecord or false
  */
 function get_timezone_record($timezonename) {
-    global $CFG, $DB;
-    static $cache = NULL;
+    global $DB;
+    static $cache = null;
 
-    if ($cache === NULL) {
+    if ($cache === null) {
         $cache = array();
     }
 
@@ -2455,128 +2515,129 @@ function get_timezone_record($timezonename) {
  * Build and store the users Daylight Saving Time (DST) table
  *
  * @package core
- * @param int $from_year Start year for the table, defaults to 1971
- * @param int $to_year End year for the table, defaults to 2035
- * @param int|float|string $strtimezone, timezone to check if dst should be applyed.
+ * @param int $fromyear Start year for the table, defaults to 1971
+ * @param int $toyear End year for the table, defaults to 2035
+ * @param int|float|string $strtimezone timezone to check if dst should be applied.
  * @return bool
  */
-function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) {
-    global $CFG, $SESSION, $DB;
+function calculate_user_dst_table($fromyear = null, $toyear = null, $strtimezone = null) {
+    global $SESSION, $DB;
 
     $usertz = get_user_timezone($strtimezone);
 
     if (is_float($usertz)) {
-        // Trivial timezone, no DST
+        // Trivial timezone, no DST.
         return false;
     }
 
     if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) {
-        // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
+        // We have pre-calculated values, but the user's effective TZ has changed in the meantime, so reset.
         unset($SESSION->dst_offsets);
         unset($SESSION->dst_range);
     }
 
-    if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) {
-        // Repeat calls which do not request specific year ranges stop here, we have already calculated the table
-        // This will be the return path most of the time, pretty light computationally
+    if (!empty($SESSION->dst_offsets) && empty($fromyear) && empty($toyear)) {
+        // Repeat calls which do not request specific year ranges stop here, we have already calculated the table.
+        // This will be the return path most of the time, pretty light computationally.
         return true;
     }
 
-    // Reaching here means we either need to extend our table or create it from scratch
+    // Reaching here means we either need to extend our table or create it from scratch.
 
-    // Remember which TZ we calculated these changes for
+    // Remember which TZ we calculated these changes for.
     $SESSION->dst_offsettz = $usertz;
 
-    if(empty($SESSION->dst_offsets)) {
-        // If we 're creating from scratch, put the two guard elements in there
-        $SESSION->dst_offsets = array(1 => NULL, 0 => NULL);
+    if (empty($SESSION->dst_offsets)) {
+        // If we 're creating from scratch, put the two guard elements in there.
+        $SESSION->dst_offsets = array(1 => null, 0 => null);
     }
-    if(empty($SESSION->dst_range)) {
-        // If creating from scratch
-        $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);
-        $to   = min((empty($to_year)   ? intval(date('Y')) + 3 : $to_year),   2035);
+    if (empty($