Merge branch 'MDL-36289-m23' of git://github.com/netspotau/moodle-mod_assign into...
authorDan Poltawski <dan@moodle.com>
Wed, 21 Nov 2012 04:01:03 +0000 (12:01 +0800)
committerDan Poltawski <dan@moodle.com>
Wed, 21 Nov 2012 04:01:03 +0000 (12:01 +0800)
189 files changed:
admin/roles/check.php
admin/roles/lib.php
admin/tool/customlang/styles.css
admin/webservice/service_users.php
auth/ldap/auth.php
auth/ldap/config.html
auth/ldap/lang/en/auth_ldap.php
auth/mnet/auth.php
auth/shibboleth/index.php
auth/shibboleth/lang/en/auth_shibboleth.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/tests/cronhelper_test.php
blocks/community/forms.php
blocks/dock.js
blocks/glossary_random/block_glossary_random.php
blocks/html/lib.php
blocks/online_users/block_online_users.php
blocks/search_forums/block_search_forums.php
calendar/lib.php
cohort/assign.php
cohort/lib.php
comment/comment.js
comment/locallib.php
course/category.php
course/externallib.php
course/index.php
course/lib.php
course/renderer.php
course/view.php
course/yui/modchooser/modchooser.js
course/yui/toolboxes/toolboxes.js
enrol/externallib.php
enrol/flatfile/lib.php
enrol/manual/externallib.php
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/bg/install.php
install/lang/en_kids/langconfig.php [new file with mode: 0644]
install/lang/fi/install.php
install/lang/he/error.php
install/lang/he/install.php
install/lang/hr/moodle.php
install/lang/it/langconfig.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/admin.php [new file with mode: 0644]
install/lang/ms/install.php [new file with mode: 0644]
install/lang/ms/moodle.php [new file with mode: 0644]
install/lang/no/error.php
install/lang/no/install.php
install/lang/ro/install.php
install/lang/sr_cr/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
lang/en/role.php
lib/adminlib.php
lib/bennu/bennu.class.php
lib/bennu/iCalendar_parameters.php
lib/blocklib.php
lib/completionlib.php
lib/db/access.php
lib/db/upgrade.php
lib/editor/tinymce/readme_moodle.txt
lib/editor/tinymce/tiny_mce/3.5.1.1/plugins/spellchecker/classes/GoogleSpell.php
lib/filebrowser/file_browser.php
lib/filebrowser/file_info.php
lib/filebrowser/file_info_context_course.php
lib/filebrowser/file_info_context_coursecat.php
lib/filebrowser/file_info_context_module.php
lib/filebrowser/file_info_stored.php
lib/filelib.php
lib/form/yui/dateselector/dateselector.js
lib/formslib.php
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/navigationlib.php
lib/outputrenderers.php
lib/phpunit/classes/data_generator.php
lib/phpunit/classes/util.php
lib/phpunit/tests/generator_test.php
lib/pluginlib.php
lib/portfoliolib.php
lib/tablelib.php
lib/tests/accesslib_test.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/tests/outputcomponents_test.php
lib/upgradelib.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/gradeform.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/locallib.php
mod/book/version.php
mod/data/field/date/field.class.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/preset.php
mod/data/view.php
mod/feedback/lib.php
mod/forum/discuss.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/locallib.php
mod/forum/styles.css
mod/glossary/locallib.php
mod/imscp/locallib.php
mod/lesson/essay.php
mod/lesson/format.php
mod/lesson/importppt.php [deleted file]
mod/lesson/importpptlib.php [deleted file]
mod/lesson/lib.php
mod/lesson/mod_form.php
mod/lesson/renderer.php
mod/lesson/version.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/scorm/mod_form.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/fileinfolib.php
mod/workshop/lang/en/workshop.php
portfolio/add.php
question/behaviour/rendererbase.php
question/editlib.php
question/format/xml/format.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/multichoice/question.php
question/type/multichoice/tests/question_test.php
question/type/numerical/styles.css
repository/filepicker.js
repository/lib.php
repository/local/lib.php
theme/base/style/core.css
theme/canvas/layout/frontpage.php
theme/canvas/layout/general.php
theme/canvas/layout/report.php
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
webservice/lib.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 c743bcf..963b289 100644 (file)
@@ -67,3 +67,7 @@
 #page-admin-tool-customlang-index .continuebutton {
     margin-top: 1em;
 }
