Merge branch 'MDL-30871-23' of git://github.com/FMCorz/moodle into MOODLE_23_STABLE
authorDan Poltawski <dan@moodle.com>
Tue, 20 Nov 2012 03:47:26 +0000 (11:47 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 20 Nov 2012 03:47:26 +0000 (11:47 +0800)
114 files changed:
admin/roles/check.php
admin/roles/lib.php
auth/mnet/auth.php
auth/shibboleth/index.php
auth/shibboleth/lang/en/auth_shibboleth.php
backup/moodle2/backup_stepslib.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/tests/cronhelper_test.php
blocks/dock.js
blocks/html/lib.php
blocks/search_forums/block_search_forums.php
cohort/assign.php
comment/comment.js
course/category.php
course/index.php
course/lib.php
course/renderer.php
course/yui/modchooser/modchooser.js
course/yui/toolboxes/toolboxes.js
enrol/paypal/return.php
enrol/renderer.php
enrol/yui/notification/assets/skins/sam/notification.css
enrol/yui/notification/notification.js
grade/edit/tree/calculation.php
grade/edit/tree/grade.php
grade/lib.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/user/lib.php
install.php
install/lang/en_kids/langconfig.php [new file with mode: 0644]
install/lang/fi/install.php
install/lang/hr/moodle.php
install/lang/ja_kids/langconfig.php [new file with mode: 0644]
install/lang/lt/install.php [new file with mode: 0644]
install/lang/ms/moodle.php
install/lang/no/error.php
install/lang/no/install.php
install/lang/zh_tw/admin.php
install/lang/zh_tw/error.php
install/lang/zh_tw/install.php
lang/en/admin.php
lang/en/form.php
lib/bennu/bennu.class.php
lib/bennu/iCalendar_parameters.php
lib/editor/tinymce/readme_moodle.txt
lib/editor/tinymce/tiny_mce/3.5.1.1/plugins/spellchecker/classes/GoogleSpell.php
lib/form/yui/dateselector/dateselector.js
lib/googleapi.php
lib/grade/grade_item.php
lib/grade/tests/grade_item_test.php
lib/gradelib.php
lib/javascript-static.js
lib/messagelib.php
lib/modinfolib.php
lib/moodlelib.php
lib/outputrenderers.php
lib/phpunit/classes/util.php
lib/pluginlib.php
lib/portfoliolib.php
lib/tablelib.php
lib/tests/gradelib_test.php [new file with mode: 0644]
lib/tests/messagelib_test.php [new file with mode: 0644]
lib/tests/moodlelib_test.php
lib/yui/2.9.0/build/charts/assets/charts.swf
lib/yui/2.9.0/build/swfstore/swfstore.swf
lib/yui/2.9.0/build/uploader/assets/uploader.swf
lib/yui/chooserdialogue/chooserdialogue.js
message/lib.php
mnet/service/enrol/course.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/assign/module.js
mod/assign/upgradelib.php
mod/assignment/type/upload/assignment.class.php
mod/book/db/upgrade.php
mod/book/db/upgradelib.php [new file with mode: 0644]
mod/book/version.php
mod/data/lib.php
mod/data/view.php
mod/feedback/lib.php
mod/forum/lib.php
mod/forum/styles.css
mod/lesson/essay.php
mod/lesson/format.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/module.js
mod/quiz/styles.css
mod/quiz/tests/generator/lib.php [new file with mode: 0644]
mod/scorm/lib.php
mod/survey/lang/en/survey.php
mod/wiki/lang/en/wiki.php
mod/wiki/lib.php
mod/wiki/locallib.php
mod/wiki/pagelib.php
mod/workshop/lang/en/workshop.php
portfolio/add.php
question/behaviour/rendererbase.php
question/editlib.php
question/type/calculated/styles.css
question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php
question/type/multichoice/edit_multichoice_form.php
question/type/numerical/styles.css
repository/filepicker.js
theme/base/style/core.css
theme/fusion/style/core.css
theme/magazine/style/core.css
theme/mymobile/style/jmobile11_rtl.css
user/edit.php
user/filters/text.php
user/selector/lib.php
user/view.php
version.php

index 470159e..d3917b0 100644 (file)
@@ -59,16 +59,11 @@ $courseid = $course->id;
 $contextname = print_context_name($context);
 
 // Get the user_selector we will need.
-// Teachers within a course just get to see the same list of people they can
-// assign roles to. Admins (people with moodle/role:manage) can run this report for any user.
-$options = array('context' => $context, 'roleid' => 0);
-if (has_capability('moodle/role:manage', $context)) {
-    $userselector = new potential_assignees_course_and_above('reportuser', $options);
-} else {
-    $userselector = roles_get_potential_user_selector($context, 'reportuser', $options);
-}
-$userselector->set_multiselect(false);
-$userselector->set_rows(10);
+// Teachers within a course just get to see the same list of enrolled users.
+// Admins (people with moodle/role:manage) can run this report for any user.
+$options = array('accesscontext' => $context);
+$userselector = new role_check_users_selector('reportuser', $options);
+$userselector->set_rows(20);
 
 // Work out an appropriate page title.
 $title = get_string('checkpermissionsin', 'role', $contextname);
index 93703c8..c201d35 100644 (file)
@@ -1076,6 +1076,134 @@ class potential_assignees_below_course extends role_assign_user_selector_base {
     }
 }
 
+/**
+ * User selector subclass for the selection of users in the check permissions page.
+ *
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ */
+class role_check_users_selector extends user_selector_base {
+    const MAX_ENROLLED_PER_PAGE = 100;
+    const MAX_POTENTIAL_PER_PAGE = 100;
+
+    /** @var bool limit listing of users to enrolled only */
+    var $onlyenrolled;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name the control name/id for use in the HTML.
+     * @param array $options other options needed to construct this selector.
+     * You must be able to clone a userselector by doing new get_class($us)($us->get_name(), $us->get_options());
+     */
+    public function __construct($name, $options) {
+        if (!isset($options['multiselect'])) {
+            $options['multiselect'] = false;
+        }
+        parent::__construct($name, $options);
+
+        $coursecontext = $this->accesscontext->get_course_context(false);
+        if ($coursecontext and $coursecontext->id != SITEID and !has_capability('moodle/role:manage', $coursecontext)) {
+            // Prevent normal teachers from looking up all users.
+            $this->onlyenrolled = true;
+        } else {
+            $this->onlyenrolled = false;
+        }
+    }
+
+    public function find_users($search) {
+        global $DB;
+
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+
+        $fields      = 'SELECT ' . $this->required_fields_sql('u');
+        $countfields = 'SELECT COUNT(1)';
+
+        $coursecontext = $this->accesscontext->get_course_context(false);
+
+        if ($coursecontext and $coursecontext != SITEID) {
+            $sql1 = " FROM {user} u
+                      JOIN {user_enrolments} ue ON (ue.userid = u.id)
+                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid1)
+                     WHERE $wherecondition";
+            $params['courseid1'] = $coursecontext->instanceid;
+
+            if ($this->onlyenrolled) {
+                $sql2 = null;
+            } else {
+                $sql2 = " FROM {user} u
+                     LEFT JOIN ({user_enrolments} ue
+                                JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid2)) ON (ue.userid = u.id)
+                         WHERE $wherecondition
+                               AND ue.id IS NULL";
+                $params['courseid2'] = $coursecontext->instanceid;
+            }
+
+        } else {
+            if ($this->onlyenrolled) {
+                // Bad luck, current user may not view only enrolled users.
+                return array();
+            }
+            $sql1 = null;
+            $sql2 = " FROM {user} u
+                     WHERE $wherecondition";
+        }
+
+        $order = " ORDER BY lastname ASC, firstname ASC";
+
+        $params['contextid'] = $this->accesscontext->id;
+
+        $result = array();
+
+        if ($search) {
+            $groupname1 = get_string('enrolledusersmatching', 'enrol', $search);
+            $groupname2 = get_string('potusersmatching', 'role', $search);
+        } else {
+            $groupname1 = get_string('enrolledusers', 'enrol');
+            $groupname2 = get_string('potusers', 'role');
+        }
+
+        if ($sql1) {
+            $enrolleduserscount = $DB->count_records_sql($countfields . $sql1, $params);
+            if (!$this->is_validating() and $enrolleduserscount > $this::MAX_ENROLLED_PER_PAGE) {
+                $result[$groupname1] = array();
+                $toomany = $this->too_many_results($search, $enrolleduserscount);
+                $result[implode(' - ', array_keys($toomany))] = array();
+
+            } else {
+                $enrolledusers = $DB->get_records_sql($fields . $sql1 . $order, $params);
+                if ($enrolledusers) {
+                    $result[$groupname1] = $enrolledusers;
+                }
+            }
+            if ($sql2) {
+                $result[''] = array();
+            }
+        }
+        if ($sql2) {
+            $otheruserscount = $DB->count_records_sql($countfields . $sql2, $params);
+            if (!$this->is_validating() and $otheruserscount > $this::MAX_POTENTIAL_PER_PAGE) {
+                $result[$groupname2] = array();
+                $toomany = $this->too_many_results($search, $otheruserscount);
+                $result[implode(' - ', array_keys($toomany))] = array();
+            } else {
+                $otherusers = $DB->get_records_sql($fields . $sql2 . $order, $params);
+                if ($otherusers) {
+                    $result[$groupname2] = $otherusers;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    protected function get_options() {
+        global $CFG;
+        $options = parent::get_options();
+        $options['file'] = $CFG->admin . '/roles/lib.php';
+        return $options;
+    }
+}
+
 /**
  * User selector subclass for the list of potential users on the assign roles page,
  * when we are assigning in a context at or above the course level. In this case we
index 0a136e8..14a42ac 100644 (file)
@@ -378,23 +378,28 @@ class auth_plugin_mnet extends auth_plugin_base {
                 $extra = $DB->get_records_sql($sql);
 
                 $keys = array_keys($courses);
-                $defaultrole = reset(get_archetype_roles('student'));
-                //$defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!!
-                foreach ($keys AS $id) {
-                    if ($courses[$id]->visible == 0) {
-                        unset($courses[$id]);
-                        continue;
+                $studentroles = get_archetype_roles('student');
+                if (!empty($studentroles)) {
+                    $defaultrole = reset($studentroles);
+                    //$defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!!
+                    foreach ($keys AS $id) {
+                        if ($courses[$id]->visible == 0) {
+                            unset($courses[$id]);
+                            continue;
+                        }
+                        $courses[$id]->cat_id          = $courses[$id]->category;
+                        $courses[$id]->defaultroleid   = $defaultrole->id;
+                        unset($courses[$id]->category);
+                        unset($courses[$id]->visible);
+
+                        $courses[$id]->cat_name        = $extra[$id]->cat_name;
+                        $courses[$id]->cat_description = $extra[$id]->cat_description;
+                        $courses[$id]->defaultrolename = $defaultrole->name;
+                        // coerce to array
+                        $courses[$id] = (array)$courses[$id];
                     }
-                    $courses[$id]->cat_id          = $courses[$id]->category;
-                    $courses[$id]->defaultroleid   = $defaultrole->id;
-                    unset($courses[$id]->category);
-                    unset($courses[$id]->visible);
-
-                    $courses[$id]->cat_name        = $extra[$id]->cat_name;
-                    $courses[$id]->cat_description = $extra[$id]->cat_description;
-                    $courses[$id]->defaultrolename = $defaultrole->name;
-                    // coerce to array
-                    $courses[$id] = (array)$courses[$id];
+                } else {
+                    throw new moodle_exception('unknownrole', 'error', '', 'student');
                 }
             } else {
                 // if the array is empty, send it anyway
index a1e4a1c..9b765df 100644 (file)
@@ -25,7 +25,7 @@
 
     // Check whether Shibboleth is configured properly
     if (empty($pluginconfig->user_attribute)) {
-        print_error('shib_not_set_up_error', 'auth');
+        print_error('shib_not_set_up_error', 'auth_shibboleth');
      }
 
 /// If we can find the Shibboleth attribute, save it in session and return to main login page
@@ -38,9 +38,9 @@
 
     /// Check if the user has actually submitted login data to us
 
-        if ($shibbolethauth->user_login($frm->username, $frm->password)) {
+        if ($shibbolethauth->user_login($frm->username, $frm->password)
+                && $user = authenticate_user_login($frm->username, $frm->password)) {
 
-            $user = authenticate_user_login($frm->username, $frm->password);
             enrol_check_plugins($user);
             session_set_user($user);
 
         }
 
         else {
-            // For some weird reason the Shibboleth user couldn't be authenticated
+            // The Shibboleth user couldn't be mapped to a valid Moodle user
+            print_error('shib_invalid_account_error', 'auth_shibboleth');
         }
     }
 
     // If we can find any (user independent) Shibboleth attributes but no user
     // attributes we probably didn't receive any user attributes
     elseif (!empty($_SERVER['HTTP_SHIB_APPLICATION_ID']) || !empty($_SERVER['Shib-Application-ID'])) {
-        print_error('shib_no_attributes_error', 'auth' , '', '\''.$pluginconfig->user_attribute.'\', \''.$pluginconfig->field_map_firstname.'\', \''.$pluginconfig->field_map_lastname.'\' and \''.$pluginconfig->field_map_email.'\'');
+        print_error('shib_no_attributes_error', 'auth_shibboleth' , '', '\''.$pluginconfig->user_attribute.'\', \''.$pluginconfig->field_map_firstname.'\', \''.$pluginconfig->field_map_lastname.'\' and \''.$pluginconfig->field_map_email.'\'');
     } else {
-        print_error('shib_not_set_up_error', 'auth');
+        print_error('shib_not_set_up_error', 'auth_shibboleth');
     }
 
 
index ca0f251..aace9a1 100644 (file)
@@ -51,6 +51,7 @@ $string['auth_shib_no_organizations_warning'] = 'If you want to use the integrat
 $string['auth_shib_only'] = 'Shibboleth only';
 $string['auth_shib_only_description'] = 'Check this option if a Shibboleth authentication shall be enforced';
 $string['auth_shib_username_description'] = 'Name of the webserver Shibboleth environment variable that shall be used as Moodle username';
+$string['shib_invalid_account_error'] = 'You seem to be Shibboleth authenticated but Moodle has no valid account for your username. Your account may not exist or it may have been suspended.';
 $string['shib_no_attributes_error'] = 'You seem to be Shibboleth authenticated but Moodle didn\'t receive any user attributes. Please check that your Identity Provider releases the necessary attributes ({$a}) to the Service Provider Moodle is running on or inform the webmaster of this server.';
 $string['shib_not_all_attributes_error'] = 'Moodle needs certain Shibboleth attributes which are not present in your case. The attributes are: {$a}<br />Please contact the webmaster of this server or your Identity Provider.';
 $string['shib_not_set_up_error'] = 'Shibboleth authentication doesn\'t seem to be set up correctly because no Shibboleth environment variables are present for this page. Please consult the <a href="README.txt">README</a> for further instructions on how to set up Shibboleth authentication or contact the webmaster of this Moodle installation.';
index 7169e19..e5e1691 100644 (file)
@@ -859,7 +859,7 @@ class backup_gradebook_structure_step extends backup_structure_step {
         $grade_category   = new backup_nested_element('grade_category', array('id'), array(
                 //'courseid',
                 'parent', 'depth', 'path', 'fullname', 'aggregation', 'keephigh',
-                'dropload', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
+                'droplow', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
                 'timecreated', 'timemodified', 'hidden'));
 
         $letters = new backup_nested_element('grade_letters');
index fb7beb3..e856ead 100644 (file)
@@ -1505,7 +1505,8 @@ abstract class restore_dbops {
             }
             $currentfullname = $fullname.$suffixfull;
             $currentshortname = substr($shortname, 0, 100 - strlen($suffixshort)).$suffixshort; // < 100cc
-            $coursefull  = $DB->get_record_select('course', 'fullname = ? AND id != ?', array($currentfullname, $courseid));
+            $coursefull  = $DB->get_record_select('course', 'fullname = ? AND id != ?',
+                    array($currentfullname, $courseid), '*', IGNORE_MULTIPLE);
             $courseshort = $DB->get_record_select('course', 'shortname = ? AND id != ?', array($currentshortname, $courseid));
             $counter++;
         } while ($coursefull || $courseshort);
index 7afd1be..d0e92af 100644 (file)
@@ -281,9 +281,13 @@ class backup_cron_helper_testcase extends advanced_testcase {
         $this->assertEquals(date('w-15:00', strtotime('tomorrow')), date('w-H:i', $next));
 
         // Let's have a Belgian beer! (UTC+1 / UTC+2 DST).
+        // Warning: Some of these tests will fail if executed "around"
+        // 'Europe/Brussels' DST changes (last Sunday in March and
+        // last Sunday in October right now - 2012). Once Moodle
+        // moves to PHP TZ support this could be fixed properly.
         date_default_timezone_set('Europe/Brussels');
         $now = strtotime('18:00:00');
-        $dst = date('I');
+        $dst = date('I', $now);
 
         $timezone = -10.0; // 7am for the user.
         $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
@@ -321,9 +325,13 @@ class backup_cron_helper_testcase extends advanced_testcase {
         $this->assertEquals($expected, date('w-H:i', $next));
 
         // The big apple! (UTC-5 / UTC-4 DST).
+        // Warning: Some of these tests will fail if executed "around"
+        // 'America/New_York' DST changes (2nd Sunday in March and
+        // 1st Sunday in November right now - 2012). Once Moodle
+        // moves to PHP TZ support this could be fixed properly.
         date_default_timezone_set('America/New_York');
         $now = strtotime('18:00:00');
-        $dst = date('I');
+        $dst = date('I', $now);
 
         $timezone = -10.0; // 1pm for the user.
         $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
@@ -365,9 +373,14 @@ class backup_cron_helper_testcase extends advanced_testcase {
         set_config('backup_auto_hour', '20', 'backup');
         set_config('backup_auto_minute', '00', 'backup');
 
+        // Note: These tests should not fail because they are "unnafected"
+        // by DST changes, as far as execution always happens on Monday and
+        // Saturday and those week days are not, right now, the ones rulez
+        // to peform the DST changes (Sunday is). This may change if rules
+        // are modified in the future.
         date_default_timezone_set('Europe/Brussels');
         $now = strtotime('next Monday 18:00:00');
-        $dst = date('I');
+        $dst = date('I', $now);
 
         $timezone = -12.0;  // 1pm for the user.
         $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
@@ -404,9 +417,14 @@ class backup_cron_helper_testcase extends advanced_testcase {
         set_config('backup_auto_hour', '02', 'backup');
         set_config('backup_auto_minute', '00', 'backup');
 
+        // Note: These tests should not fail because they are "unnafected"
+        // by DST changes, as far as execution always happens on Monday and
+        // Saturday and those week days are not, right now, the ones rulez
+        // to peform the DST changes (Sunday is). This may change if rules
+        // are modified in the future.
         date_default_timezone_set('America/New_York');
         $now = strtotime('next Monday 04:00:00');
-        $dst = date('I');
+        $dst = date('I', $now);
 
         $timezone = -12.0;  // 8pm for the user.
         $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
index 3c8a31b..a759913 100644 (file)
@@ -505,7 +505,7 @@ M.core_dock.fixTitleOrientation = function(item, title, text) {
             break;
     }
 
-    if (Y.UA.ie > 7) {
+    if (Y.UA.ie == 8) {
         // IE8 can flip the text via CSS but not handle SVG
         title.setContent(text);
         title.setAttribute('style', 'writing-mode: tb-rl; filter: flipV flipH;display:inline;');
index a2555c3..e0469d3 100644 (file)
  * @param bool $forcedownload whether or not force download
  * @param array $options additional options affecting the file serving
  * @return bool
+ * @todo MDL-36050 improve capability check on stick blocks, so we can check user capability before sending images.
  */
 function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
-    global $SCRIPT;
+    global $DB, $CFG;
 
     if ($context->contextlevel != CONTEXT_BLOCK) {
         send_file_not_found();
     }
 
-    require_course_login($course);
+    // If block is in course context, then check if user has capability to access course.
+    if ($context->get_course_context(false)) {
+        require_course_login($course);
+    } else if ($CFG->forcelogin) {
+        require_login();
+    } else {
+        // Get parent context and see if user have proper permission.
+        $parentcontext = $context->get_parent_context();
+        if ($parentcontext->contextlevel === CONTEXT_COURSECAT) {
+            // Check if category is visible and user can view this category.
+            $category = $DB->get_record('course_categories', array('id' => $parentcontext->instanceid), '*', MUST_EXIST);
+            if (!$category->visible) {
+                require_capability('moodle/category:viewhiddencategories', $parentcontext);
+            }
+        }
+        // At this point there is no way to check SYSTEM or USER context, so ignoring it.
+    }
 
     if ($filearea !== 'content') {
         send_file_not_found();
index cf22705..0e780f1 100644 (file)
@@ -27,6 +27,7 @@ class block_search_forums extends block_base {
 
         $this->content->text  = '<div class="searchform">';
         $this->content->text .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline"><fieldset class="invisiblefieldset">';
+        $this->content->text .= '<legend class="accesshide">'.$strsearch.'</legend>';
         $this->content->text .= '<input name="id" type="hidden" value="'.$this->page->course->id.'" />';  // course
         $this->content->text .= '<label class="accesshide" for="searchform_search">'.$strsearch.'</label>'.
                                 '<input id="searchform_search" name="search" type="text" size="16" />';
index b7f6589..3505c6c 100644 (file)
@@ -70,8 +70,8 @@ echo $OUTPUT->heading(get_string('assignto', 'cohort', format_string($cohort->na
 echo $OUTPUT->notification(get_string('removeuserwarning', 'core_cohort'));
 
 // Get the user_selector we will need.
-$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id));
-$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id));
+$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
+$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
 
 // Process incoming user assignments to the cohort
 
index f137299..37c44d4 100644 (file)
@@ -309,16 +309,16 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
                             CommentHelper.confirmoverlay.set('xy', [e.pageX-width-5, e.pageY]);
                             CommentHelper.confirmoverlay.set('visible', true);
                             Y.one('#canceldelete-'+scope.client_id).on('click', function(e) {
-                                                               e.preventDefault();
+                                e.preventDefault();
                                 scope.cancel_delete();
                                 });
                             Y.Event.purgeElement('#confirmdelete-'+scope.client_id, false, 'click');
                             Y.one('#confirmdelete-'+scope.client_id).on('click', function(e) {
-                                                                       e.preventDefault();
-                                    if (commentid[1]) {
-                                        scope.dodelete(commentid[1]);
-                                    }
-                                });
+                                e.preventDefault();
+                                if (commentid[1]) {
+                                    scope.dodelete(commentid[1]);
+                                }
+                            });
                         }, scope, node);
                     }
                 );
