Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of git://github.com/moodle/moodle

  • Loading branch information...
commit 372a0cf82e0323694f68419f62a5fee2f088b238 2 parents c00207c + 216f6d8
@kordan kordan authored
Showing with 8,078 additions and 2,927 deletions.
  1. +3 −1 admin/settings/subsystems.php
  2. +1 −1  backup/util/helper/restore_decode_processor.class.php
  3. +1 −1  backup/util/loggers/file_logger.class.php
  4. +5 −2 blocks/navigation/renderer.php
  5. +1 −1  calendar/lib.php
  6. +26 −0 config-dist.php
  7. +2 −2 course/format/topics/format.php
  8. +1 −1  course/lib.php
  9. +20 −24 enrol/externallib.php
  10. +1 −1  grade/grading/lib.php
  11. +2 −2 install/lang/de/install.php
  12. +2 −1  install/lang/de_du/install.php
  13. +1 −1  install/lang/de_kids/langconfig.php
  14. +1 −2  install/lang/he/install.php
  15. +2 −0  lang/en/admin.php
  16. +1 −0  lang/en/error.php
  17. +2 −2 lang/en/moodle.php
  18. +1 −0  lang/en/question.php
  19. +3,584 −0 lib/csslib.php
  20. +1 −4 lib/enrollib.php
  21. +25 −22 lib/form/dndupload.js
  22. +10 −3 lib/form/filemanager.js
  23. +2 −2 lib/form/filemanager.php
  24. +89 −47 lib/moodlelib.php
  25. +27 −4 lib/navigationlib.php
  26. +1 −1  lib/outputcomponents.php
  27. +1 −1  lib/outputrenderers.php
  28. +1 −1  lib/portfoliolib.php
  29. +2 −2 lib/simpletest/testcompletionlib.php
  30. +706 −0 lib/simpletest/testcsslib.php
  31. +2 −1  lib/simpletest/testfilterconfig.php
  32. +2,149 −2,096 lib/timezone.txt
  33. +2 −6 message/lib.php
  34. +4 −3 mod/forum/lib.php
  35. +9 −9 mod/glossary/lib.php
  36. +4 −0 mod/quiz/locallib.php
  37. +1 −1  mod/quiz/renderer.php
  38. +4 −0 mod/quiz/report/reportlib.php
  39. +13 −0 mod/resource/lang/en/resource.php
  40. +39 −14 mod/resource/lib.php
  41. +69 −3 mod/resource/locallib.php
  42. +21 −0 mod/resource/mod_form.php
  43. +6 −0 mod/resource/settings.php
  44. +4 −0 mod/resource/styles.css
  45. +26 −0 mod/workshop/form/rubric/backup/moodle1/lib.php
  46. +5 −5 question/behaviour/adaptive/simpletest/testwalkthrough.php
  47. +1 −1  question/behaviour/immediatecbm/simpletest/testwalkthrough.php
  48. +3 −3 question/behaviour/interactive/simpletest/testwalkthrough.php
  49. +6 −10 question/editlib.php
  50. +6 −2 question/engine/questionattempt.php
  51. +1 −0  question/engine/questionusage.php
  52. +0 −23 question/engine/simpletest/helpers.php
  53. +3 −1 question/preview.php
  54. +1 −0  question/type/calculated/lang/en/qtype_calculated.php
  55. +28 −8 question/type/calculated/questiontype.php
  56. +37 −0 question/type/calculated/simpletest/helper.php
  57. +20 −0 question/type/calculated/simpletest/testquestion.php
  58. +107 −0 question/type/calculated/simpletest/testquestiontype.php
  59. +5 −1 question/type/match/backup/moodle1/lib.php
  60. +4 −3 question/type/numerical/question.php
  61. +10 −1 question/type/numerical/questiontype.php
  62. +1 −2  question/type/numerical/simpletest/helper.php
  63. +35 −0 question/type/numerical/simpletest/testquestion.php
  64. +16 −1 question/type/numerical/simpletest/testquestiontype.php
  65. +6 −3 question/type/questionbase.php
  66. +3 −17 question/type/questiontypebase.php
  67. +6 −6 question/type/randomsamatch/lang/en/qtype_randomsamatch.php
  68. +10 −0 question/type/shortanswer/questiontype.php
  69. +133 −0 question/type/shortanswer/simpletest/helper.php
  70. +23 −8 question/type/shortanswer/simpletest/testquestion.php
  71. +22 −11 question/type/shortanswer/simpletest/testquestiontype.php
  72. +1 −1  question/type/truefalse/simpletest/testquestiontype.php
  73. +4 −0 question/type/upgrade.txt
  74. +3 −0  repository/draftfiles_ajax.php
  75. +1 −0  theme/afterburner/config.php
  76. +2 −1  theme/afterburner/style/afterburner_dock.css
  77. +3 −0  theme/afterburner/style/afterburner_settings.css
  78. +1 −5 theme/afterburner/style/afterburner_styles.css
  79. +1 −1  theme/formal_white/config.php
  80. +2 −0  theme/formal_white/lang/en/theme_formal_white.php
  81. +12 −10 theme/formal_white/layout/embedded.php
  82. +114 −106 theme/formal_white/layout/frontpage.php
  83. +119 −112 theme/formal_white/layout/general.php
  84. +193 −0 theme/formal_white/layout/report.php
  85. +26 −1 theme/formal_white/lib.php
  86. +9 −0 theme/formal_white/settings.php
  87. +28 −7 theme/formal_white/style/formal_white.css
  88. +2 −2 theme/formal_white/style/frame.css
  89. +3 −1 theme/formal_white/style/quiz.css
  90. +1 −1  theme/formal_white/version.php
  91. +13 −23 theme/magazine/config.php
  92. +3 −4 theme/magazine/style/colors.css
  93. +3 −36 theme/magazine/style/core.css
  94. +4 −17 theme/sky_high/config.php
  95. +43 −34 theme/sky_high/layout/frontpage.php
  96. +53 −45 theme/sky_high/layout/general.php
  97. +35 −14 theme/sky_high/style/core.css
  98. +5 −0 theme/sky_high/style/settings.css
  99. +7 −92 theme/styles.php
  100. +12 −42 theme/styles_debug.php
  101. +1 −1  user/externallib.php
  102. +2 −2 version.php
  103. +5 −1 webservice/rest/locallib.php