+
+.path-admin-tool-customlang #translator .standard.master.cell.c2 {
+    word-break: break-all;
+}
index 12db2b0..ad67c66 100644 (file)
@@ -97,7 +97,7 @@ $usersmissingcaps = $webservicemanager->get_missing_capabilities_by_users($allow
 
 //add the missing capabilities to the allowed users object to be displayed by renderer
 foreach ($allowedusers as &$alloweduser) {
-    if (!is_siteadmin($alloweduser->id) and key_exists($alloweduser->id, $usersmissingcaps)) {
+    if (!is_siteadmin($alloweduser->id) and array_key_exists($alloweduser->id, $usersmissingcaps)) {
         $alloweduser->missingcapabilities = implode(', ', $usersmissingcaps[$alloweduser->id]);
     }
 }
index c206452..88bb10d 100644 (file)
@@ -41,18 +41,6 @@ if (!defined('AUTH_GID_NOGROUP')) {
     define('AUTH_GID_NOGROUP', -2);
 }
 
-// Regular expressions for a valid NTLM username and domain name.
-if (!defined('AUTH_NTLM_VALID_USERNAME')) {
-    define('AUTH_NTLM_VALID_USERNAME', '[^/\\\\\\\\\[\]:;|=,+*?<>@"]+');
-}
-if (!defined('AUTH_NTLM_VALID_DOMAINNAME')) {
-    define('AUTH_NTLM_VALID_DOMAINNAME', '[^\\\\\\\\\/:*?"<>|]+');
-}
-// Default format for remote users if using NTLM SSO
-if (!defined('AUTH_NTLM_DEFAULT_FORMAT')) {
-    define('AUTH_NTLM_DEFAULT_FORMAT', '%domain%\\%username%');
-}
-
 require_once($CFG->libdir.'/authlib.php');
 require_once($CFG->libdir.'/ldaplib.php');
 
@@ -1582,11 +1570,8 @@ class auth_plugin_ldap extends auth_plugin_base {
 
             switch ($this->config->ntlmsso_type) {
                 case 'ntlm':
-                    // The format is now configurable, so try to extract the username
-                    $username = $this->get_ntlm_remote_user($username);
-                    if (empty($username)) {
-                        return false;
-                    }
+                    // Format is DOMAIN\username
+                    $username = substr(strrchr($username, '\\'), 1);
                     break;
                 case 'kerberos':
                     // Format is username@DOMAIN
@@ -1794,9 +1779,6 @@ class auth_plugin_ldap extends auth_plugin_base {
         if (!isset($config->ntlmsso_type)) {
             $config->ntlmsso_type = 'ntlm';
         }
-        if (!isset($config->ntlmsso_remoteuserformat)) {
-            $config->ntlmsso_remoteuserformat = '';
-        }
 
         // Try to remove duplicates before storing the contexts (to avoid problems in sync_users()).
         $config->contexts = explode(';', $config->contexts);
@@ -1836,7 +1818,6 @@ class auth_plugin_ldap extends auth_plugin_base {
         set_config('ntlmsso_subnet', trim($config->ntlmsso_subnet), $this->pluginconfig);
         set_config('ntlmsso_ie_fastpath', (int)$config->ntlmsso_ie_fastpath, $this->pluginconfig);
         set_config('ntlmsso_type', $config->ntlmsso_type, 'auth/ldap');
-        set_config('ntlmsso_remoteuserformat', trim($config->ntlmsso_remoteuserformat), 'auth/ldap');
 
         return true;
     }
@@ -2037,60 +2018,4 @@ class auth_plugin_ldap extends auth_plugin_base {
                                 $this->config->user_attribute, $this->config->search_sub);
     }
 
-
-    /**
-     * A chance to validate form data, and last chance to do stuff
-     * before it is inserted in config_plugin
-     *
-     * @param object object with submitted configuration settings (without system magic quotes)
-     * @param array $err array of error messages (passed by reference)
-     */
-    function validate_form($form, &$err) {
-        if ($form->ntlmsso_type == 'ntlm') {
-            $format = trim($form->ntlmsso_remoteuserformat);
-            if (!empty($format) && !preg_match('/%username%/i', $format)) {
-                $err['ntlmsso_remoteuserformat'] = get_string('auth_ntlmsso_missing_username', 'auth_ldap');
-            }
-        }
-    }
-
-
-    /**
-     * When using NTLM SSO, the format of the remote username we get in
-     * $_SERVER['REMOTE_USER'] may vary, depending on where from and how the web
-     * server gets the data. So we let the admin configure the format using two
-     * place holders (%domain% and %username%). This function tries to extract
-     * the username (stripping the domain part and any separators if they are
-     * present) from the value present in $_SERVER['REMOTE_USER'], using the
-     * configured format.
-     *
-     * @param string $remoteuser The value from $_SERVER['REMOTE_USER'] (converted to UTF-8)
-     *
-     * @return string The remote username (without domain part or
-     *                separators). Empty string if we can't extract the username.
-     */
-    protected function get_ntlm_remote_user($remoteuser) {
-        if (empty($this->config->ntlmsso_remoteuserformat)) {
-            $format = AUTH_NTLM_DEFAULT_FORMAT;
-        } else {
-            $format = $this->config->ntlmsso_remoteuserformat;
-        }
-
-        $format = preg_quote($format);
-        $formatregex = preg_replace(array('#%domain%#', '#%username%#'),
-                                    array('('.AUTH_NTLM_VALID_DOMAINNAME.')', '('.AUTH_NTLM_VALID_USERNAME.')'),
-                                    $format);
-        if (preg_match('#^'.$formatregex.'$#', $remoteuser, $matches)) {
-            $user = end($matches);
-            return $user;
-        }
-
-        /* We are unable to extract the username with the configured format. Probably
-         * the format specified is wrong, so log a warning for the admin and return
-         * an empty username.
-         */
-        error_log($this->errorlogtag.get_string ('auth_ntlmsso_maybeinvalidformat', 'auth_ldap'));
-        return '';
-    }
-
 } // End of the class
index 3efe253..8ad27ef 100644 (file)
@@ -94,9 +94,6 @@ if (!isset($config->ntlmsso_ie_fastpath)) {
 if (!isset($config->ntlmsso_type)) {
     $config->ntlmsso_type = 'ntlm';
 }
-if (!isset($config->ntlmsso_remoteuserformat)) {
-    $config->ntlmsso_remoteuserformat = '';
-}
 
 $yesno = array(get_string('no'), get_string('yes'));
 
@@ -542,18 +539,6 @@ $yesno = array(get_string('no'), get_string('yes'));
         <?php print_string('auth_ntlmsso_type','auth_ldap') ?>
     </td>
 </tr>
-<tr valign="top">
-    <td align="right">
-        <label for="ntlmsso_remoteuserformat"><?php print_string('auth_ntlmsso_remoteuserformat_key', 'auth_ldap') ?></label>
-    </td>
-    <td>
-        <input name="ntlmsso_remoteuserformat" id="ntlmsso_remoteuserformat" type="text" size="30" value="<?php echo $config->ntlmsso_remoteuserformat?>" />
-        <?php if (isset($err['ntlmsso_remoteuserformat'])) { echo $OUTPUT->error_text($err['ntlmsso_remoteuserformat']); } ?>
-    </td>
-    <td>
-        <?php print_string('auth_ntlmsso_remoteuserformat', 'auth_ldap') ?>
-    </td>
-</tr>
 <?php
 $help  = get_string('auth_ldapextrafields', 'auth_ldap');
 $help .= get_string('auth_updatelocal_expl', 'auth');
index 848169a..bbb0ddb 100644 (file)
@@ -101,10 +101,6 @@ $string['auth_ntlmsso_enabled'] = 'Set to yes to attempt Single Sign On with the
 $string['auth_ntlmsso_enabled_key'] = 'Enable';
 $string['auth_ntlmsso_ie_fastpath'] = 'Set to yes to enable the NTLM SSO fast path (bypasses certain steps and only works if the client\'s browser is MS Internet Explorer).';
 $string['auth_ntlmsso_ie_fastpath_key'] = 'MS IE fast path?';
-$string['auth_ntlmsso_maybeinvalidformat'] = 'Unable to extract the username from the REMOTE_USER header. Is the configured format right?';
-$string['auth_ntlmsso_missing_username'] = 'You need to specify at least %username% in the remote username format';
-$string['auth_ntlmsso_remoteuserformat_key'] = 'Remote username format';
-$string['auth_ntlmsso_remoteuserformat'] = 'If you have chosen \'NTLM\' in \'Authentication type\', you can specify the remote username format here. If you leave this empty, the default DOMAIN\\username format will be used. You can use the optional <b>%domain%</b> placeholder to specify where the domain name appears, and the mandatory <b>%username%</b> placeholder to specify where the username appears. <br /><br />Some widely used formats are <tt>%domain%\\%username%</tt> (MS Windows default), <tt>%domain%/%username%</tt>, <tt>%domain%+%username%</tt> and just <tt>%username%</tt> (if there is no domain part).';
 $string['auth_ntlmsso_subnet'] = 'If set, it will only attempt SSO with clients in this subnet. Format: xxx.xxx.xxx.xxx/bitmask. Separate multiple subnets with \',\' (comma).';
 $string['auth_ntlmsso_subnet_key'] = 'Subnet';
 $string['auth_ntlmsso_type_key'] = 'Authentication type';
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 e79dbfe..6c01ebf 100644 (file)
@@ -640,7 +640,8 @@ class moodle1_converter extends base_converter {
             return $files;
         }
         foreach ($matches[2] as $match) {
-            $files[] = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $files[] = urldecode($file);
         }
 
         return array_unique($files);
index a97720e..20aca62 100644 (file)
@@ -449,6 +449,19 @@ as it is parsed from the backup file. <br /><br /><img border="0" width="110" vs
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
     }
 
+    public function test_referenced_files_urlencoded() {
+        // This test covers MDL-36204
+        $text = 'This is a text containing links to file.php
+as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">no space</a><br />
+    <br /><a href=\'$@FILEPHP@$$@SLASH@$pics$@SLASH@$news%20with%20spaces.gif$@FORCEDOWNLOAD@$\'>with urlencoded spaces</a><br />';
+
+        $files = moodle1_converter::find_referenced_files($text);
+        $this->assertEquals(gettype($files), 'array');
+        $this->assertEquals(2, count($files));
+        $this->assertTrue(in_array('/pics/news.gif', $files));
+        $this->assertTrue(in_array('/pics/news with spaces.gif', $files));
+    }
+
     public function test_question_bank_conversion() {
         global $CFG;
 
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 dbd2c14..103fb3d 100644 (file)
@@ -2960,6 +2960,22 @@ class restore_create_categories_and_questions extends restore_structure_step {
                        AND ' . $DB->sql_compare_text('hint', 255) . ' = ' . $DB->sql_compare_text('?', 255);
             $params = array($newquestionid, $data->hint);
             $newitemid = $DB->get_field_sql($sql, $params);
+
+            // Not able to find the hint, let's try cleaning the hint text
+            // of all the question's hints in DB as slower fallback. MDL-33863.
+            if (!$newitemid) {
+                $potentialhints = $DB->get_records('question_hints',
+                        array('questionid' => $newquestionid), '', 'id, hint');
+                foreach ($potentialhints as $potentialhint) {
+                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
+                    $cleanhint = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $potentialhint->hint); // Clean CTRL chars.
+                    $cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
+                    if ($cleanhint === $data->hint) {
+                        $newitemid = $data->id;
+                    }
+                }
+            }
+
             // If we haven't found the newitemid, something has gone really wrong, question in DB
             // is missing hints, exception
             if (!$newitemid) {
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 fafab8a..5026d5b 100644 (file)
@@ -140,7 +140,7 @@ class community_hub_search_form extends moodleform {
             $options = array();
             $firsthub = false;
             foreach ($hubs as $hub) {
-                if (key_exists('id', $hub)) {
+                if (array_key_exists('id', $hub)) {
                     $params = array('hubid' => $hub['id'],
                         'filetype' => HUB_HUBSCREENSHOT_FILE_TYPE);
                     $imgurl = new moodle_url(HUB_HUBDIRECTORYURL .
index 4eff68c..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;');
@@ -958,7 +958,7 @@ M.core_dock.genericblock.prototype = {
         placeholder.replace(this.Y.Node.getDOMNode(this.cachedcontentnode));
         this.cachedcontentnode = this.Y.one('#'+this.cachedcontentnode.get('id'));
 
-        var commands = this.cachedcontentnode.one('.title .commands');
+        var commands = dockitem.commands;
         if (commands) {
             commands.all('.hidepanelicon').remove();
             commands.all('.moveto').remove();
index dac296a..102a167 100644 (file)
@@ -129,11 +129,6 @@ class block_glossary_random extends block_base {
         $course = $this->page->course;
         $modinfo = get_fast_modinfo($course);
         $glossaryid = $this->config->glossary;
-        $cm = $modinfo->instances['glossary'][$glossaryid];
-
-        if (!has_capability('mod/glossary:view', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-            return '';
-        }
 
         if (!isset($modinfo->instances['glossary'][$glossaryid])) {
             // we can get here if the glossary has been deleted, so
@@ -147,6 +142,12 @@ class block_glossary_random extends block_base {
             return $this->content;
         }
 
+        $cm = $modinfo->instances['glossary'][$glossaryid];
+
+        if (!has_capability('mod/glossary:view', get_context_instance(CONTEXT_MODULE, $cm->id))) {
+            return '';
+        }
+
         if (empty($this->config->cache)) {
             $this->config->cache = '';
         }
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 cbf1dac..2efe9a2 100644 (file)
@@ -31,7 +31,8 @@ class block_online_users extends block_base {
         if (isset($CFG->block_online_users_timetosee)) {
             $timetoshowusers = $CFG->block_online_users_timetosee * 60;
         }
-        $timefrom = 100 * floor((time()-$timetoshowusers) / 100); // Round to nearest 100 seconds for better query cache
+        $now = time();
+        $timefrom = 100 * floor(($now - $timetoshowusers) / 100); // Round to nearest 100 seconds for better query cache
 
         //Calculate if we are in separate groups
         $isseparategroups = ($this->page->course->groupmode == SEPARATEGROUPS
@@ -53,18 +54,23 @@ class block_online_users extends block_base {
         }
 
         $userfields = user_picture::fields('u', array('username'));
-
+        $params['now'] = $now;
+        $params['timefrom'] = $timefrom;
         if ($this->page->course->id == SITEID or $this->page->context->contextlevel < CONTEXT_COURSE) {  // Site-level
             $sql = "SELECT $userfields, MAX(u.lastaccess) AS lastaccess
                       FROM {user} u $groupmembers
-                     WHERE u.lastaccess > $timefrom
+                     WHERE u.lastaccess > :timefrom
+                           AND u.lastaccess <= :now
+                           AND u.deleted = 0
                            $groupselect
                   GROUP BY $userfields
                   ORDER BY lastaccess DESC ";
 
            $csql = "SELECT COUNT(u.id)
                       FROM {user} u $groupmembers
-                     WHERE u.lastaccess > $timefrom
+                     WHERE u.lastaccess > :timefrom
+                           AND u.lastaccess <= :now
+                           AND u.deleted = 0
                            $groupselect";
 
         } else {
@@ -77,9 +83,11 @@ class block_online_users extends block_base {
             $sql = "SELECT $userfields, MAX(ul.timeaccess) AS lastaccess
                       FROM {user_lastaccess} ul $groupmembers, {user} u
                       JOIN ($esqljoin) euj ON euj.id = u.id
-                     WHERE ul.timeaccess > $timefrom
+                     WHERE ul.timeaccess > :timefrom
                            AND u.id = ul.userid
                            AND ul.courseid = :courseid
+                           AND ul.timeaccess <= :now
+                           AND u.deleted = 0
                            $groupselect
                   GROUP BY $userfields
                   ORDER BY lastaccess DESC";
@@ -87,9 +95,11 @@ class block_online_users extends block_base {
            $csql = "SELECT COUNT(u.id)
                       FROM {user_lastaccess} ul $groupmembers, {user} u
                       JOIN ($esqljoin) euj ON euj.id = u.id
-                     WHERE ul.timeaccess > $timefrom
+                     WHERE ul.timeaccess > :timefrom
                            AND u.id = ul.userid
                            AND ul.courseid = :courseid
+                           AND ul.timeaccess <= :now
+                           AND u.deleted = 0
                            $groupselect";
 
             $params['courseid'] = $this->page->course->id;
@@ -138,7 +148,7 @@ class block_online_users extends block_base {
             }
             foreach ($users as $user) {
                 $this->content->text .= '<li class="listentry">';
-                $timeago = format_time(time() - $user->lastaccess); //bruno to calculate correctly on frontpage
+                $timeago = format_time($now - $user->lastaccess); //bruno to calculate correctly on frontpage
 
                 if (isguestuser($user)) {
                     $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
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 8f562a9..9dde877 100644 (file)
@@ -2080,24 +2080,22 @@ class calendar_event {
             if ($usingeditor) {
                 switch ($this->properties->eventtype) {
                     case 'user':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->courseid = 0;
+                        $this->properties->course = 0;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'site':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->courseid = SITEID;
+                        $this->properties->course = SITEID;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'course':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'group':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->userid = $USER->id;
                         break;
                     default:
@@ -2107,6 +2105,13 @@ class calendar_event {
                         break;
                 }
 
+                // If we are actually using the editor, we recalculate the context because some default values
+                // were set when calculate_context() was called from the constructor.
+                if ($usingeditor) {
+                    $this->properties->context = $this->calculate_context($this->properties);
+                    $this->editorcontext = $this->properties->context;
+                }
+
                 $editor = $this->properties->description;
                 $this->properties->format = $this->properties->description['format'];
                 $this->properties->description = $this->properties->description['text'];
@@ -2125,7 +2130,6 @@ class calendar_event {
                                                 $this->editoroptions,
                                                 $editor['text'],
                                                 $this->editoroptions['forcehttps']);
-
                 $DB->set_field('event', 'description', $this->properties->description, array('id'=>$this->properties->id));
             }
 
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 367b19e..4ab8ab7 100644 (file)
@@ -126,7 +126,7 @@ function cohort_delete_category($category) {
 }
 
 /**
- * Remove cohort member
+ * Add cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
@@ -143,7 +143,7 @@ function cohort_add_member($cohortid, $userid) {
 }
 
 /**
- * Add cohort member
+ * Remove cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
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 5a8b0d7..31b2799 100644 (file)
@@ -94,7 +94,7 @@ class comment_manager {
      */
     private function setup_course($courseid) {
         global $PAGE, $DB;
-        if (!empty($this->course)) {
+        if (!empty($this->course) && $this->course->id == $courseid) {
             // already set, stop
             return;
         }
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 b18c0a4..88fd0fa 100644 (file)
@@ -285,7 +285,7 @@ class core_course_external extends external_api {
                         array('options' => $options));
 
         //retrieve courses
-        if (!key_exists('ids', $params['options'])
+        if (!array_key_exists('ids', $params['options'])
                 or empty($params['options']['ids'])) {
             $courses = $DB->get_records('course');
         } else {
@@ -523,12 +523,12 @@ class core_course_external extends external_api {
             require_capability('moodle/course:create', $context);
 
             // Make sure lang is valid
-            if (key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
+            if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
                 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
             }
 
             // Make sure theme is valid
-            if (key_exists('forcetheme', $course)) {
+            if (array_key_exists('forcetheme', $course)) {
                 if (!empty($CFG->allowcoursethemes)) {
                     if (empty($availablethemes[$course['forcetheme']])) {
                         throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
@@ -547,10 +547,10 @@ class core_course_external extends external_api {
             //set default value for completion
             $courseconfig = get_config('moodlecourse');
             if (completion_info::is_enabled_for_site()) {
-                if (!key_exists('enablecompletion', $course)) {
+                if (!array_key_exists('enablecompletion', $course)) {
                     $course['enablecompletion'] = $courseconfig->enablecompletion;
                 }
-                if (!key_exists('completionstartonenrol', $course)) {
+                if (!array_key_exists('completionstartonenrol', $course)) {
                     $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
                 }
             } else {
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..4143375 100644 (file)
@@ -1484,15 +1484,16 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             $modcontext = context_module::instance($mod->id);
             $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
             $accessiblebutdim = false;
+            $conditionalhidden = false;
             if ($canviewhidden) {
                 $accessiblebutdim = !$mod->visible;
                 if (!empty($CFG->enableavailability)) {
-                    $accessiblebutdim = $accessiblebutdim ||
-                        $mod->availablefrom > time() ||
+                    $conditionalhidden = $mod->availablefrom > time() ||
                         ($mod->availableuntil && $mod->availableuntil < time()) ||
                         count($mod->conditionsgrade) > 0 ||
                         count($mod->conditionscompletion) > 0;
                 }
+                $accessiblebutdim = $conditionalhidden || $accessiblebutdim;
             }
 
             $liclasses = array();
@@ -1550,6 +1551,10 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 if ($accessiblebutdim) {
                     $linkclasses .= ' dimmed';
                     $textclasses .= ' dimmed_text';
+                    if ($conditionalhidden) {
+                        $linkclasses .= ' conditionalhidden';
+                        $textclasses .= ' conditionalhidden';
+                    }
                     $accesstext = '<span class="accesshide">'.
                         get_string('hiddenfromstudents').': </span>';
                 } else {
@@ -1731,11 +1736,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 +1881,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 +1980,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 cb978d8..18f0313 100644 (file)
@@ -20,6 +20,7 @@
     $marker      = optional_param('marker',-1 , PARAM_INT);
     $switchrole  = optional_param('switchrole',-1, PARAM_INT);
     $modchooser  = optional_param('modchooser', -1, PARAM_BOOL);
+    $return      = optional_param('return', 0, PARAM_LOCALURL);
 
     $params = array();
     if (!empty($name)) {
             // Redirect to site root if Editing is toggled on frontpage
             if ($course->id == SITEID) {
                 redirect($CFG->wwwroot .'/?redirect=0');
+            } else if (!empty($return)) {
+                redirect($CFG->wwwroot . $return);
             } else {
                 $url = new moodle_url($PAGE->url, array('notifyeditingon' => 1));
                 redirect($url);
             // Redirect to site root if Editing is toggled on frontpage
             if ($course->id == SITEID) {
                 redirect($CFG->wwwroot .'/?redirect=0');
+            } else if (!empty($return)) {
+                redirect($CFG->wwwroot . $return);
             } else {
                 redirect($PAGE->url);
             }
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 7d2fb5a..7e8dd4c 100644 (file)
@@ -388,7 +388,7 @@ class core_role_external extends external_api {
             // throw an exception if user is not able to assign the role in this context
             $roles = get_assignable_roles($context, ROLENAME_SHORT);
 
-            if (!key_exists($assignment['roleid'], $roles)) {
+            if (!array_key_exists($assignment['roleid'], $roles)) {
                 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
             }
 
@@ -451,7 +451,7 @@ class core_role_external extends external_api {
 
             // throw an exception if user is not able to unassign the role in this context
             $roles = get_assignable_roles($context, ROLENAME_SHORT);
-            if (!key_exists($unassignment['roleid'], $roles)) {
+            if (!array_key_exists($unassignment['roleid'], $roles)) {
                 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
             }
 
index 73762e2..716d2a2 100644 (file)
@@ -151,7 +151,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
             if(! @unlink($filename)) {
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -169,7 +169,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
                 // Send mail to admin
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -280,7 +280,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = $teacher;
                 $eventdata->userto            = $user;
@@ -303,7 +303,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                     $eventdata = new stdClass();
                     $eventdata->modulename        = 'moodle';
-                    $eventdata->component         = 'course';
+                    $eventdata->component         = 'enrol_flatfile';
                     $eventdata->name              = 'flatfile_enrolment';
                     $eventdata->userfrom          = $user;
                     $eventdata->userto            = $teacher;
index 44d90ac..81907a8 100644 (file)
@@ -101,7 +101,7 @@ class enrol_manual_external extends external_api {
 
             //throw an exception if user is not able to assign the role
             $roles = get_assignable_roles($context);
-            if (!key_exists($enrolment['roleid'], $roles)) {
+            if (!array_key_exists($enrolment['roleid'], $roles)) {
                 $errorparams = new stdClass();
                 $errorparams->roleid = $enrolment['roleid'];
                 $errorparams->courseid = $enrolment['courseid'];
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 />";
index f3bdac4..ff283b1 100644 (file)
@@ -51,4 +51,5 @@ $string['pathssubdataroot'] = 'Тази директория е място, къ
 $string['pathssubdirroot'] = 'Пълен път до директорията на Moodle.';
 $string['pathssubwwwroot'] = 'Пълен интернет адрес, на който ще се отваря Moodle. Не е възможно Moodle да се отваря чрез различни адреси. Ако Вашият сайт има няколко адреса трябва на всеки от другите адреси да направите пренасочване към този. Ако Вашият сайт се отваря както глобално от Интернет, така и от локална мрежа, настройте DNS, така че потребителите от локалната мрежа също да могат да ползват глобалния адрес. Ако адресът не е коректен, моля, променете адреса в браузъра си и започнете инсталирането с правилния адрес.';
 $string['phpextension'] = '{$a} разширение на PHP';
+$string['phpversionhelp'] = '<p>Moodle изисква версия на PHP най-малко 4.3.0 или 5.1.0 (5.0.x има значителен брой известни проблеми).</p> <p>Вие използвате в момента версия {$a}</p> <p>Трябва да обновите PHP или да се преместите на нов хост (сървър) с по-нова версия на PHP!<br /> (В случая с 5.0.x може да опитате да инсталирате по-старата версия 4.4.x)</p>';
 $string['wwwroot'] = 'Уеб адрес';
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 6a2e548..fe6d20a 100644 (file)
@@ -33,13 +33,13 @@ defined('MOODLE_INTERNAL') || die();
 $string['cannotcreatelangdir'] = 'לא ניתן ליצור סיפריית שפה.';
 $string['cannotcreatetempdir'] = 'לא ניתן ליצור סיפרייה זמנית.';
 $string['cannotdownloadcomponents'] = 'לא ניתן להוריד רכיבים.';
-$string['cannotdownloadzipfile'] = 'לא ניתן להוריד קובץ ZIP.';
+$string['cannotdownloadzipfile'] = 'לא ניתן להוריד קובץ 7Zip';
 $string['cannotfindcomponent'] = 'הרכיב לא נמצא.';
 $string['cannotsavemd5file'] = 'לא ניתן לשמור קובץ md5.';
 $string['cannotsavezipfile'] = 'לא ניתן לשמור קובץ ZIP.';
 $string['cannotunzipfile'] = 'לא ניתן לפתוח את קובץ ה-ZIP.';
 $string['componentisuptodate'] = 'הרכיב מעודכן.';
-$string['downloadedfilecheckfailed'] = '× ×\9bש×\9c×\94 ×\91×\93×\99קת ×\94ק×\95×\91×¥ ×\94×\9e×\95ר×\93.';
+$string['downloadedfilecheckfailed'] = '×\94ק×\95×\91×¥ ×\90שר ×\99ר×\93 × ×\9eצ×\90 ×©×\92×\95×\99';
 $string['invalidmd5'] = 'md5 לא חוקי';
 $string['missingrequiredfield'] = 'חסר שדה נדרש כלשהו';
 $string['remotedownloaderror'] = 'הורדת הרכיב לשרת שלך כשלה, אנא וודא את הגדרות ה-proxy שלך. תוספת PHP cURL מומלצת מאוד להתקנה.
index d6cb0ae..52508ae 100644 (file)
@@ -101,11 +101,9 @@ $string['welcomep40'] = 'החבילה כוללת בנוסף
 $string['welcomep50'] = 'השימוש בכל היישומים בחבילה זו מפוקח ע"י הרשיונות המתאימים להם. החבילה
 <strong>{$a->installername}</strong>
 השלמה היא
-<a href="http://www.opensource.org/docs/definition_plain.html"> קוד פתוח
-</a>
-והיא מבוזרת תחת רישיון
-<a>
-href="http://www.gnu.org/copyleft/gpl.html">GPL</a>';
+<a href="http://www.opensource.org/docs/definition_plain.html">קוד פתוח</a>
+והיא מופצת תחת רשיון
+<a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>';
 $string['welcomep60'] = 'העמודים הבאים יובילו אותך בצורה פשוטה דרך כמה צעדים לעיצוב הגדרות <strong>Moodle</strong> במחשבך.
 תוכל לאשר את הגדרות  ברירת המחדל או, באפשרותך, לשנותם לפי צרכיך.';
 $string['welcomep70'] = 'הקש על לחצן ה"המשך" למטה כדי להמשיך עם הגדרת ה-<strong>Moodle</strong>';
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';
index 1b8da44..4e2760f 100644 (file)
@@ -30,5 +30,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['parentlanguage'] = '';
 $string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Italiano';
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';
diff --git a/install/lang/ms/admin.php b/install/lang/ms/admin.php
new file mode 100644 (file)
index 0000000..3673ceb
--- /dev/null
@@ -0,0 +1,42 @@
+<?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['clianswerno'] = 't';
+$string['cliansweryes'] = 'y';
+$string['cliincorrectvalueerror'] = 'Ralat, nilai tidak betul "{$a->value}" untuk "{$a->option}"';
+$string['cliincorrectvalueretry'] = 'Nilai yang salah, sila cuba lagi';
+$string['clitypevalue'] = 'taip nilai';
+$string['clitypevaluedefault'] = 'taip nilai, tekan Enter untuk gunakan nilai lalai ({$a})';
+$string['cliunknowoption'] = 'Pilihan yang tidak diketahui:
+  {$a}
+Sila gunakan pilihan --help';
+$string['cliyesnoprompt'] = 'tapi y (ya) atau t (tidak)';
diff --git a/install/lang/ms/install.php b/install/lang/ms/install.php
new file mode 100644 (file)
index 0000000..a9240fc
--- /dev/null
@@ -0,0 +1,83 @@
+<?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'] = 'Direktori admin';
+$string['availablelangs'] = 'Senarai bahasa yang tersedia';
+$string['chooselanguagehead'] = 'Pilih bahasa';
+$string['chooselanguagesub'] = 'Sila pilih bahasa untuk pemasangan. Bahasa ini juga akan digunakan sebagai bahasa lalai untuk laman, ia boleh diubah kemudian.';
+$string['clialreadyinstalled'] = 'config.php fail sudah wujud, sila gunakan admin/cli/upgrade.php jika anda mahu menaik taraf laman web anda.';
+$string['cliinstallheader'] = 'Baris arahan pemasangan program Moodle {$a}';
+$string['databasehost'] = 'Hos pangkalan data';
+$string['databasename'] = 'Nama pangkalan data';
+$string['databasetypehead'] = 'Pilih pemacu pangkalan data';
+$string['dataroot'] = 'Direktori data';
+$string['dbprefix'] = 'Awalan jadual';
+$string['dirroot'] = 'Direktori Moodle';
+$string['environmenthead'] = 'Memeriksa persekitaran anda ...';
+$string['environmentsub2'] = 'Setiap pelepasan Moodle mempunyai keperluan minimum versi PHP dan beberapa sambungan mandatori PHP. Persekitaran penuh disemak sebelum setiap pemasangan dan naik taraf. Sila hubungi pentadbir pelayan jika anda tidak tahu bagaimana untuk memasang versi baru atau membolehkan sambungan PHP.';
+$string['errorsinenvironment'] = 'Semakan persekitaran gagal!';
+$string['installation'] = 'Pemasangan';
+$string['langdownloaderror'] = 'Malangnya bahasa "{$a}" tidak boleh dimuat turun. Proses pemasangan akan diteruskan dalam bahasa Bahasa Inggeris.';
+$string['memorylimithelp'] = '<p> Had memori PHP untuk pelayan anda  ditetapkan kepada {$a}. </p>
+
+<p> Hal ini akan menyebabkan Moodle untuk mendapat masalah memori di kemudian hari, terutama jika anda mempunyai banyak modul yang diaktifkan dan/atau ramai pengguna. </p>
+
+<p> Kami menyarankan anda mengkonfigurasikan PHP dengan had yang lebih tinggi jika memungkinkan, seperti 40M. Ada beberapa cara untuk melakukan hal ini yang anda boleh cuba: </p>
+<ol>
+<li> Jika anda mampu, kompil semula PHP dengan <i>--enable-memory-limit.</i> Hal ini akan membolehkan Moodle untuk menetapkan batas memori itu sendiri. </li>
+<li> Jika anda mempunyai akses ke fail php.ini, anda boleh menukar setting <b>memory_limit</b> di sana untuk sesuatu seperti 40M. Jika anda tidak mempunyai akses anda mungkin boleh meminta pentadbir anda untuk melakukan ini untuk anda. </li>
+<li> Pada beberapa server PHP anda boleh mencipta baris fail .htaccess di direktori Moodle mengandungi ini: <blockquote><div> php_value memory_limit 40M </div></blockquote><p> Namun, pada beberapa server ini akan mencegah <b>semua</b> laman PHP dari berjalan (anda akan melihat ralat ketika anda melihat halaman), sehingga anda hapuskan file .htaccess. </p></li></ol>';
+$string['paths'] = 'Laluan';
+$string['pathserrcreatedataroot'] = 'Direktori data ({$a->dataroot}) tidak boleh diwujudkan oleh pemasang.';
+$string['pathshead'] = 'Sahkan laluan';
+$string['pathsrodataroot'] = 'Direktori dataroot tidak dapat ditulis.';
+$string['pathsroparentdataroot'] = 'Direktori induk ({$a->parent}) tidak boleh tulis. Direktori data ({$a->dataroot}) tidak boleh diwujudkan oleh pemasang.';
+$string['pathssubadmindir'] = 'Beberapa webhos menggunakan /admin sebagai URL khas untuk anda akses panel kawalan atau sesuatu yang lain. Malangnya, ini bertentangan dengan lokasi standard untuk halaman pentadbiran Moodle. Anda boleh memperbaiki ini dengan menamakan semula direktori admin dalam pemasangan anda, dan meletakkan nama baru di sini. Sebagai contoh: <br /> <br /> <b> moodleadmin </ b> <br /> <br />
+Ini akan memulihkan pautan admin dalam Moodle.';
+$string['pathssubdataroot'] = 'Anda perlukan lokasi di mana Moodle boleh menyimpan fail yang dimuat naik. Direktori ini sepatutnya boleh dibaca DAN BOLEH DITULIS oleh pengguna web server (biasanya \'nobody\' atau \'apache\'), tetapi ia mestilah tidak boleh diakses secara langsung melalui laman web. Pemasang akan cuba untuk mewujudkannya jika belum ada.';
+$string['pathssubdirroot'] = 'Direktori laluan penuh untuk pemasangan Moodle.';
+$string['pathssubwwwroot'] = 'Alamat penuh web di mana Moodle akan dicapai. Adalah tidak mungkin untuk mengakses Moodle menggunakan pelbagai alamat. Jika laman anda mempunyai beberapa alamat awam, anda mesti menubuhkan pelencongan kekal kepada semua mereka kecuali yang satu ini. Jika laman web anda boleh diakses kedua-duanya dari Intranet dan Internet menggunakan alamat awam di sini dan menubuhkan DNS supaya pengguna Intranet boleh menggunakan alamat awam juga. Jika alamat itu tidak betul, sila ubah URL di pelayar anda untuk memulakan semula pemasangan dengan nilai yang berbeza.';
+$string['pathsunsecuredataroot'] = 'Lokasi dataroot tidak selamat';
+$string['pathswrongadmindir'] = 'Direktori admin tidak wujud';
+$string['phpextension'] = 'Sambungan PHP {$a}';
+$string['phpversion'] = 'Versi PHP';
+$string['phpversionhelp'] = '<p> Moodle memerlukan versi PHP minimal 4.3.0 atau 5.1.0 (5.0.x memiliki sejumlah masalah yang diketahui). </p>
+<p> Anda sedang menjalankan versi {$a} </p>
+<p> Anda harus upgrade PHP atau berpindah ke host dengan versi terbaru PHP! <br /> (Dalam kes 5.0.x Anda juga boleh turun taraf ke versi 4.4.x) </p>';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['welcomep20'] = 'Anda melihat halaman ini kerana anda telah berjaya memasang dan melancarkan pakej <strong>{$a->packname} {$a->packversion}</strong> di komputer anda. Tahniah!';
+$string['welcomep30'] = 'Keluaran ini <strong>{$a->installername}</strong> termasuk aplikasi untuk mencipta sebuah persekitaran di mana <strong>Moodle</strong> akan beroperasi, iaitu:';
+$string['welcomep40'] = 'Pakej ini juga termasuk <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
+$string['welcomep50'] = 'Penggunaan semua aplikasi di dalam pakej ini dikawal oleh lesen masing-masing. Pakej lengkap <strong>{$a->installername}</strong> adalah <a href="http://www.opensource.org/docs/definition_plain.html">sumber terbuka</a> dan diedarkan di bawah lesen <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
+$string['welcomep60'] = 'Laman-laman berikut akan membawa anda melalui beberapa langkah-langkah mudah diikuti untuk mengkonfigurasi dan menetapkan <strong>Moodle</strong> pada komputer anda. Anda mungkin menerima tetapan lalai atau, dengan pilihan, meminda untuk disesuaikan dengan keperluan anda.';
+$string['welcomep70'] = 'Klik butang "Seterusnya" di bawah untuk meneruskan penubuhan <strong>Moodle</strong>.';
+$string['wwwroot'] = 'Alamat Web';
diff --git a/install/lang/ms/moodle.php b/install/lang/ms/moodle.php
new file mode 100644 (file)
index 0000000..44849c0
--- /dev/null
@@ -0,0 +1,36 @@
+<?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['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 1971fb3..ec01ae5 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['admindirname'] = 'Admin Directory';
+$string['admindirname'] = 'Director Admin';
 $string['availablelangs'] = 'Pachete de limbă disponibile';
 $string['chooselanguagehead'] = 'Selectare limbă';
 $string['chooselanguagesub'] = 'Vă rugăm selectaţi limba pentru interfaţa de instalare, limba selectată va fi folosită EXCLUSIV în cadrul procedurii de instalare. Ulterior veţi putea selecta limba în care doriţi să fie afişată interfaţa.';
-$string['dataroot'] = 'Data Directory';
+$string['databasehost'] = 'Gazdă baza de date';
+$string['databasename'] = 'Nume baza de date';
+$string['databasetypehead'] = 'Alegere driver baza de date';
+$string['dataroot'] = 'Director date';
+$string['datarootpermission'] = 'Permisiuni directoare date';
 $string['dbprefix'] = 'Prefix tabele';
 $string['dirroot'] = 'Director Moodle';
 $string['environmenthead'] = 'Se verifică mediul...';
 $string['installation'] = 'Instalare';
+$string['paths'] = 'Căi';
+$string['pathshead'] = 'Confirmare căi';
+$string['phpextension'] = 'extensie PHP {$a}';
 $string['phpversion'] = 'Versiune PHP';
 $string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
 $string['wwwroot'] = 'Adresă Web';
index 1dc0ec5..6a243d6 100644 (file)
@@ -45,7 +45,7 @@ $string['datarootpermission'] = 'Овлашћења над директориј
 $string['dbprefix'] = 'Префикс табеле';
 $string['dirroot'] = 'Moodle директоријум';
 $string['environmenthead'] = 'Проверавање Вашег окружења...';
-$string['environmentsub2'] = 'Свака верзија Moodlea има минимум захтева по питању одговарајуће PHP верѕије и неколико обавезних PHP екстензија.
+$string['environmentsub2'] = 'Свака верзија Moodlea има минимум захтева по питању одговарајуће PHP верзије и неколико обавезних PHP екстензија.
 Пуна провера окружења се врши пре сваке инсталације или ажурирања постојеће верзије. Уколико не знате како да инсталирате нову верзију или омогућите PHP ектензије контактирајте Вашег сервер администратора.';
 $string['errorsinenvironment'] = 'Провера окружења није прошла!';
 $string['installation'] = 'Инсталација';
@@ -75,7 +75,7 @@ $string['pathssubwwwroot'] = 'Пуна веб адреса путем које 
 Ако је адреса нетачна промените URL у свом веб читачу да бисте поново покренули инсталацију са другачијом вредношћу.';
 $string['pathsunsecuredataroot'] = 'Dataroot локација није безбедна';
 $string['pathswrongadmindir'] = 'Админ директоријум не постоји';
-$string['phpextension'] = '{$a} PHP екстенѕија';
+$string['phpextension'] = '{$a} PHP екстензија';
 $string['phpversion'] = 'PHP верзија';
 $string['phpversionhelp'] = '<p>Moodle захтева најмање PHP верзију 4.3.0 или 5.1.0 (5.0.x има  бројне уочене проблеме).</p>
 <p>Тренутно користите верзију {$a}</p>
index ef41512..43f6dd0 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['clianswerno'] = 'n';
+$string['cliansweryes'] = 'y';
+$string['cliincorrectvalueerror'] = '錯誤,不正確的值 "{$a->value}" 用於 "{$a->option}"';
+$string['cliincorrectvalueretry'] = '不正確的值,請再試一次';
+$string['clitypevalue'] = '輸入值';
+$string['clitypevaluedefault'] = '輸入值,按Enter可使用預設值({$a})';
+$string['cliunknowoption'] = '不認得的選項:  {$a}
+請使用 --幫助 選項。';
+$string['cliyesnoprompt'] = '輸入y(是) 或n(否)';
 $string['environmentrequireinstall'] = '必須安裝/啟用';
 $string['environmentrequireversion'] = '要求版本為 {$a->needed} ,您目前版本為 {$a->current}';
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 8a6a935..5f47b26 100644 (file)
@@ -258,7 +258,7 @@ $string['configmycoursesperpage'] = 'Maximum number of courses to display in any
 $string['configmymoodleredirect'] = 'This setting forces redirects to /my on login for non-admins and replaces the top level site navigation with /my';
 $string['configmypagelocked'] = 'This setting prevents the default page from being edited by any non-admins';
 $string['confignavcourselimit'] = 'Limits the number of courses shown to the user when they are either not logged in or are not enrolled in any courses.';
-$string['confignavshowallcourses'] = 'If enabled users will see courses they are enrolled in both within the My Courses branch and the course structure. When disabled users with enrolments will only see the My Courses branch of the navigaiton.';
+$string['confignavshowallcourses'] = 'If enabled users will see courses they are enrolled in both within the My Courses branch and the course structure. When disabled users with enrolments will only see the My Courses branch of the navigaiton. The number of course shown would still be limited by "Course limit(navcourselimit)" setting when user is either not logged in or not enrolled in any course.';
 $string['confignavshowcategories'] = 'Show course categories in the navigation bar and navigation blocks. This does not occur with courses the user is currently enrolled in, they will still be listed under mycourses without categories.';
 $string['confignotifyloginfailures'] = 'If login failures have been recorded, email notifications can be sent out.  Who should see these notifications?';
 $string['confignotifyloginthreshold'] = 'If notifications about failed logins are active, how many failed login attempts by one user or one IP address is it worth notifying about?';
@@ -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 ffbdec3..f9ab6a9 100644 (file)
@@ -121,6 +121,7 @@ $string['course:changesummary'] = 'Change course summary';
 $string['course:enrolconfig'] = 'Configure enrol instances in courses';
 $string['course:enrolreview'] = 'Review course enrolments';
 $string['course:ignorefilesizelimits'] = 'Use files larger than any file size restrictions';
+$string['course:isincompletionreports'] = 'Be shown on completion reports';
 $string['course:manageactivities'] = 'Manage activities';
 $string['course:managefiles'] = 'Manage files';
 $string['course:managegrades'] = 'Manage grades';
index 2e8121b..c74e557 100644 (file)
@@ -7561,7 +7561,7 @@ class admin_setting_managewebservicetokens extends admin_setting {
                         array(array('id' => $token->userid)), $token->serviceid);
 
                 if (!is_siteadmin($token->userid) and
-                        key_exists($token->userid, $usermissingcaps)) {
+                        array_key_exists($token->userid, $usermissingcaps)) {
                     $missingcapabilities = implode(', ',
                             $usermissingcaps[$token->userid]);
                     if (!empty($missingcapabilities)) {
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 47885c9..9413201 100644 (file)
@@ -454,7 +454,9 @@ class block_manager {
      * @return bool True if all of the blocks within that region are docked
      */
     public function region_completely_docked($region, $output) {
-        if (!$this->page->theme->enable_dock) {
+        global $CFG;
+        // If theme doesn't allow docking or allowblockstodock is not set, then return.
+        if (!$this->page->theme->enable_dock || empty($CFG->allowblockstodock)) {
             return false;
         }
 
index fcd5910..fcc6f58 100644 (file)
@@ -1069,7 +1069,8 @@ class completion_info {
         global $DB;
 
         list($enrolledsql, $params) = get_enrolled_sql(
-                context_course::instance($this->course->id), '', $groupid, true);
+                context_course::instance($this->course->id),
+                'moodle/course:isincompletionreports', $groupid, true);
 
         $sql = 'SELECT u.id, u.firstname, u.lastname, u.idnumber';
         if ($extracontext) {
index 121750c..f2027df 100644 (file)
@@ -979,6 +979,14 @@ $capabilities = array(
         )
     ),
 
+    'moodle/course:isincompletionreports' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'student' => CAP_ALLOW,
+        ),
+    ),
+
     'moodle/course:viewscales' => array(
 
         'captype' => 'read',
index e617e1b..91263db 100644 (file)
@@ -969,5 +969,21 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012062502.03);
     }
 
+    if ($oldversion < 2012062502.07) {
+        // Find all orphaned blog associations that might exist.
+        $sql = "SELECT ba.id
+                  FROM {blog_association} ba
+             LEFT JOIN {post} p
+                    ON p.id = ba.blogid
+                 WHERE p.id IS NULL";
+        $orphanedrecordids = $DB->get_records_sql($sql);
+        // Now delete these associations.
+        foreach ($orphanedrecordids as $orphanedrecord) {
+            $DB->delete_records('blog_association', array('id' => $orphanedrecord->id));
+        }
+
+        upgrade_main_savepoint(true, 2012062502.07);
+    }
+
     return true;
 }
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 f2105cb..d5ac755 100644 (file)
@@ -201,8 +201,13 @@ class file_browser {
     private function get_file_info_context_module($context, $component, $filearea, $itemid, $filepath, $filename) {
         global $COURSE, $DB, $CFG;
 
+        static $cachedmodules = array();
 
-        if (!$cm = get_coursemodule_from_id('', $context->instanceid)) {
+        if (!array_key_exists($context->instanceid, $cachedmodules)) {
+            $cachedmodules[$context->instanceid] = get_coursemodule_from_id('', $context->instanceid);
+        }
+
+        if (!($cm = $cachedmodules[$context->instanceid])) {
             return null;
         }
 
index d5bcade..a6af3f7 100644 (file)
@@ -88,6 +88,116 @@ abstract class file_info {
      */
     public abstract function get_children();
 
+    /**
+     * Builds SQL sub query (WHERE clause) for selecting files with the specified extensions
+     *
+     * If $extensions == '*' (any file), the result is array('', array())
+     * otherwise the result is something like array('AND filename ...', array(...))
+     *
+     * @param string|array $extensions - either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param string $prefix prefix for DB table files in the query (empty by default)
+     * @return array of two elements: $sql - sql where clause and $params - array of parameters
+     */
+    protected function build_search_files_sql($extensions, $prefix = null) {
+        global $DB;
+        if (strlen($prefix)) {
+            $prefix = $prefix.'.';
+        } else {
+            $prefix = '';
+        }
+        $sql = '';
+        $params = array();
+        if (is_array($extensions) && !in_array('*', $extensions)) {
+            $likes = array();
+            $cnt = 0;
+            foreach ($extensions as $ext) {
+                $cnt++;
+                $likes[] = $DB->sql_like($prefix.'filename', ':filename'.$cnt, false);
+                $params['filename'.$cnt] = '%'.$ext;
+            }
+            $sql .= ' AND (' . join(' OR ', $likes) . ')';
+        }
+        return array($sql, $params);
+     }
+
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * It is recommended to overwrite this function so it uses a proper SQL
+     * query and does not create unnecessary file_info objects (might require a lot of time
+     * and memory usage on big sites).
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        $list = $this->get_children();
+        $nonemptylist = array();
+        foreach ($list as $fileinfo) {
+            if ($fileinfo->is_directory()) {
+                if ($fileinfo->count_non_empty_children($extensions)) {
+                    $nonemptylist[] = $fileinfo;
+                }
+            } else if ($extensions === '*') {
+                $nonemptylist[] = $fileinfo;
+            } else {
+                $filename = $fileinfo->get_visible_name();
+                $extension = textlib::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+                if (!empty($extension) && in_array('.' . $extension, $extensions)) {
+                    $nonemptylist[] = $fileinfo;
+                }
+            }
+        }
+        return $nonemptylist;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * We usually don't need the exact number of non empty children if it is >=2 (see param $limit)
+     * This function is used by repository_local to evaluate if the folder is empty. But
+     * it also can be used to check if folder has only one subfolder because in some cases
+     * this subfolder can be skipped.
+     *
+     * It is strongly recommended to overwrite this function so it uses a proper SQL
+     * query and does not create file_info objects (later might require a lot of time
+     * and memory usage on big sites).
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        $list = $this->get_children();
+        $cnt = 0;
+        // first loop through files
+        foreach ($list as $fileinfo) {
+            if (!$fileinfo->is_directory()) {
+                if ($extensions !== '*') {
+                    $filename = $fileinfo->get_visible_name();
+                    $extension = textlib::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+                    if (empty($extension) || !in_array('.' . $extension, $extensions)) {
+                        continue;
+                    }
+                }
+                if ((++$cnt) >= $limit) {
+                    return $cnt;
+                }
+            }
+        }
+        // now loop through directories
+        foreach ($list as $fileinfo) {
+            if ($fileinfo->is_directory() && $fileinfo->count_non_empty_children($extensions)) {
+                if ((++$cnt) >= $limit) {
+                    return $cnt;
+                }
+            }
+        }
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -103,12 +213,12 @@ abstract class file_info {
     public function get_params_rawencoded() {
         $params = $this->get_params();
         $encoded = array();
-        $encoded[] = 'contextid='.$params['contextid'];
-        $encoded[] = 'component='.$params['component'];
-        $encoded[] = 'filearea='.$params['filearea'];
-        $encoded[] = 'itemid='.(is_null($params['itemid']) ? -1 : $params['itemid']);
-        $encoded[] = 'filepath='.(is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
-        $encoded[] = 'filename='.((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
+        $encoded[] = 'contextid=' . $params['contextid'];
+        $encoded[] = 'component=' . $params['component'];
+        $encoded[] = 'filearea=' . $params['filearea'];
+        $encoded[] = 'itemid=' . (is_null($params['itemid']) ? -1 : $params['itemid']);
+        $encoded[] = 'filepath=' . (is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
+        $encoded[] = 'filename=' . ((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
 
         return $encoded;
     }
index 50876b3..5e61f56 100644 (file)
@@ -350,48 +350,90 @@ class file_info_context_course extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
-        $children = array();
+        return $this->get_filtered_children('*', false, true);
+    }
 
-        if ($child = $this->get_area_course_summary(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_course_section(null, null, null)) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_section(null, null, null)) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_course(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_automated(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_course_legacy(0, '/', '.')) {
-            $children[] = $child;
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
+        $areas = array(
+            array('course', 'summary'),
+            array('course', 'section'),
+            array('backup', 'section'),
+            array('backup', 'course'),
+            array('backup', 'automated'),
+            array('course', 'legacy')
+        );
+        $children = array();
+        foreach ($areas as $area) {
+            if ($child = $this->get_file_info($area[0], $area[1], 0, '/', '.')) {
+                if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                    $children[] = $child;
+                    if (($countonly !== false) && count($children) >= $countonly) {
+                        return $countonly;
+                    }
+                }
+            }
         }
 
         if (!has_capability('moodle/course:managefiles', $this->context)) {
             // 'managefiles' capability is checked in every activity module callback.
             // Don't even waste time on retrieving the modules if we can't browse the files anyway
-            return $children;
-        }
-
-        // now list all modules
-        $modinfo = get_fast_modinfo($this->course);
-        foreach ($modinfo->cms as $cminfo) {
-            if (empty($cminfo->uservisible)) {
-                continue;
-            }
-            $modcontext = get_context_instance(CONTEXT_MODULE, $cminfo->id);
-            if ($child = $this->browser->get_file_info($modcontext)) {
-                $children[] = $child;
+        } else {
+            // now list all modules
+            $modinfo = get_fast_modinfo($this->course);
+            foreach ($modinfo->cms as $cminfo) {
+                if (empty($cminfo->uservisible)) {
+                    continue;
+                }
+                $modcontext = context_module::instance($cminfo->id, IGNORE_MISSING);
+                if ($child = $this->browser->get_file_info($modcontext)) {
+                    if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                        $children[] = $child;
+                        if (($countonly !== false) && count($children) >= $countonly) {
+                            return $countonly;
+                        }
+                    }
+                }
             }
         }
 
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -473,6 +515,37 @@ class file_info_area_course_legacy extends file_info_stored {
 
         return $result;
     }
+
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        if (!$this->lf->is_directory()) {
+            return array();
+        }
+
+        $result = array();
+        $fs = get_file_storage();
+
+        $storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0,
+                                                $this->lf->get_filepath(), false, true, "filepath, filename");
+        foreach ($storedfiles as $file) {
+            $extension = textlib::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
+            if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
+                $fileinfo = new file_info_area_course_legacy($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
+                                                 $this->itemidused, $this->readaccess, $this->writeaccess, false);
+                if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
+                    $result[] = $fileinfo;
+                }
+            }
+        }
+
+        return $result;
+    }
 }
 
 /**
@@ -578,6 +651,41 @@ class file_info_area_course_section extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $params1 = array(
+            'courseid' => $this->course->id,
+            'contextid' => $this->context->id,
+            'component' => 'course',
+            'filearea' => 'section',
+            'emptyfilename' => '.');
+        $sql1 = "SELECT DISTINCT cs.id FROM {files} f, {course_sections} cs
+            WHERE cs.course = :courseid
+            AND f.contextid = :contextid
+            AND f.component = :component
+            AND f.filearea = :filearea
+            AND f.itemid = cs.id
+            AND f.filename <> :emptyfilename";
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
+        $cnt = 0;
+        foreach ($rs as $record) {
+            if ((++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -689,6 +797,41 @@ class file_info_area_backup_section extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $params1 = array(
+            'courseid' => $this->course->id,
+            'contextid' => $this->context->id,
+            'component' => 'backup',
+            'filearea' => 'section',
+            'emptyfilename' => '.');
+        $sql1 = "SELECT DISTINCT cs.id sectionid FROM {files} f, {course_sections} cs
+            WHERE cs.course = :courseid
+            AND f.contextid = :contextid
+            AND f.component = :component
+            AND f.filearea = :filearea
+            AND f.itemid = cs.id
+            AND f.filename <> :emptyfilename";
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
+        $cnt = 0;
+        foreach ($rs as $record) {
+            if ((++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
index 99b6d1f..e8d542c 100644 (file)
@@ -188,6 +188,66 @@ class file_info_context_coursecat extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $cnt = 0;
+        if (($child = $this->get_area_coursecat_description(0, '/', '.'))
+                && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+            return $cnt;
+        }
+
+        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, c.visible
+                FROM {context} ctx, {course} c
+                WHERE ctx.instanceid = c.id
+                AND ctx.contextlevel = :courselevel
+                AND c.category = :categoryid
+                ORDER BY c.visible DESC', // retrieve visible courses first
+                array('categoryid' => $this->category->id, 'courselevel' => CONTEXT_COURSE));
+        foreach ($rs as $record) {
+            $context = context::instance_by_id($record->contextid);
+            if (!$record->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            if (($child = $this->browser->get_file_info($context))
+                    && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        if ($cnt >= $limit) {
+            return $cnt;
+        }
+
+        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, cat.visible
+                FROM {context} ctx, {course_categories} cat
+                WHERE ctx.instanceid = cat.id
+                AND ctx.contextlevel = :catlevel
+                AND cat.parent = :categoryid
+                ORDER BY cat.visible DESC', // retrieve visible categories first
+                array('categoryid' => $this->category->id, 'catlevel' => CONTEXT_COURSECAT));
+        foreach ($rs as $record) {
+            $context = context::instance_by_id($record->contextid);
+            if (!$record->visible and !has_capability('moodle/category:viewhiddencategories', $context)) {
+                continue;
+            }
+            if (($child = $this->browser->get_file_info($context))
+                    && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
index 0357ee3..f4b2e52 100644 (file)
@@ -41,6 +41,8 @@ class file_info_context_module extends file_info {
     protected $modname;
     /** @var array Available file areas */
     protected $areas;
+    /** @var array caches the result of last call to get_non_empty_children() */
+    protected $nonemptychildren;
 
     /**
      * Constructor
@@ -58,6 +60,7 @@ class file_info_context_module extends file_info {
         $this->course  = $course;
         $this->cm      = $cm;
         $this->modname = $modname;
+        $this->nonemptychildren = null;
 
         include_once("$CFG->dirroot/mod/$modname/lib.php");
 
@@ -258,24 +261,91 @@ class file_info_context_module extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
-        $children = array();
+        return $this->get_filtered_children('*', false, true);
+    }
 
-        if ($child = $this->get_area_backup(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_intro(0, '/', '.')) {
-            $children[] = $child;
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
+        global $DB;
+        // prepare list of areas including intro and backup
+        $areas = array(
+            array('mod_'.$this->modname, 'intro'),
+            array('backup', 'activity')
+        );
+        foreach ($this->areas as $area => $desctiption) {
+            $areas[] = array('mod_'.$this->modname, $area);
         }
 
-        foreach ($this->areas as $area=>$desctiption) {
-            if ($child = $this->get_file_info('mod_'.$this->modname, $area, null, null, null)) {
-                $children[] = $child;
+        $params1 = array('contextid' => $this->context->id, 'emptyfilename' => '.');
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $children = array();
+        foreach ($areas as $area) {
+            if (!$returnemptyfolders) {
+                // fast pre-check if there are any files in the filearea
+                $params1['component'] = $area[0];
+                $params1['filearea'] = $area[1];
+                if (!$DB->record_exists_sql('SELECT 1 from {files}
+                        WHERE contextid = :contextid
+                        AND filename <> :emptyfilename
+                        AND component = :component
+                        AND filearea = :filearea '.$sql2,
+                        array_merge($params1, $params2))) {
+                    continue;
+                }
+            }
+            if ($child = $this->get_file_info($area[0], $area[1], null, null, null)) {
+                if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                    $children[] = $child;
+                    if ($countonly !== false && count($children) >= $countonly) {
+                        break;
+                    }
+                }
             }
         }
-
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        if ($this->nonemptychildren !== null) {
+            return $this->nonemptychildren;
+        }
+        $this->nonemptychildren = $this->get_filtered_children($extensions);
+        return $this->nonemptychildren;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        if ($this->nonemptychildren !== null) {
+            return count($this->nonemptychildren);
+        }
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
index 44fa566..e9caf4c 100644 (file)
@@ -350,6 +350,84 @@ class file_info_stored extends file_info {
         return $result;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        $result = array();
+        if (!$this->lf->is_directory()) {
+            return $result;
+        }
+
+        $fs = get_file_storage();
+
+        $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
+                                                $this->lf->get_filepath(), false, true, "filepath, filename");
+        foreach ($storedfiles as $file) {
+            $extension = textlib::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
+            if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
+                $fileinfo = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
+                                                 $this->itemidused, $this->readaccess, $this->writeaccess, false);
+                if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
+                    $result[] = $fileinfo;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        if (!$this->lf->is_directory()) {
+            return 0;
+        }
+
+        $filepath = $this->lf->get_filepath();
+        $length = textlib::strlen($filepath);
+        $sql = "SELECT filepath, filename
+                  FROM {files} f
+                 WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
+                       AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
+                       AND filename <> '.' ";
+        $params = array('contextid' => $this->context->id,
+            'component' => $this->lf->get_component(),
+            'filearea' => $this->lf->get_filearea(),
+            'itemid' => $this->lf->get_itemid(),
+            'filepath' => $filepath);
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql.' '.$sql2, array_merge($params, $params2));
+        $children = array();
+        foreach ($rs as $record) {
+            // we don't need to check access to individual files here, since the user can access parent
+            if ($record->filepath === $filepath) {
+                $children[] = $record->filename;
+            } else {
+                $path = explode('/', textlib::substr($record->filepath, $length));
+                if (!in_array($path[0], $children)) {
+                    $children[] = $path[0];
+                }
+            }
+            if (count($children) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return count($children);
+    }
+
     /**
      * Returns parent file_info instance
      *
index 465ba24..fd3c612 100644 (file)
@@ -3649,7 +3649,6 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
             if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'eventtype'=>'site'))) {
                 send_file_not_found();
             }
-            // Check that we got an event and that it's userid is that of the user
 
             // Get the file and serve if successful
             $filename = array_pop($args);
@@ -3697,8 +3696,8 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 require_login($course);
             }
 
-            // Must be able to at least view the course
-            if (!is_enrolled($context) and !is_viewing($context)) {
+            // Must be able to at least view the course. This does not apply to the front page.
+            if ($course->id != SITEID && (!is_enrolled($context)) && (!is_viewing($context))) {
                 //TODO: hmm, do we really want to block guests here?
                 send_file_not_found();
             }
@@ -3719,10 +3718,10 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 if (!has_capability('moodle/site:accessallgroups', $context) && !groups_is_member($event->groupid, $USER->id)) {
                     send_file_not_found();
                 }
-            } else if ($event->eventtype === 'course') {
-                //ok
+            } else if ($event->eventtype === 'course' || $event->eventtype === 'site') {
+                // Ok. Please note that the event type 'site' still uses a course context.
             } else {
-                // some other type
+                // Some other type.
                 send_file_not_found();
             }
 
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 c81dfb7..866ad43 100644 (file)
@@ -81,25 +81,25 @@ function form_init_date_js() {
         $function = 'M.form.dateselector.init_date_selectors';
         $config = array(array(
             'firstdayofweek'    => get_string('firstdayofweek', 'langconfig'),
-            'mon'               => strftime('%a', strtotime("Monday")),
-            'tue'               => strftime('%a', strtotime("Tuesday")),
-            'wed'               => strftime('%a', strtotime("Wednesday")),
-            'thu'               => strftime('%a', strtotime("Thursday")),
-            'fri'               => strftime('%a', strtotime("Friday")),
-            'sat'               => strftime('%a', strtotime("Saturday")),
-            'sun'               => strftime('%a', strtotime("Sunday")),
-            'january'           => strftime('%B', strtotime("January 1")),
-            'february'          => strftime('%B', strtotime("February 1")),
-            'march'             => strftime('%B', strtotime("March 1")),
-            'april'             => strftime('%B', strtotime("April 1")),
-            'may'               => strftime('%B', strtotime("May 1")),
-            'june'              => strftime('%B', strtotime("June 1")),
-            'july'              => strftime('%B', strtotime("July 1")),
-            'august'            => strftime('%B', strtotime("August 1")),
-            'september'         => strftime('%B', strtotime("September 1")),
-            'october'           => strftime('%B', strtotime("October 1")),
-            'november'          => strftime('%B', strtotime("November 1")),
-            'december'          => strftime('%B', strtotime("December 1"))
+            'mon'               => date_format_string(strtotime("Monday"), '%a', 99),
+            'tue'               => date_format_string(strtotime("Tuesday"), '%a', 99),
+            'wed'               => date_format_string(strtotime("Wednesday"), '%a', 99),
+            'thu'               => date_format_string(strtotime("Thursday"), '%a', 99),
+            'fri'               => date_format_string(strtotime("Friday"), '%a', 99),
+            'sat'               => date_format_string(strtotime("Saturday"), '%a', 99),
+            'sun'               => date_format_string(strtotime("Sunday"), '%a', 99),
+            'january'           => date_format_string(strtotime("January 1"), '%B', 99),
+            'february'          => date_format_string(strtotime("February 1"), '%B', 99),
+            'march'             => date_format_string(strtotime("March 1"), '%B', 99),
+            'april'             => date_format_string(strtotime("April 1"), '%B', 99),
+            'may'               => date_format_string(strtotime("May 1"), '%B', 99),
+            'june'              => date_format_string(strtotime("June 1"), '%B', 99),
+            'july'              => date_format_string(strtotime("July 1"), '%B', 99),
+            'august'            => date_format_string(strtotime("August 1"), '%B', 99),
+            'september'         => date_format_string(strtotime("September 1"), '%B', 99),
+            'october'           => date_format_string(strtotime("October 1"), '%B', 99),
+            'november'          => date_format_string(strtotime("November 1"), '%B', 99),
+            'december'          => date_format_string(strtotime("December 1"), '%B', 99)
         ));
         $PAGE->requires->yui_module($module, $function, $config);
         $done = true;
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 1fee1b0..e2ffd29 100644 (file)
@@ -129,10 +129,11 @@ function message_send($eventdata) {
         if (isset($defaultpreferences->{$defaultpreference})) {
             $permitted = $defaultpreferences->{$defaultpreference};
         } else {
-            //MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
-            //exist in the message_provider table (thus there is no default settings for them)
-            $preferrormsg = get_string('couldnotfindpreference', 'message', $defaultpreference);
-            throw new coding_exception($preferrormsg,'blah');
+            // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
+            // exist in the message_provider table (thus there is no default settings for them).
+            $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied
+                    to message_send() are valid.";
+            throw new coding_exception($preferrormsg);
         }
 
         // Find out if user has configured this output
@@ -387,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 167a653..32697fa 100644 (file)
@@ -2071,13 +2071,7 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
     // (because it's impossible to specify UTF-8 to fetch locale info in Win32)
 
     if (abs($timezone) > 13) {   /// Server time
-        if ($CFG->ostype == 'WINDOWS' and ($localewincharset = get_string('localewincharset', 'langconfig'))) {
-            $format = textlib::convert($format, 'utf-8', $localewincharset);
-            $datestring = strftime($format, $date);
-            $datestring = textlib::convert($datestring, $localewincharset, 'utf-8');
-        } else {
-            $datestring = strftime($format, $date);
-        }
+        $datestring = date_format_string($date, $format, $timezone);
         if ($fixday) {
             $daystring  = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date)));
             $datestring = str_replace('DD', $daystring, $datestring);
@@ -2089,13 +2083,7 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
 
     } else {
         $date += (int)($timezone * 3600);
-        if ($CFG->ostype == 'WINDOWS' and ($localewincharset = get_string('localewincharset', 'langconfig'))) {
-            $format = textlib::convert($format, 'utf-8', $localewincharset);
-            $datestring = gmstrftime($format, $date);
-            $datestring = textlib::convert($datestring, $localewincharset, 'utf-8');
-        } else {
-            $datestring = gmstrftime($format, $date);
-        }
+        $datestring = date_format_string($date, $format, $timezone);
         if ($fixday) {
             $daystring  = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %d', $date)));
             $datestring = str_replace('DD', $daystring, $datestring);
@@ -2109,6 +2097,46 @@ function userdate($date, $format = '', $timezone = 99, $fixday = true, $fixhour
     return $datestring;
 }
 
+/**
+ * Returns a formatted date ensuring it is UTF-8.
+ *
+ * If we are running under Windows convert to Windows encoding and then back to UTF-8
+ * (because it's impossible to specify UTF-8 to fetch locale info in Win32).
+ *
+ * This function does not do any calculation regarding the user preferences and should
+ * therefore receive the final date timestamp, format and timezone. Timezone being only used
+ * to differenciate the use of server time or not (strftime() against gmstrftime()).
+ *
+ * @param int $date the timestamp.
+ * @param string $format strftime format.
+ * @param int|float $timezone the numerical timezone, typically returned by {@link get_user_timezone_offset()}.
+ * @return string the formatted date/time.
+ * @since 2.3.3
+ */
+function date_format_string($date, $format, $tz = 99) {
+    global $CFG;
+    if (abs($tz) > 13) {
+        if ($CFG->ostype == 'WINDOWS') {
+            $localewincharset = get_string('localewincharset', 'langconfig');
+            $format = textlib::convert($format, 'utf-8', $localewincharset);
+            $datestring = strftime($format, $date);
+            $datestring = textlib::convert($datestring, $localewincharset, 'utf-8');
+        } else {
+            $datestring = strftime($format, $date);
+        }
+    } else {
+        if ($CFG->ostype == 'WINDOWS') {
+            $localewincharset = get_string('localewincharset', 'langconfig');
+            $format = textlib::convert($format, 'utf-8', $localewincharset);
+            $datestring = gmstrftime($format, $date);
+            $datestring = textlib::convert($datestring, $localewincharset, 'utf-8');
+        } else {
+            $datestring = gmstrftime($format, $date);
+        }
+    }
+    return $datestring;
+}
+
 /**
  * Given a $time timestamp in GMT (seconds since epoch),
  * returns an array that represents the date in user time
@@ -8393,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 9ba6f13..b95aae6 100644 (file)
@@ -3561,7 +3561,7 @@ class settings_navigation extends navigation_node {
                 $baseurl->param('sesskey', sesskey());
             } else {
                 // Edit on the main course page.
-                $baseurl = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
+                $baseurl = new moodle_url('/course/view.php', array('id'=>$course->id, 'return'=>$this->page->url->out_as_local_url(false), 'sesskey'=>sesskey()));
             }
 
             $editurl = clone($baseurl);
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 8225f23..8bfd798 100644 (file)
@@ -156,9 +156,11 @@ EOD;
         }
 
         if (!isset($record['username'])) {
-            $record['username'] = textlib::strtolower($record['firstname']).textlib::strtolower($record['lastname']);
+            $record['username'] = 'username'.$i;
+            $j = 2;
             while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
-                $record['username'] = $record['username'].'_'.$i;
+                $record['username'] = 'username'.$i.'_'.$j;
+                $j++;
             }
         }
 
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 316ee70..3787b34 100644 (file)
@@ -44,6 +44,11 @@ class core_phpunit_generator_testcase extends advanced_testcase {
         $count = $DB->count_records('user');
         $user = $generator->create_user();
         $this->assertEquals($count+1, $DB->count_records('user'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
+        $user = $generator->create_user(array('firstname'=>'Žluťoučký', 'lastname'=>'Koníček'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
 
         $count = $DB->count_records('course_categories');
         $category = $generator->create_category();
index b224d6d..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;
@@ -2514,12 +2534,4 @@ class plugininfo_local extends plugininfo_base {
     public function get_uninstall_url() {
         return new moodle_url('/admin/localplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
     }
-
-    public function get_settings_url() {
-        if (file_exists($this->full_path('settings.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'local_' . $this->name));
-        } else {
-            return parent::get_settings_url();
-        }
-    }
 }
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);
     }
 
     /**
index 6d7bc4e..cd77de4 100644 (file)
@@ -1686,7 +1686,8 @@ class accesslib_testcase extends advanced_testcase {
 
         context_helper::reset_caches();
         context_helper::preload_course($SITE->id);
-        $this->assertEquals(7, context_inspection::test_context_cache_size()); // depends on number of default blocks
+        $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
+        $this->assertEquals(6 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // depends on number of default blocks
 
         // ====== assign_capability(), unassign_capability() ====================
 
@@ -1991,7 +1992,8 @@ class accesslib_testcase extends advanced_testcase {
         load_all_capabilities();
         $context = context_course::instance($testcourses[2]);
         $page = $DB->get_record('page', array('course'=>$testcourses[2]));
-        $pagecontext = context_module::instance($page->id);
+        $pagecm = get_coursemodule_from_instance('page', $page->id);
+        $pagecontext = context_module::instance($pagecm->id);
 
         $context->mark_dirty();
         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
@@ -2230,7 +2232,8 @@ class accesslib_testcase extends advanced_testcase {
 
         context_helper::reset_caches();
         preload_course_contexts($SITE->id);
-        $this->assertEquals(context_inspection::test_context_cache_size(), 1);
+        $this->assertEquals(1 + $DB->count_records('course_modules', array('course' => $SITE->id)),
+                context_inspection::test_context_cache_size());
 
         context_helper::reset_caches();
         list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx');
@@ -2276,11 +2279,11 @@ class accesslib_testcase extends advanced_testcase {
         $url = get_context_url($coursecontext);
         $this->assertFalse($url instanceof modole_url);
 
-        $page = $DB->get_record('page', array('id'=>$testpages[7]));
-        $context = get_context_instance(CONTEXT_MODULE, $page->id);
+        $pagecm = get_coursemodule_from_instance('page', $testpages[7]);
+        $context = get_context_instance(CONTEXT_MODULE, $pagecm->id);
         $coursecontext = get_course_context($context);
         $this->assertEquals($coursecontext->contextlevel, CONTEXT_COURSE);
-        $this->assertEquals(get_courseid_from_context($context), $page->course);
+        $this->assertEquals(get_courseid_from_context($context), $pagecm->course);
 
         $caps = fetch_context_capabilities($systemcontext);
         $this->assertTrue(is_array($caps));
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 6beb856..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() {
@@ -1993,4 +2109,61 @@ class moodlelib_testcase extends advanced_testcase {
         );
         $this->assertEquals(convert_to_array($obj), $ar);
     }
+
+    /**
+     * Test the function date_format_string().
+     */
+    function test_date_format_string() {
+        // Forcing locale and timezone.
+        $oldlocale = setlocale(LC_TIME, '0');
+        setlocale(LC_TIME, 'en_AU.UTF-8');
+        $systemdefaulttimezone = date_default_timezone_get();
+        date_default_timezone_set('Australia/Perth');
+
+        $tests = array(
+            array(
+                'tz' => 99,
+                'str' => '%A, %d %B %Y, %I:%M %p',
+                'expected' => 'Saturday, 01 January 2011, 06:00 PM'
+            ),
+            array(
+                'tz' => 0,
+                'str' => '%A, %d %B %Y, %I:%M %p',
+                'expected' => 'Saturday, 01 January 2011, 10:00 AM'
+            ),
+            array(
+                'tz' => -12,
+                'str' => '%A, %d %B %Y, %I:%M %p',
+                'expected' => 'Saturday, 01 January 2011, 10:00 AM'
+            ),
+            array(
+                'tz' => 99,
+                'str' => 'Žluťoučký koníček %A',
+                'expected' => 'Žluťoučký koníček Saturday'
+            ),
+            array(
+                'tz' => 99,
+                'str' => '言語設定言語 %A',
+                'expected' => '言語設定言語 Saturday'
+            ),
+            array(
+                'tz' => 99,
+                'str' => '简体中文简体 %A',
+                'expected' => '简体中文简体 Saturday'
+            ),
+        );
+
+        // Note: date_format_string() uses the timezone only to differenciate
+        // the server time from the UTC time. It does not modify the timestamp.
+        // Hence similar results for timezones <= 13.
+        // On different systems case of AM PM changes so compare case insensitive.
+        foreach ($tests as $test) {
+            $str = date_format_string(1293876000, $test['str'], $test['tz']);
+            $this->assertEquals(textlib::strtolower($test['expected']), textlib::strtolower($str));
+        }
+
+        // Restore system default values.
+        date_default_timezone_set($systemdefaulttimezone);
+        setlocale(LC_TIME, $oldlocale);
+    }
 }
index d5d50bf..684d299 100644 (file)
@@ -148,7 +148,7 @@ class user_picture_testcase extends advanced_testcase {
         // try legacy picture == 1
         $user1->picture = 1;
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=1', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=1', $up1->get_url($page, $renderer)->out(false));
         $user1->picture = 11;
 
         // try valid user with picture when user context is not cached - 1 query expected
@@ -156,7 +156,7 @@ class user_picture_testcase extends advanced_testcase {
         $reads = $DB->perf_get_reads();
         $up1 = new user_picture($user1);
         $this->assertEquals($reads, $DB->perf_get_reads());
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
         $this->assertEquals($reads+1, $DB->perf_get_reads());
 
         // try valid user with contextid hint - no queries expected
@@ -165,7 +165,7 @@ class user_picture_testcase extends advanced_testcase {
         $reads = $DB->perf_get_reads();
         $up1 = new user_picture($user1);
         $this->assertEquals($reads, $DB->perf_get_reads());
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
         $this->assertEquals($reads, $DB->perf_get_reads());
 
         // try valid user without image - no queries expected
@@ -214,13 +214,13 @@ class user_picture_testcase extends advanced_testcase {
         $this->assertEquals('http://www.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=http%3A%2F%2Fwww.example.com%2Fmoodle%2Fpix%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
         // uploaded image takes precedence before gravatar
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         // https version
         $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
 
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->httpswwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->httpswwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         $up3 = new user_picture($user3);
         $this->assertEquals($CFG->httpswwwroot.'/theme/image.php/standard/core/1/u/f2', $up3->get_url($page, $renderer)->out(false));
@@ -258,7 +258,7 @@ class user_picture_testcase extends advanced_testcase {
         $renderer = $page->get_renderer('core');
 
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/formal_white/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/formal_white/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         $up2 = new user_picture($user2);
         $this->assertEquals($CFG->wwwroot.'/theme/image.php/formal_white/core/1/u/f2', $up2->get_url($page, $renderer)->out(false));
index c299ffe..cde67d9 100644 (file)
@@ -964,7 +964,7 @@ function external_update_descriptions($component) {
             $dbfunction->classpath = $function['classpath'];
             $update = true;
         }
-        $functioncapabilities = key_exists('capabilities', $function)?$function['capabilities']:'';
+        $functioncapabilities = array_key_exists('capabilities', $function)?$function['capabilities']:'';
         if ($dbfunction->capabilities != $functioncapabilities) {
             $dbfunction->capabilities = $functioncapabilities;
             $update = true;
@@ -980,7 +980,7 @@ function external_update_descriptions($component) {
         $dbfunction->methodname = $function['methodname'];
         $dbfunction->classpath  = empty($function['classpath']) ? null : $function['classpath'];
         $dbfunction->component  = $component;
-        $dbfunction->capabilities = key_exists('capabilities', $function)?$function['capabilities']:'';
+        $dbfunction->capabilities = array_key_exists('capabilities', $function)?$function['capabilities']:'';
         $dbfunction->id = $DB->insert_record('external_functions', $dbfunction);
     }
     unset($functions);
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..95701d9 100644 (file)
@@ -20,29 +20,42 @@ 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
-                lightbox : true, // This dialogue should be modal
-                shim : true
+                visible : false, // Hide by default.
+                zindex : 100, // Display in front of other items.
+                lightbox : true // This dialogue should be modal.
             }
 
             // 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 +76,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 8f73ac1..366d5d8 100644 (file)
@@ -59,6 +59,17 @@ class mod_assign_grade_form extends moodleform {
         }
     }
 
+    /**
+     * This is required so when using "Save and next", each form is not defaulted to the previous form.
+     * Giving each form a unique identitifer is enough to prevent this (include the rownum in the form name).
+     *
+     * @return string - The unique identifier for this form.
+     */
+    protected function get_form_identifier() {
+        $params = $this->_customdata[2];
+        return get_class($this) . '_' . $params['rownum'];
+    }
+
     /**
      * Perform minimal validation on the grade form
      * @param array $data
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 bd8b95c..12e3a44 100644 (file)
@@ -2761,7 +2761,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);
+            }
         }
     }
 
@@ -3049,7 +3051,9 @@ class assign {
         if (!$last){
             $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
         }
-        $mform->addGroup($buttonarray, 'navar', '', array(' '), false);