index a04e499..cb1d0c7 100644 (file)
@@ -31,7 +31,6 @@ require_once($CFG->libdir.'/textlib.class.php');
 
 $id = required_param('id', PARAM_INT); // Category id
 $page = optional_param('page', 0, PARAM_INT); // which page to show
-$perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); // how many per page
 $categoryedit = optional_param('categoryedit', -1, PARAM_BOOL);
 $hide = optional_param('hide', 0, PARAM_INT);
 $show = optional_param('show', 0, PARAM_INT);
@@ -41,6 +40,16 @@ $moveto = optional_param('moveto', 0, PARAM_INT);
 $resort = optional_param('resort', 0, PARAM_BOOL);
 $sesskey = optional_param('sesskey', '', PARAM_RAW);
 
+// MDL-27824 - This is a temporary fix until we have the proper
+// way to check/initialize $CFG value.
+// @todo MDL-35138 remove this temporary solution
+if (!empty($CFG->coursesperpage)) {
+    $defaultperpage =  $CFG->coursesperpage;
+} else {
+    $defaultperpage = 20;
+}
+$perpage = optional_param('perpage', $defaultperpage, PARAM_INT); // how many per page
+
 if (empty($id)) {
     print_error("unknowcategory");
 }
index e086dcf..f315d4e 100644 (file)
@@ -254,7 +254,7 @@ $parentlist = array();
 $displaylist[0] = get_string('top');
 make_categories_list($displaylist, $parentlist);
 
-echo '<table class="generalbox editcourse boxaligncenter"><tr class="header">';
+echo '<table class="generaltable editcourse boxaligncenter"><tr class="header">';
 echo '<th class="header" scope="col">'.$strcategories.'</th>';
 echo '<th class="header" scope="col">'.$strcourses.'</th>';
 echo '<th class="header" scope="col">'.$stredit.'</th>';