View
4 admin/settings/subsystems.php
@@ -43,4 +43,6 @@
$checkbox->set_affects_modinfo(true);
$optionalsubsystems->add(new admin_setting_configcheckbox('enableplagiarism', new lang_string('enableplagiarism','plagiarism'), new lang_string('configenableplagiarism','plagiarism'), 0));
-}
+
+ $optionalsubsystems->add(new admin_setting_configcheckbox('enablecssoptimiser', new lang_string('enablecssoptimiser','admin'), new lang_string('enablecssoptimiser_desc','admin'), 0));
+}
View
2  backup/util/helper/restore_decode_processor.class.php
@@ -63,7 +63,7 @@ public function add_content($content) {
public function add_rule($rule) {
if (!$rule instanceof restore_decode_rule) {
- throw new restore_decode_processor_exception('incorrect_restore_decode_rule', get_class($content));
+ throw new restore_decode_processor_exception('incorrect_restore_decode_rule', get_class($rule));
}
$rule->set_restoreid($this->restoreid);
$rule->set_wwwroots($this->sourcewwwroot, $this->targetwwwroot);
View
2  backup/util/loggers/file_logger.class.php
@@ -61,7 +61,7 @@ public function __sleep() {
public function __wakeup() {
if ($this->level > backup::LOG_NONE) { // Only create the file if we are going to log something
if (! $this->fhandle = fopen($this->fullpath, 'a')) {
- throw new base_logger_exception('error_opening_file', $fullpath);
+ throw new base_logger_exception('error_opening_file', $this->fullpath);
}
}
}
View
7 blocks/navigation/renderer.php
@@ -30,12 +30,14 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
$isexpandable = (empty($expansionlimit) || ($item->type > navigation_node::TYPE_ACTIVITY || $item->type < $expansionlimit) || ($item->contains_active_node() && $item->children->count() > 0));
$isbranch = $isexpandable && ($item->children->count() > 0 || ($item->has_children() && (isloggedin() || $item->type <= navigation_node::TYPE_CATEGORY)));
- $hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY )&& $item->icon instanceof renderable);
+ $hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY || $item->type == navigation_node::TYPE_RESOURCE) && $item->icon instanceof renderable);
if ($hasicon) {
$icon = $this->output->render($item->icon);
- $content = $icon.$content; // use CSS for spacing of icons
+ } else {
+ $icon = '';
}
+ $content = $icon.$content; // use CSS for spacing of icons
if ($item->helpbutton !== null) {
$content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
}
@@ -57,6 +59,7 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
} else if ($item->action instanceof action_link) {
//TODO: to be replaced with something else
$link = $item->action;
+ $link->text = $icon.$link->text;
$link->attributes = array_merge($link->attributes, $attributes);
$content = $this->output->render($link);
$linkrendered = true;
View
2  calendar/lib.php
@@ -1796,7 +1796,7 @@ public function __isset($key) {
* @return stdClass
*/
protected function calculate_context(stdClass $data) {
- global $USER;
+ global $USER, $DB;
$context = null;
if (isset($data->courseid) && $data->courseid > 0) {
View
26 config-dist.php
@@ -385,6 +385,32 @@
//
// $CFG->extramemorylimit = 1G;
//
+// The CSS files the Moodle produces can be extremely large and complex, especially
+// if you are using a custom theme that builds upon several other themes.
+// In Moodle 2.3 a CSS optimiser was added as an experimental feature for advanced
+// users. The CSS optimiser organises the CSS in order to reduce the overall number
+// of rules and styles being sent to the client. It does this by collating the
+// CSS before it is cached removing excess styles and rules and stripping out any
+// extraneous content such as comments and empty rules.
+// The following settings are used to enable and control the optimisation.
+//
+// Enable the CSS optimiser. This will only optimise the CSS if themedesignermode
+// is not enabled. This can be set through the UI however it is noted here as well
+// because the other CSS optimiser settings can not be set through the UI.
+//
+// $CFG->enablecssoptimiser = true;
+//
+// If set the CSS optimiser will add stats about the optimisation to the top of
+// the optimised CSS file. You can then inspect the CSS to see the affect the CSS
+// optimiser is having.
+//
+// $CFG->cssoptimiserstats = true;
+//
+// If set the CSS that is optimised will still retain a minimalistic formatting
+// so that anyone wanting to can still clearly read it.
+//
+// $CFG->cssoptimiserpretty = true;
+//
//=========================================================================
// 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//=========================================================================
View
4 course/format/topics/format.php
@@ -189,9 +189,9 @@
if ($PAGE->user_is_editing() && has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id))) {
if ($course->marker == $section) { // Show the "light globe" on/off
- echo '<a href="view.php?id='.$course->id.'&amp;marker=0&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkedthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marked') . '" alt="'.$strmarkedthistopic.'" /></a><br />';
+ echo '<a href="view.php?id='.$course->id.'&amp;marker=0&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkedthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marked') . '" alt="'.$strmarkedthistopic.'" class="icon"/></a><br />';
} else {
- echo '<a href="view.php?id='.$course->id.'&amp;marker='.$section.'&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marker') . '" alt="'.$strmarkthistopic.'" /></a><br />';
+ echo '<a href="view.php?id='.$course->id.'&amp;marker='.$section.'&amp;sesskey='.sesskey().'#section-'.$section.'" title="'.$strmarkthistopic.'">'.'<img src="'.$OUTPUT->pix_url('i/marker') . '" alt="'.$strmarkthistopic.'" class="icon"/></a><br />';
}
if ($thissection->visible) { // Show the hide/show eye
View
2  course/lib.php
@@ -818,7 +818,7 @@ function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
- $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $context)));
+ $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)));
$myxls->write_date($row, 1, $log->time);
$myxls->write_string($row, 2, $log->ip);
$fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
View
44 enrol/externallib.php
@@ -172,26 +172,8 @@ public static function get_enrolled_users($courseid, $options) {
}
}
- // to overwrite this parameter, you need role:review capability
- if ($withcapability) {
- require_capability('moodle/role:review', $coursecontext);
- }
- // need accessallgroups capability if you want to overwrite this option
- if (!empty($groupid) && groups_is_member($groupid)) {
- require_capability('moodle/site:accessallgroups', $context);
- }
- // to overwrite this option, you need course:enrolereview permission
- if ($onlyactive) {
- require_capability('moodle/course:enrolreview', $coursecontext);
- }
-
- list($coursectxselect, $coursectxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
- $coursesql = "SELECT c.* $coursectxselect
- FROM {course} c $coursectxjoin
- WHERE c.id = $courseid";
- $course = $DB->get_record_sql($coursesql);
- context_instance_preload($course);
- $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']);
+ $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
if ($courseid == SITEID) {
$context = get_system_context();
} else {
@@ -206,9 +188,26 @@ public static function get_enrolled_users($courseid, $options) {
throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
}
+ if ($courseid == SITEID) {
+ require_capability('moodle/site:viewparticipants', $context);
+ } else {
+ require_capability('moodle/course:viewparticipants', $context);
+ }
+ // to overwrite this parameter, you need role:review capability
+ if ($withcapability) {
+ require_capability('moodle/role:review', $coursecontext);
+ }
+ // need accessallgroups capability if you want to overwrite this option
+ if (!empty($groupid) && groups_is_member($groupid)) {
+ require_capability('moodle/site:accessallgroups', $coursecontext);
+ }
+ // to overwrite this option, you need course:enrolereview permission
+ if ($onlyactive) {
+ require_capability('moodle/course:enrolreview', $coursecontext);
+ }
+
list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
list($ctxselect, $ctxjoin) = context_instance_preload_sql('u.id', CONTEXT_USER, 'ctx');
- $records = $DB->get_records_sql($enrolledsql, $enrolledparams);
$sqlparams['courseid'] = $courseid;
$sql = "SELECT u.* $ctxselect
FROM {user} u $ctxjoin
@@ -217,9 +216,6 @@ public static function get_enrolled_users($courseid, $options) {
$enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams);
$users = array();
foreach ($enrolledusers as $user) {
- if (!empty($user->deleted)) {
- continue;
- }
context_instance_preload($user);
if ($userdetails = user_get_user_details($user, $course, $userfields)) {
$users[] = $userdetails;
View
2  grade/grading/lib.php
@@ -461,7 +461,7 @@ public function extend_settings_navigation(settings_navigation $settingsnav, nav
* @return grading_controller
*/
public function get_controller($method) {
- global $CFG;
+ global $CFG, $DB;
$this->ensure_isset(array('context', 'component', 'area'));
View
4 install/lang/de/install.php
@@ -33,7 +33,7 @@
$string['admindirname'] = 'Admin-Verzeichnis';
$string['availablelangs'] = 'Verfügbare Sprachpakete';
$string['chooselanguagehead'] = 'Sprache wählen';
-$string['chooselanguagesub'] = 'Wählen Sie eine Sprache, die Sie während der Installation verwenden wollen. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber können Sie die Sprache jederzeit ändern.';
+$string['chooselanguagesub'] = 'Wählen Sie eine Sprache, die Sie während der Installation verwenden wollen. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber Sie dürfen die Sprache jederzeit ändern.';
$string['clialreadyconfigured'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/install_database.php, wenn sie diese Website installieren möchten.';
$string['clialreadyinstalled'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/upgrade.php, wenn Sie diese Website aktualisieren möchten.';
$string['cliinstallheader'] = 'Installation von Moodle {$a} über die Kommandozeile';
@@ -45,7 +45,7 @@
$string['dbprefix'] = 'Tabellen-Prefix';
$string['dirroot'] = 'Moodle-Verzeichnis';
$string['environmenthead'] = 'Installationsvoraussetzungen werden geprüft ...';
-$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für einige verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird immer eine vollständige Prüfung der Serverausstattung durchgeführt. Bitte fragen Sie den Administrator Ihres Servers, wenn Sie mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommen.';
+$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird eine vollständige Prüfung durchgeführt. Bitte fragen Sie den Server-Administrator, wenn Sie mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommen.';
$string['errorsinenvironment'] = 'Fehler bei der Prüfung der Systemvoraussetzungen!';
$string['installation'] = 'Installation';
$string['langdownloaderror'] = 'Leider konnte das Sprachpaket \'{$a}\' nicht heruntergeladen werden. Die Installation wird in englischer Sprache fortgesetzt.';
View
3  install/lang/de_du/install.php
@@ -30,7 +30,8 @@
defined('MOODLE_INTERNAL') || die();
-$string['chooselanguagesub'] = 'Wähle eine Sprache, die du während der Installation verwenden möchtest. Nach der Installation kannst du die Sprache für die Oberfläche und die Nutzer/innen festlegen.';
+$string['chooselanguagesub'] = 'Wähle eine Sprache, die du während der Installation verwenden möchtest. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber du darfst die Sprache jederzeit ändern.';
+$string['environmentsub2'] = 'Jede Moodle-Version hat Mindestvoraussetzungen für der PHP-Version und für verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird eine vollständige Prüfung durchgeführt. Bitte frage den Server-Administrator, wenn du mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommst.';
$string['memorylimithelp'] = '<p>Die PHP-Einstellung memory_limit für deinen Server ist zur Zeit auf {$a} eingestellt. </p>
<p>Dies wird vermutlich zu Problemen führen, wenn du Moodle mit vielen Aktivitäten oder vielen Nutzer/innen verwendst. </p>
<p>Wir empfehlen die Einstellung zu erhöhen. Empfohlen werden 40M oder mehr. Dies kannst du auf verschiedene Arten machen:</p>
View
2  install/lang/de_kids/langconfig.php
@@ -31,5 +31,5 @@
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'de_du';
-$string['thisdirection'] = '';
+$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Deutsch - Kids';
View
3  install/lang/he/install.php
@@ -92,8 +92,7 @@
(במקרים של גרסת 5.0.x תוכל גם לרדת בגירסה ל- 4.4.x)
</p>';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
-$string['welcomep20'] = 'הינך רואה את עמוד זה מפני שהתקנת והפעלת בהלכה את <strong> $a-packname {$a->packversion}
-</strong>
+$string['welcomep20'] = 'הינך רואה את עמוד זה מפני שהתקנת והפעלת בהלכה את <strong>{$a->packname} {$a->packversion}</strong>
חבילה במחשבך. ברכותינו!';
$string['welcomep30'] = 'גירסת <strong>{$a->installername}</strong> כוללת את היישומים ליצור סביבה אשר בה <strong> Moodle </strong>
יפעל דהיינו:';
View
2  lang/en/admin.php
@@ -466,6 +466,8 @@
$string['enablecourseajax'] = 'Enable AJAX course editing';
$string['enablecourseajax_desc'] = 'Allow AJAX when editing main course pages. Note that the course format and the theme must support AJAX editing and the user has to enable AJAX in their profiles, too.';
$string['enablecourserequests'] = 'Enable course requests';
+$string['enablecssoptimiser'] = 'Enable CSS optimiser';
+$string['enablecssoptimiser_desc'] = 'When enabled CSS will be run through an optimisation process before being cached. The optimiser processes the CSS removing duplicate rules and styles, as well as white space removeable and reformatting. Please note turning this on at the same time as theme designer mode is aweful for performance but will help theme designers create optimised CSS.';
$string['enabledevicedetection'] = 'Enable device detection';
$string['enablegravatar'] = 'Enable Gravatar';
$string['enablegravatar_help'] = 'When enabled Moodle will attempt to fetch a user profile picture from Gravatar if the user has not uploaded an image.';
View
1  lang/en/error.php
@@ -435,6 +435,7 @@
$string['secretalreadyused'] = 'Change password confirmation link was already used, password was not changed';
$string['sectionnotexist'] = 'This section does not exist';
$string['sendmessage'] = 'Send message';
+$string['serverconnection'] = 'Error connecting to the server';
$string['servicedonotexist'] = 'The service does not exist';
$string['sessionwaiterr'] = 'Timed out while waiting for session lock.<br />Wait for your current requests to finish and try again later.';
$string['sessioncookiesdisable'] = 'Incorrect use of require_key_login() - session cookies must be disabled!';
View
4 lang/en/moodle.php
@@ -451,9 +451,9 @@
$string['displayingrecords'] = 'Displaying {$a} records';
$string['displayingusers'] = 'Displaying users {$a->start} to {$a->end}';
$string['displayonpage'] = 'Display on page';
-$string['dndenabled'] = 'You can drag and drop files into this box to upload them';
+$string['dndenabled'] = 'Drag and drop available';
$string['dndenabled_help'] = 'You can drag one or more files from your desktop and drop them onto the box below to upload them.<br />Note: this may not work with other web browsers';
-$string['dndenabled_single'] = 'you can drag and drop a file into this box to upload it';
+$string['dndenabled_insentence'] = 'drag and drop available';
$string['documentation'] = 'Moodle documentation';
$string['down'] = 'Down';
$string['download'] = 'Download';
View
1  lang/en/question.php
@@ -99,6 +99,7 @@
$string['deletequestionscheck'] = 'Are you absolutely sure you want to delete the following questions?<br /><br />{$a}';
$string['deletingbehaviour'] = 'Deleting question behaviour \'{$a}\'';
$string['deletingqtype'] = 'Deleting question type \'{$a}\'';
+$string['didnotmatchanyanswer'] = '[Did not match any answer]';
$string['disabled'] = 'Disabled';
$string['disterror'] = 'The distribution {$a} caused problems';
$string['donothing'] = 'Don\'t copy or move files or change links.';
View
3,584 lib/csslib.php
3,584 additions, 0 deletions not shown
View
5 lib/enrollib.php
@@ -1039,10 +1039,7 @@ public function get_description_text($instance) {
protected function load_config() {
if (!isset($this->config)) {
$name = $this->get_name();
- if (!$config = get_config("enrol_$name")) {
- $config = new stdClass();
- }
- $this->config = $config;
+ $this->config = get_config("enrol_$name");
}
}
View
47 lib/form/dndupload.js
@@ -163,13 +163,13 @@ M.form_dndupload = {
return false;
}
+ e.preventDefault();
+ e.stopPropagation();
+
if (this.reached_maxfiles()) {
return false;
}
- e.preventDefault();
- e.stopPropagation();
-
return true;
},
@@ -324,7 +324,6 @@ M.form_dndupload = {
update_filemanager: function() {
if (this.filemanager) {
// update the filemanager that we've uploaded the files
- this.hide_progress_spinner();
this.filemanager.filepicker_callback();
}
},
@@ -335,6 +334,7 @@ M.form_dndupload = {
upload_file: function(file) {
if (file.size > this.maxbytes && this.maxbytes > 0) {
// Check filesize before attempting to upload
+ this.hide_progress_spinner();
alert(M.util.get_string('uploadformlimit', 'moodle')+"\n'"+file.name+"'");
return false;
}
@@ -348,26 +348,29 @@ M.form_dndupload = {
var xhr = new XMLHttpRequest();
var self = this;
xhr.onreadystatechange = function() { // Process the server response
- if (xhr.readyState == 4 && xhr.status == 200) {
- var result = JSON.parse(xhr.responseText);
- if (result) {
- if (result.error) {
- self.hide_progress_spinner();
- alert(result.error);
- } else if (self.callback) {
- // Only update the filepicker if there were no errors
- self.hide_progress_spinner();
- if (result.event == 'fileexists') {
- // Do not worry about this, as we only care about the last
- // file uploaded, with the filepicker
- result.file = result.newfile.filename;
- result.url = result.newfile.url;
+ if (xhr.readyState == 4) {
+ self.hide_progress_spinner();
+ if (xhr.status == 200) {
+ var result = JSON.parse(xhr.responseText);
+ if (result) {
+ if (result.error) {
+ alert(result.error);
+ } else if (self.callback) {
+ // Only update the filepicker if there were no errors
+ if (result.event == 'fileexists') {
+ // Do not worry about this, as we only care about the last
+ // file uploaded, with the filepicker
+ result.file = result.newfile.filename;
+ result.url = result.newfile.url;
+ }
+ result.client_id = self.clientid;
+ self.callback(result);
+ } else {
+ self.update_filemanager();
}
- result.client_id = self.clientid;
- self.callback(result);
- } else {
- self.update_filemanager();
}
+ } else {
+ alert(M.util.get_string('serverconnection', 'error'));
}
}
};
View
13 lib/form/filemanager.js
@@ -76,7 +76,7 @@ M.form_filemanager.init = function(Y, options) {
this.filecount = 0;
}
this.setup_buttons();
- this.render();
+ this.refresh(this.currentpath); // MDL-31113 get latest list from server
},
wait: function(client_id) {
@@ -141,15 +141,18 @@ M.form_filemanager.init = function(Y, options) {
}
},
filepicker_callback: function(obj) {
- var button_addfile = Y.one("#btnadd-"+this.client_id);
this.filecount++;
+ this.check_buttons();
+ this.refresh(this.currentpath);
+ },
+ check_buttons: function() {
+ var button_addfile = Y.one("#btnadd-"+this.client_id);
if (this.filecount > 0) {
Y.one("#btndwn-"+this.client_id).setStyle('display', 'inline');
}
if (this.filecount >= this.maxfiles && this.maxfiles!=-1) {
button_addfile.setStyle('display', 'none');
}
- this.refresh(this.currentpath);
},
refresh: function(filepath) {
var scope = this;
@@ -164,6 +167,8 @@ M.form_filemanager.init = function(Y, options) {
scope: scope,
params: {'filepath':filepath},
callback: function(id, obj, args) {
+ scope.filecount = obj.filecount;
+ scope.check_buttons();
scope.options = obj;
scope.render(obj);
}
@@ -298,6 +303,8 @@ M.form_filemanager.init = function(Y, options) {
scope: scope,
params: params,
callback: function(id, obj, args) {
+ scope.filecount = obj.filecount;
+ scope.check_buttons();
scope.options = obj;
scope.render(obj);
}
View
4 lib/form/filemanager.php
@@ -274,7 +274,7 @@ function form_filemanager_render($options) {
}
$maxsize = get_string('maxfilesize', 'moodle', display_size(get_max_upload_file_size($CFG->maxbytes, $course_maxbytes, $options->maxbytes)));
- $strdndenabled = get_string('dndenabled', 'moodle').$OUTPUT->help_icon('dndenabled');
+ $strdndenabled = get_string('dndenabled_insentence', 'moodle').$OUTPUT->help_icon('dndenabled');
$html .= <<<FMHTML
<div class="filemanager-loading mdl-align" id='filemanager-loading-{$client_id}'>
$icon_progress
@@ -315,7 +315,7 @@ function form_filemanager_render($options) {
array('cannotdeletefile', 'error'), array('confirmdeletefile', 'repository'),
array('nopathselected', 'repository'), array('popupblockeddownload', 'repository'),
array('draftareanofiles', 'repository'), array('path', 'moodle'), array('setmainfile', 'repository'),
- array('moving', 'repository'), array('files', 'moodle')
+ array('moving', 'repository'), array('files', 'moodle'), array('serverconnection', 'error')
)
);
$PAGE->requires->js_module($module);
View
136 lib/moodlelib.php
@@ -1318,7 +1318,7 @@ function get_config($plugin, $name = NULL) {
if ($localcfg) {
return (object)$localcfg;
} else {
- return null;
+ return new stdClass();
}
} else {
@@ -1815,17 +1815,19 @@ function get_user_preferences($name = null, $default = null, $user = null) {
/**
* Given date parts in user time produce a GMT timestamp.
*
- * @todo Finish documenting this function
+ * @package core
+ * @category time
* @param int $year The year part to create timestamp of
* @param int $month The month part to create timestamp of
* @param int $day The day part to create timestamp of
* @param int $hour The hour part to create timestamp of
* @param int $minute The minute part to create timestamp of
* @param int $second The second part to create timestamp of
- * @param mixed $timezone Timezone modifier, if 99 then use default user's timezone
+ * @param int|float|string $timezone Timezone modifier, used to calculate GMT time offset.
+ * if 99 then default user's timezone is used {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @param bool $applydst Toggle Daylight Saving Time, default true, will be
* applied only if timezone is 99 or string.
- * @return int timestamp
+ * @return int GMT timestamp
*/
function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
@@ -1856,6 +1858,8 @@ function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0,
* Given an amount of time in seconds, returns string
* formatted nicely as weeks, days, hours etc as needed
*
+ * @package core
+ * @category time
* @uses MINSECS
* @uses HOURSECS
* @uses DAYSECS
@@ -1931,13 +1935,16 @@ function format_time($totalsecs, $str=NULL) {
* If parameter fixday = true (default), then take off leading
* zero from %d, else maintain it.
*
+ * @package core
+ * @category time
* @param int $date the timestamp in UTC, as obtained from the database.
* @param string $format strftime format. You should probably get this using
- * get_string('strftime...', 'langconfig');
- * @param mixed $timezone by default, uses the user's time zone. if numeric and
- * not 99 then daylight saving will not be added.
+ * get_string('strftime...', 'langconfig');
+ * @param int|float|string $timezone by default, uses the user's time zone. if numeric and
+ * not 99 then daylight saving will not be added.
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @param bool $fixday If true (default) then the leading zero from %d is removed.
- * If false then the leading zero is maintained.
+ * If false then the leading zero is maintained.
* @return string the formatted date/time.
*/
function userdate($date, $format = '', $timezone = 99, $fixday = true) {
@@ -2001,11 +2008,12 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true) {
* Given a $time timestamp in GMT (seconds since epoch),
* returns an array that represents the date in user time
*
- * @todo Finish documenting this function
+ * @package core
+ * @category time
* @uses HOURSECS
* @param int $time Timestamp in GMT
- * @param mixed $timezone offset time with timezone, if float and not 99, then no
- * dst offset is applyed
+ * @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}
* @return array An array that represents the date in user time
*/
function usergetdate($time, $timezone=99) {
@@ -2050,9 +2058,13 @@ function usergetdate($time, $timezone=99) {
* Given a GMT timestamp (seconds since epoch), offsets it by
* the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds
*
+ * @package core
+ * @category time
* @uses HOURSECS
- * @param int $date Timestamp in GMT
- * @param float $timezone
+ * @param int $date Timestamp in GMT
+ * @param float|int|string $timezone timezone to calculate GMT time offset before
+ * calculating user time, 99 is default user timezone
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @return int
*/
function usertime($date, $timezone=99) {
@@ -2069,8 +2081,12 @@ function usertime($date, $timezone=99) {
* Given a time, return the GMT timestamp of the most recent midnight
* for the current user.
*
+ * @package core
+ * @category time
* @param int $date Timestamp in GMT
- * @param float $timezone Defaults to user's timezone
+ * @param float|int|string $timezone timezone to calculate GMT time offset before
+ * calculating user midnight time, 99 is default user timezone
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @return int Returns a GMT timestamp
*/
function usergetmidnight($date, $timezone=99) {
@@ -2085,7 +2101,11 @@ function usergetmidnight($date, $timezone=99) {
/**
* Returns a string that prints the user's timezone
*
- * @param float $timezone The user's timezone
+ * @package core
+ * @category time
+ * @param float|int|string $timezone timezone to calculate GMT time offset before
+ * calculating user timezone, 99 is default user timezone
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @return string
*/
function usertimezone($timezone=99) {
@@ -2121,9 +2141,11 @@ function usertimezone($timezone=99) {
* Returns a float which represents the user's timezone difference from GMT in hours
* Checks various settings and picks the most dominant of those which have a value
*
- * @global object
- * @global object
- * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
+ * @package core
+ * @category time
+ * @param float|int|string $tz timezone to calculate GMT time offset for user,
+ * 99 is default user timezone
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @return float
*/
function get_user_timezone_offset($tz = 99) {
@@ -2146,9 +2168,11 @@ function get_user_timezone_offset($tz = 99) {
/**
* Returns an int which represents the systems's timezone difference from GMT in seconds
*
- * @global object
- * @param mixed $tz timezone
- * @return int if found, false is timezone 99 or error
+ * @package core
+ * @category time
+ * @param float|int|string $tz timezone for which offset is required.
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
+ * @return int|bool if found, false is timezone 99 or error
*/
function get_timezone_offset($tz) {
global $CFG;
@@ -2173,10 +2197,12 @@ function get_timezone_offset($tz) {
* means that for this timezone there are also DST rules to be taken into account
* Checks various settings and picks the most dominant of those which have a value
*
- * @global object
- * @global object
- * @param mixed $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
- * @return mixed
+ * @package core
+ * @category time
+ * @param float|int|string $tz timezone to calculate GMT time offset before
+ * calculating user timezone, 99 is default user timezone
+ * {@link http://docs.moodle.org/dev/Time_API#Timezone}
+ * @return float|string
*/
function get_user_timezone($tz = 99) {
global $USER, $CFG;
@@ -2200,10 +2226,9 @@ function get_user_timezone($tz = 99) {
/**
* Returns cached timezone record for given $timezonename
*
- * @global object
- * @global object
- * @param string $timezonename
- * @return mixed timezonerecord object or false
+ * @package core
+ * @param string $timezonename name of the timezone
+ * @return stdClass|bool timezonerecord or false
*/
function get_timezone_record($timezonename) {
global $CFG, $DB;
@@ -2218,18 +2243,16 @@ function get_timezone_record($timezonename) {
}
return $cache[$timezonename] = $DB->get_record_sql('SELECT * FROM {timezone}
- WHERE name = ? ORDER BY year DESC', array($timezonename), true);
+ WHERE name = ? ORDER BY year DESC', array($timezonename), IGNORE_MULTIPLE);
}
/**
* Build and store the users Daylight Saving Time (DST) table
*
- * @global object
- * @global object
- * @global object
- * @param mixed $from_year Start year for the table, defaults to 1971
- * @param mixed $to_year End year for the table, defaults to 2035
- * @param mixed $strtimezone, if null or 99 then user's default timezone is used
+ * @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.
* @return bool
*/
function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) {
@@ -2357,11 +2380,13 @@ function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezo
/**
* Calculates the required DST change and returns a Timestamp Array
*
+ * @package core
+ * @category time
* @uses HOURSECS
* @uses MINSECS
- * @param mixed $year Int or String Year to focus on
+ * @param int|string $year Int or String Year to focus on
* @param object $timezone Instatiated Timezone object
- * @return mixed Null, or Array dst=>xx, 0=>xx, std=>yy, 1=>yy
+ * @return array|null Array dst=>xx, 0=>xx, std=>yy, 1=>yy or NULL
*/
function dst_changes_for_year($year, $timezone) {
@@ -2392,10 +2417,11 @@ function dst_changes_for_year($year, $timezone) {
* Calculates the Daylight Saving Offset for a given date/time (timestamp)
* - Note: Daylight saving only works for string timezones and not for float.
*
- * @global object
+ * @package core
+ * @category time
* @param int $time must NOT be compensated at all, it has to be a pure timestamp
- * @param mixed $strtimezone timezone for which offset is expected, if 99 or null
- * then user's default timezone is used.
+ * @param int|float|string $strtimezone timezone for which offset is expected, if 99 or null
+ * then user's default timezone is used. {@link http://docs.moodle.org/dev/Time_API#Timezone}
* @return int
*/
function dst_offset_on($time, $strtimezone = NULL) {
@@ -2440,13 +2466,14 @@ function dst_offset_on($time, $strtimezone = NULL) {
}
/**
- * ?
+ * Calculates when the day appears in specific month
*
- * @todo Document what this function does
- * @param int $startday
- * @param int $weekday
- * @param int $month
- * @param int $year
+ * @package core
+ * @category time
+ * @param int $startday starting day of the month
+ * @param int $weekday The day when week starts (normally taken from user preferences)
+ * @param int $month The month whose day is sought
+ * @param int $year The year of the month whose day is sought
* @return int
*/
function find_day_in_month($startday, $weekday, $month, $year) {
@@ -2508,6 +2535,8 @@ function find_day_in_month($startday, $weekday, $month, $year) {
/**
* Calculate the number of days in a given month
*
+ * @package core
+ * @category time
* @param int $month The month whose day count is sought
* @param int $year The year of the month whose day count is sought
* @return int
@@ -2519,6 +2548,8 @@ function days_in_month($month, $year) {
/**
* Calculate the position in the week of a specific calendar day
*
+ * @package core
+ * @category time
* @param int $day The day of the date whose position in the week is sought
* @param int $month The month of the date whose position in the week is sought
* @param int $year The year of the date whose position in the week is sought
@@ -5028,6 +5059,17 @@ function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $a
return true;
}
+ if (!validate_email($user->email)) {
+ // we can not send emails to invalid addresses - it might create security issue or confuse the mailer
+ $invalidemail = "User $user->id (".fullname($user).") email ($user->email) is invalid! Not sending.";
+ error_log($invalidemail);
+ if (CLI_SCRIPT) {
+ // do not print this in standard web pages
+ mtrace($invalidemail);
+ }
+ return false;
+ }
+
if (over_bounce_threshold($user)) {
$bouncemsg = "User $user->id (".fullname($user).") is over bounce threshold! Not sending.";
error_log($bouncemsg);
View
31 lib/navigationlib.php
@@ -127,6 +127,8 @@ class navigation_node implements renderable {
protected static $fullmeurl = null;
/** @var bool toogles auto matching of active node */
public static $autofindactive = true;
+ /** @var mixed If set to an int, that section will be included even if it has no activities */
+ public $includesectionnum = false;
/**
* Constructs a new navigation_node
@@ -1172,13 +1174,29 @@ public function initialise() {
}
$this->add_course_essentials($coursenode, $course);
+
+ // Get section number from $cm (if provided) - we need this
+ // before loading sections in order to tell it to load this section
+ // even if it would not normally display (=> it contains only
+ // a label, which we are now editing)
+ $sectionnum = isset($cm->sectionnum) ? $cm->sectionnum : 0;
+ if ($sectionnum) {
+ // This value has to be stored in a member variable because
+ // otherwise we would have to pass it through a public API
+ // to course formats and they would need to change their
+ // functions to pass it along again...
+ $this->includesectionnum = $sectionnum;
+ } else {
+ $this->includesectionnum = false;
+ }
+
// Load the course sections into the page
$sections = $this->load_course_sections($course, $coursenode);
if ($course->id != SITEID) {
// Find the section for the $CM associated with the page and collect
// its section number.
- if (isset($cm->sectionnum)) {
- $cm->sectionnumber = $cm->sectionnum;
+ if ($sectionnum) {
+ $cm->sectionnumber = $sectionnum;
} else {
foreach ($sections as $section) {
if ($section->id == $cm->section) {
@@ -1630,7 +1648,9 @@ protected function generate_sections_and_activities(stdClass $course) {
}
}
$activities[$cmid] = $activity;
- $sections[$key]->hasactivites = true;
+ if ($activity->display) {
+ $sections[$key]->hasactivites = true;
+ }
}
}
$this->cache->set('course_sections_'.$course->id, $sections);
@@ -1678,7 +1698,8 @@ public function load_generic_course_sections(stdClass $course, navigation_node $
if ($course->id == SITEID) {
$this->load_section_activities($coursenode, $section->section, $activities);
} else {
- if ((!$viewhiddensections && !$section->visible) || (!$this->showemptysections && !$section->hasactivites)) {
+ if ((!$viewhiddensections && !$section->visible) || (!$this->showemptysections &&
+ !$section->hasactivites && $this->includesectionnum !== $section->section)) {
continue;
}
if ($namingfunctionexists) {
@@ -4047,6 +4068,8 @@ protected function convert_child($child, $depth=1) {
$attributes['link'] = $child->action;
} else if ($child->action instanceof moodle_url) {
$attributes['link'] = $child->action->out();
+ } else if ($child->action instanceof action_link) {
+ $attributes['link'] = $child->action->url->out();
}
$attributes['hidden'] = ($child->hidden);
$attributes['haschildren'] = ($child->children->count()>0 || $child->type == navigation_node::TYPE_CATEGORY);
View
2  lib/outputcomponents.php
@@ -1199,7 +1199,7 @@ public static function select_time($type, $name, $currenttime=0, $step=5, array
$timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
}
$userdatetype = 'month';
- $currentdate['month'] = $currentdate['mon'];
+ $currentdate['month'] = (int)$currentdate['mon'];
break;
case 'days':
for ($i=1; $i<=31; $i++) {
View
2  lib/outputrenderers.php
@@ -1902,7 +1902,7 @@ public function render_file_picker(file_picker $fp) {
$strsaved = get_string('filesaved', 'repository');
$straddfile = get_string('openpicker', 'repository');
$strloading = get_string('loading', 'repository');
- $strdndenabled = get_string('dndenabled_single', 'moodle');
+ $strdndenabled = get_string('dndenabled_insentence', 'moodle');
$icon_progress = $OUTPUT->pix_icon('i/loading_small', $strloading).'';
$currentfile = $options->currentfile;
View
2  lib/portfoliolib.php
@@ -353,7 +353,7 @@ public function to_html($format=null, $addstr=null) {
$formoutput .= "\n" . '</form>';
break;
case PORTFOLIO_ADD_ICON_LINK:
- $linkoutput .= '"><img class="portfolio-add-icon" src="' . $OUTPUT->pix_url('t/portfolioadd') . '" alt="' . $addstr .'" /></a>';
+ $linkoutput .= '"><img class="portfolio-add-icon iconsmall" src="' . $OUTPUT->pix_url('t/portfolioadd') . '" alt="' . $addstr .'" /></a>';
break;
case PORTFOLIO_ADD_TEXT_LINK:
$linkoutput .= '">' . $addstr .'</a>';
View
4 lib/simpletest/testcompletionlib.php
@@ -46,11 +46,11 @@ function valid() {
}
function close() {
- $closed=true;
+ $this->closed=true;
}
function was_closed() {
- return $closed;
+ return $this->closed;
}
}
View
706 lib/simpletest/testcsslib.php
@@ -0,0 +1,706 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the unittests for the css optimiser in csslib.php
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+require_once($CFG->libdir . '/csslib.php');
+
+
+/**
+ * CSS optimiser test class
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_optimiser_test extends UnitTestCase {
+
+ /**
+ * Sets up the test class
+ */
+ public function setUp() {
+ global $CFG;
+ parent::setUp();
+ // We need to disable these if they are enabled to that we can predict
+ // the output.
+ $CFG->cssoptimiserstats = false;
+ $CFG->cssoptimiserpretty = false;
+ }
+
+ /**
+ * Test the process method
+ */
+ public function test_process() {
+ $optimiser = new css_optimiser;
+
+ $this->check_background($optimiser);
+ $this->check_borders($optimiser);
+ $this->check_colors($optimiser);
+ $this->check_margins($optimiser);
+ $this->check_padding($optimiser);
+ $this->check_widths($optimiser);
+
+ $this->try_broken_css_found_in_moodle($optimiser);
+ $this->try_invalid_css_handling($optimiser);
+ $this->try_bulk_processing($optimiser);
+ $this->try_break_things($optimiser);
+ }
+
+ /**
+ * Background colour tests
+ * @param css_optimiser $optimiser
+ */
+ protected function check_background(css_optimiser $optimiser) {
+
+ $cssin = '.test {background-color: #123456;}';
+ $cssout = '.test{background:#123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {background-image: url(\'test.png\');}';
+ $cssout = '.test{background-image:url(\'test.png\');}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {background: #123456 url(\'test.png\') no-repeat top left;}';
+ $cssout = '.test{background:#123456 url(\'test.png\') no-repeat top left;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {background: url(\'test.png\') no-repeat top left;}.test{background-position: bottom right}.test {background-color:#123456;}';
+ $cssout = '.test{background:#123456 url(\'test.png\') no-repeat bottom right;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {background: url( \'test.png\' )}.test{background: bottom right}.test {background:#123456;}';
+ $cssout = '.test{background-image:url(\'test.png\');background-position:bottom right;background-color:#123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {background-color: #123456;background-repeat: repeat-x; background-position: 100% 0%;}';
+ $cssout = '.test{background-color:#123456;background-repeat:repeat-x;background-position:100% 0%;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 10%;background-repeat: no-repeat;}
+ .tree_item.branch.navigation_node {background-image:none;padding-left:0;}';
+ $cssout = '.tree_item.branch{background:url([[pix:t/expanded]]) no-repeat 0 10%;} .tree_item.branch.navigation_node{background-image:none;padding-left:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty]]);background-position: 0% 5%;background-repeat: no-repeat;}
+ .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed]]);}';
+ $cssout = '.block_tree .tree_item.emptybranch{background:url([[pix:t/collapsed_empty]]) no-repeat 0% 5%;} .block_tree .collapsed .tree_item.branch{background-image:url([[pix:t/collapsed]]);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Border tests
+ * @param css_optimiser $optimiser
+ */
+ protected function check_borders(css_optimiser $optimiser) {
+ $cssin = '.test {border: 1px solid #654321} .test {border-bottom-color: #123456}';
+ $cssout = '.test{border:1px solid;border-color:#654321 #654321 #123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {border:1px solid red;}';
+ $cssout = '.one{border:1px solid #FF0000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {border:1px solid;} .one {border:2px dotted #DDD;}';
+ $cssout = '.one{border:2px dotted #DDDDDD;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {border:2px dotted #DDD;}.one {border:1px solid;} ';
+ $cssout = '.one{border:1px solid #DDDDDD;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one, .two {border:1px solid red;}';
+ $cssout = ".one, .two{border:1px solid #FF0000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one, .two {border:0px;}';
+ $cssout = ".one, .two{border-width:0;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one, .two {border-top: 5px solid white;}';
+ $cssout = ".one, .two{border-top:5px solid #FFFFFF;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {border:1px solid red;} .two {border:1px solid red;}';
+ $cssout = ".one, .two{border:1px solid #FF0000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {border:1px solid red;width:20px;} .two {border:1px solid red;height:20px;}';
+ $cssout = ".one{width:20px;border:1px solid #FF0000;} .two{height:20px;border:1px solid #FF0000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border: 1px solid #123456;} .test {border-color: #654321}';
+ $cssout = '.test{border:1px solid #654321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border-width: 1px; border-style: solid; border-color: #123456;}';
+ $cssout = '.test{border:1px solid #123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border:1px solid #123456;border-top:2px dotted #654321;}';
+ $cssout = '.test{border:1px solid #123456;border-top:2px dotted #654321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border:1px solid #123456;border-left:2px dotted #654321;}';
+ $cssout = '.test{border:1px solid #123456;border-left:2px dotted #654321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border-left:2px dotted #654321;border:1px solid #123456;}';
+ $cssout = '.test{border:1px solid #123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border:1px solid;border-top-color:#123456;}';
+ $cssout = '.test{border:1px solid;border-top-color:#123456;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;}';
+ $cssout = '.test{border:1px solid;border-top-color:#111;border-bottom-color:#222;border-left-color:#333;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;border-right-color:#444;}';
+ $cssout = '.test{border:1px solid;border-color:#111 #444 #222 #333;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.generaltable .cell {border-color:#EEEEEE;} .generaltable .cell {border-width: 1px;border-style: solid;}';
+ $cssout = '.generaltable .cell{border:1px solid #EEEEEE;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '#page-admin-roles-override .rolecap {border:none;border-bottom:1px solid #CECECE;}';
+ $cssout = '#page-admin-roles-override .rolecap{border-top:0;border-right:0;border-bottom:1px solid #CECECE;border-left:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Test colour styles
+ * @param css_optimiser $optimiser
+ */
+ protected function check_colors(css_optimiser $optimiser) {
+ $css = '.css{}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = '.css{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = '#some{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div.css{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div#some{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div[type=blah]{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div.css[type=blah]{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div#some[type=blah]{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = '#some.css[type=blah]{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = '#some .css[type=blah]{color:#123456;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $cssin = '.one {color:red;} .two {color:#F00;}';
+ $cssout = ".one, .two{color:#F00;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:#123;color:#321;}';
+ $cssout = '.one{color:#321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:#123; color : #321 ;}';
+ $cssout = '.one{color:#321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:#123;} .one {color:#321;}';
+ $cssout = '.one{color:#321;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:#123 !important;color:#321;}';
+ $cssout = '.one{color:#123 !important;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:#123 !important;} .one {color:#321;}';
+ $cssout = '.one{color:#123 !important;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:rgb(255, 128, 1)}';
+ $cssout = '.one{color:rgb(255, 128, 1);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:rgba(255, 128, 1, 0.5)}';
+ $cssout = '.one{color:rgba(255, 128, 1, 0.5);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:hsl(120, 65%, 75%)}';
+ $cssout = '.one{color:hsl(120, 65%, 75%);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:hsla(120,65%,75%,0.5)}';
+ $cssout = '.one{color:hsla(120,65%,75%,0.5);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Try some invalid colours to make sure we don't mangle them.
+ $css = 'div#some{color:#1;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div#some{color:#12;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div#some{color:#1234;}';
+ $this->assertEqual($css, $optimiser->process($css));
+
+ $css = 'div#some{color:#12345;}';
+ $this->assertEqual($css, $optimiser->process($css));
+ }
+
+ protected function check_widths(css_optimiser $optimiser) {
+ $cssin = '.css {width:0}';
+ $cssout = '.css{width:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.css {width:0px}';
+ $cssout = '.css{width:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.css {width:0em}';
+ $cssout = '.css{width:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.css {width:0pt}';
+ $cssout = '.css{width:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.css {width:0mm}';
+ $cssout = '.css{width:0;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.css {width:100px}';
+ $cssout = '.css{width:100px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Test margin styles
+ * @param css_optimiser $optimiser
+ */
+ protected function check_margins(css_optimiser $optimiser) {
+ $cssin = '.one {margin: 1px 2px 3px 4px}';
+ $cssout = '.one{margin:1px 2px 3px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin-top:1px; margin-left:4px; margin-right:2px; margin-bottom: 3px;}';
+ $cssout = '.one{margin:1px 2px 3px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin-top:1px; margin-left:4px;}';
+ $cssout = '.one{margin-top:1px;margin-left:4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin:1px; margin-left:4px;}';
+ $cssout = '.one{margin:1px 1px 1px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin:1px; margin-bottom:4px;}';
+ $cssout = '.one{margin:1px 1px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one, .two, .one.two, .one .two {margin:0;} .one.two {margin:0 7px;}';
+ $cssout = '.one, .two, .one .two{margin:0;} .one.two{margin:0 7px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Test padding styles
+ *
+ * @param css_optimiser $optimiser
+ */
+ protected function check_padding(css_optimiser $optimiser) {
+ $cssin = '.one {margin: 1px 2px 3px 4px}';
+ $cssout = '.one{margin:1px 2px 3px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin-top:1px; margin-left:4px; margin-right:2px; margin-bottom: 3px;}';
+ $cssout = '.one{margin:1px 2px 3px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin-top:1px; margin-left:4px;}';
+ $cssout = '.one{margin-top:1px;margin-left:4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin:1px; margin-left:4px;}';
+ $cssout = '.one{margin:1px 1px 1px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin:1px; margin-bottom:4px;}';
+ $cssout = '.one{margin:1px 1px 4px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {margin:0 !important;}';
+ $cssout = '.one{margin:0 !important;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {padding:0 !important;}';
+ $cssout = '.one{padding:0 !important;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one, .two, .one.two, .one .two {margin:0;} .one.two {margin:0 7px;}';
+ $cssout = '.one, .two, .one .two{margin:0;} .one.two{margin:0 7px;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Test some totally invalid CSS optimisation
+ *
+ * @param css_optimiser $optimiser
+ */
+ protected function try_invalid_css_handling(css_optimiser $optimiser) {
+
+ $cssin = array(
+ '.one{}',
+ '.one {:}',
+ '.one {;}',
+ '.one {;;;;;}',
+ '.one {:;}',
+ '.one {:;:;:;:::;;;}',
+ '.one {!important}',
+ '.one {:!important}',
+ '.one {:!important;}',
+ '.one {;!important}'
+ );
+ $cssout = '.one{}';
+ foreach ($cssin as $css) {
+ $this->assertEqual($cssout, $optimiser->process($css));
+ }
+
+ $cssin = array(
+ '.one{background-color:red;}',
+ '.one {background-color:red;} .one {background-color:}',
+ '.one {background-color:red;} .one {background-color;}',
+ '.one {background-color:red;} .one {background-color}',
+ '.one {background-color:red;} .one {background-color:;}',
+ '.one {background-color:red;} .one {:blue;}',
+ '.one {background-color:red;} .one {:#00F}',
+ );
+ $cssout = '.one{background:#F00;}';
+ foreach ($cssin as $css) {
+ $this->assertEqual($cssout, $optimiser->process($css));
+ }
+
+ $cssin = '..one {background-color:color:red}';
+ $cssout = '..one{background-color:color:red;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '#.one {background-color:color:red}';
+ $cssout = '#.one{background-color:color:red;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '##one {background-color:color:red}';
+ $cssout = '##one{background-color:color:red;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {background-color:color:red}';
+ $cssout = '.one{background-color:color:red;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {background-color:red;color;border-color:blue}';
+ $cssout = '.one{background:#F00;border-color:#00F;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '{background-color:#123456;color:red;}{color:green;}';
+ $cssout = "{color:#008000;background:#123456;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ $cssin = '.one {color:red;} {color:green;} .one {background-color:blue;}';
+ $cssout = ".one{color:#F00;background:#00F;} {color:#008000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * Try to break some things
+ * @param css_optimiser $optimiser
+ */
+ protected function try_break_things(css_optimiser $optimiser) {
+ // Wildcard test
+ $cssin = '* {color: black;}';
+ $cssout = '*{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Wildcard test
+ $cssin = '.one * {color: black;}';
+ $cssout = '.one *{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Wildcard test
+ $cssin = '* .one * {color: black;}';
+ $cssout = '* .one *{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Wildcard test
+ $cssin = '*,* {color: black;}';
+ $cssout = '*{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Wildcard test
+ $cssin = '*, * .one {color: black;}';
+ $cssout = "*,\n* .one{color:#000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Wildcard test
+ $cssin = '*, *.one {color: black;}';
+ $cssout = "*,\n*.one{color:#000;}";
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Psedo test
+ $cssin = '.one:before {color: black;}';
+ $cssout = '.one:before{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Psedo test
+ $cssin = '.one:after {color: black;}';
+ $cssout = '.one:after{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Psedo test
+ $cssin = '.one:onclick {color: black;}';
+ $cssout = '.one:onclick{color:#000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Test complex CSS rules that don't really exist but mimic other CSS rules
+ $cssin = '.one {master-of-destruction: explode(\' \', "What madness";}';
+ $cssout = '.one{master-of-destruction:explode(\' \', "What madness";}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Test some complex IE css... I couldn't even think of a more complext solution
+ // than the CSS they came up with.
+ $cssin = 'a { opacity: 0.5; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); }';
+ $cssout = 'a{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+
+ /**
+ * A bulk processing test
+ * @param css_optimiser $optimiser
+ */
+ protected function try_bulk_processing(css_optimiser $optimiser) {
+ global $CFG;
+ $cssin = <<<CSS
+.test .one {
+ margin:5px;
+ border:0;
+}
+.test .one {
+ margin: 10px;
+ color: red;
+}
+
+.test.one {
+ margin: 15px;
+}
+
+#test .one {margin: 20px;}
+#test #one {margin: 25px;}.test #one {margin: 30px;}
+ .test .one { background-color: #123; }
+.test.one{border:1px solid blue}.test.one{border-color:green;}
+
+@media print {
+ #test .one {margin: 35px;}
+}
+
+@media print {
+ #test .one {margin: 40px;color: #123456;}
+ #test #one {margin: 45px;}
+}
+
+@media print,screen {
+ #test .one {color: #654321;}
+}
+
+#test .one,
+#new.style {color:#000;}
+CSS;
+
+ $cssout = <<<CSS
+.test .one{color:#F00;margin:10px;border-width:0;background:#123;}
+.test.one{margin:15px;border:1px solid #008000;}
+#test .one{color:#000;margin:20px;}
+#test #one{margin:25px;}
+.test #one{margin:30px;}
+#new.style{color:#000;}
+
+
+@media print {
+ #test .one{color:#123456;margin:40px;}
+ #test #one{margin:45px;}
+}
+
+@media print,screen {
+ #test .one{color:#654321;}
+}
+CSS;
+ $CFG->cssoptimiserpretty = 1;
+ $this->assertEqual($optimiser->process($cssin), $cssout);
+ }
+
+ /**
+ * Test CSS colour matching
+ */
+ public function test_css_is_colour() {
+ // First lets test hex colours
+ $this->assertTrue(css_is_colour('#123456'));
+ $this->assertTrue(css_is_colour('#123'));
+ $this->assertTrue(css_is_colour('#ABCDEF'));
+ $this->assertTrue(css_is_colour('#ABC'));
+ $this->assertTrue(css_is_colour('#abcdef'));
+ $this->assertTrue(css_is_colour('#abc'));
+ $this->assertTrue(css_is_colour('#aBcDeF'));
+ $this->assertTrue(css_is_colour('#aBc'));
+ $this->assertTrue(css_is_colour('#1a2Bc3'));
+ $this->assertTrue(css_is_colour('#1Ac'));
+
+ // Note the following two colour's arn't really colours but browsers process
+ // them still.
+ $this->assertTrue(css_is_colour('#A'));
+ $this->assertTrue(css_is_colour('#12'));
+ // Having four or five characters however are not valid colours and
+ // browsers don't parse them. They need to fail so that broken CSS
+ // stays broken after optimisation.
+ $this->assertFalse(css_is_colour('#1234'));
+ $this->assertFalse(css_is_colour('#12345'));
+
+ $this->assertFalse(css_is_colour('#BCDEFG'));
+ $this->assertFalse(css_is_colour('#'));
+ $this->assertFalse(css_is_colour('#0000000'));
+ $this->assertFalse(css_is_colour('#132-245'));
+ $this->assertFalse(css_is_colour('#13 23 43'));
+ $this->assertFalse(css_is_colour('123456'));
+
+ // Next lets test real browser mapped colours
+ $this->assertTrue(css_is_colour('black'));
+ $this->assertTrue(css_is_colour('blue'));
+ $this->assertTrue(css_is_colour('BLACK'));
+ $this->assertTrue(css_is_colour('Black'));
+ $this->assertTrue(css_is_colour('bLACK'));
+ $this->assertTrue(css_is_colour('mediumaquamarine'));
+ $this->assertTrue(css_is_colour('mediumAquamarine'));
+ $this->assertFalse(css_is_colour('monkey'));
+ $this->assertFalse(css_is_colour(''));
+ $this->assertFalse(css_is_colour('not a colour'));
+
+ // Next lets test rgb(a) colours
+ $this->assertTrue(css_is_colour('rgb(255,255,255)'));
+ $this->assertTrue(css_is_colour('rgb(0, 0, 0)'));
+ $this->assertTrue(css_is_colour('RGB (255, 255 , 255)'));
+ $this->assertTrue(css_is_colour('rgba(0,0,0,0)'));
+ $this->assertTrue(css_is_colour('RGBA(255,255,255,1)'));
+ $this->assertTrue(css_is_colour('rgbA(255,255,255,0.5)'));
+ $this->assertFalse(css_is_colour('rgb(-255,-255,-255)'));
+ $this->assertFalse(css_is_colour('rgb(256,-256,256)'));
+
+ // Now lets test HSL colours
+ $this->assertTrue(css_is_colour('hsl(0,0%,100%)'));
+ $this->assertTrue(css_is_colour('hsl(180, 0%, 10%)'));
+ $this->assertTrue(css_is_colour('hsl (360, 100% , 95%)'));
+
+ // Finally test the special values
+ $this->assertTrue(css_is_colour('inherit'));
+ }
+
+ /**
+ * Test the css_is_width function
+ */
+ public function test_css_is_width() {
+
+ $this->assertTrue(css_is_width('0'));
+ $this->assertTrue(css_is_width('0px'));
+ $this->assertTrue(css_is_width('0em'));
+ $this->assertTrue(css_is_width('199px'));
+ $this->assertTrue(css_is_width('199em'));
+ $this->assertTrue(css_is_width('199%'));
+ $this->assertTrue(css_is_width('-1'));
+ $this->assertTrue(css_is_width('-1px'));
+ $this->assertTrue(css_is_width('auto'));
+ $this->assertTrue(css_is_width('inherit'));
+
+ $this->assertFalse(css_is_width('-'));
+ $this->assertFalse(css_is_width('bananas'));
+ $this->assertFalse(css_is_width(''));
+ $this->assertFalse(css_is_width('top'));
+ }
+
+ /**
+ * This function tests some of the broken crazy CSS we have in Moodle.
+ * For each of these things the value needs to be corrected if we can be 100%
+ * certain what is going wrong, Or it needs to be left as is.
+ *
+ * @param css_optimiser $optimiser
+ */
+ public function try_broken_css_found_in_moodle(css_optimiser $optimiser) {
+ // Notice how things are out of order here but that they get corrected
+ $cssin = '.test {background:url([[pix:theme|pageheaderbgred]]) top center no-repeat}';
+ $cssout = '.test{background:url([[pix:theme|pageheaderbgred]]) no-repeat top center;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Cursor hand isn't valid
+ $cssin = '.test {cursor: hand;}';
+ $cssout = '.test{cursor:hand;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Zoom property isn't valid
+ $cssin = '.test {zoom: 1;}';
+ $cssout = '.test{zoom:1;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Left isn't a valid position property
+ $cssin = '.test {position: left;}';
+ $cssout = '.test{position:left;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // The dark red color isn't a valid HTML color but has a standardised
+ // translation of #8B0000
+ $cssin = '.test {color: darkred;}';
+ $cssout = '.test{color:#8B0000;}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // You can't use argb colours as border colors
+ $cssin = '.test {border-bottom: 1px solid rgba(0,0,0,0.25);}';
+ $cssout = '.test{border-bottom:1px solid rgba(0,0,0,0.25);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+
+ // Opacity with annoying IE equivilants....
+ $cssin = '.test {opacity: 0.5; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50);}';
+ $cssout = '.test{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
+ $this->assertEqual($cssout, $optimiser->process($cssin));
+ }
+}
View
3  lib/simpletest/testfilterconfig.php
@@ -747,7 +747,7 @@ public function test_filter_delete_all_for_filter() {
$expectedconfig = new stdClass;
$expectedconfig->configname = 'Other config value';
$this->assertEqual($expectedconfig, get_config('filter_other'));
- $this->assertNull(get_config('filter_name'));
+ $this->assertIdentical(get_config('filter_name'), new stdClass());
}
public function test_filter_delete_all_for_context() {
@@ -785,6 +785,7 @@ public function setUp() {
}
public function tearDown() {
+ global $CFG;
$CFG->stringfilters = $this->origcfgstringfilters;
$CFG->filterall = $this->origcfgfilterall;
View
4,245 lib/timezone.txt
2,149 additions, 2,096 deletions not shown
View
8 message/lib.php
@@ -2347,11 +2347,7 @@ function get_message_processor($type) {
* @return object $processors object containing information on message processors
*/
function get_message_output_default_preferences() {
- $preferences = get_config('message');
- if (!$preferences) {
- $preferences = new stdClass();
- }
- return $preferences;
+ return get_config('message');
}
/**
@@ -2383,7 +2379,7 @@ function translate_message_default_setting($plugindefault, $processorname) {
// Validate the value. It should not exceed the maximum size
if (!is_int($plugindefault) || ($plugindefault > 0x0f)) {
- $OUTPUT->notification(get_string('errortranslatingdefault', 'message'), 'notifyproblem');
+ debugging(get_string('errortranslatingdefault', 'message'));
$plugindefault = $default;
}
// Use plugin default setting of 'permitted' is 0
View
7 mod/forum/lib.php
@@ -4543,9 +4543,10 @@ function forum_get_subscribed_forums($course) {
$sql = "SELECT f.id
FROM {forum} f
LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?)
- WHERE f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
+ WHERE f.course = ?
+ AND f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)";
- if ($subscribed = $DB->get_records_sql($sql, array($USER->id))) {
+ if ($subscribed = $DB->get_records_sql($sql, array($USER->id, $course->id))) {
foreach ($subscribed as $s) {
$subscribed[$s->id] = $s->id;
}
@@ -7897,7 +7898,7 @@ function forum_get_forums_user_posted_in($user, array $courseids = null, $discus
* ->posts: An array containing the posts to show for this request.
*/
function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false, $discussionsonly = false, $limitfrom = 0, $limitnum = 50) {
- global $DB, $USER;
+ global $DB, $USER, $CFG;
$return = new stdClass;
$return->totalcount = 0; // The total number of posts that the current user is able to view
View
18 mod/glossary/lib.php
@@ -2231,24 +2231,24 @@ function glossary_count_unrated_entries($glossaryid, $userid) {
// Now we need to count the ratings that this user has made
$sql = "SELECT COUNT('x') AS num
FROM {glossary_entries} e
- JOIN {ratings} r ON r.itemid = e.id
+ JOIN {rating} r ON r.itemid = e.id
WHERE e.glossaryid = :glossaryid AND
r.userid = :userid AND
r.component = 'mod_glossary' AND
r.ratingarea = 'entry' AND
r.contextid = :contextid";
- $params = array('glossaryid' => $glossaryid, 'userid' => $userid, 'contextid' => $context->id);
+ $params = array('glossaryid' => $glossaryid, 'userid' => $userid, 'contextid' => $contextid);
$rated = $DB->count_records_sql($sql, $params);
if ($rated) {
// The number or enties minus the number or rated entries equals the number of unrated
// entries
- if ($entries->num > $rated->num) {
- return $entries->num - $rated->num;
+ if ($entries > $rated) {
+ return $entries - $rated;
} else {
return 0; // Just in case there was a counting error
}
} else {
- return $entries->num;
+ return (int)$entries;
}
} else {
return 0;
@@ -2531,8 +2531,8 @@ function glossary_reset_userdata($data) {
$status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossariesall', 'glossary'), 'error'=>false);
} else if (!empty($data->reset_glossary_types)) {
- $mainentriessql = "$allentries AND g.mainglossary=1";
- $secondaryentriessql = "$allentries AND g.mainglossary=0";
+ $mainentriessql = "$allentriessql AND g.mainglossary=1";
+ $secondaryentriessql = "$allentriessql AND g.mainglossary=0";
$mainglossariessql = "$allglossariessql AND g.mainglossary=1";
$secondaryglossariessql = "$allglossariessql AND g.mainglossary=0";
@@ -2561,14 +2561,14 @@ function glossary_reset_userdata($data) {
glossary_reset_gradebook($data->courseid, 'main');
}
- $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary'), 'error'=>false);
+ $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('mainglossary', 'glossary'), 'error'=>false);
} else if (in_array('secondary', $data->reset_glossary_types)) {
$params[] = 'glossary_entry';
$DB->delete_records_select('comments', "itemid IN ($secondaryentriessql) AND commentarea=?", $params);
$DB->delete_records_select('glossary_entries', "glossaryid IN ($secondaryglossariessql)", $params);
// remove exported source flag from entries in main glossary
- $DB->execute("UPDATE {glossary_entries
+ $DB->execute("UPDATE {glossary_entries}
SET sourceglossaryid=0
WHERE glossaryid IN ($mainglossariessql)", $params);
View
4 mod/quiz/locallib.php
@@ -353,6 +353,10 @@ function quiz_feedback_for_grade($grade, $quiz, $context) {
return '';
}
+ // With CBM etc, it is possible to get -ve grades, which would then not match
+ // any feedback. Therefore, we replace -ve grades with 0.
+ $grade = max($grade, 0);
+
$feedback = $DB->get_record_select('quiz_feedback',
'quizid = ? AND mingrade <= ? AND ? < maxgrade', array($quiz->id, $grade, $grade));
View
2  mod/quiz/renderer.php