index ef150d0..7c05094 100644 (file)
@@ -1548,8 +1548,8 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 $linkclasses = '';
                 $textclasses = '';
                 if ($accessiblebutdim) {
-                    $linkclasses .= ' dimmed';
-                    $textclasses .= ' dimmed_text';
+                    $linkclasses .= ' dimmed conditionalhidden';
+                    $textclasses .= ' dimmed_text conditionalhidden';
                     $accesstext = '<span class="accesshide">'.
                         get_string('hiddenfromstudents').': </span>';
                 } else {
@@ -1731,11 +1731,15 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             // see the activity itself, or for staff)
             if (!$mod->uservisible) {
                 echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
-            } else if ($canviewhidden && !empty($CFG->enableavailability) && $mod->visible) {
+            } else if ($canviewhidden && !empty($CFG->enableavailability)) {
+                $visibilityclass = '';
+                if (!$mod->visible) {
+                    $visibilityclass = 'accesshide';
+                }
                 $ci = new condition_info($mod);
                 $fullinfo = $ci->get_full_information();
                 if($fullinfo) {
-                    echo '<div class="availabilityinfo">'.get_string($mod->showavailability
+                    echo '<div class="availabilityinfo '.$visibilityclass.'">'.get_string($mod->showavailability
                         ? 'userrestriction_visible'
                         : 'userrestriction_hidden','condition',
                         $fullinfo).'</div>';
@@ -1872,8 +1876,9 @@ function print_section_add_menus($course, $section, $modnames, $vertical=false,
             $output = html_writer::tag('div', $output, array('class' => 'hiddenifjs addresourcedropdown'));
             $modchooser = html_writer::tag('div', $modchooser, array('class' => 'visibleifjs addresourcemodchooser'));
         } else {
-            $output = html_writer::tag('div', $output, array('class' => 'visibleifjs addresourcedropdown'));
-            $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hiddenifjs addresourcemodchooser'));
+            // If the module chooser is disabled, we need to ensure that the dropdowns are shown even if javascript is disabled
+            $output = html_writer::tag('div', $output, array('class' => 'show addresourcedropdown'));
+            $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hide addresourcemodchooser'));
         }
         $output = $modchooser . $output;
     }
@@ -1970,7 +1975,7 @@ function get_module_metadata($course, $modnames, $sectionreturn = null) {
                 if ($sm->string_exists('modulename_link', $modname)) {  // Link to further info in Moodle docs
                     $link = get_string('modulename_link', $modname);
                     $linktext = get_string('morehelp');
-                    $module->help .= html_writer::tag('div', $OUTPUT->doc_link($link, $linktext), array('class' => 'helpdoclink'));
+                    $module->help .= html_writer::tag('div', $OUTPUT->doc_link($link, $linktext, true), array('class' => 'helpdoclink'));
                 }
             }
             $module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
index 2206a5e..a980841 100644 (file)
@@ -188,17 +188,19 @@ class core_course_renderer extends plugin_renderer_base {
         // Put all options into one tag 'alloptions' to allow us to handle scrolling
         $formcontent .= html_writer::start_tag('div', array('class' => 'alloptions'));
 
-        // Activities
-        $activities = array_filter($modules,
-                create_function('$mod', 'return ($mod->archetype !== MOD_CLASS_RESOURCE);'));
+         // Activities
+        $activities = array_filter($modules, function($mod) {
+            return ($mod->archetype !== MOD_ARCHETYPE_RESOURCE && $mod->archetype !== MOD_ARCHETYPE_SYSTEM);
+        });
         if (count($activities)) {
             $formcontent .= $this->course_modchooser_title('activities');
             $formcontent .= $this->course_modchooser_module_types($activities);
         }
 
         // Resources
-        $resources = array_filter($modules,
-                create_function('$mod', 'return ($mod->archetype === MOD_CLASS_RESOURCE);'));
+        $resources = array_filter($modules, function($mod) {
+            return ($mod->archetype === MOD_ARCHETYPE_RESOURCE);
+        });
         if (count($resources)) {
             $formcontent .= $this->course_modchooser_title('resources');
             $formcontent .= $this->course_modchooser_module_types($resources);
index aea4040..28b9081 100644 (file)
@@ -28,44 +28,12 @@ YUI.add('moodle-course-modchooser', function(Y) {
             };
             this.setup_chooser_dialogue(dialogue, header, params);
 
-            this.jumplink = this.container.one('#jump');
-
             // Initialize existing sections and register for dynamically created sections
             this.setup_for_section();
             M.course.coursebase.register_module(this);
 
             // Catch the page toggle
             Y.all('.block_settings #settingsnav .type_course .modchoosertoggle a').on('click', this.toggle_mod_chooser, this);
-
-            // Ensure that help links are opened in an appropriate popup
-            this.container.all('div.helpdoclink a').on('click', function(e) {
-                var anchor = e.target.ancestor('a', true);
-
-                var args = {
-                    'name'          : 'popup',
-                    'url'           : anchor.getAttribute('href'),
-                    'option'        : ''
-                };
-                var options = [
-                    'height=600',
-                    'width=800',
-                    'top=0',
-                    'left=0',
-                    'menubar=0',
-                    'location=0',
-                    'scrollbars',
-                    'resizable',
-                    'toolbar',
-                    'status',
-                    'directories=0',
-                    'fullscreen=0',
-                    'dependent'
-                ]
-                args.options = options.join(',');
-
-                // Note: openpopup is provided by lib/javascript-static.js
-                openpopup(e, args);
-            });
         },
         /**
          * Update any section areas within the scope of the specified
index 0f70c2c..b3eb066 100644 (file)
@@ -33,7 +33,11 @@ YUI.add('moodle-course-toolboxes', function(Y) {
         SECTIONIDPREFIX : 'section-',
         SECTIONLI : 'li.section',
         SHOW : 'a.editing_show',
-        SHOWHIDE : 'a.editing_showhide'
+        SHOWHIDE : 'a.editing_showhide',
+        CONDITIONALHIDDEN : 'conditionalhidden',
+        AVAILABILITYINFODIV : 'div.availabilityinfo',
+        SHOWCLASS : 'editing_show',
+        ACCESSHIDECLASS : 'accesshide'
     };
 
     /**
@@ -68,18 +72,14 @@ YUI.add('moodle-course-toolboxes', function(Y) {
 
             var status = '';
             var value;
-            if (dimarea.hasClass(toggle_class)) {
+            if (button.hasClass(CSS.SHOWCLASS)) {
                 status = 'hide';
                 value = 1;
             } else {
                 status = 'show';
                 value = 0;
             }
-
-            // Change the UI
-            dimarea.toggleClass(toggle_class);
-            // We need to toggle dimming on the description too
-            element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
+            // Update button info.
             var newstring = M.util.get_string(status, 'moodle');
             hideicon.setAttrs({
                 'alt' : newstring,
@@ -88,6 +88,19 @@ YUI.add('moodle-course-toolboxes', function(Y) {
             button.set('title', newstring);
             button.set('className', 'editing_'+status);
 
+            // If activity is conditionally hidden, then don't toggle.
+            if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
+                // Change the UI.
+                dimarea.toggleClass(toggle_class);
+                // We need to toggle dimming on the description too.
+                element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
+            }
+            // Toggle availablity info for conditional activities.
+            var availabilityinfo = element.one(CSS.AVAILABILITYINFODIV);
+
+            if (availabilityinfo) {
+                availabilityinfo.toggleClass(CSS.ACCESSHIDECLASS);
+            }
             return value;
         },
         /**
@@ -446,10 +459,16 @@ YUI.add('moodle-course-toolboxes', function(Y) {
          */
         add_moveleft : function(target) {
             var left_string = M.util.get_string('moveleft', 'moodle');
+            var moveimage = 't/left'; // ltr mode
+            if ( Y.one(document.body).hasClass('dir-rtl') ) {
+                moveimage = 't/right';
+            } else {
+                moveimage = 't/left';
+            }
             var newicon = Y.Node.create('<img />')
                 .addClass(CSS.GENERICICONCLASS)
                 .setAttrs({
-                    'src'   : M.util.image_url('t/left', 'moodle'),
+                    'src'   : M.util.image_url(moveimage, 'moodle'),
                     'alt'   : left_string
                 });
             var moveright = target.one(CSS.MOVERIGHT);
index a8a6557..d65b83d 100644 (file)
@@ -35,10 +35,11 @@ if (!$course = $DB->get_record("course", array("id"=>$id))) {
 }
 
 $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+$PAGE->set_context($context);
 
 require_login();
 
-if ($SESSION->wantsurl) {
+if (!empty($SESSION->wantsurl)) {
     $destination = $SESSION->wantsurl;
     unset($SESSION->wantsurl);
 } else {
index fafbfd7..1a98a46 100644 (file)
@@ -431,7 +431,7 @@ class course_enrolment_table extends html_table implements renderable {
 
         $this->page           = optional_param(self::PAGEVAR, 0, PARAM_INT);
         $this->perpage        = optional_param(self::PERPAGEVAR, self::DEFAULTPERPAGE, PARAM_INT);
-        $this->sort           = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHA);
+        $this->sort           = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHANUM);
         $this->sortdirection  = optional_param(self::SORTDIRECTIONVAR, self::DEFAULTSORTDIRECTION, PARAM_ALPHA);
 
         $this->attributes = array('class'=>'userenrolment');
index f49a799..66cddda 100644 (file)
@@ -6,7 +6,7 @@
 .moodle-dialogue-base .moodle-dialogue-hd {font-size:110%;color:inherit;font-weight:bold;text-align:left;padding:5px 6px;margin:0;border-bottom:1px solid #ccc;background-color:#f6f6f6;}
 .moodle-dialogue-base .closebutton {background-image:url(sprite.png);width:25px;height:15px;background-repeat:no-repeat;float:right;vertical-align:middle;display:inline-block;cursor:pointer;}
 .moodle-dialogue-base .moodle-dialogue-bd {padding:5px; overflow: auto;}
-.moodle-dialogue-base .moodle-dialogue-fd {}
+.moodle-dialogue-base .moodle-dialogue-ft {}
 
 .moodle-dialogue-confirm .confirmation-dialogue {text-align:center;}
 .moodle-dialogue-confirm .confirmation-message {margin:0.5em 1em;}
index 8b91443..2ed9af0 100644 (file)
@@ -21,7 +21,7 @@ var DIALOGUE_NAME = 'Moodle dialogue',
         HEADER : 'moodle-dialogue-hd',
         BODY : 'moodle-dialogue-bd',
         CONTENT : 'moodle-dialogue-content',
-        FOOTER : 'moodle-dialogue-fd',
+        FOOTER : 'moodle-dialogue-ft',
         HIDDEN : 'hidden',
         LIGHTBOX : 'moodle-dialogue-lightbox'
     };
@@ -35,7 +35,7 @@ var DIALOGUE = function(config) {
             .append(C('<div id="'+id+'" class="'+CSS.WRAP+'"></div>')
                 .append(C('<div class="'+CSS.HEADER+' yui3-widget-hd"></div>'))
                 .append(C('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
-                .append(C('<div class="'+CSS.CONTENT+' yui3-widget-ft"></div>')));
+                .append(C('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
     Y.one(document.body).append(config.notificationBase);
     config.srcNode =    '#'+id;
     config.width =      config.width || '400px';
index a18d0c1..4ca8065 100644 (file)
@@ -48,7 +48,7 @@ require_capability('moodle/grade:manage', $context);
 
 // default return url
 $gpr = new grade_plugin_return();
-$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report.php?id='.$course->id);
+$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id);
 
 if (!$grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$course->id))) {
     print_error('invaliditemid');
index d44b2ca..2d61e68 100644 (file)
@@ -57,7 +57,7 @@ if (!has_capability('moodle/grade:manage', $context)) {
 
 // default return url
 $gpr = new grade_plugin_return();
-$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report.php?id='.$course->id);
+$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id);
 
 // security checks!
 if (!empty($id)) {
index d7d3b5b..a1e634b 100644 (file)
@@ -1503,7 +1503,7 @@ class grade_structure {
 
             $url->param('action', 'show');
 
-            $hideicon = $OUTPUT->action_icon($url, new pix_icon('t/'.$type, $tooltip, 'moodle', array('alt'=>$strshow, 'class'=>'iconsmall')));
+            $hideicon = $OUTPUT->action_icon($url, new pix_icon('t/'.$type, $tooltip, 'moodle', array('alt'=>$strshow, 'class'=>'smallicon')));
 
         } else {
             $url->param('action', 'hide');
index 4549995..2770aed 100644 (file)
@@ -107,7 +107,7 @@ grade_regrade_final_grades($courseid);
 
 // Perform actions
 if (!empty($target) && !empty($action) && confirm_sesskey()) {
-    grade_report_grader::process_action($target, $action);
+    grade_report_grader::do_process_action($target, $action);
 }
 
 $reportname = get_string('pluginname', 'gradereport_grader');
index ea8f076..3b4cc42 100644 (file)
@@ -225,7 +225,7 @@ class grade_report_grader extends grade_report {
                 $changedgrades = true;
 
             } else if ($datatype === 'feedback') {
-                if ($oldvalue->feedback === $postedvalue) {
+                if (($oldvalue->feedback === $postedvalue) or ($oldvalue->feedback === NULL and empty($postedvalue))) {
                     continue;
                 }
             }
@@ -1580,13 +1580,17 @@ class grade_report_grader extends grade_report {
         return $icon;
     }
 
+    public function process_action($target, $action) {
+        return self::do_process_action($target, $action);
+    }
+
     /**
      * Processes a single action against a category, grade_item or grade.
      * @param string $target eid ({type}{id}, e.g. c4 for category4)
      * @param string $action Which action to take (edit, delete etc...)
      * @return
      */
-    public function process_action($target, $action) {
+    public static function do_process_action($target, $action) {
         // TODO: this code should be in some grade_tree static method
         $targettype = substr($target, 0, 1);
         $targetid = substr($target, 1);
index cd2e951..1058c07 100644 (file)
@@ -348,13 +348,17 @@ class grade_report_user extends grade_report {
                     ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil()))) {
                 $hide = true;
             } else if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) {
-                // The grade object can be marked visible but still be hidden if "enablegroupmembersonly"
-                // is on and it's an activity assigned to a grouping the user is not in.
+                // The grade object can be marked visible but still be hidden if...
+                //  1) "enablegroupmembersonly" is on and the activity is assigned to a grouping the user is not in.
+                //  2) the student cannot see the activity due to conditional access and its set to be hidden entirely.
                 $instances = $this->gtree->modinfo->get_instances_of($grade_object->itemmodule);
                 if (!empty($instances[$grade_object->iteminstance])) {
                     $cm = $instances[$grade_object->iteminstance];
-                    if ($cm->is_user_access_restricted_by_group()) {
-                        $hide = true;
+                    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()) {
+                            $hide = true;
+                        }
                     }
                 }
             }
index c737e63..69118df 100644 (file)
@@ -56,10 +56,8 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
 @error_reporting(E_ALL);
 @ini_set('display_errors', '1');
 
-// Check that PHP is of a sufficient version
-// PHP 5.2.0 is intentionally checked here even though a higher version is required by the environment
-// check. This is not a typo - see MDL-18112
-if (version_compare(phpversion(), "5.2.0") < 0) {
+// Check that PHP is of a sufficient version.
+if (version_compare(phpversion(), '5.3.2') < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN not move it after installib
     echo "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).<br />";
diff --git a/install/lang/en_kids/langconfig.php b/install/lang/en_kids/langconfig.php
new file mode 100644 (file)
index 0000000..7a195a4
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['thislanguage'] = 'English for kids';
index 57673d4..006fcb9 100644 (file)
@@ -54,14 +54,14 @@ $string['memorylimithelp'] = '<p>PHP muistiraja palvelimellesi on tällä hetkel
 
 <p>Tämä saattaa aiheuttaa Moodlelle muistiongelmia myöhemmin, varsinkin jos sinulla on paljon mahdollisia moduuleita ja/tai paljon käyttäjiä.</p>
 
-<p>Suosittelemme, että valitset asetuksiksi PHP:n korkeimmalla mahdollisella raja-arvolla, esimerkiksi 16M.
+<p>Suosittelemme, että valitset asetuksiksi PHP:n korkeimmalla mahdollisella raja-arvolla, esimerkiksi 40M.
 On olemassa monia tapoja joilla voit yrittää tehdä tämän:</p>
 <ol>
 <li>Jos pystyt, uudelleenkäännä PHP <i>--enable-memory-limit</i>. :llä.
 Tämä sallii Moodlen asettaa muistirajan itse.</li>
-<li>Jos sinulla on pääsy php.ini tiedostoosi, voit muuttaa <b>memory_limit</b> asetuksen siellä johonkin kuten 16M. Jos sinulla ei ole pääsyoikeutta, voit kenties pyytää ylläpitäjää tekemään tämän puolestasi.</li>
+<li>Jos sinulla on pääsy php.ini tiedostoosi, voit muuttaa <b>memory_limit</b> asetuksen siellä johonkin kuten 40M. Jos sinulla ei ole pääsyoikeutta, voit kenties pyytää ylläpitäjää tekemään tämän puolestasi.</li>
 <li>Joillain PHP palvelimilla voit luoda a .htaccess tiedoston Moodle hakemistossa, sisältäen tämän rivin:
-<p><blockquote>php_value memory_limit 16M</blockquote></p>
+<p><blockquote>php_value memory_limit 40M</blockquote></p>
 <p>Kuitenkin, joillain palvelimilla tämä estää  <b>kaikkia</b> PHP sivuja toimimasta (näet virheet, kun katsot sivuja), joten sinun täytyy poistaa .htaccess tiedosto.</p></li>
 </ol>';
 $string['paths'] = 'Polut';
index 62aec64..17b22ef 100644 (file)
@@ -31,6 +31,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['language'] = 'Jezik';
-$string['next'] = 'Nastavi';
+$string['next'] = 'Nastavite';
 $string['previous'] = 'Prethodni';
 $string['reload'] = 'Učitaj ponovno';
diff --git a/install/lang/ja_kids/langconfig.php b/install/lang/ja_kids/langconfig.php
new file mode 100644 (file)
index 0000000..6eb3957
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['parentlanguage'] = 'ja';
+$string['thisdirection'] = 'ltr';
+$string['thislanguage'] = '日本語 小学生';
diff --git a/install/lang/lt/install.php b/install/lang/lt/install.php
new file mode 100644 (file)
index 0000000..cf54d95
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['admindirname'] = 'Administratoriaus aplankas';
+$string['availablelangs'] = 'Galimų kalbų sąrašas';
+$string['chooselanguagehead'] = 'Pasirinkite kalbą';
+$string['chooselanguagesub'] = 'Prašome pasirinkti diegimo kalbą. Ši kalba taip pat taps numatytąją svetainės kalba. Tačiau vėliau, jei norėsite, Jūs galėsite ją pakeisti.';
+$string['clialreadyconfigured'] = 'config.php failas jau egzistuoja. Prašome naudotis admin/cli/install_database.php, jei Jūs norite įdiegti šią svetainę.';
+$string['clialreadyinstalled'] = 'config.php failas jau egzistuoja. Prašome naudoti admin/cli/upgrade.php jei Jūs norite išplėtoti šią svetainę.';
+$string['cliinstallheader'] = '"Moodle" {$a} komandinės eilutės įdiegimo programa';
+$string['databasehost'] = 'Duomenų bazės serveris';
+$string['databasename'] = 'Duomenų bazės pavadinimas';
+$string['databasetypehead'] = 'Pasirinkite duomenų bazės tvarkyklę';
+$string['dataroot'] = 'Duomenų aplankas';
+$string['datarootpermission'] = 'Duomenų aplanko leidimai';
+$string['dbprefix'] = 'Lentelių priešdėlis';
+$string['dirroot'] = '"Moodle" aplankas';
+$string['environmenthead'] = 'Tikrinama Jūsų aplinka...';
+$string['environmentsub2'] = 'Kiekviena "Moodle" laida turi kai kuriuos minimalius reikalavimus PHP versijai ir tam tikrą privalomų PHP plėtinių skaičių.
+Pilnas aplinkos patikrinimas yra padaromas prieš kiekvieną įdiegimą ar plėtotę. Prašome susisiekti su serverio administratoriumi, jeigu Jūs nežinote kaip įdiegti naują versiją ar įgalinti PHP plėtinius.';
+$string['errorsinenvironment'] = 'Aplinkos patikrinimas nesėkmingas!';
+$string['installation'] = 'Įdiegimas';
+$string['langdownloaderror'] = 'Deja "{$a}" kalba negali būti parsiųsta. Diegimo procesas bus tęsiamas anglų kalba.';
+$string['memorylimithelp'] = '<p>Šiuo metu Jūsų serverio PHP atminties limitas yra {$a}.</p>
+
+<p>Vėliau tai gali sukelti atminties trūkumo problemų "Moodle", ypač jei Jūs turite daug įgalintų modulių ir/ar daug vartotojų.</p>
+
+<p>Mes rekomenduojame suderinti PHP taip, kad jis turėtų kiek galima didesnį limitą, pvz.: 40MB.
+ Tam yra keletas būdų, kuriuos Jūs galite pabandyti:</p>
+<ol>
+<li>Jei Jūs galite, sukompiliuokite iš naujo PHP panaudodami <i>--enable-memory-limit</i> raktą.
+   Tai leis nustatyti atminties limitą pačiai "Moodle".</li>
+<li>Jei Jūs turite galimybę koreguoti Jūsų "php.ini" failą, tada pakeiskite <b>memory_limit</b> nuostatą į kažką artimo 40MB. Jei Jūs negalite koreguoti, tuomet gal galite paprašyti, kad Jūsų serverio administratorius tai padarytų už Jus.</li>
+<li>Kai kuriuose PHP serveriuose Jūs galite sukurti ".htaccess" failą "Moodle" aplanke ir įrašykite į jį šią eilutę:
+<blockquote><div>php_value memory_limit 40M</div></blockquote>
+<p>Tačiau kai kuriuose serveriuose tai neleis veikti <b>visiems</b> PHP puslapiams (Jūs matysite klaidas bandydami peržiūrėti tinklapius). Taigi Jums gali tekti pašalinti ".htaccess" failą.</p></li>
+</ol>';
+$string['paths'] = 'Keliai';
+$string['pathserrcreatedataroot'] = 'Diegiklis negali sukurti duomenų katalogo ({$a->dataroot}).';
+$string['pathshead'] = 'Patvirtinkite kelius';
+$string['pathsrodataroot'] = 'Dataroot katalogas yra neįrašomas';
+$string['pathsroparentdataroot'] = 'Virškatalogis ({$a->parent}) yra neįrašomas. Diegiklis negali sukurti duomenų katalogo ({$a->dataroot}).';
+$string['pathssubadmindir'] = 'Nedaugelis interneto paslaugų tiekėjų naudoja /admin kaip specialų URL skirtą prisijungti prie valdymo skydo ar panašiai. Deja tai kelia konfliktus su įprastais Moodle administravimo puslapių talpinimo keliais. Jūs galite tai pataisyti
+pervardydami admin katalogą Jūsų diegime bei įrašydami naują pavadinimą čia. Pavyzdžiui: <em>moodleadmin</em>. Tai pakeis visas admin nuorodas Moodle diegime.';
+$string['pathssubdataroot'] = 'Jums reikia vietos kur Moodle gali išsaugoti įkeliamus failus. Šis katalogas turi būti skaitomas IR ĮRAŠOMAS web serverio naudotojo
+(dažniausiai tai \'nobody\' arba \'apache\'), tačiau jis neturi būti pasiekiamas tiesiogiai per internetą. Diegiklis pabandys sukurti katalogą, jei tokio nėra.';
+$string['pathssubdirroot'] = 'Pilnas kelias iki Moodle diegimo vietos.';
+$string['pathssubwwwroot'] = 'Pilnas internetinis adresas, kuriuo bus pasiekiamas Moodle.
+Pasiekti Moodle naudojantis keliais adresais yra neįmanoma.
+Jei Jūsų svetainėje yra keletas viešų adresų, Jūs turite visuose adresuose nustatyti pastovų peradresavimą į šį adresą.
+Jei Jūsų svetainė yra pasiekiama ir iš Intraneto, ir iš Interneto - panaudokite viešą adresą čia ir nustatykite DNS taip, kad Intraneto naudotojai taip pat galėtų matyti viešą adresą.
+Jei adresas neteisingas - prašome pakeisti URL Jūsų naršyklėje ir pradėti diegimą su nauju adresu.';
+$string['pathsunsecuredataroot'] = 'Dataroot katalogo vieta yra nesaugi.';
+$string['pathswrongadmindir'] = 'Admin katalogas neegzistuoja';
+$string['phpextension'] = '{$a} PHP plėtinys';
+$string['phpversion'] = 'PHP versija';
+$string['phpversionhelp'] = '<p>Moodle reikalauja, kad būtų įdiegta bent 4.3.0 arba 5.1.0 PHP versija (5.0.x versija turi keletą žymių problemų).
+</p>
+<p>Jūs šiuo metu naudojate {$a} versiją</p>
+<p>Jūs turite išplėtoti turimą PHP versiją iki naujesnės arba persikelti pas kitą interneto paslaugų tiekėją, turintį naujesnę PHP versiją!<br/>
+(Turint 5.0.x versiją, Jūs turėtumėte pereiti prie žemesnės 4.4.x versijos)</p>';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['welcomep20'] = 'Jūs matote šį puslapį, nes sėkmingai įdiegėte ir užkrovėte <strong>{$a->packname} {$a->packversion}</strong> paketą savo kompiuteryje.
+Sveikiname!';
+$string['welcomep30'] = 'Šis <strong>{$a->installername}</strong> leidimas turi programas skirtas sukurti aplinką, kurioje <strong>Moodle</strong> veiks. Būtent:';
+$string['welcomep40'] = 'Šis paketas taip pat turi  <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
+$string['welcomep50'] = 'Pakete esančių programų naudojimas yra reguliuojamas atitinkamų licencijų. Pilnas <strong>{$a->installername}</strong> paketas  yra <a href="http://www.opensource.org/docs/definition_plain.html">atviro kodo</a> ir platinamas remiantis <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licencija.';
+$string['welcomep60'] = 'Sekantys puslapiai ves Jus per keletą lengvų žingsnių, kurie padės sukonfigūruoti ir nustatyti <strong>Moodle</strong> Jūsų kompiuteryje. Jūs galite priimti nustatymus pagal nutylėjimą arba, pasirinktinai, pakeisti juos pagal savo poreikius.';
+$string['welcomep70'] = 'Spauskite "Toliau" mygtuką, norėdami tęsti <strong>Moodle</strong> nustatymą.';
+$string['wwwroot'] = 'Interneto adresas';
index 145ec13..44849c0 100644 (file)
@@ -33,3 +33,4 @@ defined('MOODLE_INTERNAL') || die();
 $string['language'] = 'Bahasa';
 $string['next'] = 'Seterusnya';
 $string['previous'] = 'Sebelum';
+$string['reload'] = 'Muat semula';
index 26b9927..8d39a3c 100644 (file)
@@ -42,7 +42,7 @@ $string['componentisuptodate'] = 'Komponenten er oppdatert';
 $string['downloadedfilecheckfailed'] = 'Sjekk av nedlastet fil mislykkes.';
 $string['invalidmd5'] = 'Ugyldig md5, prøv igjen';
 $string['missingrequiredfield'] = 'Noen påkrevde felt mangler';
-$string['remotedownloaderror'] = 'Mislykkes i å laste ned komponenten til din server, vennligst sjekk proxy-innstillingene. PHP cURL tillegget er sterkt anbefalt. <br /><br />Du må laste ned <a href="{$a->url}">{$a->url}</a> filen manuelt, kopiere den til "{$a->dest}" på serveren din og pakke den ut der.';
+$string['remotedownloaderror'] = 'Mislykkes i å laste ned komponenten til din server, vennligst sjekk proxy-innstillingene. PHP cURL-tillegget er sterkt anbefalt. <br /><br />Du må laste ned <a href="{$a->url}">{$a->url}</a> filen manuelt, kopiere den til "{$a->dest}" på serveren din og pakke den ut der.';
 $string['wrongdestpath'] = 'Gal målmappe';
 $string['wrongsourcebase'] = 'Feil kilde URL base';
 $string['wrongzipfilename'] = 'Galt ZIP-filnavn.';
index bd29cd8..e414a2a 100644 (file)
@@ -72,7 +72,7 @@ $string['pathssubwwwroot'] = 'Full webadresse til der hvor Moodle skal vises. De
 Dersom adressen ikke er korrekt, vennligst endre URL i nettleseren slik at at installasjonen restartes med andre verdier.';
 $string['pathsunsecuredataroot'] = 'Dataroot plassering er ikke sikker';
 $string['pathswrongadmindir'] = 'Adminkatalog finnes ikke';
-$string['phpextension'] = '{$a} PHP etternavn';
+$string['phpextension'] = '{$a} PHP-tillegg';
 $string['phpversion'] = 'PHP versjon';
 $string['phpversionhelp'] = '<p>Moodle trenger en PHP versjon minst 4.3.0 eller 5.1.0 (5.0.x har rekke kjente problem).</p>
 <Du kjører nå versjon {$a}</p>
index 5eb6cc3..43f6dd0 100644 (file)
@@ -34,8 +34,8 @@ $string['clianswerno'] = 'n';
 $string['cliansweryes'] = 'y';
 $string['cliincorrectvalueerror'] = '錯誤,不正確的值 "{$a->value}" 用於 "{$a->option}"';
 $string['cliincorrectvalueretry'] = '不正確的值,請再試一次';
-$string['clitypevalue'] = '打入一值';
-$string['clitypevaluedefault'] = '打入一值,按Enter可使用預設值({$a})';
+$string['clitypevalue'] = '輸入值';
+$string['clitypevaluedefault'] = '輸入值,按Enter可使用預設值({$a})';
 $string['cliunknowoption'] = '不認得的選項:  {$a}
 請使用 --幫助 選項。';
 $string['cliyesnoprompt'] = '輸入y(是) 或n(否)';
index e678417..28af033 100644 (file)
@@ -39,10 +39,10 @@ $string['cannotsavemd5file'] = '無法儲存 md5 檔案。';
 $string['cannotsavezipfile'] = '無法儲存 ZIP 檔案。';
 $string['cannotunzipfile'] = '無法解壓縮檔案。';
 $string['componentisuptodate'] = '元件已經是最新的了。';
-$string['downloadedfilecheckfailed'] = '下載檔案檢查錯誤。';
-$string['invalidmd5'] = '無效的 md5';
+$string['downloadedfilecheckfailed'] = '下載的檔案檢查失敗';
+$string['invalidmd5'] = '這檢查變項是錯的,再試一次。';
 $string['missingrequiredfield'] = '缺少部份必填欄位';
 $string['remotedownloaderror'] = '下載元件至伺服器失敗,檢查代理伺服器的設定、高度建議安裝PHP cURL,您必須手動下載<a href="{$a->url}">{$a->url}</a>,並且複製到伺服器"{$a->dest}" 解壓縮';
 $string['wrongdestpath'] = '錯誤的目的路徑。';
-$string['wrongsourcebase'] = '錯誤的來源網址基礎。';
+$string['wrongsourcebase'] = '錯誤的來源基礎網址。';
 $string['wrongzipfilename'] = '錯誤的 ZIP 檔名。';
index 07cb23e..ddfffbe 100644 (file)
@@ -33,23 +33,45 @@ defined('MOODLE_INTERNAL') || die();
 $string['admindirname'] = '管理目錄';
 $string['availablelangs'] = '可使用的語言包';
 $string['chooselanguagehead'] = '選擇一種語言';
-$string['chooselanguagesub'] = '請選擇在安裝過程中使用的語言。稍後您可以根據需要重新選擇用於網站和使用者的語言。';
+$string['chooselanguagesub'] = '請選擇在安裝過程中使用的語言。這語言將成為此網站預設的語言。稍後您可以根據需要重新選擇。';
+$string['clialreadyconfigured'] = '檔案 config.php  已經存在,若你要安裝這一網站,請使用dmin/cli/install_database.php';
+$string['clialreadyinstalled'] = '檔案 config.php  已經存在,若你要升級這一網站,請使用admin/cli/upgrade.php';
+$string['cliinstallheader'] = 'Moodle {$a} 命令列安裝程式';
+$string['databasehost'] = '資料庫主機';
+$string['databasename'] = '資料庫名稱';
+$string['databasetypehead'] = '選擇資料庫裝置';
 $string['dataroot'] = '資料目錄';
+$string['datarootpermission'] = '資料目錄存取授權';
 $string['dbprefix'] = '資料表名稱的前置字元';
 $string['dirroot'] = 'Moodle目錄';
 $string['environmenthead'] = '檢查您的環境中...';
+$string['environmentsub2'] = '每一個Moodle版本都有一些PHP版本的最低要求和一堆強制開啟的PHP擴展。在進行安裝或升級之前都需要作完整的環境檢查。<br />
+若你不知道要怎樣新的PHP版本或啟用PHP擴展,請聯絡伺服器管理員。';
+$string['errorsinenvironment'] = '環境檢查失敗!';
 $string['installation'] = '安裝';
 $string['langdownloaderror'] = '很不幸地,語言“{$a}”並未安裝。安裝過程將以英文繼續。';
 $string['memorylimithelp'] = '<p>PHP記憶體上限目前設定為{$a}。</p>
-<p>稍後它可能會造成Moodle記憶體的問題,尤其是您啟動了很多的模組及大量的使用者後。
-<p>建議您儘可能將PHP的上限設得高一點,比如16M。
+<p>稍後它可能會造成Moodle記憶體的問題,尤其是您啟動了很多的模組及大量的用戶之後。
+<p>建議您儘可能將PHP的上限設得高一點,比如40M。
 以下有幾種方式您可以試試:
 <ol>
-<li>如果可以的話,用<i>--enable-memory-limit</i>重新編譯PHP。讓Moodle自己設定記憶體上限.
-<li>如果您要使用php.ini檔, 您可以改變<b>memory_limit</b>這個設定值,例如到16M。如果您無法使用這個檔,您可以請您的管理者幫您做
-<li>在一些PHP伺服器上,您可以在Moodle目錄下,建立.htaccess檔,包含這行:<p><blockquote>php_value memory_limit 16M</blockquote></p>
+<li>如果可以的話,用<i>--enable-memory-limit</i>重新編譯PHP。讓 Moodle 自己設定記憶體上限。
+<li>如果您要使用 php.ini 檔,您可以改變<b>memory_limit</b>這個設定值,例如到40M。如果您無法使用這個檔,您可以請您的管理者幫您做
+<li>在一些PHP伺服器上,您可以在Moodle目錄下,建立 .htaccess 檔,包含這行:<p><blockquote>php_value memory_limit 16M</blockquote></p>
 <p>然而,在一些伺服器上,這將造成<b>所有的</b> PHP 網頁無法運作(當您看這些網頁時,您就會看到錯誤) 因此,您就必須將 .htaccess 檔案移除。
 </ol>';
+$string['paths'] = '路徑';
+$string['pathserrcreatedataroot'] = '資料目錄 ({$a->dataroot})無法由這安裝程式建立';
+$string['pathshead'] = '確認路徑';
+$string['pathsrodataroot'] = '資料根目錄是無法寫入的';
+$string['pathsroparentdataroot'] = '上層目錄({$a->parent})是不可寫入的。安裝程式無法建立資料目錄({$a->dataroot})。';
+$string['pathssubadmindir'] = '有些網站主機使用/admin這個網址來瀏覽控制面版或其他功能。很不幸,這個設定和Moodle管理頁面的標準路徑產生衝突。這個問題可以解決,只需在您的安裝目錄中把admin更換名稱,然後把新名稱輸入到這裡。例如<em>moodleadmin</em>這麼做會改變Moodle中的管理連接。';
+$string['pathssubdataroot'] = '你需要有一個地方讓Moodle可以儲存上傳的檔案。這一目錄對於網頁伺服器用戶(通常是"nobody"或"apache")而言,應該是可讀的和<b>可寫的</b>。但是它必須不能經由網頁直接存取。若此目錄不存在,這安裝程式將會試著建立它。';
+$string['pathssubdirroot'] = 'Moodle安裝的完整目錄路徑。';
+$string['pathssubwwwroot'] = '可以瀏覽到Moodle的完整網址。Moodle不支援透過多個網址瀏覽,如果您的網站有多個公開網址,您必須把這個網址以外的網址都設定為永久重新導向。如果您的網站可以透過內部網址瀏覽,有可以透過這個公開網址瀏覽,那麼請設定DNS使網內用戶也能使用這公開的網址。如果此網址不正確,請在你的瀏覽器中修改URL來重新安裝,並設定另一個網址。';
+$string['pathsunsecuredataroot'] = '資料根(Dataroot)目錄的位置不安全';
+$string['pathswrongadmindir'] = '管理目錄不存在';
+$string['phpextension'] = '{$a} PHP擴展';
 $string['phpversion'] = 'PHP版本';
 $string['phpversionhelp'] = '<p>Moodle 需要至少4.1.0.的PHP版本 </p>
 <p>您目前執行的是{$a} 版</p>
index 919a7a9..5f47b26 100644 (file)
@@ -997,7 +997,7 @@ $string['updateavailable_version'] = 'Version {$a}';
 $string['updateavailablenot'] = 'Your Moodle code is up-to-date!';
 $string['updatenotifications'] = 'Update notifications';
 $string['updatenotificationfooter'] = 'Your Moodle site {$a->siteurl} is configured to automatically check for available updates. You are receiving this message as the administrator of the site. You can disable automatic checks for available updates in the Site administration section of the Settings block. You can customize the delivery of this message via your personal Messaging setting in the My profile settings section.';
-$string['updatenotificationsubject'] = 'There are available updates for your Moodle site';
+$string['updatenotificationsubject'] = 'Moodle updates are available ({$a->siteurl})';
 $string['updateautocheck'] = 'Automatically check for available updates';
 $string['updateautocheck_desc'] = 'If enabled, your site will automatically check for available updates for both Moodle code and all additional plugins. If there is a new update available, a notification will be sent to site admins.';
 $string['updateminmaturity'] = 'Required code maturity';
index d55667d..d926cd2 100644 (file)
@@ -25,6 +25,7 @@
 
 $string['addfields'] = 'Add {$a} fields to form';
 $string['advancedelement'] = 'Advanced element';
+$string['close'] = 'Close';
 $string['day'] = 'Day';
 $string['display'] = 'Display';
 $string['err_alphanumeric'] = 'You must enter only letters or numbers here.';
index 57d643f..b278c14 100644 (file)
  */
 
 class Bennu {
-    function timestamp_to_datetime($t = NULL) {
+    static function timestamp_to_datetime($t = NULL) {
         if($t === NULL) {
             $t = time();
         }
         return gmstrftime('%Y%m%dT%H%M%SZ', $t);
     }
 
-    function generate_guid() {
+    static function generate_guid() {
         // Implemented as per the Network Working Group draft on UUIDs and GUIDs
     
         // These two octets get special treatment
index 6614b39..d264083 100644 (file)
@@ -14,7 +14,7 @@
  */
 
 class iCalendar_parameter {
-    function multiple_values_allowed($parameter) {
+    static function multiple_values_allowed($parameter) {
         switch($parameter) {
             case 'DELEGATED-FROM':
             case 'DELEGATED-TO':
@@ -25,7 +25,7 @@ class iCalendar_parameter {
         }
     }
 
-    function default_value($parameter) {
+    static function default_value($parameter) {
         switch($parameter) {
             case 'CUTYPE':   return 'INDIVIDUAL';
             case 'FBTYPE':   return 'BUSY';
@@ -38,7 +38,7 @@ class iCalendar_parameter {
         }
     }
 
-    function is_valid_value(&$parent_property, $parameter, $value) {
+    static function is_valid_value(&$parent_property, $parameter, $value) {
         switch($parameter) {
             // These must all be a URI
             case 'ALTREP':
@@ -191,7 +191,7 @@ class iCalendar_parameter {
         }
     }
 
-    function do_value_formatting($parameter, $value) {
+    static function do_value_formatting($parameter, $value) {
         switch($parameter) {
             // Parameters of type CAL-ADDRESS or URI MUST be double-quoted
             case 'ALTREP':
@@ -232,7 +232,7 @@ class iCalendar_parameter {
         }
     }
 
-    function undo_value_formatting($parameter, $value) {
+    static function undo_value_formatting($parameter, $value) {
     }
 
 }
index e2eb6d1..d603634 100644 (file)
@@ -50,6 +50,7 @@ Modified:
  * string processing - uses our lang framework
  * form hacks
  * MDL-27890 - allow editor to be smaller
+ * MDL-25736 - French spellchecker fixes.
 
  TODO:
  * update strings to integrate with AMOS
index afb60da..e3acf2d 100644 (file)
@@ -39,7 +39,7 @@ class GoogleSpell extends SpellChecker {
                $matches = $this->_getMatches($lang, $word);\r
 \r
                if (count($matches) > 0)\r
-                       $sug = explode("\t", utf8_encode($this->_unhtmlentities($matches[0][4])));\r
+                       $sug = explode("\t", $this->_unhtmlentities($matches[0][4]));\r
 \r
                // Remove empty\r
                foreach ($sug as $item) {\r
index 1109b22..9414db0 100644 (file)
@@ -220,7 +220,7 @@ YUI.add('moodle-form-dateselector', function(Y) {
                     config.september,
                     config.october,
                     config.november,
-                    config.december ],
+                    config.december ]
             });
             this.calendar.changePageEvent.subscribe(function(){
                 this.fix_position();
index 3ace654..4b1307e 100644 (file)
@@ -81,11 +81,19 @@ class google_docs {
         if ($search) {
             $url.='?q='.urlencode($search);
         }
-        $content = $this->googleoauth->get($url);
-
-        $xml = new SimpleXMLElement($content);
 
         $files = array();
+        $content = $this->googleoauth->get($url);
+        try {
+            if (strpos($content, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($content);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
         foreach ($xml->entry as $gdoc) {
             $docid  = (string) $gdoc->children('http://schemas.google.com/g/2005')->resourceId;
             list($type, $docid) = explode(':', $docid);
@@ -317,10 +325,19 @@ class google_picasa {
      * @return mixes $files Array in the format get_listing uses for folders
      */
     public function get_albums() {
+        $files = array();
         $content = $this->googleoauth->get(self::LIST_ALBUMS_URL);
-        $xml = new SimpleXMLElement($content);
 
-        $files = array();
+        try {
+            if (strpos($content, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($content);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
 
         foreach ($xml->entry as $album) {
             $gphoto = $album->children('http://schemas.google.com/photos/2007');
@@ -338,7 +355,6 @@ class google_picasa {
                 'thumbnail_height' => 160,
                 'children' => array(),
             );
-
         }
 
         return $files;
@@ -352,12 +368,20 @@ class google_picasa {
      * @return mixed $files A list of files for the file picker
      */
     public function get_photo_details($rawxml) {
+        $files = array();
 
-        $xml = new SimpleXMLElement($rawxml);
+        try {
+            if (strpos($rawxml, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($rawxml);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
         $this->lastalbumname = (string)$xml->title;
 
-        $files = array();
-
         foreach ($xml->entry as $photo) {
             $gphoto = $photo->children('http://schemas.google.com/photos/2007');
 
index 12afe84..8378b78 100644 (file)
@@ -1423,30 +1423,33 @@ class grade_item extends grade_object {
      * Refetch grades from modules, plugins.
      *
      * @param int $userid optional, limit the refetch to a single user
+     * @return bool Returns true on success or if there is nothing to do
      */
     public function refresh_grades($userid=0) {
         global $DB;
         if ($this->itemtype == 'mod') {
             if ($this->is_outcome_item()) {
                 //nothing to do
-                return;
+                return true;
             }
 
             if (!$activity = $DB->get_record($this->itemmodule, array('id' => $this->iteminstance))) {
                 debugging("Can not find $this->itemmodule activity with id $this->iteminstance");
-                return;
+                return false;
             }
 
             if (!$cm = get_coursemodule_from_instance($this->itemmodule, $activity->id, $this->courseid)) {
                 debugging('Can not find course module');
-                return;
+                return false;
             }
 
             $activity->modname    = $this->itemmodule;
             $activity->cmidnumber = $cm->idnumber;
 
-            grade_update_mod_grades($activity);
+            return grade_update_mod_grades($activity, $userid);
         }
+
+        return true;
     }
 
     /**
index dab82c2..78bfa20 100644 (file)
@@ -58,6 +58,7 @@ class grade_item_testcase extends grade_base_testcase {
         $this->sub_test_grade_item_is_course_item();
         $this->sub_test_grade_item_fetch_course_item();
         $this->sub_test_grade_item_depends_on();
+        $this->sub_test_refresh_grades();
         $this->sub_test_grade_item_is_calculated();
         $this->sub_test_grade_item_set_calculation();
         $this->sub_test_grade_item_get_calculation();
@@ -483,6 +484,13 @@ class grade_item_testcase extends grade_base_testcase {
         $this->assertEquals($res, $deps);
     }
 
+    protected function sub_test_refresh_grades() {
+        // Testing with the grade item for a mod_assignment instance.
+        $grade_item = new grade_item($this->grade_items[0], false);
+        $this->assertTrue(method_exists($grade_item, 'refresh_grades'));
+        $this->assertTrue($grade_item->refresh_grades());
+    }
+
     protected function sub_test_grade_item_is_calculated() {
         $grade_item = new grade_item($this->grade_items[1], false);
         $this->assertTrue(method_exists($grade_item, 'is_calculated'));
index 8de67cf..c59bd1c 100644 (file)
@@ -1190,7 +1190,7 @@ function grade_update_mod_grades($modinstance, $userid=0) {
         $updategradesfunc($modinstance, $userid);
 
     } else {
-        // mudule does not support grading??
+        // Module does not support grading?
     }
 
     return true;
index 4189c37..8c274cd 100644 (file)
@@ -1374,6 +1374,42 @@ function hide_item(itemid) {
     }
 }
 
+M.util.help_popups = {
+    setup : function(Y) {
+        Y.one('body').delegate('click', this.open_popup, 'a.helplinkpopup', this);
+    },
+    open_popup : function(e) {
+        // Prevent the default page action
+        e.preventDefault();
+
+        // Grab the anchor that was clicked
+        var anchor = e.target.ancestor('a', true);
+        var args = {
+            'name'          : 'popup',
+            'url'           : anchor.getAttribute('href'),
+            'options'       : ''
+        };
+        var options = [
+            'height=600',
+            'width=800',
+            'top=0',
+            'left=0',
+            'menubar=0',
+            'location=0',
+            'scrollbars',
+            'resizable',
+            'toolbar',
+            'status',
+            'directories=0',
+            'fullscreen=0',
+            'dependent'
+        ]
+        args.options = options.join(',');
+
+        openpopup(e, args);
+    }
+}
+
 M.util.help_icon = {
     Y : null,
     instance : null,
@@ -1388,16 +1424,17 @@ M.util.help_icon = {
         event.preventDefault();
         if (M.util.help_icon.instance === null) {
             var Y = M.util.help_icon.Y;
-            Y.use('overlay', 'io-base', 'event-mouseenter', 'node', 'event-key', function(Y) {
+            Y.use('overlay', 'io-base', 'event-mouseenter', 'node', 'event-key', 'escape', function(Y) {
                 var help_content_overlay = {
                     helplink : null,
                     overlay : null,
                     init : function() {
 
-                        var closebtn = Y.Node.create('<a id="closehelpbox" href="#"><img  src="'+M.util.image_url('t/delete', 'moodle')+'" /></a>');
+                        var strclose = Y.Escape.html(M.str.form.close);
+                        var footerbtn = Y.Node.create('<button class="closebtn">'+strclose+'</button>');
                         // Create an overlay from markup
                         this.overlay = new Y.Overlay({
-                            headerContent: closebtn,
+                            footerContent: footerbtn,
                             bodyContent: '',
                             id: 'helppopupbox',
                             width:'400px',
@@ -1406,7 +1443,7 @@ M.util.help_icon = {
                         });
                         this.overlay.render(Y.one(document.body));
 
-                        closebtn.on('click', this.overlay.hide, this.overlay);
+                        footerbtn.on('click', this.overlay.hide, this.overlay);
 
                         var boundingBox = this.overlay.get("boundingBox");
 
@@ -1422,9 +1459,6 @@ M.util.help_icon = {
                                 this.overlay.hide();
                             }
                         }, this);
-
-                        Y.on("key", this.close, closebtn , "down:13", this);
-                        closebtn.on('click', this.close, this);
                     },
 
                     close : function(e) {
@@ -1434,9 +1468,16 @@ M.util.help_icon = {
                     },
 
                     display : function(event, args) {
+                        if (Y.one('html').get('dir') == 'rtl') {
+                            var overlayPosition = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.LC];
+                        } else {
+                            var overlayPosition = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC];
+                        }
+
                         this.helplink = args.node;
+
                         this.overlay.set('bodyContent', Y.Node.create('<img src="'+M.cfg.loadingicon+'" class="spinner" />'));
-                        this.overlay.set("align", {node:args.node, points:[Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC]});
+                        this.overlay.set("align", {node:args.node, points: overlayPosition});
 
                         var fullurl = args.url;
                         if (!args.url.match(/https?:\/\//)) {
@@ -1464,8 +1505,6 @@ M.util.help_icon = {
 
                         Y.io(ajaxurl, cfg);
                         this.overlay.show();
-
-                        Y.one('#closehelpbox').focus();
                     },
 
                     display_callback : function(content) {
index 3d57163..e2ffd29 100644 (file)
@@ -388,32 +388,98 @@ function message_get_my_providers() {
 function message_get_providers_for_user($userid) {
     global $DB, $CFG;
 
-    $systemcontext = get_context_instance(CONTEXT_SYSTEM);
-
     $providers = get_message_providers();
 
-    // Remove all the providers we aren't allowed to see now
-    foreach ($providers as $providerid => $provider) {
-        if (!empty($provider->capability)) {
-            if (!has_capability($provider->capability, $systemcontext, $userid)) {
-                unset($providers[$providerid]);   // Not allowed to see this
-                continue;
+    // Ensure user is not allowed to configure instantmessage if it is globally disabled.
+    if (!$CFG->messaging) {
+        foreach ($providers as $providerid => $provider) {
+            if ($provider->name == 'instantmessage') {
+                unset($providers[$providerid]);
+                break;
             }
         }
+    }
 
-        // Ensure user is not allowed to configure instantmessage if it is globally disabled.
-        if (!$CFG->messaging && $provider->name == 'instantmessage') {
+    // If the component is an enrolment plugin, check it is enabled
+    foreach ($providers as $providerid => $provider) {
+        list($type, $name) = normalize_component($provider->component);
+        if ($type == 'enrol' && !enrol_is_enabled($name)) {
             unset($providers[$providerid]);
+        }
+    }
+
+    // Now we need to check capabilities. We need to eliminate the providers
+    // where the user does not have the corresponding capability anywhere.
+    // Here we deal with the common simple case of the user having the
+    // capability in the system context. That handles $CFG->defaultuserroleid.
+    // For the remaining providers/capabilities, we need to do a more complex
+    // query involving all overrides everywhere.
+    $unsureproviders = array();
+    $unsurecapabilities = array();
+    $systemcontext = context_system::instance();
+    foreach ($providers as $providerid => $provider) {
+        if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) {
+            // The provider is relevant to this user.
             continue;
         }
 
-        // If the component is an enrolment plugin, check it is enabled
-        list($type, $name) = normalize_component($provider->component);
-        if ($type == 'enrol') {
-            if (!enrol_is_enabled($name)) {
-                unset($providers[$providerid]);
-                continue;
-            }
+        $unsureproviders[$providerid] = $provider;
+        $unsurecapabilities[$provider->capability] = 1;
+        unset($providers[$providerid]);
+    }
+
+    if (empty($unsureproviders)) {
+        // More complex checks are not required.
+        return $providers;
+    }
+
+    // Now check the unsure capabilities.
+    list($capcondition, $params) = $DB->get_in_or_equal(
+            array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+    $params['userid'] = $userid;
+
+    $sql = "SELECT DISTINCT rc.capability, 1
+
+              FROM {role_assignments} ra
+              JOIN {context} actx ON actx.id = ra.contextid
+              JOIN {role_capabilities} rc ON rc.roleid = ra.roleid
+              JOIN {context} cctx ON cctx.id = rc.contextid
+
+             WHERE ra.userid = :userid
+               AND rc.capability $capcondition
+               AND rc.permission > 0
+               AND (".$DB->sql_concat('actx.path', "'/'")." LIKE ".$DB->sql_concat('cctx.path', "'/%'").
+               " OR ".$DB->sql_concat('cctx.path', "'/'")." LIKE ".$DB->sql_concat('actx.path', "'/%'").")";
+
+    if (!empty($CFG->defaultfrontpageroleid)) {
+        $frontpagecontext = context_course::instance(SITEID);
+
+        list($capcondition2, $params2) = $DB->get_in_or_equal(
+                array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+        $params = array_merge($params, $params2);
+        $params['frontpageroleid'] = $CFG->defaultfrontpageroleid;
+        $params['frontpagepathpattern'] = $frontpagecontext->path . '/';
+
+        $sql .= "
+             UNION
+
+            SELECT DISTINCT rc.capability, 1
+
+              FROM {role_capabilities} rc
+              JOIN {context} cctx ON cctx.id = rc.contextid
+
+             WHERE rc.roleid = :frontpageroleid
+               AND rc.capability $capcondition2
+               AND rc.permission > 0
+               AND ".$DB->sql_concat('cctx.path', "'/'")." LIKE :frontpagepathpattern";
+    }
+
+    $relevantcapabilities = $DB->get_records_sql_menu($sql, $params);
+
+    // Add back any providers based on the detailed capability check.
+    foreach ($unsureproviders as $providerid => $provider) {
+        if (array_key_exists($provider->capability, $relevantcapabilities)) {
+            $providers[$providerid] = $provider;
         }
     }
 
index c787896..5b8d3ef 100644 (file)
@@ -1077,8 +1077,12 @@ class cm_info extends stdClass {
     }
 
     /**
-     * Works out whether activity is visible *for current user* - if this is false, they
-     * aren't allowed to access it.
+     * Works out whether activity is available to the current user
+     *
+     * If the activity is unavailable, additional checks are required to determine if its hidden or greyed out
+     *
+     * @see is_user_access_restricted_by_group()
+     * @see is_user_access_restricted_by_conditional_access()
      * @return void
      */
     private function update_user_visible() {
@@ -1086,39 +1090,76 @@ class cm_info extends stdClass {
         $modcontext = get_context_instance(CONTEXT_MODULE, $this->id);
         $userid = $this->modinfo->get_user_id();
         $this->uservisible = true;
-        // Check visibility/availability conditions.
+
+        // If the user cannot access the activity set the uservisible flag to false.
+        // Additional checks are required to determine whether the activity is entirely hidden or just greyed out.
         if ((!$this->visible or !$this->available) and
                 !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
-            // If the activity is hidden or unavailable, and you don't have viewhiddenactivities,
-            // set it so that user can't see or access it.
+
             $this->uservisible = false;
         }
-        // Check group membership. The grouping option makes the activity
-        // completely invisible as it does not apply to the user at all.
+
+        // Check group membership.
         if ($this->is_user_access_restricted_by_group()) {
-            $this->uservisible = false;
-            // Ensure activity is completely hidden from user.
+
+             $this->uservisible = false;
+            // Ensure activity is completely hidden from the user.
             $this->showavailability = 0;
         }
     }
 
     /**
-     * Checks whether the module group settings restrict the user access.
-     * @return bool true if the user access is restricted
+     * Checks whether the module's group settings restrict the current user's access
+     *
+     * @return bool True if the user access is restricted
      */
     public function is_user_access_restricted_by_group() {
         global $CFG;
+
+        if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly)) {
+            $modcontext = context_module::instance($this->id);
+            $userid = $this->modinfo->get_user_id();
+            if (!has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
+                // If the activity has 'group members only' and you don't have accessallgroups...
+                $groups = $this->modinfo->get_groups($this->groupingid);
+                if (empty($groups)) {
+                    // ...and you don't belong to a group, then set it so you can't see/access it
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the module's conditional access settings mean that the user cannot see the activity at all
+     *
+     * @return bool True if the user cannot see the module. False if the activity is either available or should be greyed out.
+     */
+    public function is_user_access_restricted_by_conditional_access() {
+        global $CFG, $USER;
+
+        if (empty($CFG->enableavailability)) {
+            return false;
+        }
+
+        // If module will always be visible anyway (but greyed out), don't bother checking anything else
+        if ($this->showavailability == CONDITION_STUDENTVIEW_SHOW) {
+            return false;
+        }
+
+        // Can the user see hidden modules?
         $modcontext = context_module::instance($this->id);
         $userid = $this->modinfo->get_user_id();
-        if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly)
-                and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
-            // If the activity has 'group members only' and you don't have accessallgroups...
-            $groups = $this->modinfo->get_groups($this->groupingid);
-            if (empty($groups)) {
-                // ...and you don't belong to a group, then set it so you can't see/access it
-                return true;
-            }
+        if (has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
+            return false;
         }
+
+        // Is the module hidden due to unmet conditions?
+        if (!$this->available) {
+            return true;
+        }
+
         return false;
     }
 
index 1de567f..32697fa 100644 (file)
@@ -8421,18 +8421,45 @@ function check_php_version($version='5.2.4') {
 
 
       case 'Gecko':   /// Gecko based browsers
-          if (empty($version) and substr_count($agent, 'Camino')) {
-              // MacOS X Camino support
-              $version = 20041110;
+          // Do not look for dates any more, we expect real Firefox version here.
+          if (empty($version)) {
+              $version = 1;
+          } else if ($version > 20000000) {
+              // This is just a guess, it is not supposed to be 100% accurate!
+              if (preg_match('/^201/', $version)) {
+                  $version = 3.6;
+              } else if (preg_match('/^200[7-9]/', $version)) {
+                  $version = 3;
+              } else if (preg_match('/^2006/', $version)) {
+                  $version = 2;
+              } else {
+                  $version = 1.5;
+              }
           }
-
-          // the proper string - Gecko/CCYYMMDD Vendor/Version
-          // Faster version and work-a-round No IDN problem.
-          if (preg_match("/Gecko\/([0-9]+)/i", $agent, $match)) {
-              if ($match[1] > $version) {
-                      return true;
+          if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $agent, $match)) {
+              // Use real Firefox version if specified in user agent string.
+              if (version_compare($match[2], $version) >= 0) {
+                  return true;
+              }
+          } else if (preg_match("/Gecko\/([0-9\.]+)/i", $agent, $match)) {
+              // Gecko might contain date or Firefox revision, let's just guess the Firefox version from the date.
+              $browserver = $match[1];
+              if ($browserver > 20000000) {
+                  // This is just a guess, it is not supposed to be 100% accurate!
+                  if (preg_match('/^201/', $browserver)) {
+                      $browserver = 3.6;
+                  } else if (preg_match('/^200[7-9]/', $browserver)) {
+                      $browserver = 3;
+                  } else if (preg_match('/^2006/', $version)) {
+                      $browserver = 2;
+                  } else {
+                      $browserver = 1.5;
                   }
               }
+              if (version_compare($browserver, $version) >= 0) {
+                  return true;
+              }
+          }
           break;
 
 
index dd03dc8..1a0df04 100644 (file)
@@ -372,6 +372,9 @@ class core_renderer extends renderer_base {
         // flow player embedding support
         $this->page->requires->js_function_call('M.util.load_flowplayer');
 
+        // Set up help link popups for all links with the helplinkpopup class
+        $this->page->requires->js_init_call('M.util.help_popups.setup');
+
         $this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));
 
         $focus = $this->page->focuscontrol;
@@ -1495,9 +1498,10 @@ class core_renderer extends renderer_base {
      *
      * @param string $path The page link after doc root and language, no leading slash.
      * @param string $text The text to be displayed for the link
+     * @param boolean $forcepopup Whether to force a popup regardless of the value of $CFG->doctonewwindow
      * @return string
      */
-    public function doc_link($path, $text = '') {
+    public function doc_link($path, $text = '', $forcepopup = false) {
         global $CFG;
 
         $icon = $this->pix_icon('docs', $text, 'moodle', array('class'=>'iconhelp'));
@@ -1505,8 +1509,8 @@ class core_renderer extends renderer_base {
         $url = new moodle_url(get_docs_url($path));
 
         $attributes = array('href'=>$url);
-        if (!empty($CFG->doctonewwindow)) {
-            $attributes['id'] = $this->add_action_handler(new popup_action('click', $url));
+        if (!empty($CFG->doctonewwindow) || $forcepopup) {
+            $attributes['class'] = 'helplinkpopup';
         }
 
         return html_writer::tag('a', $icon.$text, $attributes);
@@ -1638,7 +1642,13 @@ class core_renderer extends renderer_base {
             $ratinghtml .= html_writer::empty_tag('input', $attributes);
 
             if (!$rating->settings->scale->isnumeric) {
-                $ratinghtml .= $this->help_icon_scale($rating->settings->scale->courseid, $rating->settings->scale);
+                // If a global scale, try to find current course ID from the context
+                if (empty($rating->settings->scale->courseid) and $coursecontext = $rating->context->get_course_context(false)) {
+                    $courseid = $coursecontext->instanceid;
+                } else {
+                    $courseid = $rating->settings->scale->courseid;
+                }
+                $ratinghtml .= $this->help_icon_scale($courseid, $rating->settings->scale);
             }
             $ratinghtml .= html_writer::end_tag('span');
             $ratinghtml .= html_writer::end_tag('div');
@@ -1733,6 +1743,7 @@ class core_renderer extends renderer_base {
         $output = html_writer::tag('a', $output, $attributes);
 
         $this->page->requires->js_init_call('M.util.help_icon.add', array(array('id'=>$id, 'url'=>$url->out(false))));
+        $this->page->requires->string_for_js('close', 'form');
 
         // and finally span
         return html_writer::tag('span', $output, array('class' => 'helplink'));
@@ -1798,6 +1809,7 @@ class core_renderer extends renderer_base {
         $output = html_writer::tag('a', $output, $attributes);
 
         $this->page->requires->js_init_call('M.util.help_icon.add', array(array('id'=>$id, 'url'=>$url->out(false))));
+        $this->page->requires->string_for_js('close', 'form');
 
         // and finally span
         return html_writer::tag('span', $output, array('class' => 'helplink'));
index f9dc68d..f5a20f7 100644 (file)
@@ -625,6 +625,14 @@ class phpunit_util {
         $reset = 'reset';
         get_fast_modinfo($reset);
 
+        // Reset other singletons.
+        if (class_exists('plugin_manager')) {
+            plugin_manager::reset_caches(true);
+        }
+        if (class_exists('available_update_checker')) {
+            available_update_checker::reset_caches(true);
+        }
+
         // purge dataroot directory
         self::reset_dataroot();
 
index 470fa88..5d78cf7 100644 (file)
@@ -88,6 +88,16 @@ class plugin_manager {
         return self::$singletoninstance;
     }
 
+    /**
+     * Reset any caches
+     * @param bool $phpunitreset
+     */
+    public static function reset_caches($phpunitreset = false) {
+        if ($phpunitreset) {
+            self::$singletoninstance = null;
+        }
+    }
+
     /**
      * Returns a tree of known plugins and information about them
      *
@@ -649,6 +659,16 @@ class available_update_checker {
         return self::$singletoninstance;
     }
 
+    /**
+     * Reset any caches
+     * @param bool $phpunitreset
+     */
+    public static function reset_caches($phpunitreset = false) {
+        if ($phpunitreset) {
+            self::$singletoninstance = null;
+        }
+    }
+
     /**
      * Returns the timestamp of the last execution of {@link fetch()}
      *
@@ -1302,7 +1322,7 @@ class available_update_checker {
             $message->name              = 'availableupdate';
             $message->userfrom          = $mainadmin;
             $message->userto            = $admin;
-            $message->subject           = get_string('updatenotifications', 'core_admin');
+            $message->subject           = get_string('updatenotificationsubject', 'core_admin', array('siteurl' => $CFG->wwwroot));
             $message->fullmessage       = $text;
             $message->fullmessageformat = FORMAT_PLAIN;
             $message->fullmessagehtml   = $html;
index 8405102..be531b1 100644 (file)
@@ -1271,6 +1271,74 @@ function portfolio_rewrite_pluginfile_url_callback($contextid, $component, $file
     return $format->file_output($file, $options);
 }
 
+/**
+* Function to require any potential callback files, throwing exceptions
+* if an issue occurs.
+*
+* @param string $callbackfile This is the location of the callback file '/mod/forum/locallib.php'
+* @param string $class Name of the class containing the callback functions
+* activity components should ALWAYS use their name_portfolio_caller
+* other locations must use something unique
+*/
+function portfolio_include_callback_file($callbackfile, $class = null) {
+    global $CFG;
+    require_once($CFG->libdir . '/adminlib.php');
+
+    // Get the last occurrence of '/' in the file path.
+    $pos = strrpos($callbackfile, '/');
+    // Get rid of the first slash (if it exists).
+    $callbackfile = ltrim($callbackfile, '/');
+    // Get a list of valid plugin types.
+    $plugintypes = get_plugin_types(false);
+    // Assume it is not valid for now.
+    $isvalid = false;
+    // Go through the plugin types.
+    foreach ($plugintypes as $type => $path) {
+        if (strrpos($callbackfile, $path) === 0) {
+            // Found the plugin type.
+            $isvalid = true;
+            $plugintype = $type;
+            $pluginpath = $path;
+        }
+    }
+    // Throw exception if not a valid component.
+    if (!$isvalid) {
+        throw new coding_exception('Somehow a non-valid plugin path was passed, could be a hackz0r attempt, exiting.');
+    }
+    // Keep record of the filename.
+    $filename = substr($callbackfile, $pos);
+    // Remove the file name.
+    $component = trim(substr($callbackfile, 0, $pos), '/');
+    // Replace the path with the type.
+    $component = str_replace($pluginpath, $plugintype, $component);
+    // Ok, replace '/' with '_'.
+    $component = str_replace('/', '_', $component);
+    // Check that it is a valid component.
+    if (!get_component_version($component)) {
+        throw new portfolio_button_exception('nocallbackcomponent', 'portfolio', '', $component);
+    }
+
+    // Obtain the component's location.
+    if (!$componentloc = get_component_directory($component)) {
+        throw new portfolio_button_exception('nocallbackcomponent', 'portfolio', '', $component);
+    }
+
+    // Check if the filename does not meet any of the expected names.
+    if (($filename != 'locallib.php') && ($filename != 'portfoliolib.php') && ($filename != 'portfolio_callback.php')) {
+        debugging('Please standardise your plugin by keeping your portfolio callback functionality in the file locallib.php.', DEBUG_DEVELOPER);
+    }
+
+    // Throw error if file does not exist.
+    if (!file_exists($componentloc . '/' . $filename)) {
+        throw new portfolio_button_exception('nocallbackfile', 'portfolio', '', $callbackfile);
+    }
+
+    require_once($componentloc . '/' . $filename);
+
+    if (!is_null($class) && !class_exists($class)) {
+        throw new portfolio_button_exception('nocallbackclass', 'portfolio', '', $class);
+    }
+}
 
 /**
  * Go through all the @@PLUGINFILE@@ matches in some text,
index 6de22af..2a08616 100644 (file)
@@ -727,16 +727,19 @@ class flexible_table {
     function col_fullname($row) {
         global $COURSE, $CFG;
 
-        if (!$this->download) {
-            $profileurl = new moodle_url('/user/profile.php', array('id' => $row->{$this->useridfield}));
-            if ($COURSE->id != SITEID) {
-                $profileurl->param('course', $COURSE->id);
-            }
-            return html_writer::link($profileurl, fullname($row));
+        $name = fullname($row);
+        if ($this->download) {
+            return $name;
+        }
 
+        $userid = $row->{$this->useridfield};
+        if ($COURSE->id == SITEID) {
+            $profileurl = new moodle_url('/user/profile.php', array('id' => $userid));
         } else {
-            return fullname($row);
+            $profileurl = new moodle_url('/user/view.php',
+                    array('id' => $userid, 'course' => $COURSE->id));
         }
+        return html_writer::link($profileurl, $name);
     }
 
     /**
diff --git a/lib/tests/gradelib_test.php b/lib/tests/gradelib_test.php
new file mode 100644 (file)
index 0000000..659efc7
--- /dev/null
@@ -0,0 +1,51 @@
+<?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/>.
+
+/**
+ * Unit tests for /lib/gradelib.php.
+ *
+ * @package   core_grade
+ * @category  phpunit
+ * @copyright 2012 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/gradelib.php');
+
+class gradelib_testcase extends advanced_testcase {
+
+    public function test_grade_update_mod_grades() {
+
+        $this->resetAfterTest(true);
+
+        // Create a course and instance of mod_assignment.
+        $course = $this->getDataGenerator()->create_course();
+
+        $assigndata['course'] = $course->id;
+        $assigndata['name'] = 'lightwork assignment';
+        $modinstance = self::getDataGenerator()->create_module('assignment', $assigndata);
+
+        // grade_update_mod_grades() requires 2 additional properties, cmidnumber and modname.
+        $cm = get_coursemodule_from_instance('assignment', $modinstance->id, 0, false, MUST_EXIST);
+        $modinstance->cmidnumber = $cm->id;
+        $modinstance->modname = 'assignment';
+
+        $this->assertTrue(grade_update_mod_grades($modinstance));
+    }
+}
diff --git a/lib/tests/messagelib_test.php b/lib/tests/messagelib_test.php
new file mode 100644 (file)
index 0000000..dab7a64
--- /dev/null
@@ -0,0 +1,157 @@
+<?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/>.
+
+/**
+ * Tests for messagelib.php.
+ *
+ * @package    core_message
+ * @copyright  2012 The Open Universtiy
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+class messagelib_testcase extends advanced_testcase {
+
+    public function test_message_get_providers_for_user() {
+        global $CFG, $DB;
+
+        $this->resetAfterTest(true);
+
+        $generator = $this->getDataGenerator();
+
+        // Create a course category and course
+        $cat = $generator->create_category(array('parent' => 0));
+        $course = $generator->create_course(array('category' => $cat->id));
+        $quiz = $generator->create_module('quiz', array('course' => $course->id));
+        $user = $generator->create_user();
+
+        $coursecontext = context_course::instance($course->id);
+        $quizcontext = context_module::instance($quiz->cmid);
+        $frontpagecontext = context_course::instance(SITEID);
+
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+
+        // The user is a student in a course, and has the capability for quiz
+        // confirmation emails in one quiz in that course.
+        role_assign($studentrole->id, $user->id, $coursecontext->id);
+        assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, $quizcontext->id);
+
+        // Give this message type to the front page role.
+        assign_capability('mod/quiz:emailwarnoverdue', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext->id);
+
+        $providers = message_get_providers_for_user($user->id);
+        $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+        $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+        $this->assertTrue($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+
+        // A user is a student in a different course, they should not get confirmation.
+        $course2 = $generator->create_course(array('category' => $cat->id));
+        $user2 = $generator->create_user();
+        $coursecontext2 = context_course::instance($course2->id);
+        role_assign($studentrole->id, $user2->id, $coursecontext2->id);
+        accesslib_clear_all_caches_for_unit_testing();
+        $providers = message_get_providers_for_user($user2->id);
+        $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'confirmation', $providers));
+
+        // Now remove the frontpage role id, and attempt_overdue message should go away.
+        unset_config('defaultfrontpageroleid');
+        accesslib_clear_all_caches_for_unit_testing();
+
+        $providers = message_get_providers_for_user($user->id);
+        $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+    }
+
+    public function test_message_get_providers_for_user_more() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Create a course
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = context_course::instance($course->id);
+
+        // It would probably be better to use a quiz instance as it has capability controlled messages
+        // however mod_quiz doesn't have a data generator
+        // Instead we're going to use backup notifications and give and take away the capability at various levels
+        $assignment = $this->getDataGenerator()->create_module('assignment', array('course'=>$course->id));
+        $modulecontext = context_module::instance($assignment->id);
+
+        // Create and enrol a teacher
+        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
+        $teacher = $this->getDataGenerator()->create_user();
+        role_assign($teacherrole->id, $teacher->id, $coursecontext);
+        $enrolplugin = enrol_get_plugin('manual');
+        $enrolplugin->add_instance($course);
+        $enrolinstances = enrol_get_instances($course->id, false);
+        foreach ($enrolinstances as $enrolinstance) {
+            if ($enrolinstance->enrol === 'manual') {
+                break;
+            }
+        }
+        $enrolplugin->enrol_user($enrolinstance, $teacher->id);
+
+        // Make the teacher the current user
+        $this->setUser($teacher);
+
+        // Teacher shouldn't have the required capability so they shouldn't be able to see the backup message
+        $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+        $providers = message_get_providers_for_user($teacher->id);
+        $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+
+        // Give the user the required capability in an activity module
+        // They should now be able to see the backup message
+        assign_capability('moodle/site:config', CAP_ALLOW, $teacherrole->id, $modulecontext->id, true);
+        accesslib_clear_all_caches_for_unit_testing();
+        $modulecontext = context_module::instance($assignment->id);
+        $this->assertTrue(has_capability('moodle/site:config', $modulecontext));
+
+        $providers = message_get_providers_for_user($teacher->id);
+        $this->assertTrue($this->message_type_present('moodle', 'backup', $providers));
+
+        // Prohibit the capability for the user at the course level
+        // This overrules the CAP_ALLOW at the module level
+        // They should not be able to see the backup message
+        assign_capability('moodle/site:config', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id, true);
+        accesslib_clear_all_caches_for_unit_testing();
+        $modulecontext = context_module::instance($assignment->id);
+        $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+
+        $providers = message_get_providers_for_user($teacher->id);
+        // Actually, handling PROHIBITs would be too expensive. We do not
+        // care if users with PROHIBITs see a few more preferences than they should.
+        // $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+    }
+
+    /**
+     * Is a particular message type in the list of message types.
+     * @param string $name a message name.
+     * @param array $providers as returned by message_get_providers_for_user.
+     * @return bool whether the message type is present.
+     */
+    protected function message_type_present($component, $name, $providers) {
+        foreach ($providers as $provider) {
+            if ($provider->component == $component && $provider->name == $name) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
index 09c2fad..c26bab7 100644 (file)
@@ -49,8 +49,17 @@ class moodlelib_testcase extends advanced_testcase {
             '1.5'     => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8) Gecko/20051107 Firefox/1.5'),
             '1.5.0.1' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1'),
             '2.0'     => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1',
-                'Ubuntu Linux AMD64' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy)'),
-            '3.0.6' => array('SUSE' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-1.4 Firefox/3.0.6'),
+                               'Ubuntu Linux AMD64' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy)'),
+            '3.0.6'   => array('SUSE' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-1.4 Firefox/3.0.6'),
+            '3.6'     => array('Linux' => 'Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20100101 Firefox/3.6'),
+            '11.0'    => array('Windows' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko Firefox/11.0'),
+            '15.0a2'  => array('Windows' => 'Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2'),
+            '18.0'    => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:18.0) Gecko/18.0 Firefox/18.0'),
+        ),
+        'SeaMonkey' => array(
+            '2.0' => array('Windows' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b3pre) Gecko/20081208 SeaMonkey/2.0'),
+            '2.1' => array('Linux' => 'Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20110609 Firefox/4.0.1 SeaMonkey/2.1'),
+            '2.3' => array('FreeBSD' => 'Mozilla/5.0 (X11; FreeBSD amd64; rv:6.0) Gecko/20110818 Firefox/6.0 SeaMonkey/2.3'),
         ),
         'Safari' => array(
             '312' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312'),
@@ -248,6 +257,113 @@ class moodlelib_testcase extends advanced_testcase {
         $this->assertTrue(check_browser_version('Firefox'));
         $this->assertTrue(check_browser_version('Firefox', '1.5'));
         $this->assertFalse(check_browser_version('Firefox', '3.0'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['1.0.6']['Windows XP'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Gecko', '1'));
+        $this->assertFalse(check_browser_version('Gecko', 20030516));
+        $this->assertFalse(check_browser_version('Gecko', 20051106));
+        $this->assertFalse(check_browser_version('Gecko', 2006010100));
+        $this->assertFalse(check_browser_version('Firefox', '1.5'));
+        $this->assertFalse(check_browser_version('Firefox', '3.0'));
+        $this->assertFalse(check_browser_version('Gecko', '2'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', '1.5'));
+        $this->assertTrue(check_browser_version('Gecko', '1'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertFalse(check_browser_version('Firefox', '3.0'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['3.6']['Linux'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', '1.5'));
+        $this->assertTrue(check_browser_version('Firefox', '3.0'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', '3.6'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertFalse(check_browser_version('Firefox', '4'));
+        $this->assertFalse(check_browser_version('Firefox', '10'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['3.6']['Linux'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', '1.5'));
+        $this->assertTrue(check_browser_version('Firefox', '3.0'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', '3.6'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertFalse(check_browser_version('Firefox', '4'));
+        $this->assertFalse(check_browser_version('Firefox', '10'));
+        $this->assertFalse(check_browser_version('Firefox', '18'));
+        $this->assertFalse(check_browser_version('Gecko', '4'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['15.0a2']['Windows'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', '1.5'));
+        $this->assertTrue(check_browser_version('Firefox', '3.0'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', '3.6'));
+        $this->assertTrue(check_browser_version('Gecko', '15.0'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertTrue(check_browser_version('Firefox', '4'));
+        $this->assertTrue(check_browser_version('Firefox', '10'));
+        $this->assertTrue(check_browser_version('Firefox', '15'));
+        $this->assertFalse(check_browser_version('Firefox', '18'));
+        $this->assertFalse(check_browser_version('Gecko', '18'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['18.0']['Mac OS X'];
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', '1.5'));
+        $this->assertTrue(check_browser_version('Firefox', '3.0'));
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', '3.6'));
+        $this->assertTrue(check_browser_version('Gecko', '15.0'));
+        $this->assertTrue(check_browser_version('Gecko', '18.0'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertTrue(check_browser_version('Firefox', '4'));
+        $this->assertTrue(check_browser_version('Firefox', '10'));
+        $this->assertTrue(check_browser_version('Firefox', '15'));
+        $this->assertTrue(check_browser_version('Firefox', '18'));
+        $this->assertFalse(check_browser_version('Firefox', '19'));
+        $this->assertFalse(check_browser_version('Gecko', '19'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['SeaMonkey']['2.0']['Windows'];
+
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertFalse(check_browser_version('Gecko', '3.6'));
+        $this->assertFalse(check_browser_version('Gecko', '4.0'));
+        $this->assertFalse(check_browser_version('Firefox'));
+
+        $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['SeaMonkey']['2.1']['Linux'];
+        $this->assertTrue(check_browser_version('Gecko', '2'));
+        $this->assertTrue(check_browser_version('Gecko', '3.6'));
+        $this->assertTrue(check_browser_version('Gecko', '4.0'));
+        $this->assertTrue(check_browser_version('Gecko', 20030516));
+        $this->assertTrue(check_browser_version('Gecko', 20051106));
+        $this->assertTrue(check_browser_version('Gecko', 2006010100));
+        $this->assertTrue(check_browser_version('Firefox'));
+        $this->assertTrue(check_browser_version('Firefox', 4.0));
+        $this->assertFalse(check_browser_version('Firefox', 5));
+        $this->assertFalse(check_browser_version('Gecko', '18.0'));
+
     }
 
     function test_get_browser_version_classes() {
index 740b531..2dfc918 100644 (file)
Binary files a/lib/yui/2.9.0/build/charts/assets/charts.swf and b/lib/yui/2.9.0/build/charts/assets/charts.swf differ
index a7b838d..9c26ed1 100644 (file)
Binary files a/lib/yui/2.9.0/build/swfstore/swfstore.swf and b/lib/yui/2.9.0/build/swfstore/swfstore.swf differ
index 389505d..dbf1fc7 100644 (file)
Binary files a/lib/yui/2.9.0/build/uploader/assets/uploader.swf and b/lib/yui/2.9.0/build/uploader/assets/uploader.swf differ
index e67af29..9044c6a 100644 (file)
@@ -20,11 +20,25 @@ YUI.add('moodle-core-chooserdialogue', function(Y) {
         // The initial overflow setting
         initialoverflow : '',
 
+        bodycontent : null,
+        headercontent : null,
+        instanceconfig : null,
+
         setup_chooser_dialogue : function(bodycontent, headercontent, config) {
+            this.bodycontent = bodycontent;
+            this.headercontent = headercontent;
+            this.instanceconfig = config;
+        },
+
+        prepare_chooser : function () {
+            if (this.overlay) {
+                return;
+            }
+
             // Set Default options
             var params = {
-                bodyContent : bodycontent.get('innerHTML'),
-                headerContent : headercontent.get('innerHTML'),
+                bodyContent : this.bodycontent.get('innerHTML'),
+                headerContent : this.headercontent.get('innerHTML'),
                 draggable : true,
                 visible : false, // Hide by default
                 zindex : 100, // Display in front of other items
@@ -33,16 +47,16 @@ YUI.add('moodle-core-chooserdialogue', function(Y) {
             }
 
             // Override with additional options
-            for (paramkey in config) {
-              params[paramkey] = config[paramkey];
+            for (paramkey in this.instanceconfig) {
+              params[paramkey] = this.instanceconfig[paramkey];
             }
 
             // Create the overlay
             this.overlay = new M.core.dialogue(params);
 
             // Remove the template for the chooser
-            bodycontent.remove();
-            headercontent.remove();
+            this.bodycontent.remove();
+            this.headercontent.remove();
 
             // Hide and then render the overlay
             this.overlay.hide();
@@ -63,6 +77,8 @@ YUI.add('moodle-core-chooserdialogue', function(Y) {
          * @return void
          */
         display_chooser : function (e) {
+            this.prepare_chooser();
+
             // Stop the default event actions before we proceed
             e.preventDefault();
 
index 5263217..cac237b 100644 (file)
@@ -1215,7 +1215,7 @@ function message_print_search_results($frm, $showicontext=false, $currentuser=nu
                 echo html_writer::end_tag('td');
 
                 echo html_writer::start_tag('td', array('class' => 'summary'));
-                echo message_get_fragment($message->fullmessage, $keywords);
+                echo message_get_fragment($message->smallmessage, $keywords);
                 echo html_writer::start_tag('div', array('class' => 'link'));
 
                 //If the user clicks the context link display message sender on the left
@@ -1600,10 +1600,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
     ///    c.  Messages to and from user
 
     if ($courseid == SITEID) { /// admin is searching all messages
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
@@ -1628,10 +1628,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
             $params['userid'] = $userid;
         }
 
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
index d67eaf6..632e4d3 100644 (file)
@@ -62,7 +62,7 @@ echo $OUTPUT->heading($icon . s($course->fullname));
 
 // collapsible course summary
 if (!empty($course->summary)) {
-    unset($options);
+    $options = new stdClass();
     $options->trusted = false;
     $options->para    = false;
     $options->filter  = false;
index fce13ea..6afdcf7 100644 (file)
@@ -121,10 +121,6 @@ class assign_grading_table extends table_sql implements renderable {
         if (!$this->is_downloading()) {
             $columns[] = 'select';
             $headers[] = get_string('select') . '<div class="selectall"><input type="checkbox" name="selectall" title="' . get_string('selectall') . '"/></div>';
-
-            // We have to call this column userid so we can use userid as a default sortable column.
-            $columns[] = 'userid';
-            $headers[] = get_string('edit');
         }
 
         // User picture
@@ -145,6 +141,11 @@ class assign_grading_table extends table_sql implements renderable {
         // Grade
         $columns[] = 'grade';
         $headers[] = get_string('grade');
+        if (!$this->is_downloading()) {
+            // We have to call this column userid so we can use userid as a default sortable column.
+            $columns[] = 'userid';
+            $headers[] = get_string('edit');
+        }
 
         // Submission plugins
         if ($assignment->is_any_submission_plugin_enabled()) {
@@ -207,6 +208,22 @@ class assign_grading_table extends table_sql implements renderable {
 
     }
 
+    /**
+     * Before adding each row to the table make sure rownum is incremented
+     *
+     * @param array $row row of data from db used to make one row of the table.
+     * @return array one row for the table
+     */
+    function format_row($row) {
+        if ($this->rownum < 0) {
+            $this->rownum = $this->currpage * $this->pagesize;
+        } else {
+            $this->rownum += 1;
+        }
+
+        return parent::format_row($row);
+    }
+
     /**
      * Add the userid to the row class so it can be updated via ajax
      *
@@ -276,7 +293,7 @@ class assign_grading_table extends table_sql implements renderable {
 
 
     /**
-     * Format a user picture for display (and update rownum as a sideeffect)
+     * Format a user picture for display
      *
      * @param stdClass $row
      * @return string
@@ -434,11 +451,6 @@ class assign_grading_table extends table_sql implements renderable {
      */
     function col_userid(stdClass $row) {
         $edit = '';
-        if ($this->rownum < 0) {
-            $this->rownum = $this->currpage * $this->pagesize;
-        } else {
-            $this->rownum += 1;
-        }
 
         $actions = array();
 
index 7238b70..3d44847 100644 (file)
@@ -2760,7 +2760,9 @@ class assign {
                                                                  'showquickgrading'=>false));
         if ($formdata = $mform->get_data()) {
             set_user_preference('assign_perpage', $formdata->perpage);
-            set_user_preference('assign_filter', $formdata->filter);
+            if (isset($formdata->filter)) {
+                set_user_preference('assign_filter', $formdata->filter);
+            }
         }
     }
 
@@ -3048,7 +3050,9 @@ class assign {
         if (!$last){
             $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
         }
-        $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        if (!empty($buttonarray)) {
+            $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        }
     }
 
 
index a1cbe07..ec5e071 100644 (file)
@@ -117,9 +117,11 @@ M.mod_assign.init_grading_options = function(Y) {
             Y.one('form.gradingoptionsform').submit();
         });
         var filterelement = Y.one('#id_filter');
-        filterelement.on('change', function(e) {
-            Y.one('form.gradingoptionsform').submit();
-        });
+        if (filterelement) {
+            filterelement.on('change', function(e) {
+                Y.one('form.gradingoptionsform').submit();
+            });
+        }
         var quickgradingelement = Y.one('#id_quickgrading');
         if (quickgradingelement) {
             quickgradingelement.on('change', function(e) {
@@ -139,4 +141,4 @@ M.mod_assign.init_grade_change = function(Y) {
             }
         });
     }
-};
\ No newline at end of file
+};
index 92d59a0..e9a7f0f 100644 (file)
@@ -162,6 +162,11 @@ class assign_upgrade_manager {
             }
             $completiondone = true;
 
+            // Migrate log entries so we don't lose them.
+            $logparams = array('cmid' => $oldcoursemodule->id, 'course' => $oldcoursemodule->course);
+            $DB->set_field('log', 'module', 'assign', $logparams);
+            $DB->set_field('log', 'cmid', $newcoursemodule->id, $logparams);
+
 
             // copy all the submission data (and get plugins to do their bit)
             $oldsubmissions = $DB->get_records('assignment_submissions', array('assignment'=>$oldassignmentid));
@@ -260,6 +265,10 @@ class assign_upgrade_manager {
                     $DB->update_record('course_completion_criteria', $criteria);
                 }
             }
+            // Roll back the log changes
+            $logparams = array('cmid' => $newcoursemodule->id, 'course' => $newcoursemodule->course);
+            $DB->set_field('log', 'module', 'assignment', $logparams);
+            $DB->set_field('log', 'cmid', $oldcoursemodule->id, $logparams);
             // roll back the advanced grading update
             if ($gradingarea) {
                 foreach ($gradeidmap as $newgradeid => $oldsubmissionid) {
index 91c5f5f..8ca17bd 100644 (file)
@@ -1164,9 +1164,9 @@ class assignment_upload extends assignment_base {
         }
         $filename = str_replace(' ', '_', clean_filename($this->course->shortname.'-'.$this->assignment->name.'-'.$groupname.$this->assignment->id.".zip")); //name of new zip file.
         foreach ($submissions as $submission) {
-            // If assignment is open and submission is not finalized then don't add it to zip.
+            // If assignment is open and submission is not finalized and marking button enabled then don't add it to zip.
             $submissionstatus = $this->is_finalized($submission);
-            if ($this->isopen() && empty($submissionstatus)) {
+            if ($this->isopen() && empty($submissionstatus) && !empty($this->assignment->var4)) {
                 continue;
             }
             $a_userid = $submission->userid; //get userid
index 3fb1085..c4709f0 100644 (file)
@@ -40,6 +40,152 @@ function xmldb_book_upgrade($oldversion) {
     // Moodle v2.3.0 release upgrade line
     // Put any upgrade step following this
 
+    // Note: The next steps (up to 2012061710 included, are a "replay" of old upgrade steps,
+    // because some sites updated to Moodle 2.3 didn't have the latest contrib mod_book
+    // installed, so some required changes were missing.
+    //
+    // All the steps are run conditionally so sites upgraded from latest contrib mod_book or
+    // new (2.3 and upwards) sites won't get affected.
+    //
+    // See MDL-35297 and commit msg for more information.
+
+    if ($oldversion < 2012061703) {
+        // Rename field summary on table book to intro
+        $table = new xmldb_table('book');
+        $field = new xmldb_field('summary', XMLDB_TYPE_TEXT, null, null, null, null, null, 'name');
+
+        // Launch rename field summary
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->rename_field($table, $field, 'intro');
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061703, 'book');
+    }
+
+    if ($oldversion < 2012061704) {
+        // Define field introformat to be added to book
+        $table = new xmldb_table('book');
+        $field = new xmldb_field('introformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'intro');
+
+        // Launch add field introformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+            // Conditionally migrate to html format in intro
+            // Si está activo el htmleditor!!!!!
+            if ($CFG->texteditors !== 'textarea') {
+                $rs = $DB->get_recordset('book', array('introformat'=>FORMAT_MOODLE), '', 'id,intro,introformat');
+                foreach ($rs as $b) {
+                    $b->intro       = text_to_html($b->intro, false, false, true);
+                    $b->introformat = FORMAT_HTML;
+                    $DB->update_record('book', $b);
+                    upgrade_set_timeout();
+                }
+                unset($b);
+                $rs->close();
+            }
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061704, 'book');
+    }
+
+    if ($oldversion < 2012061705) {
+        // Define field introformat to be added to book
+        $table = new xmldb_table('book_chapters');
+        $field = new xmldb_field('contentformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'content');
+
+        // Launch add field introformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+
+            $DB->set_field('book_chapters', 'contentformat', FORMAT_HTML, array());
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061705, 'book');
+    }
+
+    if ($oldversion < 2012061706) {
+        require_once("$CFG->dirroot/mod/book/db/upgradelib.php");
+
+        $sqlfrom = "FROM {book} b
+                    JOIN {modules} m ON m.name = 'book'
+                    JOIN {course_modules} cm ON (cm.module = m.id AND cm.instance = b.id)";
+
+        $count = $DB->count_records_sql("SELECT COUNT('x') $sqlfrom");
+
+        if ($rs = $DB->get_recordset_sql("SELECT b.id, b.course, cm.id AS cmid $sqlfrom ORDER BY b.course, b.id")) {
+
+            $pbar = new progress_bar('migratebookfiles', 500, true);
+
+            $i = 0;
+            foreach ($rs as $book) {
+                $i++;
+                upgrade_set_timeout(360); // set up timeout, may also abort execution
+                $pbar->update($i, $count, "Migrating book files - $i/$count.");
+
+                $context = context_module::instance($book->cmid);
+
+                mod_book_migrate_moddata_dir_to_legacy($book, $context, '/');
+
+                // remove dirs if empty
+                @rmdir("$CFG->dataroot/$book->course/$CFG->moddata/book/$book->id/");
+                @rmdir("$CFG->dataroot/$book->course/$CFG->moddata/book/");
+                @rmdir("$CFG->dataroot/$book->course/$CFG->moddata/");
+                @rmdir("$CFG->dataroot/$book->course/");
+            }
+            $rs->close();
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061706, 'book');
+    }
+
+    if ($oldversion < 2012061707) {
+        // Define field disableprinting to be dropped from book
+        $table = new xmldb_table('book');
+        $field = new xmldb_field('disableprinting');
+
+        // Conditionally launch drop field disableprinting
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061707, 'book');
+    }
+
+    if ($oldversion < 2012061708) {
+        unset_config('book_tocwidth');
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061708, 'book');
+    }
+
+    if ($oldversion < 2012061709) {
+        require_once("$CFG->dirroot/mod/book/db/upgradelib.php");
+
+        mod_book_migrate_all_areas();
+
+        upgrade_mod_savepoint(true, 2012061709, 'book');
+    }
+
+    if ($oldversion < 2012061710) {
+
+        // Define field revision to be added to book
+        $table = new xmldb_table('book');
+        $field = new xmldb_field('revision', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'customtitles');
+
+        // Conditionally launch add field revision
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // book savepoint reached
+        upgrade_mod_savepoint(true, 2012061710, 'book');
+    }
+    // End of MDL-35297 "replayed" steps.
 
     return true;
 }
diff --git a/mod/book/db/upgradelib.php b/mod/book/db/upgradelib.php
new file mode 100644 (file)
index 0000000..5411930
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+// This file is part of Book module for 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/>.
+
+/**
+ * Book module upgrade related helper functions
+ *
+ * @package    mod_book
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Migrate book files stored in moddata folders.
+ *
+ * Please note it was a big mistake to store the files there in the first place!
+ *
+ * @param stdClass $book
+ * @param stdClass $context
+ * @param string $path
+ * @return void
+ */
+function mod_book_migrate_moddata_dir_to_legacy($book, $context, $path) {
+    global $OUTPUT, $CFG;
+
+    $base = "$CFG->dataroot/$book->course/$CFG->moddata/book/$book->id";
+    $fulldir = $base.$path;
+
+    if (!is_dir($fulldir)) {
+        // does not exist
+        return;
+    }
+
+    $fs      = get_file_storage();
+    $items   = new DirectoryIterator($fulldir);
+
+    foreach ($items as $item) {
+        if ($item->isDot()) {
+            unset($item); // release file handle
+            continue;
+        }
+
+        if ($item->isLink()) {
+            // do not follow symlinks - they were never supported in moddata, sorry
+            unset($item); // release file handle
+            continue;
+        }
+
+        if ($item->isFile()) {
+            if (!$item->isReadable()) {
+                echo $OUTPUT->notification(" File not readable, skipping: ".$fulldir.$item->getFilename());
+                unset($item); // release file handle
+                continue;
+            }
+
+            $filepath = clean_param("/$CFG->moddata/book/$book->id".$path, PARAM_PATH);
+            $filename = clean_param($item->getFilename(), PARAM_FILE);
+
+            if ($filename === '') {
+                // unsupported chars, sorry
+                unset($item); // release file handle
+                continue;
+            }
+
+            if (textlib::strlen($filepath) > 255) {
+                echo $OUTPUT->notification(" File path longer than 255 chars, skipping: ".$fulldir.$item->getFilename());
+                unset($item); // release file handle
+                continue;
+            }
+
+            if (!$fs->file_exists($context->id, 'course', 'legacy', '0', $filepath, $filename)) {
+                $file_record = array('contextid'=>$context->id, 'component'=>'course', 'filearea'=>'legacy', 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename,
+                                     'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
+                $fs->create_file_from_pathname($file_record, $fulldir.$item->getFilename());
+            }
+            $oldpathname = $fulldir.$item->getFilename();
+            unset($item); // release file handle
+            @unlink($oldpathname);
+
+        } else {
+            // migrate recursively all subdirectories
+            $oldpathname = $base.$item->getFilename().'/';
+            $subpath     = $path.$item->getFilename().'/';
+            unset($item);  // release file handle
+            mod_book_migrate_moddata_dir_to_legacy($book, $context, $subpath);
+            @rmdir($oldpathname); // deletes dir if empty
+        }
+    }
+    unset($items); // release file handles
+}
+
+/**
+ * Migrate legacy files in intro and chapters
+ * @return void
+ */
+function mod_book_migrate_all_areas() {
+    global $DB;
+
+    $rsbooks = $DB->get_recordset('book');
+    foreach($rsbooks as $book) {
+        upgrade_set_timeout(360); // set up timeout, may also abort execution
+        $cm = get_coursemodule_from_instance('book', $book->id);
+        $context = context_module::instance($cm->id);
+        mod_book_migrate_area($book, 'intro', 'book', $book->course, $context, 'mod_book', 'intro', 0);
+
+        $rschapters = $DB->get_recordset('book_chapters', array('bookid'=>$book->id));
+        foreach ($rschapters as $chapter) {
+            mod_book_migrate_area($chapter, 'content', 'book_chapters', $book->course, $context, 'mod_book', 'chapter', $chapter->id);
+        }
+        $rschapters->close();
+    }
+    $rsbooks->close();
+}
+
+/**
+ * Migrate one area, this should be probably part of moodle core...
+ *
+ * @param stdClass $record object to migrate files (book, chapter)
+ * @param string $field field in the record we are going to migrate
+ * @param string $table DB table containing the information to migrate
+ * @param int $courseid id of the course the book module belongs to
+ * @param context_module $context context of the book module
+ * @param string $component component to be used for the migrated files
+ * @param string $filearea filearea to be used for the migrated files
+ * @param int $itemid id to be used for the migrated files
+ * @return void
+ */
+function mod_book_migrate_area($record, $field, $table, $courseid, $context, $component, $filearea, $itemid) {
+    global $CFG, $DB;
+
+    $fs = get_file_storage();
+
+    foreach(array(get_site()->id, $courseid) as $cid) {
+        $matches = null;
+        $ooldcontext = context_course::instance($cid);
+        if (preg_match_all("|$CFG->wwwroot/file.php(\?file=)?/$cid(/[^\s'\"&\?#]+)|", $record->$field, $matches)) {
+            $file_record = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
+            foreach ($matches[2] as $i=>$filepath) {
+                if (!$file = $fs->get_file_by_hash(sha1("/$ooldcontext->id/course/legacy/0".$filepath))) {
+                    continue;
+                }
+                try {
+                    if (!$newfile = $fs->get_file_by_hash(sha1("/$context->id/$component/$filearea/$itemid".$filepath))) {
+                        $fs->create_file_from_storedfile($file_record, $file);
+                    }
+                    $record->$field = str_replace($matches[0][$i], '@@PLUGINFILE@@'.$filepath, $record->$field);
+                } catch (Exception $ex) {
+                    // ignore problems
+                }
+                $DB->set_field($table, $field, $record->$field, array('id'=>$record->id));
+            }
+        }
+    }
+}
\ No newline at end of file
index e187cb3..f37c00d 100644 (file)
@@ -25,6 +25,6 @@
 defined('MOODLE_INTERNAL') || die;
 
 $module->component = 'mod_book'; // Full name of the plugin (used for diagnostics)
-$module->version   = 2012061702; // The current module version (Date: YYYYMMDDXX)
+$module->version   = 2012061710; // The current module version (Date: YYYYMMDDXX)
 $module->requires  = 2012061700; // Requires this Moodle version
 $module->cron      = 0;          // Period for cron to check this module (secs)
index 583ea73..d78acc5 100644 (file)
@@ -3514,7 +3514,7 @@ function data_get_recordids($alias, $searcharray, $dataid, $recordids) {
  * @param int $sort            DATA_*
  * @param stdClass $data       Data module object
  * @param array $recordids     An array of record IDs.
- * @param string $selectdata   Information for the select part of the sql statement.
+ * @param string $selectdata   Information for the where and select part of the sql statement.
  * @param string $sortorder    Additional sort parameters
  * @return array sqlselect     sqlselect['sql'] has the sql string, sqlselect['params'] contains an array of parameters.
  */
@@ -3559,13 +3559,17 @@ function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $so
                                  {user} u ';
         $groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, ' .$sortcontentfull;
     }
-    $nestfromsql = 'WHERE c.recordid = r.id
-                      AND r.dataid = :dataid
-                      AND r.userid = u.id';
+
+    // Default to a standard Where statement if $selectdata is empty.
+    if ($selectdata == '') {
+        $selectdata = 'WHERE c.recordid = r.id
+                         AND r.dataid = :dataid
+                         AND r.userid = u.id ';
+    }
 
     // Find the field we are sorting on
     if ($sort > 0 or data_get_field_from_id($sort, $data)) {
-        $nestfromsql .= ' AND c.fieldid = :sort';
+        $selectdata .= ' AND c.fieldid = :sort';
     }
 
     // If there are no record IDs then return an sql statment that will return no rows.
@@ -3574,7 +3578,7 @@ function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $so
     } else {
         list($insql, $inparam) = $DB->get_in_or_equal(array('-1'), SQL_PARAMS_NAMED);
     }
-    $nestfromsql .= ' AND c.recordid ' . $insql . $selectdata . $groupsql;
+    $nestfromsql = $selectdata . ' AND c.recordid ' . $insql . $groupsql;
     $sqlselect['sql'] = "$nestselectsql $nestfromsql $sortorder";
     $sqlselect['params'] = $inparam;
     return $sqlselect;
index 1b2d85b..0b76db0 100644 (file)
@@ -456,11 +456,15 @@ if ($showactivity) {
             $requiredentries_allowed = false;
         }
 
+        // Initialise the first group of params for advanced searches.
+        $initialparams   = array();
+
     /// setup group and approve restrictions
         if (!$approvecap && $data->approval) {
             if (isloggedin()) {
                 $approveselect = ' AND (r.approved=1 OR r.userid=:myid1) ';
                 $params['myid1'] = $USER->id;
+                $initialparams['myid1'] = $params['myid1'];
             } else {
                 $approveselect = ' AND r.approved=1 ';
             }
@@ -471,6 +475,7 @@ if ($showactivity) {
         if ($currentgroup) {
             $groupselect = " AND (r.groupid = :currentgroup OR r.groupid = 0)";
             $params['currentgroup'] = $currentgroup;
+            $initialparams['currentgroup'] = $params['currentgroup'];
         } else {
             if ($canviewallrecords) {
                 $groupselect = ' ';
@@ -486,6 +491,8 @@ if ($showactivity) {
         $advwhere        = '';
         $advtables       = '';
         $advparams       = array();
+        // This is used for the initial reduction of advanced search results with required entries.
+        $entrysql        = '';
 
     /// Find the field we are sorting on
         if ($sort <= 0 or !$sortfield = data_get_field_from_id($sort, $data)) {
@@ -514,8 +521,7 @@ if ($showactivity) {
             $tables = '{data_content} c,{data_records} r, {data_content} cs, {user} u ';
             $where =  'WHERE c.recordid = r.id
                          AND r.dataid = :dataid
-                         AND r.userid = u.id
-                         AND cs.recordid = r.id ';
+                         AND r.userid = u.id ';
             $params['dataid'] = $data->id;
             $sortorder = ' ORDER BY '.$ordering.', r.id ASC ';
             $searchselect = '';
@@ -523,7 +529,9 @@ if ($showactivity) {
             // If requiredentries is not reached, only show current user's entries
             if (!$requiredentries_allowed) {
                 $where .= ' AND u.id = :myid2 ';
+                $entrysql = ' AND r.userid = :myid3 ';
                 $params['myid2'] = $USER->id;
+                $initialparams['myid3'] = $params['myid2'];
             }
 
             if (!empty($advanced)) {                                                  //If advanced box is checked.
@@ -541,6 +549,7 @@ if ($showactivity) {
                     $advparams = array_merge($advparams, $val->params);
                 }
             } else if ($search) {
+                $where .= ' AND cs.recordid = r.id ';
                 $searchselect = " AND (".$DB->sql_like('cs.content', ':search1', false)." OR ".$DB->sql_like('u.firstname', ':search2', false)." OR ".$DB->sql_like('u.lastname', ':search3', false)." ) ";
                 $params['search1'] = "%$search%";
                 $params['search2'] = "%$search%";
@@ -558,10 +567,11 @@ if ($showactivity) {
             $count = ' COUNT(DISTINCT c.recordid) ';
             $tables = '{data_content} c, {data_records} r, {data_content} cs, {user} u ';
             $where =  'WHERE c.recordid = r.id
-                         AND c.fieldid = :sort
                          AND r.dataid = :dataid
-                         AND r.userid = u.id
-                         AND cs.recordid = r.id ';
+                         AND r.userid = u.id ';
+            if (!$advanced) {
+                $where .=  'AND c.fieldid = :sort';
+            }
             $params['dataid'] = $data->id;
             $params['sort'] = $sort;
             $sortorder = ' ORDER BY sortorder '.$order.' , r.id ASC ';
@@ -569,8 +579,10 @@ if ($showactivity) {
 
             // If requiredentries is not reached, only show current user's entries
             if (!$requiredentries_allowed) {
-                $where .= ' AND u.id = ' . $USER->id;
+                $where .= ' AND u.id = :myid2';
+                $entrysql = ' AND r.userid = :myid3';
                 $params['myid2'] = $USER->id;
+                $initialparams['myid3'] = $params['myid2'];
             }
             $i = 0;
             if (!empty($advanced)) {                                                  //If advanced box is checked.
@@ -587,6 +599,7 @@ if ($showactivity) {
                     $advparams = array_merge($advparams, $val->params);
                 }
             } else if ($search) {
+                $where .= ' AND cs.recordid = r.id ';
                 $searchselect = " AND (".$DB->sql_like('cs.content', ':search1', false)." OR ".$DB->sql_like('u.firstname', ':search2', false)." OR ".$DB->sql_like('u.lastname', ':search3', false)." ) ";
                 $params['search1'] = "%$search%";
                 $params['search2'] = "%$search%";
@@ -599,23 +612,15 @@ if ($showactivity) {
     /// To actually fetch the records
 
         $fromsql    = "FROM $tables $advtables $where $advwhere $groupselect $approveselect $searchselect $advsearchselect";
-        $sqlcount   = "SELECT $count $fromsql";   // Total number of records when searching
-        $sqlmax     = "SELECT $count FROM $tables $where $groupselect $approveselect"; // number of all recoirds user may see
         $allparams  = array_merge($params, $advparams);
 
         // Provide initial sql statements and parameters to reduce the number of total records.
-        $selectdata = $groupselect . $approveselect;
-        $initialparams = array();
-        if ($currentgroup) {
-            $initialparams['currentgroup'] = $params['currentgroup'];
-        }
-        if (!$approvecap && $data->approval && isloggedin()) {
-            $initialparams['myid1'] = $params['myid1'];
-        }
+        $initialselect = $groupselect . $approveselect . $entrysql;
 
-        $recordids = data_get_all_recordids($data->id, $selectdata, $initialparams);
+        $recordids = data_get_all_recordids($data->id, $initialselect, $initialparams);
         $newrecordids = data_get_advance_search_ids($recordids, $search_array, $data->id);
         $totalcount = count($newrecordids);
+        $selectdata = $where . $groupselect . $approveselect;
 
         if (!empty($advanced)) {
             $advancedsearchsql = data_get_advanced_search_sql($sort, $data, $newrecordids, $selectdata, $sortorder);
index 8c6dd59..e4a0b8a 100644 (file)
@@ -400,7 +400,7 @@ function feedback_get_recent_mod_activity(&$activities, &$index,
 
     $sql .= " WHERE fc.timemodified > ? AND fk.id = ? ";
     $sqlargs[] = $timemodified;
-    $sqlargs[] = $cm->instace;
+    $sqlargs[] = $cm->instance;
 
     if ($userid) {
         $sql .= " AND u.id = ? ";
index 7cfb638..a706571 100644 (file)
@@ -8032,29 +8032,37 @@ function forum_get_courses_user_posted_in($user, $discussionsonly = false, $incl
 function forum_get_forums_user_posted_in($user, array $courseids = null, $discussionsonly = false, $limitfrom = null, $limitnum = null) {
     global $DB;
 
-    $where = array("m.name = 'forum'");
-    $params = array();
     if (!is_null($courseids)) {
         list($coursewhere, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'courseid');
-        $where[] = 'f.course '.$coursewhere;
-    }
-    if (!$discussionsonly) {
-        $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id
-                    JOIN {forum_posts} fp ON fp.discussion = fd.id';
-        $where[] = 'fp.userid = :userid';
+        $coursewhere = ' AND f.course '.$coursewhere;
     } else {
-        $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id';
-        $where[] = 'fd.userid = :userid';
+        $coursewhere = '';
+        $params = array();
     }
     $params['userid'] = $user->id;
-    $wheresql = join(' AND ', $where);
+    $params['forum'] = 'forum';
+
+    if ($discussionsonly) {
+        $join = 'JOIN {forum_discussions} ff ON ff.forum = f.id';
+    } else {
+        $join = 'JOIN {forum_discussions} fd ON fd.forum = f.id
+                 JOIN {forum_posts} ff ON ff.discussion = fd.id';
+    }
+
+    $sql = "SELECT f.*, cm.id AS cmid
+              FROM {forum} f
+              JOIN {course_modules} cm ON cm.instance = f.id
+              JOIN {modules} m ON m.id = cm.module
+              JOIN (
+                  SELECT f.id
+                    FROM {forum} f
+                    {$join}
+                   WHERE ff.userid = :userid
+                GROUP BY f.id
+                   ) j ON j.id = f.id
+             WHERE m.name = :forum
+                 {$coursewhere}";
 
-    $sql = "SELECT DISTINCT f.*, cm.id AS cmid
-            FROM {forum} f
-            JOIN {course_modules} cm ON cm.instance = f.id
-            JOIN {modules} m ON m.id = cm.module
-            $joinsql
-            WHERE $wheresql";
     $courseforums = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
     return $courseforums;
 }
@@ -8309,8 +8317,10 @@ function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false,
         $forumsearchwhere[] = "(d.forum $fullidsql)";
     }
 
-    // Prepare SQL to both count and search
-    $userfields = user_picture::fields('u', null, 'userid');
+    // Prepare SQL to both count and search.
+    // We alias user.id to useridx because we forum_posts already has a userid field and not aliasing this would break
+    // oracle and mssql.
+    $userfields = user_picture::fields('u', null, 'useridx');
     $countsql = 'SELECT COUNT(*) ';
     $selectsql = 'SELECT p.*, d.forum, d.name AS discussionname, '.$userfields.' ';
     $wheresql = implode(" OR ", $forumsearchwhere);
index fe824c4..0e544df 100644 (file)
 
 /** Unknown Styles ??? */
 #email .unsubscribelink {margin-top:20px;}
+
+
+/* Forumpost unread
+-------------------------*/
+#page-mod-forum-view .unread,
+.forumpost.unread .row.header,
+.path-course-view .unread,
+span.unread {
+    background-color: #FFD;
+}
+.forumpost.unread .row.header {
+    border-bottom: 1px solid #DDD;
+}
\ No newline at end of file
index e60f43e..63ef013 100644 (file)
@@ -269,12 +269,12 @@ switch ($mode) {
                 // Get all the users who have taken this lesson, order by their last name
                 $ufields = user_picture::fields('u');
                 if (!empty($cm->groupingid)) {
-                    $params["groupinid"] = $cm->groupingid;
+                    $params["groupingid"] = $cm->groupingid;
                     $sql = "SELECT DISTINCT $ufields
                             FROM {lesson_attempts} a
                                 INNER JOIN {user} u ON u.id = a.userid
                                 INNER JOIN {groups_members} gm ON gm.userid = u.id
-                                INNER JOIN {groupings_groups} gg ON gm.groupid = :groupinid
+                                INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid AND gg.groupingid = :groupingid
                             WHERE a.lessonid = :lessonid
                             ORDER BY u.lastname";
                 } else {
index 134c14b..9eb2c18 100644 (file)
@@ -262,6 +262,7 @@ function lesson_save_question_options($question, $lesson) {
             // The first answer should always be the correct answer
             $correctanswer = clone($defaultanswer);
             $correctanswer->answer = get_string('thatsthecorrectanswer', 'lesson');
+            $correctanswer->jumpto = LESSON_NEXTPAGE;
             $DB->insert_record("lesson_answers", $correctanswer);
 
             // The second answer should always be the wrong answer
index 6d16aa7..d02f57b 100644 (file)
@@ -1138,6 +1138,15 @@ class quiz_question_bank_view extends question_bank_view {
                 'editaction', 'previewaction');
     }
 
+    /**
+     * Specify the column heading
+     *
+     * @return string Column name for the heading
+     */
+    protected function heading_column() {
+        return 'questionnametext';
+    }
+
     protected function default_sort() {
         $this->requiredcolumns['qtype'] = $this->knowncolumntypes['qtype'];
         $this->requiredcolumns['questionnametext'] = $this->knowncolumntypes['questionnametext'];
index be061bb..813f046 100644 (file)
@@ -1024,6 +1024,8 @@ function quiz_process_options($quiz) {
         $quiz->feedbackboundaries[-1] = $quiz->grade + 1;
         $quiz->feedbackboundaries[$numboundaries] = 0;
         $quiz->feedbackboundarycount = $numboundaries;
+    } else {
+        $quiz->feedbackboundarycount = -1;
     }
 
     // Combing the individual settings into the review columns.
index ce0d359..70332fa 100644 (file)
@@ -101,6 +101,7 @@ M.mod_quiz.timer = {
             if (form.one('input[name=finishattempt]')) {
                 form.one('input[name=finishattempt]').set('value', 0);
             }
+            M.core_formchangechecker.set_form_submitted();
             form.submit();
             return;
         }
index 322ccdd..23fe7d5 100644 (file)
@@ -401,7 +401,7 @@ bank window's title is prominent enough*/
 #page-mod-quiz-edit.dir-rtl div.question {clear: right;}
 #page-mod-quiz-edit.dir-rtl div.question div.qnum {float: right;}
 #page-mod-quiz-edit.dir-rtl div.editq div.question div.content {float: right;height: 40px;}
-#page-mod-quiz-edit.dir-rtl div.question div.content div.points {left: 5px;right:auto;}
+#page-mod-quiz-edit.dir-rtl div.question div.content div.points {left: 50px;right:auto;}
 #page-mod-quiz-edit.dir-rtl div.question div.content div.questioncontrols {float: left;left: 0.3em; right:auto;}
 #page-mod-quiz-edit.dir-rtl .editq div.question div.content .singlequestion .questioneditbutton .questionname,
 #page-mod-quiz-edit.dir-rtl .editq div.question div.content .singlequestion .questioneditbutton .questiontext {float: right; padding-right: 0.3em;}
@@ -414,4 +414,7 @@ bank window's title is prominent enough*/
 #page-mod-quiz-edit.dir-rtl .quizpagedelete {left: 0.2em;right:auto;}
 #page-mod-quiz-edit.dir-rtl div.quizcontents {clear: right;float: right;}
 #page-mod-quiz-edit.dir-rtl .questionbankwindow.block {float: left;}
-#page-question-edit.dir-rtl td.creatorname, #page-question-edit.dir-rtl td.modifiername {text-align: center;}
\ No newline at end of file
+#page-question-edit.dir-rtl td.creatorname, #page-question-edit.dir-rtl td.modifiername {text-align: center;}
+.path-question.dir-rtl input[name="maxmark"],
+.path-question-type.dir-rtl input[name="defaultmark"],
+#page-mod-quiz-edit.dir-rtl div.points input {direction: ltr;text-align: left;}
diff --git a/mod/quiz/tests/generator/lib.php b/mod/quiz/tests/generator/lib.php
new file mode 100644 (file)
index 0000000..24ed645
--- /dev/null
@@ -0,0 +1,106 @@
+<?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/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Quiz module test data generator class
+ *
+ * @package mod_quiz
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mod_quiz_generator extends phpunit_module_generator {
+
+    /**
+     * Create new quiz module instance.
+     * @param array|stdClass $record
+     * @param array $options (mostly course_module properties)
+     * @return stdClass activity record with extra cmid field
+     */
+    public function create_instance($record = null, array $options = null) {
+        global $CFG;
+        require_once("$CFG->dirroot/mod/quiz/locallib.php");
+
+        $this->instancecount++;
+        $i = $this->instancecount;
+
+        $record = (object)(array)$record;
+        $options = (array)$options;
+
+        if (empty($record->course)) {
+            throw new coding_exception('module generator requires $record->course');
+        }
+        if (isset($options['idnumber'])) {
+            $record->cmidnumber = $options['idnumber'];
+        } else {
+            $record->cmidnumber = '';
+        }
+
+        $alwaysvisible = mod_quiz_display_options::DURING | mod_quiz_display_options::IMMEDIATELY_AFTER |
+                mod_quiz_display_options::LATER_WHILE_OPEN | mod_quiz_display_options::AFTER_CLOSE;
+
+        $defaultquizsettings = array(
+            'name'                   => get_string('pluginname', 'data').' '.$i,
+            'intro'                  => 'Test quiz ' . $i,
+            'introformat'            => FORMAT_MOODLE,
+            'timeopen'               => 0,
+            'timeclose'              => 0,
+            'preferredbehaviour'     => 'deferredfeedback',
+            'attempts'               => 0,
+            'attemptonlast'          => 0,
+            'grademethod'            => QUIZ_GRADEHIGHEST,
+            'decimalpoints'          => 2,
+            'questiondecimalpoints'  => -1,
+            'reviewattempt'          => $alwaysvisible,
+            'reviewcorrectness'      => $alwaysvisible,
+            'reviewmarks'            => $alwaysvisible,
+            'reviewspecificfeedback' => $alwaysvisible,
+            'reviewgeneralfeedback'  => $alwaysvisible,
+            'reviewrightanswer'      => $alwaysvisible,
+            'reviewoverallfeedback'  => $alwaysvisible,
+            'questionsperpage'       => 1,
+            'shufflequestions'       => 0,
+            'shuffleanswers'         => 1,
+            'questions'              => '',
+            'sumgrades'              => 0,
+            'grade'                  => 0,
+            'timecreated'            => time(),
+            'timemodified'           => time(),
+            'timelimit'              => 0,
+            'overduehandling'        => 'autoabandon',
+            'graceperiod'            => 86400,
+            'quizpassword'           => '',
+            'subnet'                 => '',
+            'browsersecurity'        => '',
+            'delay1'                 => 0,
+            'delay2'                 => 0,
+            'showuserpicture'        => 0,
+            'showblocks'             => 0,
+            'navmethod'              => QUIZ_NAVMETHOD_FREE,
+        );
+
+        foreach ($defaultquizsettings as $name => $value) {
+            if (!isset($record->{$name})) {
+                $record->{$name} = $value;
+            }
+        }
+
+        $record->coursemodule = $this->precreate_course_module($record->course, $options);
+        $id = quiz_add_instance($record);
+        return $this->post_add_instance($id, $record->coursemodule);
+    }
+}
index 27b4722..c3de373 100644 (file)
@@ -1328,22 +1328,25 @@ function scorm_dndupload_handle($uploadinfo) {
  * @param array $grades grades array of users with grades - used when $userid = 0
  */
 function scorm_set_completion($scorm, $userid, $completionstate = COMPLETION_COMPLETE, $grades = array()) {
-    if (!completion_info::is_enabled()) {
-        return;
-    }
-
     $course = new stdClass();
     $course->id = $scorm->course;
+    $completion = new completion_info($course);
+
+    // Check if completion is enabled site-wide, or for the course
+    if (!$completion->is_enabled()) {
+        return;
+    }
 
     $cm = get_coursemodule_from_instance('scorm', $scorm->id, $scorm->course);
-    if (!empty($cm)) {
-        $completion = new completion_info($course);
-        if (empty($userid)) { //we need to get all the relevant users from $grades param.
-            foreach ($grades as $grade) {
-                $completion->update_state($cm, $completionstate, $grade->userid);
-            }
-        } else {
-            $completion->update_state($cm, $completionstate, $userid);
+    if (empty($cm) || !$completion->is_enabled($cm)) {
+            return;
+    }
+
+    if (empty($userid)) { //we need to get all the relevant users from $grades param.
+        foreach ($grades as $grade) {
+            $completion->update_state($cm, $completionstate, $grade->userid);
         }
+    } else {
+        $completion->update_state($cm, $completionstate, $userid);
     }
 }
index ea1fbb6..028ca08 100644 (file)
@@ -256,4 +256,4 @@ $string['thanksforanswers'] = 'Thanks for answering this survey, {$a}';
 $string['time'] = 'Time';
 $string['viewsurveyresponses'] = 'View {$a} survey responses';
 $string['notyetanswered'] = 'Not yet answered';
-$string['allquestionrequireanswer'] = 'All questions are required and must be answered';
+$string['allquestionrequireanswer'] = 'All questions are required and must be answered.';
index 80fbdf4..d332563 100644 (file)
@@ -22,8 +22,10 @@ $string['backoldversion'] = 'Back to old version';
 $string['backpage'] = 'Back to page';
 $string['backtomapmenu'] = 'Back to map menu';
 $string['changerate'] = 'Do you wish to change it?';
+$string['cannoteditpage'] = 'You can not edit this page.';
 $string['cannotmanagefiles'] = 'You don\'t have permission to manage the wiki files.';
 $string['cannotviewfiles'] = 'You don\'t have permission to view the wiki files.';
+$string['cannotviewpage'] = 'You can not view this page.';
 $string['comparesel'] = 'Compare selected';
 $string['comments'] = 'Comments';
 $string['commentscount'] = 'Comments ({$a})';
index 133178a..974c46d 100644 (file)
@@ -531,7 +531,7 @@ function wiki_extend_navigation(navigation_node $navref, $course, $module, $cm)
             $node = $navref->add(get_string('view', 'wiki'), $link, navigation_node::TYPE_SETTING);
         }
 
-        if (has_capability('mod/wiki:editpage', $context)) {
+        if (wiki_user_can_edit($subwiki)) {
             $link = new moodle_url('/mod/wiki/edit.php', array('pageid' => $pageid));
             $node = $navref->add(get_string('edit', 'wiki'), $link, navigation_node::TYPE_SETTING);
         }
index ff3b67a..079ce2a 100644 (file)
@@ -767,7 +767,7 @@ function wiki_user_can_view($subwiki) {
         //      Each person owns a wiki.
         if ($wiki->wikimode == 'collaborative' || $wiki->wikimode == 'individual') {
             // Only members of subwiki group could view that wiki
-            if ($subwiki->groupid == groups_get_activity_group($cm)) {
+            if (groups_is_member($subwiki->groupid)) {
                 // Only view capability needed
                 return has_capability('mod/wiki:viewpage', $context);
 
index 4fe9d50..511f48f 100644 (file)
@@ -172,13 +172,8 @@ abstract class page_wiki {
             if (!$manage and !($edit and groups_is_member($currentgroup))) {
                 unset($this->tabs['edit']);
             }
-        } else {
-            if (!has_capability('mod/wiki:editpage', $PAGE->context)) {
-                unset($this->tabs['edit']);
-            }
         }
 
-
         if (empty($options)) {
             $this->tabs_options = array('activetab' => substr(get_class($this), 10));
         } else {
@@ -326,8 +321,7 @@ class page_wiki_view extends page_wiki {
                 }
             }
         } else {
-            // @TODO: Tranlate it
-            echo "You can not view this page";
+            echo get_string('cannotviewpage', 'wiki');
         }
     }
 
@@ -420,8 +414,7 @@ class page_wiki_edit extends page_wiki {
         if (wiki_user_can_edit($this->subwiki)) {
             $this->print_edit();
         } else {
-            // @TODO: Translate it
-            echo "You can not edit this page";
+            echo get_string('cannoteditpage', 'wiki');
         }
     }
 
index 6efe849..4c483e2 100644 (file)
@@ -83,7 +83,7 @@ $string['configgradedecimals'] = 'Default number of digits that should be shown
 $string['configgradinggrade'] = 'Default maximum grade for assessment in workshops';
 $string['configmaxbytes'] = 'Default maximum submission file size for all workshops on the site (subject to course limits and other local settings)';
 $string['configstrategy'] = 'Default grading strategy for workshops';
-$string['createsubmission'] = 'Submit';
+$string['createsubmission'] = 'Start preparing your submission';
 $string['daysago'] = '{$a} days ago';
 $string['daysleft'] = '{$a} days left';
 $string['daystoday'] = 'today';
index b5a1c4a..cb2aff1 100644 (file)
@@ -173,13 +173,10 @@ if (!empty($dataid)) {
             $callbackargs[substr($key, 3)] = $value;
         }
     }
-    // righto, now we have the callback args set up
-    // load up the caller file and class and tell it to set up all the data
-    // it needs
-    require_once($CFG->dirroot . $callbackfile);
-    if (!class_exists($callbackclass) || !is_subclass_of($callbackclass, 'portfolio_caller_base')) {
-        throw new portfolio_caller_exception('callbackclassinvalid', 'portfolio');
-    }
+
+    // Ensure that we found a file we can use, if not throw an exception.
+    portfolio_include_callback_file($callbackfile, $callbackclass);
+
     $caller = new $callbackclass($callbackargs);
     $caller->set('user', $USER);
     if ($formats = explode(',', $callerformats)) {
index e04a638..6a24e38 100644 (file)
@@ -86,7 +86,6 @@ abstract class qbehaviour_renderer extends plugin_renderer_base {
 
         $commenteditor = html_writer::tag('div', html_writer::tag('textarea', s($commenttext),
                 array('id' => $id, 'name' => $inputname, 'rows' => 10, 'cols' => 60)));
-        $commenteditor .= html_writer::end_tag('div');
 
         $editorformat = '';
         if (count($formats) == 1) {
index e688add..5b9c894 100644 (file)
@@ -134,6 +134,9 @@ abstract class question_bank_column_base {
      */
     protected $qbank;
 
+    /** @var bool determine whether the column is td or th. */
+    protected $isheading = false;
+
     /**
      * Constructor.
      * @param $qbank the question_bank_view we are helping to render.
@@ -150,6 +153,13 @@ abstract class question_bank_column_base {
     protected function init() {
     }
 
+    /**
+     * Set the column as heading
+     */
+    public function set_as_heading() {
+        $this->isheading = true;
+    }
+
     public function is_extra_row() {
         return false;
     }
@@ -258,8 +268,20 @@ abstract class question_bank_column_base {
         $this->display_end($question, $rowclasses);
     }
 
+    /**
+     * Output the opening column tag.  If it is set as heading, it will use <th> tag instead of <td>
+     *
+     * @param stdClass $question
+     * @param array $rowclasses
+     */
     protected function display_start($question, $rowclasses) {
-        echo '<td class="' . $this->get_classes() . '">';
+        $tag = 'td';
+        $attr = array('class' => $this->get_classes());
+        if ($this->isheading) {
+            $tag = 'th';
+            $attr['scope'] = 'row';
+        }
+        echo html_writer::start_tag($tag, $attr);
     }
 
     /**
@@ -292,8 +314,18 @@ abstract class question_bank_column_base {
      */
     protected abstract function display_content($question, $rowclasses);
 
+    /**
+     * Output the closing column tag
+     *
+     * @param object $question
+     * @param string $rowclasses
+     */
     protected function display_end($question, $rowclasses) {
-        echo "</td>\n";
+        $tag = 'td';
+        if ($this->isheading) {
+            $tag = 'th';
+        }
+        echo html_writer::end_tag($tag);
     }
 
     /**
@@ -885,7 +917,7 @@ class question_bank_view {
         $this->lastchangedid = optional_param('lastchanged',0,PARAM_INT);
 
         $this->init_column_types();
-        $this->init_columns($this->wanted_columns());
+        $this->init_columns($this->wanted_columns(), $this->heading_column());
         $this->init_sort();
 
         $PAGE->requires->yui2_lib('container');
@@ -901,6 +933,15 @@ class question_bank_view {
         return $columns;
     }
 
+    /**
+     * Specify the column heading
+     *
+     * @return string Column name for the heading
+     */
+    protected function heading_column() {
+        return 'questionname';
+    }
+
     protected function known_field_types() {
         return array(
             new question_bank_checkbox_column($this),
@@ -923,7 +964,13 @@ class question_bank_view {
         }
     }
 
-    protected function init_columns($wanted) {
+    /**
+     * Initializing table columns
+     *
+     * @param array $wanted Collection of column names
+     * @param string $heading The name of column that is set as heading
+     */
+    protected function init_columns($wanted, $heading = '') {
         $this->visiblecolumns = array();
         $this->extrarows = array();
         foreach ($wanted as $colname) {
@@ -938,6 +985,9 @@ class question_bank_view {
             }
         }
         $this->requiredcolumns = array_merge($this->visiblecolumns, $this->extrarows);
+        if (array_key_exists($heading, $this->requiredcolumns)) {
+            $this->requiredcolumns[$heading]->set_as_heading();
+        }
     }
 
     /**
index 132b629..d8f5dbf 100644 (file)
@@ -6,3 +6,17 @@
 .que.calculated .answer input[type="text"] {
     width: 30%;
 }
+
+/* Numeric INPUT text boxes should be left aligned in RTL mode
+*/
+#page-question-type-calculated.dir-rtl input[name^="answer"],
+#page-question-type-calculated.dir-rtl input[name^="unit"],
+#page-question-type-calculated.dir-rtl input[name^="multiplier"],
+#page-question-type-calculated.dir-rtl input[name^="calcmax"],
+#page-question-type-calculated.dir-rtl input[name^="calcmin"],
+#page-question-type-calculated.dir-rtl input[name^="number"],
+#page-question-type-calculated.dir-rtl input[name^="tolerance"]
+{
+    direction: ltr;
+    text-align: left;
+}
index 85e248f..b58c47a 100644 (file)
@@ -137,6 +137,25 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
                     array($newquestionid, $data->questiontext, $data->answertext),
                     'id', IGNORE_MULTIPLE);
 
+            // Not able to find the answer, let's try cleaning the answertext
+            // of all the question answers in DB as slower fallback. MDL-36683 / MDL-30018.
+            if (!$sub) {
+                $params = array('question' => $newquestionid);
+                $potentialsubs = $DB->get_records('question_match_sub', array('question' => $newquestionid), '', 'id, questiontext, answertext');
+                foreach ($potentialsubs as $potentialsub) {
+                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
+                    $cleanquestion = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $potentialsub->questiontext); // Clean CTRL chars.
+                    $cleanquestion = preg_replace("/\r\n|\r/", "\n", $cleanquestion); // Normalize line ending.
+
+                    $cleananswer = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $potentialsub->answertext); // Clean CTRL chars.
+                    $cleananswer = preg_replace("/\r\n|\r/", "\n", $cleananswer); // Normalize line ending.
+
+                    if ($cleanquestion === $data->questiontext && $cleananswer == $data->answertext) {
+                        $sub = $potentialsub;
+                    }
+                }
+            }
+
             // Found, let's create the mapping
             if ($sub) {
                 $this->set_mapping('question_match_sub', $oldid, $sub->id);
index e94c202..031605e 100644 (file)
@@ -110,10 +110,10 @@ class qtype_multichoice_edit_form extends question_edit_form {
             //check no of choices
             $trimmedanswer = trim($answer['text']);
             $fraction = (float) $data['fraction'][$key];
-            if (empty($trimmedanswer) && empty($fraction)) {
+            if ($trimmedanswer === '' && empty($fraction)) {
                 continue;
             }
-            if (empty($trimmedanswer)) {
+            if ($trimmedanswer === '') {
                 $errors['fraction['.$key.']'] = get_string('errgradesetanswerblank', 'qtype_multichoice');
             }
 
index 784c855..50958df 100644 (file)
@@ -6,3 +6,15 @@
 .que.numerical .answer input[type="text"] {
     width: 30%;
 }
+
+/* Numeric INPUT text boxes should be left aligned in RTL mode
+*/
+#page-question-type-numerical.dir-rtl input[name="unitpenalty"],
+#page-question-type-numerical.dir-rtl input[name^="answer"],
+#page-question-type-numerical.dir-rtl input[name^="tolerance"],
+#page-question-type-numerical.dir-rtl input[name^="multiplier"],
+#page-question-type-numerical.dir-rtl input[name^="unit"]
+{
+    direction: ltr;
+    text-align: left;
+}
index 5e848cf..d946739 100644 (file)
@@ -1024,7 +1024,7 @@ M.core_filepicker.init = function(Y, options) {
             var nextpage = this.active_repo.page+1;
             var args = {
                 page: nextpage,
-                repo_id: this.active_repo.id,
+                repo_id: this.active_repo.id
             };
             var action = this.active_repo.issearchresult ? 'search' : 'list';
             this.request({
index 4a5151b..c19030e 100644 (file)
@@ -477,8 +477,11 @@ body.tag .managelink {padding: 5px;}
 #helppopupbox {background-color: #eee; border: 1px solid #848484;z-index: 10000 !important;}
 #helppopupbox .yui3-widget-hd {float:right;margin:3px 3px 0 0;}
 #helppopupbox .yui3-widget-bd {margin:0 1em 1em 1em;border-top:1px solid #eee;}
+#helppopupbox .yui3-widget-ft {text-align: center;}
+#helppopupbox .yui3-widget-ft .closebtn {margin:0 1em 1em 1em;}
 #helppopupbox .helpheading {font-size: 1em;}
 #helppopupbox .spinner {margin:1em;}
+.dir-rtl #helppopupbox .yui3-widget-hd {float:left;margin:3px 0 0 3px;}
 
 /**
  * Custom menu
@@ -664,7 +667,6 @@ body.tag .managelink {padding: 5px;}
 .dir-rtl .mod-indent-15,
 .dir-rtl .mod-indent-huge {margin-right:300px;margin-left:0;}
 
-.dir-rtl .felement.feditor select {margin-right:18.75%;margin-left:auto;}
 .dir-rtl .mform .fitem .felement {margin-right: 16%;margin-left:auto;text-align: right;}
 .dir-rtl .mform .fitem .felement input[name=email],
 .dir-rtl .mform .fitem .felement input[name=email2],
index bc31d35..7e94997 100644 (file)
@@ -390,3 +390,8 @@ h2.headingblock {
 #page-mod-assignment-submissions #attempts th.header {
     font-size: 80%;
 }
+
+.course-content ul.topics,
+.course-content ul.weeks {
+    clear: both;
+}
\ No newline at end of file
index a617287..a8db47e 100644 (file)
@@ -52,6 +52,10 @@ div#headerinner {
     line-height: 30px;
     min-width: 400px;
 }
+.dir-rtl #headright {
+    float: left;
+    min-width: 300px;
+}
 
 #headright a {
     color: #fff;
@@ -66,6 +70,10 @@ div#headerinner {
     font-size: 9px;
 }
 
+.dir-rtl #headright div.langmenu {
+    float: left !important;
+}
+
 #headleft {
     float: left;
     height: 30px;
@@ -74,6 +82,10 @@ div#headerinner {
     font-size: 12px;
 }
 
+.dir-rtl #headleft {
+    float: right;
+}
+
 /** Logo and menu bar divs and wrap **/
 
 #textcontainer-wrap {
@@ -99,6 +111,10 @@ div.thetitle {
     float: left;
 }
 
+.dir-rtl div.thetitle {
+    float: right;
+}
+
 #nologo {
     width: 337px;
     float: left;
@@ -107,6 +123,11 @@ div.thetitle {
     margin-top: 40px;
     }
 
+.dir-rtl #nologo {
+    float: right;
+    padding-right: 3px;
+}
+
 #nologo.nomenu {
     width: 100%;
 }
@@ -118,25 +139,27 @@ div.thetitle {
     font-weight: 800;
     letter-spacing: -1px;
     line-height: 45px;
-
-
 }
+
 #nologo a:hover {
     text-decoration: none;
 }
+
 div.rightinfo {
     float: right;
     min-width: 470px;
     height: 130px;
     overflow: hidden;
 }
+.dir-rtl div.rightinfo {
+    float: left;
+}
 
 #menucontainer {
     height: 45px;
     margin-top: 40px;
 }
 
-
 /** main content wraps **/
 
 #outercontainer {
@@ -191,6 +214,10 @@ div.jcontrolsleft {
     float: left;
     min-width: 100px;
 }
+.dir-rtl div.jcontrolsleft {
+    float:right;
+    margin-right: 15px;
+}
 
 div.jcontrolsright {
     float: right;
@@ -199,6 +226,11 @@ div.jcontrolsright {
     margin-right: 15px;
 }
 
+.dir-rtl div.jcontrolsright {
+    float: left;
+    margin-left: 15px;
+}
+
 div.jcontrolsright div.singlebutton, div.jcontrolsright div.forumsearch {
     margin: 5px 0px 0px;
 }
@@ -243,6 +275,9 @@ div.johndocsleft {
     color: #333333;
     font-size: 11px;
 }
+.dir-rtl div.johndocsleft {
+    float: right;
+}
 
 /** Some breadcrumb style **/
 
@@ -410,7 +445,12 @@ div#jcontrols_button span.arrow.sep {
 
 /** inner block style for decent display of recent news, etc **/
 
-.block_recent_activity.block_docked div.content h2.main, .block_recent_activity.block_docked div.content h3.main, .side-pre .block div.content h2.main,.side-post .block div.content h2.main,.block div.content h3.main,.block div.content h2 {
+.block_recent_activity.block_docked div.content h2.main,
+.block_recent_activity.block_docked div.content h3.main,
+.side-pre .block div.content h2.main,
+.side-post .block div.content h2.main,
+.block div.content h3.main,
+.block div.content h2 {
     font-size: 13px;
     color: #333;
     font-weight: 800;
@@ -448,7 +488,7 @@ div.yui3-widget-bd h1.helpheading {
 
 .yui-module.yui-overlay.yui-panel div.hd {
     background: #eeeeee url([[pix:theme|bart5]]) repeat-x 0% 90%;
-    }
+}
 
 /**moodle dropdown css for yui menu **/
 
@@ -851,6 +891,10 @@ div.coursebox h3.name a {
     padding-left: 5px !important;
 }
 
+.dir-rtl .unlist ul.teachers {
+    padding-right: 5px;
+}
+
 .unlist ul.teachers li {
     font-size: 10px;
 }
@@ -893,6 +937,9 @@ div.course_category_tree div.category,div.course_category_tree div.category_labe
 div.course_category_tree div.category.with_children div.category_label {
     background-position: 0 55% !important;
 }
+.dir-rtl div.course_category_tree div.category.with_children div.category_label {
+    background-position: right center !important;
+}
 
 div.course_category_tree div.course.odd {
     background: #eee;
@@ -906,6 +953,23 @@ div.category.subcategory.with_children {
     margin-left: 20px;
 }
 
+.dir-rtl div.category.subcategory.with_children {
+    margin-right: 20px;
+}
+
+.dir-rtl .course_category_tree .category .course_info {
+    padding-right: 20px;
+}
+
+.dir-rtl.jsenabled .course_category_tree .category.with_children.collapsed .category_label {
+    backkground-position: right center !important;
+}
+
+.dir-rtl .course_category_tree .category_label,
+.dir-rtl .course_category_tree .category .course {
+    padding-right: 35px;
+}
+
 div.course_category_tree div.controls {
     font-size: 10px;
     color: #666;
@@ -1025,6 +1089,9 @@ table.glossarypost td.entry h3 {
     margin-bottom: 3px;
     margin-top: 2px;
 }
+#page-mod-wiki-view.dir-rtl h2.wiki_headingtitle {
+    text-align: right;
+}
 
 /**HR in content blocks **/
 
@@ -1040,7 +1107,7 @@ table.glossarypost td.entry h3 {
 
 div#intro {
     border: none;
-    text-align: center;
+    text-align: left;
     font-size: 14px;
     color: #333333;
 }
@@ -1126,4 +1193,4 @@ div.singlebutton.forumaddnew {
 
 #page-course-info div.generalbox ul, #page-course-info div.generalbox li {
     list-style-type: none;
-}
\ No newline at end of file
+}
index 7d2ba26..30466f5 100644 (file)
@@ -41,8 +41,6 @@
     padding-right: 7px;
 }
 
-.dir-rtl .ui-li-count { left:38px; right:auto;}
-
 .dir-rtl .ui-checkbox .ui-btn, .dir-rtl .ui-radio .ui-btn {
     text-align: right;
 }
 
 .path-mod-quiz.dir-rtl .que .control {
     width: 75%;
+}
+
+.dir-rtl .categorybox .ui-btn-inner,
+.dir-rtl .coursebox .ui-btn-inner {
+    text-align: right;
+ }
+
+.dir-rtl .categorybox .ui-btn-text,
+.dir-rtl .coursebox .ui-btn-text {
+    padding-right: 25px;
 }
\ No newline at end of file
index aa88131..0ca7509 100644 (file)
@@ -293,6 +293,7 @@ $PAGE->set_title("$course->shortname: $streditmyprofile");
 $PAGE->set_heading($course->fullname);
 
 echo $OUTPUT->header();
+echo $OUTPUT->heading($userfullname);
 
 if ($email_changed) {
     echo $email_changed_html;
index 2f54d4e..8be8f1f 100644 (file)
@@ -62,7 +62,12 @@ class user_filter_text extends user_filter_type {
                 // no data - no change except for empty filter
                 return false;
             }
-            return array('operator'=>(int)$formdata->$operator, 'value'=>$formdata->$field);
+            // If field value is set then use it, else it's null.
+            $fieldvalue = null;
+            if (isset($formdata->$field)) {
+                $fieldvalue = $formdata->$field;
+            }
+            return array('operator' => (int)$formdata->$operator, 'value' =>  $fieldvalue);
         }
 
         return false;
index 2319332..f757f31 100644 (file)
@@ -137,7 +137,7 @@ abstract class user_selector_base {
      * Clear the list of excluded user ids.
      */
     public function clear_exclusions() {
-        $exclude = array();
+        $this->exclude = array();
     }
 
     /**
index 68d36ac..83792d4 100644 (file)
@@ -148,9 +148,12 @@ if ($currentuser) {
     }
 }
 
+$PAGE->set_title("$course->fullname: $strpersonalprofile: $fullname");
+$PAGE->set_heading($course->fullname);
+$PAGE->set_pagelayout('standard');
 
-/// We've established they can see the user's name at least, so what about the rest?
-
+// Locate the users settings in the settings navigation and force it open.
+// This MUST be done after we've set up the page as it is going to cause theme and output to initialise.
 if (!$currentuser) {
     $PAGE->navigation->extend_for_user($user);
     if ($node = $PAGE->settingsnav->get('userviewingsettings'.$user->id)) {
@@ -163,9 +166,6 @@ if ($node = $PAGE->settingsnav->get('courseadmin')) {
     $node->forceopen = false;
 }
 
-$PAGE->set_title("$course->fullname: $strpersonalprofile: $fullname");
-$PAGE->set_heading($course->fullname);
-$PAGE->set_pagelayout('standard');
 echo $OUTPUT->header();
 
 echo '<div class="userprofile">';
index 6ccbfde..4fc0668 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012062502.09;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062503.01;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.3.2+ (Build: 20121018)'; // Human-friendly version name
+$release  = '2.3.3+ (Build: 20121116)';  // Human-friendly version name
 
 $branch   = '23';                       // this version's branch
 $maturity = MATURITY_STABLE;            // this version's maturity level