Merge branch 'MDL-34248_master' of https://github.com/markn86/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 22 Jul 2014 11:01:04 +0000 (12:01 +0100)
committerDan Poltawski <dan@moodle.com>
Tue, 22 Jul 2014 11:01:04 +0000 (12:01 +0100)
141 files changed:
admin/index.php
admin/renderer.php
admin/settings/users.php
admin/tool/behat/tests/behat/manipulate_forms.feature
admin/tool/uploaduser/user_form.php
auth/cas/auth.php
backup/util/ui/backup_ui_setting.class.php
badges/edit_form.php
blog/edit.php
blog/edit_form.php
blog/external_blog_edit.php
blog/external_blog_edit_form.php
blog/external_blogs.php
blog/index.php
blog/lib.php
blog/locallib.php
blog/preferences.php
blog/preferences_form.php
blog/renderer.php
blog/rsslib.php
blog/tests/bloglib_test.php
cache/admin.php
cache/classes/helper.php
cache/classes/store.php
cache/locallib.php
cache/renderer.php
cache/stores/memcache/lang/en/cachestore_memcache.php
cache/stores/memcache/lib.php
cache/stores/memcached/lang/en/cachestore_memcached.php
cache/stores/memcached/lib.php
calendar/event_form.php
calendar/lib.php
calendar/tests/lib_test.php
course/classes/management/helper.php
course/classes/management_renderer.php
course/lib.php
course/management.php
course/renderer.php
course/tests/behat/category_resort.feature
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_resort.feature
enrol/imsenterprise/lib.php
files/renderer.php
filter/activitynames/filter.php
group/autogroup.php
group/autogroup_form.php
group/lib.php
group/tests/behat/auto_creation.feature
install/lang/af/moodle.php [new file with mode: 0644]
install/lang/ru/install.php
lang/en/admin.php
lang/en/group.php
lang/en/moodle.php
lib/authlib.php
lib/classes/collator.php
lib/classes/event/recent_activity_viewed.php
lib/classes/task/adhoc_task.php
lib/classes/task/manager.php
lib/completionlib.php
lib/coursecatlib.php
lib/filelib.php
lib/form/filemanager.js
lib/htmlpurifier/locallib.php
lib/moodlelib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/testing/generator/data_generator.php
lib/tests/collator_test.php
lib/tests/coursecatlib_test.php
lib/tests/htmlpurifier_test.php
lib/upgrade.txt
lib/weblib.php
lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js
lib/yui/build/moodle-core-dock/moodle-core-dock-min.js
lib/yui/build/moodle-core-dock/moodle-core-dock.js
lib/yui/src/dock/js/dock.js
login/index.php
login/index_form.html
message/lib.php
message/tests/messagelib_test.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/mod_form.php
mod/assign/renderable.php
mod/assign/renderer.php
mod/assign/tests/locallib_test.php
mod/feedback/item/textarea/lib.php
mod/feedback/item/textfield/lib.php
mod/forum/classes/event/user_report_viewed.php
mod/forum/lib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/tests/behat/completion_condition_attempts_used.feature [new file with mode: 0644]
mod/quiz/tests/behat/completion_condition_passing_grade.feature [new file with mode: 0644]
mod/quiz/version.php
mod/scorm/datamodels/aicc.js [moved from mod/scorm/datamodels/aicc.js.php with 81% similarity]
mod/scorm/datamodels/aicc.php [new file with mode: 0644]
mod/scorm/datamodels/aicclib.php
mod/scorm/datamodels/debug.js.php
mod/scorm/datamodels/scorm_12.js [moved from mod/scorm/datamodels/scorm_12.js.php with 75% similarity]
mod/scorm/datamodels/scorm_12.php [new file with mode: 0644]
mod/scorm/datamodels/scorm_12lib.php
mod/scorm/datamodels/scorm_13.js [moved from mod/scorm/datamodels/scorm_13.js.php with 87% similarity]
mod/scorm/datamodels/scorm_13.php [new file with mode: 0644]
mod/scorm/datamodels/scorm_13lib.php
mod/scorm/loaddatamodel.php [deleted file]
mod/scorm/locallib.php
mod/scorm/module.js
mod/scorm/player.php
mod/scorm/version.php
mod/scorm/view.php
mod/survey/classes/event/report_downloaded.php
mod/survey/classes/event/report_viewed.php
mod/wiki/pagelib.php
mod/wiki/tests/behat/wiki_comments.feature [new file with mode: 0644]
notes/delete.php
notes/edit.php
notes/edit_form.php
notes/externallib.php
notes/index.php
notes/lib.php
question/qengine.js
report/eventlist/tests/behat/mainsection.feature
report/outline/classes/event/activity_report_viewed.php
report/outline/classes/event/report_viewed.php
repository/filepicker.js
theme/base/style/core.css
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/style/moodle.css
user/editlib.php
user/lib.php
user/profile.php
user/view.php
version.php

index aaa530a..34c0f95 100644 (file)
@@ -589,10 +589,14 @@ $availableupdatesfetch = $updateschecker->get_last_timefetched();
 $buggyiconvnomb = (!function_exists('mb_convert_encoding') and @iconv('UTF-8', 'UTF-8//IGNORE', '100'.chr(130).'€') !== '100€');
 //check if the site is registered on Moodle.org
 $registered = $DB->count_records('registration_hubs', array('huburl' => HUB_MOODLEORGHUBURL, 'confirmed' => 1));
+// Check if there are any cache warnings.
+$cachewarnings = cache_helper::warnings();
 
 admin_externalpage_setup('adminnotifications');
 
+/* @var core_admin_renderer $output */
 $output = $PAGE->get_renderer('core', 'admin');
-echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
-        $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
-        $registered);
+
+echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed, $cronoverdue, $dbproblems,
+                                       $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
+                                       $registered, $cachewarnings);
index 954b977..99109a2 100644 (file)
@@ -303,12 +303,13 @@ class core_admin_renderer extends plugin_renderer_base {
      * @param bool $buggyiconvnomb warn iconv problems
      * @param array|null $availableupdates array of \core\update\info objects or null
      * @param int|null $availableupdatesfetch timestamp of the most recent updates fetch or null (unknown)
+     * @param string[] $cachewarnings An array containing warnings from the Cache API.
      *
      * @return string HTML to output.
      */
     public function admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
             $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch,
-            $buggyiconvnomb, $registered) {
+            $buggyiconvnomb, $registered, array $cachewarnings = array()) {
         global $CFG;
         $output = '';
 
@@ -321,6 +322,7 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->cron_overdue_warning($cronoverdue);
         $output .= $this->db_problems($dbproblems);
         $output .= $this->maintenance_mode_warning($maintenancemode);
+        $output .= $this->cache_warnings($cachewarnings);
         $output .= $this->registration_warning($registered);
 
         //////////////////////////////////////////////////////////////////////////////////////////////////
@@ -595,6 +597,19 @@ class core_admin_renderer extends plugin_renderer_base {
         return $this->warning($dbproblems);
     }
 
+    /**
+     * Renders cache warnings if there are any.
+     *
+     * @param string[] $cachewarnings
+     * @return string
+     */
+    public function cache_warnings(array $cachewarnings) {
+        if (!count($cachewarnings)) {
+            return '';
+        }
+        return join("\n", array_map(array($this, 'warning'), $cachewarnings));
+    }
+
     /**
      * Render an appropriate message if the site in in maintenance mode.
      * @param bool $maintenancemode
index 2231bab..5a7e75a 100644 (file)
@@ -24,6 +24,46 @@ if ($hassiteconfig
     $ADMIN->add('accounts', new admin_externalpage('editusers', new lang_string('userlist','admin'), "$CFG->wwwroot/$CFG->admin/user.php", array('moodle/user:update', 'moodle/user:delete')));
     $ADMIN->add('accounts', new admin_externalpage('userbulk', new lang_string('userbulk','admin'), "$CFG->wwwroot/$CFG->admin/user/user_bulk.php", array('moodle/user:update', 'moodle/user:delete')));
     $ADMIN->add('accounts', new admin_externalpage('addnewuser', new lang_string('addnewuser'), "$securewwwroot/user/editadvanced.php?id=-1", 'moodle/user:create'));
+
+    // "User default preferences" settingpage.
+    $temp = new admin_settingpage('userdefaultpreferences', new lang_string('userdefaultpreferences', 'admin'));
+    if ($ADMIN->fulltree) {
+        $choices = array();
+        $choices['0'] = new lang_string('emaildisplayno');
+        $choices['1'] = new lang_string('emaildisplayyes');
+        $choices['2'] = new lang_string('emaildisplaycourse');
+        $temp->add(new admin_setting_configselect('defaultpreference_maildisplay', new lang_string('emaildisplay'),
+            '', 2, $choices));
+
+        $choices = array();
+        $choices['0'] = new lang_string('textformat');
+        $choices['1'] = new lang_string('htmlformat');
+        $temp->add(new admin_setting_configselect('defaultpreference_mailformat', new lang_string('emailformat'), '', 1, $choices));
+
+        $choices = array();
+        $choices['0'] = new lang_string('emaildigestoff');
+        $choices['1'] = new lang_string('emaildigestcomplete');
+        $choices['2'] = new lang_string('emaildigestsubjects');
+        $temp->add(new admin_setting_configselect('defaultpreference_maildigest', new lang_string('emaildigest'),
+            new lang_string('emaildigest_help'), 0, $choices));
+
+
+        $choices = array();
+        $choices['1'] = new lang_string('autosubscribeyes');
+        $choices['0'] = new lang_string('autosubscribeno');
+        $temp->add(new admin_setting_configselect('defaultpreference_autosubscribe', new lang_string('autosubscribe'),
+            '', 1, $choices));
+
+        if (!empty($CFG->forum_trackreadposts)) {
+            $choices = array();
+            $choices['0'] = new lang_string('trackforumsno');
+            $choices['1'] = new lang_string('trackforumsyes');
+            $temp->add(new admin_setting_configselect('defaultpreference_trackforums', new lang_string('trackforums'),
+                '', 0, $choices));
+        }
+    }
+    $ADMIN->add('accounts', $temp);
+
     $ADMIN->add('accounts', new admin_externalpage('profilefields', new lang_string('profilefields','admin'), "$CFG->wwwroot/user/profile/index.php", 'moodle/site:config'));
     $ADMIN->add('accounts', new admin_externalpage('cohorts', new lang_string('cohorts', 'cohort'), $CFG->wwwroot . '/cohort/index.php', array('moodle/cohort:manage', 'moodle/cohort:view')));
 
index 3696b81..952b65a 100644 (file)
@@ -11,6 +11,7 @@ Feature: Forms manipulation
     When I set the field "First name" to "Field value"
     And I set the field "Text editor" to "Plain text area"
     And I set the field "Unmask" to "1"
+    And I expand all fieldsets
     Then the field "First name" matches value "Field value"
     And the "Text editor" select box should contain "Plain text area"
     And the field "Unmask" matches value "1"
index f922995..1ec16e0 100644 (file)
@@ -221,21 +221,21 @@ class admin_uploaduser_form2 extends moodleform {
 
         $choices = array(0 => get_string('emaildisplayno'), 1 => get_string('emaildisplayyes'), 2 => get_string('emaildisplaycourse'));
         $mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices);
-        $mform->setDefault('maildisplay', 2);
+        $mform->setDefault('maildisplay', $CFG->defaultpreference_maildisplay);
 
         $choices = array(0 => get_string('textformat'), 1 => get_string('htmlformat'));
         $mform->addElement('select', 'mailformat', get_string('emailformat'), $choices);
-        $mform->setDefault('mailformat', 1);
+        $mform->setDefault('mailformat', $CFG->defaultpreference_mailformat);
         $mform->setAdvanced('mailformat');
 
         $choices = array(0 => get_string('emaildigestoff'), 1 => get_string('emaildigestcomplete'), 2 => get_string('emaildigestsubjects'));
         $mform->addElement('select', 'maildigest', get_string('emaildigest'), $choices);
-        $mform->setDefault('maildigest', 0);
+        $mform->setDefault('maildigest', $CFG->defaultpreference_maildigest);
         $mform->setAdvanced('maildigest');
 
         $choices = array(1 => get_string('autosubscribeyes'), 0 => get_string('autosubscribeno'));
         $mform->addElement('select', 'autosubscribe', get_string('autosubscribe'), $choices);
-        $mform->setDefault('autosubscribe', 1);
+        $mform->setDefault('autosubscribe', $CFG->defaultpreference_autosubscribe);
 
         $mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="25"');
         $mform->setType('city', PARAM_TEXT);
index 6615b75..9d83dd1 100644 (file)
@@ -111,6 +111,26 @@ class auth_plugin_cas extends auth_plugin_ldap {
             return;
         }
 
+        // If the multi-authentication setting is used, check for the param before connecting to CAS.
+        if ($this->config->multiauth) {
+            $authCAS = optional_param('authCAS', '', PARAM_RAW);
+            if ($authCAS == 'NOCAS') {
+                return;
+            }
+            // Show authentication form for multi-authentication.
+            // Test pgtIou parameter for proxy mode (https connection in background from CAS server to the php server).
+            if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
+                $PAGE->set_url('/login/index.php');
+                $PAGE->navbar->add($CASform);
+                $PAGE->set_title("$site->fullname: $CASform");
+                $PAGE->set_heading($site->fullname);
+                echo $OUTPUT->header();
+                include($CFG->dirroot.'/auth/cas/cas_form.html');
+                echo $OUTPUT->footer();
+                exit();
+            }
+        }
+
         // Connection to CAS server
         $this->connectCAS();
 
@@ -134,27 +154,6 @@ class auth_plugin_cas extends auth_plugin_ldap {
             return;
         }
 
-        if ($this->config->multiauth) {
-            $authCAS = optional_param('authCAS', '', PARAM_RAW);
-            if ($authCAS == 'NOCAS') {
-                return;
-            }
-
-            // Show authentication form for multi-authentication
-            // test pgtIou parameter for proxy mode (https connection
-            // in background from CAS server to the php server)
-            if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
-                $PAGE->set_url('/login/index.php');
-                $PAGE->navbar->add($CASform);
-                $PAGE->set_title("$site->fullname: $CASform");
-                $PAGE->set_heading($site->fullname);
-                echo $OUTPUT->header();
-                include($CFG->dirroot.'/auth/cas/cas_form.html');
-                echo $OUTPUT->footer();
-                exit();
-            }
-        }
-
         // Force CAS authentication (if needed).
         if (!phpCAS::isAuthenticated()) {
             phpCAS::setLang($this->config->language);
index 9ef5865..1712a73 100644 (file)
@@ -139,7 +139,8 @@ class base_setting_ui {
      * @param string $label
      */
     public function set_label($label) {
-        if ((string)$label === '' || $label !== clean_param($label, PARAM_TEXT)) {
+        $label = (string)$label;
+        if ($label === '' || $label !== clean_param($label, PARAM_TEXT)) {
             throw new base_setting_ui_exception('setting_invalid_ui_label');
         }
         $this->label = $label;
index 24e52d7..cebc483 100644 (file)
@@ -54,7 +54,7 @@ class edit_details_form extends moodleform {
         $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
 
         $mform->addElement('textarea', 'description', get_string('description', 'badges'), 'wrap="virtual" rows="8" cols="70"');
-        $mform->setType('description', PARAM_CLEANHTML);
+        $mform->setType('description', PARAM_NOTAGS);
         $mform->addRule('description', null, 'required');
 
         $str = $action == 'new' ? get_string('badgeimage', 'badges') : get_string('newimage', 'badges');
index fadc8ad..c3052f7 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 require_once(dirname(dirname(__FILE__)).'/config.php');
-include_once('lib.php');
-include_once('locallib.php');
+require_once('lib.php');
+require_once('locallib.php');
 
 $action   = required_param('action', PARAM_ALPHA);
 $id       = optional_param('entryid', 0, PARAM_INT);
 $confirm  = optional_param('confirm', 0, PARAM_BOOL);
-$modid    = optional_param('modid', 0, PARAM_INT); // To associate the entry with a module instance
-$courseid = optional_param('courseid', 0, PARAM_INT); // To associate the entry with a course
-
-$PAGE->set_url('/blog/edit.php', array('action' => $action, 'entryid' => $id, 'confirm' => $confirm, 'modid' => $modid, 'courseid' => $courseid));
+$modid = optional_param('modid', 0, PARAM_INT); // To associate the entry with a module instance.
+$courseid = optional_param('courseid', 0, PARAM_INT); // To associate the entry with a course.
 
-// If action is add, we ignore $id to avoid any further problems
-if (!empty($id) && $action == 'add') {
-    $id = null;
+if ($action == 'edit') {
+    $id = required_param('entryid', PARAM_INT);
 }
 
-$returnurl = new moodle_url('/blog/index.php');
+$PAGE->set_url('/blog/edit.php', array('action' => $action,
+                                       'entryid' => $id,
+                                       'confirm' => $confirm,
+                                       'modid' => $modid,
+                                       'courseid' => $courseid));
 
-if (!empty($courseid) && empty($modid)) {
-    $returnurl->param('courseid', $courseid);
-}
-
-// If a modid is given, guess courseid
-if (!empty($modid)) {
-    $returnurl->param('modid', $modid);
-    $courseid = $DB->get_field('course_modules', 'course', array('id' => $modid));
-    $returnurl->param('courseid', $courseid);
+// If action is add, we ignore $id to avoid any further problems.
+if (!empty($id) && $action == 'add') {
+    $id = null;
 }
 
 // Blogs are always in system context.
 $sitecontext = context_system::instance();
 $PAGE->set_context($sitecontext);
 
-
-$blogheaders = blog_get_headers();
-
 require_login($courseid);
 
-if ($action == 'edit') {
-    $id = required_param('entryid', PARAM_INT);
-}
-
 if (empty($CFG->enableblogs)) {
     print_error('blogdisable', 'blog');
 }
@@ -75,11 +62,26 @@ if (isguestuser()) {
     print_error('noguestentry', 'blog');
 }
 
+$returnurl = new moodle_url('/blog/index.php');
+
+if (!empty($courseid) && empty($modid)) {
+    $returnurl->param('courseid', $courseid);
+}
+
+// If a modid is given, guess courseid.
+if (!empty($modid)) {
+    $returnurl->param('modid', $modid);
+    $courseid = $DB->get_field('course_modules', 'course', array('id' => $modid));
+    $returnurl->param('courseid', $courseid);
+}
+
+$blogheaders = blog_get_headers();
+
 if (!has_capability('moodle/blog:create', $sitecontext) && !has_capability('moodle/blog:manageentries', $sitecontext)) {
     print_error('cannoteditentryorblog');
 }
 
-// Make sure that the person trying to edit has access right
+// Make sure that the person trying to edit has access right.
 if ($id) {
     if (!$entry = new blog_entry($id)) {
         print_error('wrongentryid', 'blog');
@@ -94,7 +96,7 @@ if ($id) {
 
 } else {
     if (!has_capability('moodle/blog:create', $sitecontext)) {
-        print_error('noentry', 'blog'); // manageentries is not enough for adding
+        print_error('noentry', 'blog'); // The capability "manageentries" is not enough for adding.
     }
     $entry  = new stdClass();
     $entry->id = null;
@@ -105,14 +107,14 @@ $returnurl->param('userid', $userid);
 // Blog renderer.
 $output = $PAGE->get_renderer('blog');
 
-$strblogs = get_string('blogs','blog');
+$strblogs = get_string('blogs', 'blog');
 
-if ($action === 'delete'){
+if ($action === 'delete') {
     if (empty($entry->id)) {
         print_error('wrongentryid', 'blog');
     }
     if (data_submitted() && $confirm && confirm_sesskey()) {
-        // Make sure the current user is the author of the blog entry, or has some deleteanyentry capability
+        // Make sure the current user is the author of the blog entry, or has some deleteanyentry capability.
         if (!blog_user_can_edit_entry($entry)) {
             print_error('nopermissionstodeleteentry', 'blog');
         } else {
@@ -132,7 +134,9 @@ if ($action === 'delete'){
         echo $output->render($entry);
 
         echo '<br />';
-        echo $OUTPUT->confirm(get_string('blogdeleteconfirm', 'blog'), new moodle_url('edit.php', $optionsyes),new moodle_url( 'index.php', $optionsno));
+        echo $OUTPUT->confirm(get_string('blogdeleteconfirm', 'blog'),
+                              new moodle_url('edit.php', $optionsyes),
+                              new moodle_url('index.php', $optionsno));
         echo $OUTPUT->footer();
         die;
     }
@@ -167,10 +171,21 @@ $summaryoptions = array('maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes, 'trusttext'
     'subdirs'=>file_area_contains_subdirs($sitecontext, 'blog', 'post', $entry->id));
 $attachmentoptions = array('subdirs'=>false, 'maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes);
 
-$blogeditform = new blog_edit_form(null, compact('entry', 'summaryoptions', 'attachmentoptions', 'sitecontext', 'courseid', 'modid'));
+$blogeditform = new blog_edit_form(null, compact('entry',
+                                                 'summaryoptions',
+                                                 'attachmentoptions',
+                                                 'sitecontext',
+                                                 'courseid',
+                                                 'modid'));
 
 $entry = file_prepare_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog', 'post', $entry->id);
-$entry = file_prepare_standard_filemanager($entry, 'attachment', $attachmentoptions, $sitecontext, 'blog', 'attachment', $entry->id);
+$entry = file_prepare_standard_filemanager($entry,
+                                           'attachment',
+                                           $attachmentoptions,
+                                           $sitecontext,
+                                           'blog',
+                                           'attachment',
+                                           $entry->id);
 
 if (!empty($CFG->usetags) && !empty($entry->id)) {
     include_once($CFG->dirroot.'/tag/lib.php');
@@ -178,13 +193,13 @@ if (!empty($CFG->usetags) && !empty($entry->id)) {
 }
 
 $entry->action = $action;
-// set defaults
+// Set defaults.
 $blogeditform->set_data($entry);
 
 if ($blogeditform->is_cancelled()) {
     redirect($returnurl);
 
-} else if ($data = $blogeditform->get_data()){
+} else if ($data = $blogeditform->get_data()) {
 
     switch ($action) {
         case 'add':
@@ -209,23 +224,23 @@ if ($blogeditform->is_cancelled()) {
 }
 
 
-// gui setup
+// GUI setup.
 switch ($action) {
     case 'add':
-        // prepare new empty form
+        // Prepare new empty form.
         $entry->publishstate = 'site';
         $strformheading = get_string('addnewentry', 'blog');
         $entry->action       = $action;
 
         if ($CFG->useblogassociations) {
 
-            //pre-select the course for associations
+            // Pre-select the course for associations.
             if ($courseid) {
                 $context = context_course::instance($courseid);
                 $entry->courseassoc = $context->id;
             }
 
-            //pre-select the mod for associations
+            // Pre-select the mod for associations.
             if ($modid) {
                 $context = context_module::instance($modid);
                 $entry->modassoc = $context->id;
index 2b28a73..805bf79 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -16,7 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
+    die('Direct access to this script is forbidden.');    //  It must be included from a Moodle page.
 }
 
 require_once($CFG->libdir.'/formslib.php');
@@ -24,7 +23,10 @@ require_once($CFG->libdir.'/formslib.php');
 class blog_edit_form extends moodleform {
     public $modnames = array();
 
-    function definition() {
+    /**
+     * Blog form definition.
+     */
+    public function definition() {
         global $CFG, $DB;
 
         $mform =& $this->_form;
@@ -50,12 +52,12 @@ class blog_edit_form extends moodleform {
 
         $mform->addElement('filemanager', 'attachment_filemanager', get_string('attachment', 'forum'), null, $attachmentoptions);
 
-        //disable publishstate options that are not allowed
+        // Disable publishstate options that are not allowed.
         $publishstates = array();
         $i = 0;
 
         foreach (blog_entry::get_applicable_publish_states() as $state => $desc) {
-            $publishstates[$state] = $desc;   //no maximum was set
+            $publishstates[$state] = $desc;   // No maximum was set.
             $i++;
         }
 
@@ -87,7 +89,12 @@ class blog_edit_form extends moodleform {
                 }
 
                 $mform->addElement('header', 'assochdr', get_string('associations', 'blog'));
-                $mform->addElement('advcheckbox', 'courseassoc', get_string('associatewithcourse', 'blog', $a), null, null, array(0, $contextid));
+                $mform->addElement('advcheckbox',
+                                   'courseassoc',
+                                   get_string('associatewithcourse', 'blog', $a),
+                                   null,
+                                   null,
+                                   array(0, $contextid));
                 $mform->setDefault('courseassoc', $contextid);
 
             } else if ((!empty($entry->modassoc) || !empty($modid))) {
@@ -107,7 +114,12 @@ class blog_edit_form extends moodleform {
                 }
 
                 $mform->addElement('header', 'assochdr', get_string('associations', 'blog'));
-                $mform->addElement('advcheckbox', 'modassoc', get_string('associatewithmodule', 'blog', $a), null, null, array(0, $context->id));
+                $mform->addElement('advcheckbox',
+                                   'modassoc',
+                                   get_string('associatewithmodule', 'blog', $a),
+                                   null,
+                                   null,
+                                   array(0, $context->id));
                 $mform->setDefault('modassoc', $context->id);
             }
         }
@@ -130,12 +142,18 @@ class blog_edit_form extends moodleform {
         $mform->setDefault('courseid', $courseid);
     }
 
-    function validation($data, $files) {
+    /**
+     * Validate the blog form data.
+     * @param array $data Data to be validated
+     * @param array $files unused
+     * @return array|bool
+     */
+    public function validation($data, $files) {
         global $CFG, $DB, $USER;
 
         $errors = array();
 
-        // validate course association
+        // Validate course association.
         if (!empty($data['courseassoc'])) {
             $coursecontext = context::instance_by_id($data['courseassoc']);
 
@@ -144,16 +162,16 @@ class blog_edit_form extends moodleform {
             }
         }
 
-        // validate mod association
+        // Validate mod association.
         if (!empty($data['modassoc'])) {
             $modcontextid = $data['modassoc'];
             $modcontext = context::instance_by_id($modcontextid);
 
             if ($modcontext->contextlevel == CONTEXT_MODULE) {
-                // get context of the mod's course
+                // Get context of the mod's course.
                 $coursecontext = $modcontext->get_course_context(true);
 
-                // ensure only one course is associated
+                // Ensure only one course is associated.
                 if (!empty($data['courseassoc'])) {
                     if ($data['courseassoc'] != $coursecontext->id) {
                         $errors['modassoc'] = get_string('onlyassociateonecourse', 'blog');
index b28860a..0aa122b 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -35,7 +34,9 @@ require_login();
 $context = context_system::instance();
 require_capability('moodle/blog:manageexternal', $context);
 
-// TODO redirect if $CFG->useexternalblogs is off, $CFG->maxexternalblogsperuser == 0, or if user doesn't have caps to manage external blogs
+// TODO redirect if $CFG->useexternalblogs is off,
+//                  $CFG->maxexternalblogsperuser == 0,
+//                  or if user doesn't have caps to manage external blogs.
 
 $id = optional_param('id', null, PARAM_INT);
 $url = new moodle_url('/blog/external_blog_edit.php');
@@ -52,24 +53,24 @@ $action = (empty($id)) ? 'add' : 'edit';
 
 $external = new stdClass();
 
-// Check that this id exists
+// Check that this id exists.
 if (!empty($id) && !$DB->record_exists('blog_external', array('id' => $id))) {
     print_error('wrongexternalid', 'blog');
-} elseif (!empty($id)) {
+} else if (!empty($id)) {
     $external = $DB->get_record('blog_external', array('id' => $id));
 }
 
 $strformheading = ($action == 'edit') ? get_string('editexternalblog', 'blog') : get_string('addnewexternalblog', 'blog');
-$strexternalblogs = get_string('externalblogs','blog');
-$strblogs = get_string('blogs','blog');
+$strexternalblogs = get_string('externalblogs', 'blog');
+$strblogs = get_string('blogs', 'blog');
 
 $externalblogform = new blog_edit_external_form();
 
-if ($externalblogform->is_cancelled()){
+if ($externalblogform->is_cancelled()) {
     redirect($returnurl);
 
 } else if ($data = $externalblogform->get_data()) {
-    //save stuff in db
+    // Save stuff in db.
     switch ($action) {
         case 'add':
             $rss = new moodle_simplepie($data->url);
@@ -79,13 +80,16 @@ if ($externalblogform->is_cancelled()){
             $newexternal->description = (empty($data->description)) ? $rss->get_description() : $data->description;
             $newexternal->userid = $USER->id;
             $newexternal->url = $data->url;
-            $newexternal->filtertags = $data->filtertags;
+            $newexternal->filtertags = (!empty($data->filtertags)) ? $data->filtertags : null;
             $newexternal->timemodified = time();
 
             $newexternal->id = $DB->insert_record('blog_external', $newexternal);
             blog_sync_external_entries($newexternal);
-            tag_set('blog_external', $newexternal->id, explode(',', $data->autotags), 'core',
-                context_user::instance($newexternal->userid)->id);
+            if ($CFG->usetags) {
+                $autotags = (!empty($data->autotags)) ? $data->autotags : null;
+                tag_set('blog_external', $newexternal->id, explode(',', $autotags), 'core',
+                    context_user::instance($newexternal->userid)->id);
+            }
 
             break;
 
@@ -99,13 +103,15 @@ if ($externalblogform->is_cancelled()){
                 $external->description = (empty($data->description)) ? $rss->get_description() : $data->description;
                 $external->userid = $USER->id;
                 $external->url = $data->url;
-                $external->filtertags = $data->filtertags;
+                $external->filtertags = (!empty($data->filtertags)) ? $data->filtertags : null;
                 $external->timemodified = time();
 
                 $DB->update_record('blog_external', $external);
-                tag_set('blog_external', $external->id, explode(',', $data->autotags), 'core',
-                    context_user::instance($newexternal->userid)->id);
-
+                if ($CFG->usetags) {
+                    $autotags = (!empty($data->autotags)) ? $data->autotags : null;
+                    tag_set('blog_external', $external->id, explode(',', $autotags), 'core',
+                        context_user::instance($external->userid)->id);
+                }
             } else {
                 print_error('wrongexternalid', 'blog');
             }
index 696e79a..fc28e9a 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -26,7 +25,7 @@
  */
 
 if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
+    die('Direct access to this script is forbidden.');    // It must be included from a Moodle page.
 }
 
 require_once($CFG->libdir.'/formslib.php');
@@ -118,9 +117,11 @@ class blog_edit_external_form extends moodleform {
         if ($id = $mform->getElementValue('id')) {
             $mform->setDefault('autotags', implode(',', tag_get_tags_array('blog_external', $id)));
             $mform->freeze('url');
-            $mform->freeze('filtertags');
+            if ($mform->elementExists('filtertags')) {
+                $mform->freeze('filtertags');
+            }
             // TODO change the filtertags element to a multiple select, using the tags of the external blog
-            // Use $rss->get_channel_tags()
+            // Use $rss->get_channel_tags().
         }
     }
 }
index 57af11a..800c85c 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -36,23 +35,25 @@ require_capability('moodle/blog:manageexternal', $context);
 
 $delete = optional_param('delete', null, PARAM_INT);
 
-$strexternalblogs = get_string('externalblogs','blog');
-$straddnewexternalblog = get_string('addnewexternalblog','blog');
-$strblogs = get_string('blogs','blog');
+$strexternalblogs = get_string('externalblogs', 'blog');
+$straddnewexternalblog = get_string('addnewexternalblog', 'blog');
+$strblogs = get_string('blogs', 'blog');
 $message = null;
 
 if ($delete && confirm_sesskey()) {
     $externalbloguserid = $DB->get_field('blog_external', 'userid', array('id' => $delete));
     if ($externalbloguserid == $USER->id) {
-        // Delete the external blog
+        // Delete the external blog.
         $DB->delete_records('blog_external', array('id' => $delete));
 
-        // Delete the external blog's posts
+        // Delete the external blog's posts.
         $deletewhere = 'module = :module
                             AND userid = :userid
                             AND ' . $DB->sql_isnotempty('post', 'uniquehash', false, false) . '
                             AND ' . $DB->sql_compare_text('content') . ' = ' . $DB->sql_compare_text(':delete');
-        $DB->delete_records_select('post', $deletewhere, array('module' => 'blog_external', 'userid' => $USER->id, 'delete' => $delete));
+        $DB->delete_records_select('post', $deletewhere, array('module' => 'blog_external',
+                                                               'userid' => $USER->id,
+                                                               'delete' => $delete));
 
         $message = get_string('externalblogdeleted', 'blog');
     }
@@ -77,7 +78,11 @@ if (!empty($blogs)) {
     $table = new html_table();
     $table->cellpadding = 4;
     $table->attributes['class'] = 'generaltable boxaligncenter';
-    $table->head = array(get_string('name'), get_string('url', 'blog'), get_string('timefetched', 'blog'), get_string('valid', 'blog'), get_string('actions'));
+    $table->head = array(get_string('name'),
+                         get_string('url', 'blog'),
+                         get_string('timefetched', 'blog'),
+                         get_string('valid', 'blog'),
+                         get_string('actions'));
 
     foreach ($blogs as $blog) {
         if ($blog->failedlastsync) {
@@ -92,7 +97,11 @@ if (!empty($blogs)) {
         $deletelink = new moodle_url('/blog/external_blogs.php', array('delete' => $blog->id, 'sesskey'=>sesskey()));
         $deleteicon = $OUTPUT->action_icon($deletelink, new pix_icon('t/delete', get_string('deleteexternalblog', 'blog')));
 
-        $table->data[] = new html_table_row(array($blog->name, $blog->url, userdate($blog->timefetched), $validicon, $editicon . '&nbsp'. $deleteicon));
+        $table->data[] = new html_table_row(array($blog->name,
+                                                  $blog->url,
+                                                  userdate($blog->timefetched),
+                                                  $validicon,
+                                                  $editicon . '&nbsp'. $deleteicon));
     }
     echo html_writer::table($table);
 }
index efa99ea..4c58f34 100644 (file)
@@ -1,4 +1,18 @@
 <?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/>.
 
 /**
  * file index.php
@@ -34,11 +48,7 @@ foreach ($url_params as $var => $val) {
 }
 $PAGE->set_url('/blog/index.php', $url_params);
 
-if (empty($CFG->enableblogs)) {
-    print_error('blogdisable', 'blog');
-}
-
-//correct tagid if a text tag is provided as a param
+// Correct tagid if a text tag is provided as a param.
 if (!empty($tag)) {
     if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
         $tagid = $tagrec->id;
@@ -47,43 +57,47 @@ if (!empty($tag)) {
     }
 }
 
-// add courseid if modid or groupid is specified: This is used for navigation and title
-if (!empty($modid) && empty($courseid)) {
-    $courseid = $DB->get_field('course_modules', 'course', array('id'=>$modid));
-}
-
-if (!empty($groupid) && empty($courseid)) {
-    $courseid = $DB->get_field('groups', 'courseid', array('id'=>$groupid));
-}
-
 $sitecontext = context_system::instance();
 // Blogs are always in system context.
 $PAGE->set_context($sitecontext);
 
-// check basic permissions
+// Check basic permissions.
 if ($CFG->bloglevel == BLOG_GLOBAL_LEVEL) {
-    // everybody can see anything - no login required unless site is locked down using forcelogin
+    // Everybody can see anything - no login required unless site is locked down using forcelogin.
     if ($CFG->forcelogin) {
         require_login();
     }
 
 } else if ($CFG->bloglevel == BLOG_SITE_LEVEL) {
-    // users must log in and can not be guests
+    // Users must log in and can not be guests.
     require_login();
     if (isguestuser()) {
-        // they must have entered the url manually...
+        // They must have entered the url manually.
         print_error('blogdisable', 'blog');
     }
 
 } else if ($CFG->bloglevel == BLOG_USER_LEVEL) {
-    // users can see own blogs only! with the exception of ppl with special cap
+    // Users can see own blogs only! with the exception of people with special cap.
     require_login();
 
 } else {
-    // weird!
+    // Weird!
     print_error('blogdisable', 'blog');
 }
 
+if (empty($CFG->enableblogs)) {
+    print_error('blogdisable', 'blog');
+}
+
+// Add courseid if modid or groupid is specified: This is used for navigation and title.
+if (!empty($modid) && empty($courseid)) {
+    $courseid = $DB->get_field('course_modules', 'course', array('id' => $modid));
+}
+
+if (!empty($groupid) && empty($courseid)) {
+    $courseid = $DB->get_field('groups', 'courseid', array('id' => $groupid));
+}
+
 
 if (!$userid && has_capability('moodle/blog:view', $sitecontext) && $CFG->bloglevel > BLOG_USER_LEVEL) {
     if ($entryid) {
@@ -206,13 +220,10 @@ if ($CFG->enablerssfeeds) {
     }
     $rsstitle = $blogheaders['heading'];
 
-    //check we haven't started output by outputting an error message
+    // Check we haven't started output by outputting an error message.
     if ($PAGE->state == moodle_page::STATE_BEFORE_HEADER) {
         blog_rss_add_http_header($rsscontext, $rsstitle, $filtertype, $thingid, $tagid);
     }
-
-    //this works but there isn't a great place to put the link
-    //blog_rss_print_link($rsscontext, $filtertype, $thingid, $tagid);
 }
 
 echo $OUTPUT->header();
index 9190e46..5b26916 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -26,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-/**
+/*
  * Library of functions and constants for blog
  */
 require_once($CFG->dirroot .'/blog/rsslib.php');
@@ -45,11 +44,11 @@ function blog_user_can_edit_entry($blogentry) {
     $sitecontext = context_system::instance();
 
     if (has_capability('moodle/blog:manageentries', $sitecontext)) {
-        return true; // can edit any blog entry
+        return true; // Can edit any blog entry.
     }
 
     if ($blogentry->userid == $USER->id && has_capability('moodle/blog:create', $sitecontext)) {
-        return true; // can edit own when having blog:create capability
+        return true; // Can edit own when having blog:create capability.
     }
 
     return false;
@@ -168,12 +167,12 @@ function blog_sync_external_entries($externalblog) {
     if (empty($rss->data)) {
         return null;
     }
-    //used to identify blog posts that have been deleted from the source feed
+    // Used to identify blog posts that have been deleted from the source feed.
     $oldesttimestamp = null;
     $uniquehashes = array();
 
     foreach ($rss->get_items() as $entry) {
-        // If filtertags are defined, use them to filter the entries by RSS category
+        // If filtertags are defined, use them to filter the entries by RSS category.
         if (!empty($externalblog->filtertags)) {
             $containsfiltertag = false;
             $categories = $entry->get_categories();
@@ -201,26 +200,26 @@ function blog_sync_external_entries($externalblog) {
         $newentry->uniquehash = $entry->get_permalink();
         $newentry->publishstate = 'site';
         $newentry->format = FORMAT_HTML;
-        // Clean subject of html, just in case
+        // Clean subject of html, just in case.
         $newentry->subject = clean_param($entry->get_title(), PARAM_TEXT);
-        // Observe 128 max chars in DB
-        // TODO: +1 to raise this to 255
+        // Observe 128 max chars in DB.
+        // TODO: +1 to raise this to 255.
         if (core_text::strlen($newentry->subject) > 128) {
             $newentry->subject = core_text::substr($newentry->subject, 0, 125) . '...';
         }
         $newentry->summary = $entry->get_description();
 
-        //used to decide whether to insert or update
-        //uses enty permalink plus creation date if available
+        // Used to decide whether to insert or update.
+        // Uses enty permalink plus creation date if available.
         $existingpostconditions = array('uniquehash' => $entry->get_permalink());
 
-        //our DB doesnt allow null creation or modified timestamps so check the external blog supplied one
+        // Our DB doesnt allow null creation or modified timestamps so check the external blog supplied one.
         $entrydate = $entry->get_date('U');
         if (!empty($entrydate)) {
             $existingpostconditions['created'] = $entrydate;
         }
 
-        //the post ID or false if post not found in DB
+        // The post ID or false if post not found in DB.
         $postid = $DB->get_field('post', 'id', $existingpostconditions);
 
         $timestamp = null;
@@ -230,14 +229,14 @@ function blog_sync_external_entries($externalblog) {
             $timestamp = $entrydate;
         }
 
-        //only set created if its a new post so we retain the original creation timestamp if the post is edited
+        // Only set created if its a new post so we retain the original creation timestamp if the post is edited.
         if ($postid === false) {
             $newentry->created = $timestamp;
         }
         $newentry->lastmodified = $timestamp;
 
         if (empty($oldesttimestamp) || $timestamp < $oldesttimestamp) {
-            //found an older post
+            // Found an older post.
             $oldesttimestamp = $timestamp;
         }
 
@@ -252,7 +251,7 @@ function blog_sync_external_entries($externalblog) {
         if ($postid === false) {
             $id = $DB->insert_record('post', $newentry);
 
-            // Set tags
+            // Set tags.
             if ($tags = tag_get_tags_array('blog_external', $externalblog->id)) {
                 tag_set('post', $id, $tags, 'core', context_user::instance($externalblog->userid)->id);
             }
@@ -273,7 +272,7 @@ function blog_sync_external_entries($externalblog) {
     $dbposts = $DB->get_records_sql($sql, array('blogid' => $externalblog->id, 'ts' => $oldesttimestamp));
 
     $todelete = array();
-    foreach($dbposts as $dbpost) {
+    foreach ($dbposts as $dbpost) {
         if ( !in_array($dbpost->uniquehash, $uniquehashes) ) {
             $todelete[] = $dbpost->id;
         }
@@ -324,13 +323,13 @@ function blog_get_all_options(moodle_page $page, stdClass $userid = null) {
 
     $options = array();
 
-    // If blogs are enabled and the user is logged in and not a guest
+    // If blogs are enabled and the user is logged in and not a guest.
     if (blog_is_enabled_for_user()) {
-        // If the context is the user then assume we want to load for the users context
+        // If the context is the user then assume we want to load for the users context.
         if (is_null($userid) && $page->context->contextlevel == CONTEXT_USER) {
             $userid = $page->context->instanceid;
         }
-        // Check the userid var
+        // Check the userid var.
         if (!is_null($userid) && $userid!==$USER->id) {
             // Load the user from the userid... it MUST EXIST throw a wobbly if it doesn't!
             $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
@@ -339,33 +338,36 @@ function blog_get_all_options(moodle_page $page, stdClass $userid = null) {
         }
 
         if ($CFG->useblogassociations && $page->cm !== null) {
-            // Load for the module associated with the page
+            // Load for the module associated with the page.
             $options[CONTEXT_MODULE] = blog_get_options_for_module($page->cm, $user);
         } else if ($CFG->useblogassociations && $page->course->id != SITEID) {
-            // Load the options for the course associated with the page
+            // Load the options for the course associated with the page.
             $options[CONTEXT_COURSE] = blog_get_options_for_course($page->course, $user);
         }
 
-        // Get the options for the user
+        // Get the options for the user.
         if ($user !== null and !isguestuser($user)) {
-            // Load for the requested user
+            // Load for the requested user.
             $options[CONTEXT_USER+1] = blog_get_options_for_user($user);
         }
-        // Load for the current user
+        // Load for the current user.
         if (isloggedin() and !isguestuser()) {
             $options[CONTEXT_USER] = blog_get_options_for_user();
         }
     }
 
-    // If blog level is global then display a link to view all site entries
-    if (!empty($CFG->enableblogs) && $CFG->bloglevel >= BLOG_GLOBAL_LEVEL && has_capability('moodle/blog:view', context_system::instance())) {
+    // If blog level is global then display a link to view all site entries.
+    if (!empty($CFG->enableblogs)
+        && $CFG->bloglevel >= BLOG_GLOBAL_LEVEL
+        && has_capability('moodle/blog:view', context_system::instance())) {
+
         $options[CONTEXT_SYSTEM] = array('viewsite' => array(
             'string' => get_string('viewsiteentries', 'blog'),
             'link' => new moodle_url('/blog/index.php')
         ));
     }
 
-    // Return the options
+    // Return the options.
     return $options;
 }
 
@@ -380,16 +382,16 @@ function blog_get_all_options(moodle_page $page, stdClass $userid = null) {
  */
 function blog_get_options_for_user(stdClass $user=null) {
     global $CFG, $USER;
-    // Cache
+    // Cache.
     static $useroptions = array();
 
     $options = array();
-    // Blogs must be enabled and the user must be logged in
+    // Blogs must be enabled and the user must be logged in.
     if (!blog_is_enabled_for_user()) {
         return $options;
     }
 
-    // Sort out the user var
+    // Sort out the user var.
     if ($user === null || $user->id == $USER->id) {
         $user = $USER;
         $iscurrentuser = true;
@@ -397,7 +399,7 @@ function blog_get_options_for_user(stdClass $user=null) {
         $iscurrentuser = false;
     }
 
-    // If we've already generated serve from the cache
+    // If we've already generated serve from the cache.
     if (array_key_exists($user->id, $useroptions)) {
         return $useroptions[$user->id];
     }
@@ -406,22 +408,22 @@ function blog_get_options_for_user(stdClass $user=null) {
     $canview = has_capability('moodle/blog:view', $sitecontext);
 
     if (!$iscurrentuser && $canview && ($CFG->bloglevel >= BLOG_SITE_LEVEL)) {
-        // Not the current user, but we can view and its blogs are enabled for SITE or GLOBAL
+        // Not the current user, but we can view and its blogs are enabled for SITE or GLOBAL.
         $options['userentries'] = array(
             'string' => get_string('viewuserentries', 'blog', fullname($user)),
             'link' => new moodle_url('/blog/index.php', array('userid'=>$user->id))
         );
     } else {
-        // It's the current user
+        // It's the current user.
         if ($canview) {
-            // We can view our own blogs .... BIG surprise
+            // We can view our own blogs .... BIG surprise.
             $options['view'] = array(
                 'string' => get_string('viewallmyentries', 'blog'),
                 'link' => new moodle_url('/blog/index.php', array('userid'=>$USER->id))
             );
         }
         if (has_capability('moodle/blog:create', $sitecontext)) {
-            // We can add to our own blog
+            // We can add to our own blog.
             $options['add'] = array(
                 'string' => get_string('addnewentry', 'blog'),
                 'link' => new moodle_url('/blog/edit.php', array('action'=>'add'))
@@ -432,12 +434,12 @@ function blog_get_options_for_user(stdClass $user=null) {
         $options['rss'] = array(
             'string' => get_string('rssfeed', 'blog'),
             'link' => new moodle_url(rss_get_url($sitecontext->id, $USER->id, 'blog', 'user/'.$user->id))
-       );
+        );
     }
 
-    // Cache the options
+    // Cache the options.
     $useroptions[$user->id] = $options;
-    // Return the options
+    // Return the options.
     return $options;
 }
 
@@ -451,47 +453,46 @@ function blog_get_options_for_user(stdClass $user=null) {
  */
 function blog_get_options_for_course(stdClass $course, stdClass $user=null) {
     global $CFG, $USER;
-    // Cache
+    // Cache.
     static $courseoptions = array();
 
     $options = array();
 
-    // User must be logged in and blogs must be enabled
+    // User must be logged in and blogs must be enabled.
     if (!blog_is_enabled_for_user()) {
         return $options;
     }
 
-    // Check that the user can associate with the course
+    // Check that the user can associate with the course.
     $sitecontext = context_system::instance();
-    // Generate the cache key
+    // Generate the cache key.
     $key = $course->id.':';
     if (!empty($user)) {
         $key .= $user->id;
     } else {
         $key .= $USER->id;
     }
-    // Serve from the cache if we've already generated for this course
+    // Serve from the cache if we've already generated for this course.
     if (array_key_exists($key, $courseoptions)) {
         return $courseoptions[$key];
     }
 
-
     if (has_capability('moodle/blog:view', $sitecontext)) {
         // We can view!
         if ($CFG->bloglevel >= BLOG_SITE_LEVEL) {
-            // View entries about this course
+            // View entries about this course.
             $options['courseview'] = array(
                 'string' => get_string('viewcourseblogs', 'blog'),
                 'link' => new moodle_url('/blog/index.php', array('courseid' => $course->id))
             );
         }
-        // View MY entries about this course
+        // View MY entries about this course.
         $options['courseviewmine'] = array(
             'string' => get_string('viewmyentriesaboutcourse', 'blog'),
             'link' => new moodle_url('/blog/index.php', array('courseid' => $course->id, 'userid' => $USER->id))
         );
         if (!empty($user) && ($CFG->bloglevel >= BLOG_SITE_LEVEL)) {
-            // View the provided users entries about this course
+            // View the provided users entries about this course.
             $options['courseviewuser'] = array(
                 'string' => get_string('viewentriesbyuseraboutcourse', 'blog', fullname($user)),
                 'link' => new moodle_url('/blog/index.php', array('courseid' => $course->id, 'userid' => $user->id))
@@ -500,17 +501,16 @@ function blog_get_options_for_course(stdClass $course, stdClass $user=null) {
     }
 
     if (has_capability('moodle/blog:create', $sitecontext)) {
-        // We can blog about this course
+        // We can blog about this course.
         $options['courseadd'] = array(
             'string' => get_string('blogaboutthiscourse', 'blog'),
             'link' => new moodle_url('/blog/edit.php', array('action' => 'add', 'courseid' => $course->id))
         );
     }
 
-
-    // Cache the options for this course
+    // Cache the options for this course.
     $courseoptions[$key] = $options;
-    // Return the options
+    // Return the options.
     return $options;
 }
 
@@ -524,18 +524,18 @@ function blog_get_options_for_course(stdClass $course, stdClass $user=null) {
  */
 function blog_get_options_for_module($module, $user=null) {
     global $CFG, $USER;
-    // Cache
+    // Cache.
     static $moduleoptions = array();
 
     $options = array();
-    // User must be logged in, blogs must be enabled
+    // User must be logged in, blogs must be enabled.
     if (!blog_is_enabled_for_user()) {
         return $options;
     }
 
     $sitecontext = context_system::instance();
 
-    // Generate the cache key
+    // Generate the cache key.
     $key = $module->id.':';
     if (!empty($user)) {
         $key .= $user->id;
@@ -543,18 +543,17 @@ function blog_get_options_for_module($module, $user=null) {
         $key .= $USER->id;
     }
     if (array_key_exists($key, $moduleoptions)) {
-        // Serve from the cache so we don't have to regenerate
+        // Serve from the cache so we don't have to regenerate.
         return $moduleoptions[$key];
     }
 
-
     if (has_capability('moodle/blog:view', $sitecontext)) {
         // Save correct module name for later usage.
         $modulename = get_string('modulename', $module->modname);
 
         // We can view!
         if ($CFG->bloglevel >= BLOG_SITE_LEVEL) {
-            // View all entries about this module
+            // View all entries about this module.
             $a = new stdClass;
             $a->type = $modulename;
             $options['moduleview'] = array(
@@ -562,13 +561,13 @@ function blog_get_options_for_module($module, $user=null) {
                 'link' => new moodle_url('/blog/index.php', array('modid'=>$module->id))
             );
         }
-        // View MY entries about this module
+        // View MY entries about this module.
         $options['moduleviewmine'] = array(
             'string' => get_string('viewmyentriesaboutmodule', 'blog', $modulename),
             'link' => new moodle_url('/blog/index.php', array('modid'=>$module->id, 'userid'=>$USER->id))
         );
         if (!empty($user) && ($CFG->bloglevel >= BLOG_SITE_LEVEL)) {
-            // View the given users entries about this module
+            // View the given users entries about this module.
             $a = new stdClass;
             $a->mod = $modulename;
             $a->user = fullname($user);
@@ -580,15 +579,15 @@ function blog_get_options_for_module($module, $user=null) {
     }
 
     if (has_capability('moodle/blog:create', $sitecontext)) {
-        // The user can blog about this module
+        // The user can blog about this module.
         $options['moduleadd'] = array(
             'string' => get_string('blogaboutthismodule', 'blog', $modulename),
             'link' => new moodle_url('/blog/edit.php', array('action'=>'add', 'modid'=>$module->id))
         );
     }
-    // Cache the options
+    // Cache the options.
     $moduleoptions[$key] = $options;
-    // Return the options
+    // Return the options.
     return $options;
 }
 
@@ -626,7 +625,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
     $action   = optional_param('action', null, PARAM_ALPHA);
     $confirm  = optional_param('confirm', false, PARAM_BOOL);
 
-    // Ignore userid when action == add
+    // Ignore userid when action == add.
     if ($action == 'add' && $userid) {
         unset($userid);
         $PAGE->url->remove_params(array('userid'));
@@ -644,11 +643,11 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
 
     $site = $DB->get_record('course', array('id' => SITEID));
     $sitecontext = context_system::instance();
-    // Common Lang strings
+    // Common Lang strings.
     $strparticipants = get_string("participants");
     $strblogentries  = get_string("blogentries", 'blog');
 
-    // Prepare record objects as needed
+    // Prepare record objects as needed.
     if (!empty($courseid)) {
         $headers['filters']['course'] = $courseid;
         $course = $DB->get_record('course', array('id' => $courseid));
@@ -659,7 +658,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $user = $DB->get_record('user', array('id' => $userid));
     }
 
-    if (!empty($groupid)) { // groupid always overrides courseid
+    if (!empty($groupid)) { // The groupid always overrides courseid.
         $headers['filters']['group'] = $groupid;
         $group = $DB->get_record('groups', array('id' => $groupid));
         $course = $DB->get_record('course', array('id' => $group->courseid));
@@ -667,11 +666,11 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
 
     $PAGE->set_pagelayout('standard');
 
-    // modid always overrides courseid, so the $course object may be reset here
+    // The modid always overrides courseid, so the $course object may be reset here.
     if (!empty($modid) && $CFG->useblogassociations) {
 
         $headers['filters']['module'] = $modid;
-        // A groupid param may conflict with this coursemod's courseid. Ignore groupid in that case
+        // A groupid param may conflict with this coursemod's courseid. Ignore groupid in that case.
         $courseid = $DB->get_field('course_modules', 'course', array('id'=>$modid));
         $course = $DB->get_record('course', array('id' => $courseid));
         $cm = $DB->get_record('course_modules', array('id' => $modid));
@@ -685,17 +684,16 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
     }
 
     // Case 1: No entry, mod, course or user params: all site entries to be shown (filtered by search and tag/tagid)
-    // Note: if action is set to 'add' or 'edit', we do this at the end
+    // Note: if action is set to 'add' or 'edit', we do this at the end.
     if (empty($entryid) && empty($modid) && empty($courseid) && empty($userid) && !in_array($action, array('edit', 'add'))) {
         $shortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $PAGE->navbar->add($strblogentries, $blogurl);
         $PAGE->set_title("$shortname: " . get_string('blog', 'blog'));
         $PAGE->set_heading("$shortname: " . get_string('blog', 'blog'));
         $headers['heading'] = get_string('siteblog', 'blog', $shortname);
-        // $headers['strview'] = get_string('viewsiteentries', 'blog');
     }
 
-    // Case 2: only entryid is requested, ignore all other filters. courseid is used to give more contextual information
+    // Case 2: only entryid is requested, ignore all other filters. courseid is used to give more contextual information.
     if (!empty($entryid)) {
         $headers['filters']['entry'] = $entryid;
         $sql = 'SELECT u.* FROM {user} u, {post} p WHERE p.id = ? AND p.userid = u.id';
@@ -720,15 +718,16 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $PAGE->set_heading("$shortname: " . fullname($user) . ": $entry->subject");
         $headers['heading'] = get_string('blogentrybyuser', 'blog', fullname($user));
 
-        // We ignore tag and search params
+        // We ignore tag and search params.
         if (empty($action) || !$CFG->useblogassociations) {
             $headers['url'] = $blogurl;
             return $headers;
         }
     }
 
-    // Case 3: A user's blog entries
     if (!empty($userid) && empty($entryid) && ((empty($courseid) && empty($modid)) || !$CFG->useblogassociations)) {
+        // Case 3: A user's blog entries.
+
         $shortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $blogurl->param('userid', $userid);
         $PAGE->set_title("$shortname: " . fullname($user) . ": " . get_string('blog', 'blog'));
@@ -736,23 +735,21 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $headers['heading'] = get_string('userblog', 'blog', fullname($user));
         $headers['strview'] = get_string('viewuserentries', 'blog', fullname($user));
 
-    } else
+    } else if (!$CFG->useblogassociations && empty($userid) && !in_array($action, array('edit', 'add'))) {
+        // Case 4: No blog associations, no userid.
 
-    // Case 4: No blog associations, no userid
-    if (!$CFG->useblogassociations && empty($userid) && !in_array($action, array('edit', 'add'))) {
         $shortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $PAGE->set_title("$shortname: " . get_string('blog', 'blog'));
         $PAGE->set_heading("$shortname: " . get_string('blog', 'blog'));
         $headers['heading'] = get_string('siteblog', 'blog', $shortname);
-    } else
+    } else if (!empty($userid) && !empty($modid) && empty($entryid)) {
+        // Case 5: Blog entries associated with an activity by a specific user (courseid ignored).
 
-    // Case 5: Blog entries associated with an activity by a specific user (courseid ignored)
-    if (!empty($userid) && !empty($modid) && empty($entryid)) {
         $shortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $blogurl->param('userid', $userid);
         $blogurl->param('modid', $modid);
 
-        // Course module navigation is handled by build_navigation as the second param
+        // Course module navigation is handled by build_navigation as the second param.
         $headers['cm'] = $cm;
         $PAGE->navbar->add(fullname($user), "$CFG->wwwroot/user/view.php?id=$user->id");
         $PAGE->navbar->add($strblogentries, $blogurl);
@@ -767,10 +764,9 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $headers['heading'] = get_string('blogentriesbyuseraboutmodule', 'blog', $a);
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
-    } else
+    } else if (!empty($userid) && !empty($courseid) && empty($modid) && empty($entryid)) {
+        // Case 6: Blog entries associated with a course by a specific user.
 
-    // Case 6: Blog entries associated with a course by a specific user
-    if (!empty($userid) && !empty($courseid) && empty($modid) && empty($entryid)) {
         $siteshortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
         $blogurl->param('userid', $userid);
@@ -789,12 +785,11 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewblogentries', 'blog', $a);
 
-        // Remove the userid from the URL to inform the blog_menu block correctly
+        // Remove the userid from the URL to inform the blog_menu block correctly.
         $blogurl->remove_params(array('userid'));
-    } else
+    } else if (!empty($groupid) && empty($modid) && empty($entryid)) {
+        // Case 7: Blog entries by members of a group, associated with that group's course.
 
-    // Case 7: Blog entries by members of a group, associated with that group's course
-    if (!empty($groupid) && empty($modid) && empty($entryid)) {
         $siteshortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
         $blogurl->param('courseid', $course->id);
@@ -814,10 +809,9 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $headers['heading'] = get_string('blogentriesbygroupaboutcourse', 'blog', $a);
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewblogentries', 'blog', $a);
-    } else
+    } else if (!empty($groupid) && !empty($modid) && empty($entryid)) {
+        // Case 8: Blog entries by members of a group, associated with an activity in that course.
 
-    // Case 8: Blog entries by members of a group, associated with an activity in that course
-    if (!empty($groupid) && !empty($modid) && empty($entryid)) {
         $siteshortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
         $headers['cm'] = $cm;
@@ -838,10 +832,9 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
 
-    } else
+    } else if (!empty($modid) && empty($userid) && empty($groupid) && empty($entryid)) {
+        // Case 9: All blog entries associated with an activity.
 
-    // Case 9: All blog entries associated with an activity
-    if (!empty($modid) && empty($userid) && empty($groupid) && empty($entryid)) {
         $siteshortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
         $PAGE->set_cm($cm, $course);
@@ -854,10 +847,9 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $a->type = get_string('modulename', $cm->modname);
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewallmodentries', 'blog', $a);
-    } else
+    } else if (!empty($courseid) && empty($userid) && empty($groupid) && empty($modid) && empty($entryid)) {
+        // Case 10: All blog entries associated with a course.
 
-    // Case 10: All blog entries associated with a course
-    if (!empty($courseid) && empty($userid) && empty($groupid) && empty($modid) && empty($entryid)) {
         $siteshortname = format_string($site->shortname, true, array('context' => context_course::instance(SITEID)));
         $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
         $blogurl->param('courseid', $courseid);
@@ -866,20 +858,24 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         $PAGE->set_heading("$siteshortname: $courseshortname: " . get_string('blogentries', 'blog'));
         $a = new stdClass();
         $a->type = get_string('course');
-        $headers['heading'] = get_string('blogentriesabout', 'blog', format_string($course->fullname, true, array('context' => context_course::instance($course->id))));
+        $headers['heading'] = get_string('blogentriesabout',
+                                         'blog',
+                                         format_string($course->fullname,
+                                                       true,
+                                                       array('context' => context_course::instance($course->id))));
         $headers['stradd'] = get_string('blogaboutthis', 'blog', $a);
         $headers['strview'] = get_string('viewblogentries', 'blog', $a);
         $blogurl->remove_params(array('userid'));
     }
 
     if (!in_array($action, array('edit', 'add'))) {
-        // Append Tag info
+        // Append Tag info.
         if (!empty($tagid)) {
             $headers['filters']['tag'] = $tagid;
             $blogurl->param('tagid', $tagid);
             $tagrec = $DB->get_record('tag', array('id'=>$tagid));
             $PAGE->navbar->add($tagrec->name, $blogurl);
-        } elseif (!empty($tag)) {
+        } else if (!empty($tag)) {
             if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
                 $tagid = $tagrec->id;
                 $headers['filters']['tag'] = $tagid;
@@ -888,7 +884,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
             }
         }
 
-        // Append Search info
+        // Append Search info.
         if (!empty($search)) {
             $headers['filters']['search'] = $search;
             $blogurl->param('search', $search);
@@ -896,7 +892,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
         }
     }
 
-    // Append edit mode info
+    // Append edit mode info.
     if (!empty($action) && $action == 'add') {
 
     } else if (!empty($action) && $action == 'edit') {
index 5561050..5df39ad 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -40,7 +39,7 @@ require_once($CFG->libdir . '/filelib.php');
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class blog_entry implements renderable {
-    // Public Database fields
+    // Public Database fields.
     public $id;
     public $userid;
     public $subject;
@@ -49,7 +48,7 @@ class blog_entry implements renderable {
     public $attachment;
     public $publishstate;
 
-    // Locked Database fields (Don't touch these)
+    // Locked Database fields (Don't touch these).
     public $courseid = 0;
     public $groupid = 0;
     public $module = 'blog';
@@ -62,14 +61,13 @@ class blog_entry implements renderable {
     public $created;
     public $usermodified;
 
-    // Other class variables
+    // Other class variables.
     public $form;
     public $tags = array();
 
     /** @var StdClass Data needed to render the entry */
     public $renderable;
 
-    // Methods
     /**
      * Constructor. If given an id, will fetch the corresponding record from the DB.
      *
@@ -137,11 +135,12 @@ class blog_entry implements renderable {
             if ($externalblog = $DB->get_record('blog_external', array('id' => $this->content))) {
                 $urlparts = parse_url($externalblog->url);
                 $this->renderable->externalblogtext = get_string('retrievedfrom', 'blog') . get_string('labelsep', 'langconfig');
-                $this->renderable->externalblogtext .= html_writer::link($urlparts['scheme'] . '://'.$urlparts['host'], $externalblog->name);
+                $this->renderable->externalblogtext .= html_writer::link($urlparts['scheme'] . '://' . $urlparts['host'],
+                                                                         $externalblog->name);
             }
         }
 
-        // Retrieve associations
+        // Retrieve associations.
         $this->renderable->unassociatedentry = false;
         if (!empty($CFG->useblogassociations)) {
 
@@ -165,9 +164,11 @@ class blog_entry implements renderable {
 
                     // Course associations.
                     if ($context->contextlevel ==  CONTEXT_COURSE) {
-                        $instancename = $DB->get_field('course', 'shortname', array('id' => $context->instanceid)); //TODO: performance!!!!
+                        // TODO: performance!!!!
+                        $instancename = $DB->get_field('course', 'shortname', array('id' => $context->instanceid));
 
-                        $associations[$key]->url = $assocurl = new moodle_url('/course/view.php', array('id' => $context->instanceid));
+                        $associations[$key]->url = $assocurl = new moodle_url('/course/view.php',
+                                                                              array('id' => $context->instanceid));
                         $associations[$key]->text = $instancename;
                         $associations[$key]->icon = new pix_icon('i/course', $associations[$key]->text);
                     }
@@ -175,15 +176,17 @@ class blog_entry implements renderable {
                     // Mod associations.
                     if ($context->contextlevel ==  CONTEXT_MODULE) {
 
-                        // Getting the activity type and the activity instance id
+                        // Getting the activity type and the activity instance id.
                         $sql = 'SELECT cm.instance, m.name FROM {course_modules} cm
                                   JOIN {modules} m ON m.id = cm.module
                                  WHERE cm.id = :cmid';
                         $modinfo = $DB->get_record_sql($sql, array('cmid' => $context->instanceid));
-                        $instancename = $DB->get_field($modinfo->name, 'name', array('id' => $modinfo->instance)); //TODO: performance!!!!
+                        // TODO: performance!!!!
+                        $instancename = $DB->get_field($modinfo->name, 'name', array('id' => $modinfo->instance));
 
                         $associations[$key]->type = get_string('modulename', $modinfo->name);
-                        $associations[$key]->url = new moodle_url('/mod/' . $modinfo->name . '/view.php', array('id' => $context->instanceid));
+                        $associations[$key]->url = new moodle_url('/mod/' . $modinfo->name . '/view.php',
+                                                                  array('id' => $context->instanceid));
                         $associations[$key]->text = $instancename;
                         $associations[$key]->icon = new pix_icon('icon', $associations[$key]->text, $modinfo->name);
                     }
@@ -203,7 +206,7 @@ class blog_entry implements renderable {
      * Gets the entry attachments list
      * @return array List of blog_entry_attachment instances
      */
-    function get_attachments() {
+    public function get_attachments() {
 
         global $CFG;
 
@@ -293,7 +296,13 @@ class blog_entry implements renderable {
         }
 
         $entry = file_postupdate_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog', 'post', $entry->id);
-        $entry = file_postupdate_standard_filemanager($entry, 'attachment', $attachmentoptions, $sitecontext, 'blog', 'attachment', $entry->id);
+        $entry = file_postupdate_standard_filemanager($entry,
+                                                      'attachment',
+                                                      $attachmentoptions,
+                                                      $sitecontext,
+                                                      'blog',
+                                                      'attachment',
+                                                      $entry->id);
 
         if (!empty($CFG->useblogassociations)) {
             $entry->add_associations();
@@ -427,7 +436,7 @@ class blog_entry implements renderable {
 
         if ($otags = optional_param('otags', '', PARAM_INT)) {
             foreach ($otags as $tagid) {
-                // TODO : make this use the tag name in the form
+                // TODO : make this use the tag name in the form.
                 if ($tag = tag_get('id', $tagid)) {
                     $tags[] = $tag->name;
                 }
@@ -456,11 +465,11 @@ class blog_entry implements renderable {
         $sitecontext = context_system::instance();
 
         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
-            return true; // can edit any blog entry
+            return true; // Can edit any blog entry.
         }
 
         if ($this->userid == $userid && has_capability('moodle/blog:create', $sitecontext)) {
-            return true; // can edit own when having blog:create capability
+            return true; // Can edit own when having blog:create capability.
         }
 
         return false;
@@ -480,23 +489,23 @@ class blog_entry implements renderable {
         $sitecontext = context_system::instance();
 
         if (empty($CFG->enableblogs) || !has_capability('moodle/blog:view', $sitecontext)) {
-            return false; // blog system disabled or user has no blog view capability
+            return false; // Blog system disabled or user has no blog view capability.
         }
 
         if (isloggedin() && $USER->id == $targetuserid) {
-            return true; // can view own entries in any case
+            return true; // Can view own entries in any case.
         }
 
         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
-            return true; // can manage all entries
+            return true; // Can manage all entries.
         }
 
-        // coming for 1 entry, make sure it's not a draft
+        // Coming for 1 entry, make sure it's not a draft.
         if ($this->publishstate == 'draft' && !has_capability('moodle/blog:viewdrafts', $sitecontext)) {
-            return false;  // can not view draft of others
+            return false;  // Can not view draft of others.
         }
 
-        // coming for 1 entry, make sure user is logged in, if not a public blog
+        // Coming for 1 entry, make sure user is logged in, if not a public blog.
         if ($this->publishstate != 'public' && !isloggedin()) {
             return false;
         }
@@ -507,7 +516,7 @@ class blog_entry implements renderable {
                 break;
 
             case BLOG_SITE_LEVEL:
-                if (isloggedin()) { // not logged in viewers forbidden
+                if (isloggedin()) { // Not logged in viewers forbidden.
                     return true;
                 }
                 return false;
@@ -533,7 +542,7 @@ class blog_entry implements renderable {
         global $CFG;
         $options = array();
 
-        // everyone gets draft access
+        // Everyone gets draft access.
         if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
             $options['draft'] = get_string('publishtonoone', 'blog');
         }
@@ -577,7 +586,7 @@ class blog_listing {
      * @param array $filters An associative array of filtername => filterid
      */
     public function __construct($filters=array()) {
-        // Unset filters overridden by more specific filters
+        // Unset filters overridden by more specific filters.
         foreach ($filters as $type => $id) {
             if (!empty($type) && !empty($id)) {
                 $this->filters[$type] = blog_filter::get_instance($id, $type);
@@ -615,50 +624,53 @@ class blog_listing {
     public function get_entry_fetch_sql($count=false, $sort='lastmodified DESC', $userid = false) {
         global $DB, $USER, $CFG;
 
-        if(!$userid) {
+        if (!$userid) {
             $userid = $USER->id;
         }
 
         $allnamefields = get_all_user_name_fields(true, 'u');
         // The query used to locate blog entries is complicated.  It will be built from the following components:
-        $requiredfields = "p.*, $allnamefields, u.email";  // the SELECT clause
-        $tables = array('p' => 'post', 'u' => 'user');   // components of the FROM clause (table_id => table_name)
-        $conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')');  // components of the WHERE clause (conjunction)
+        $requiredfields = "p.*, $allnamefields, u.email";  // The SELECT clause.
+        $tables = array('p' => 'post', 'u' => 'user');   // Components of the FROM clause (table_id => table_name).
+        // Components of the WHERE clause (conjunction).
+        $conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')');
 
-        // build up a clause for permission constraints
+        // Build up a clause for permission constraints.
 
         $params = array();
 
-        // fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs
-        // admins can see all blogs regardless of publish states, as described on the help page
+        // Fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs.
+        // Admins can see all blogs regardless of publish states, as described on the help page.
         if (has_capability('moodle/user:readuserblogs', context_system::instance())) {
-            // don't add permission constraints
+            // Don't add permission constraints.
 
-        } else if(!empty($this->filters['user']) && has_capability('moodle/user:readuserblogs',
-                context_user::instance((empty($this->filters['user']->id) ? 0 : $this->filters['user']->id)))) {
-            // don't add permission constraints
+        } else if (!empty($this->filters['user'])
+                   && has_capability('moodle/user:readuserblogs',
+                                     context_user::instance((empty($this->filters['user']->id) ? 0 : $this->filters['user']->id)))) {
+            // Don't add permission constraints.
 
         } else {
             if (isloggedin() and !isguestuser()) {
-                $assocexists = $DB->record_exists('blog_association', array());  //dont check association records if there aren't any
+                // Dont check association records if there aren't any.
+                $assocexists = $DB->record_exists('blog_association', array());
 
-                //begin permission sql clause
+                // Begin permission sql clause.
                 $permissionsql =  '(p.userid = ? ';
                 $params[] = $userid;
 
-                if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // add permission to view site-level entries
+                if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // Add permission to view site-level entries.
                     $permissionsql .= " OR p.publishstate = 'site' ";
                 }
 
-                if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // add permission to view global entries
+                if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // Add permission to view global entries.
                     $permissionsql .= " OR p.publishstate = 'public' ";
                 }
 
-                $permissionsql .= ') ';   //close permissions sql clause
-            } else {  // default is access to public entries
+                $permissionsql .= ') ';   // Close permissions sql clause.
+            } else {  // Default is access to public entries.
                 $permissionsql = "p.publishstate = 'public'";
             }
-            $conditions[] = $permissionsql;  //add permission constraints
+            $conditions[] = $permissionsql;  // Add permission constraints.
         }
 
         foreach ($this->filters as $type => $blogfilter) {
@@ -667,7 +679,7 @@ class blog_listing {
             $tables = array_merge($tables, $blogfilter->tables);
         }
 
-        $tablessql = '';  // build up the FROM clause
+        $tablessql = '';  // Build up the FROM clause.
         foreach ($tables as $tablename => $table) {
             $tablessql .= ($tablessql ? ', ' : '').'{'.$table.'} '.$tablename;
         }
@@ -688,7 +700,7 @@ class blog_listing {
         global $CFG, $USER, $DB, $OUTPUT, $PAGE;
         $sitecontext = context_system::instance();
 
-        // Blog renderer
+        // Blog renderer.
         $output = $PAGE->get_renderer('blog');
 
         $page  = optional_param('blogpage', 0, PARAM_INT);
@@ -711,7 +723,7 @@ class blog_listing {
         echo $OUTPUT->render($pagingbar);
 
         if (has_capability('moodle/blog:create', $sitecontext)) {
-            //the user's blog is enabled and they are viewing their own blog
+            // The user's blog is enabled and they are viewing their own blog.
             $userid = optional_param('userid', null, PARAM_INT);
 
             if (empty($userid) || (!empty($userid) && $userid == $USER->id)) {
@@ -744,7 +756,7 @@ class blog_listing {
             foreach ($entries as $entry) {
                 $blogentry = new blog_entry(null, $entry);
 
-                // Get the required blog entry data to render it
+                // Get the required blog entry data to render it.
                 $blogentry->prepare_render();
                 echo $output->render($blogentry);
 
@@ -762,7 +774,7 @@ class blog_listing {
         }
     }
 
-    /// Find the base url from $_GET variables, for print_paging_bar
+    // Find the base url from $_GET variables, for print_paging_bar.
     public function get_baseurl() {
         $getcopy  = $_GET;
 
@@ -897,11 +909,13 @@ class blog_filter_context extends blog_filter {
             $this->type = $type;
         }
 
-        $this->availabletypes = array('site' => get_string('site'), 'course' => get_string('course'), 'module' => get_string('activity'));
+        $this->availabletypes = array('site' => get_string('site'),
+                                      'course' => get_string('course'),
+                                      'module' => get_string('activity'));
 
         switch ($this->type) {
             case 'course': // Careful of site course!
-                // Ignore course filter if blog associations are not enabled
+                // Ignore course filter if blog associations are not enabled.
                 if ($this->id != $SITE->id && !empty($CFG->useblogassociations)) {
                     $this->overrides = array('site');
                     $context = context_course::instance($this->id);
@@ -910,11 +924,11 @@ class blog_filter_context extends blog_filter {
                     $this->conditions[] = 'ba.contextid = '.$context->id;
                     break;
                 } else {
-                    // We are dealing with the site course, do not break from the current case
+                    // We are dealing with the site course, do not break from the current case.
                 }
 
             case 'site':
-                // No special constraints
+                // No special constraints.
                 break;
             case 'module':
                 if (!empty($CFG->useblogassociations)) {
@@ -961,7 +975,7 @@ class blog_filter_user extends blog_filter {
             $this->params = array($this->id);
             $this->overrides = array('group');
 
-        } elseif ($this->type == 'group') {
+        } else if ($this->type == 'group') {
             $this->overrides = array('course', 'site');
 
             $this->tables['gm'] = 'groups_members';
@@ -969,7 +983,7 @@ class blog_filter_user extends blog_filter {
             $this->conditions[] = 'gm.groupid = ?';
             $this->params[]     = $this->id;
 
-            if (!empty($CFG->useblogassociations)) {  // only show blog entries associated with this course
+            if (!empty($CFG->useblogassociations)) {  // Only show blog entries associated with this course.
                 $coursecontext     = context_course::instance($DB->get_field('groups', 'courseid', array('id' => $this->id)));
                 $this->tables['ba'] = 'blog_association';
                 $this->conditions[] = 'gm.groupid = ?';
@@ -1068,7 +1082,8 @@ class blog_entry_attachment implements renderable {
 
         $this->file = $file;
         $this->filename = $file->get_filename();
-        $this->url = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog/attachment/'.$entryid.'/'.$this->filename);
+        $this->url = file_encode_url($CFG->wwwroot . '/pluginfile.php',
+                                     '/' . SYSCONTEXTID . '/blog/attachment/' . $entryid . '/' . $this->filename);
     }
 
 }
index e3d82fc..59aea44 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -66,7 +65,7 @@ if (empty($CFG->enableblogs)) {
 // The preference is site wide not blog specific. Hence user should have permissions in site level.
 require_capability('moodle/blog:view', $sitecontext);
 
-/// If data submitted, then process and store.
+// If data submitted, then process and store.
 
 $mform = new blog_preferences_form('preferences.php');
 $mform->set_data(array('pagesize' => get_user_preferences('blogpagesize')));
@@ -80,7 +79,7 @@ if (!$mform->is_cancelled() && $data = $mform->get_data()) {
     set_user_preference('blogpagesize', $pagesize);
 }
 
-if ($mform->is_cancelled()){
+if ($mform->is_cancelled()) {
     redirect($CFG->wwwroot . '/blog/index.php');
 }
 
index d5c5005..12edfed 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -26,7 +25,7 @@
  */
 
 if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
+    die('Direct access to this script is forbidden.');    //  It must be included from a Moodle page.
 }
 
 require_once($CFG->libdir.'/formslib.php');
index af1724d..832d4b1 100644 (file)
@@ -63,7 +63,9 @@ class core_blog_renderer extends plugin_renderer_base {
         $o .= $this->output->container_start('topic starter header clearfix');
 
         // Title.
-        $titlelink =  html_writer::link(new moodle_url('/blog/index.php', array('entryid' => $entry->id)), format_string($entry->subject));
+        $titlelink = html_writer::link(new moodle_url('/blog/index.php',
+                                                       array('entryid' => $entry->id)),
+                                                       format_string($entry->subject));
         $o .= $this->output->container($titlelink, 'subject');
 
         // Post by.
@@ -244,9 +246,14 @@ class core_blog_renderer extends plugin_renderer_base {
             $o = html_writer::empty_tag('img', $attrs);
             $class = 'attachedimages';
         } else {
-            $image = $this->output->pix_icon(file_file_icon($attachment->file), $attachment->filename, 'moodle', array('class'=>'icon'));
+            $image = $this->output->pix_icon(file_file_icon($attachment->file),
+                                             $attachment->filename,
+                                             'moodle',
+                                             array('class' => 'icon'));
             $o = html_writer::link($attachment->url, $image);
-            $o .= format_text(html_writer::link($attachment->url, $attachment->filename), FORMAT_HTML, array('context' => $syscontext));
+            $o .= format_text(html_writer::link($attachment->url, $attachment->filename),
+                              FORMAT_HTML,
+                              array('context' => $syscontext));
             $class = 'attachments';
         }
 
index 12854d6..3f26eb0 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -97,9 +96,6 @@ function blog_rss_print_link($context, $filtertype, $filterselect=0, $tagid=0, $
 function blog_rss_add_http_header($context, $title, $filtertype, $filterselect=0, $tagid=0) {
     global $PAGE, $USER, $CFG;
 
-    //$componentname = 'blog';
-    //rss_add_http_header($context, $componentname, $filterselect, $title);
-
     if (!isloggedin()) {
         $userid = $CFG->siteguest;
     } else {
@@ -159,7 +155,7 @@ function blog_rss_get_feed($context, $args) {
 
     if ($CFG->bloglevel == BLOG_SITE_LEVEL) {
         if (isguestuser()) {
-            debugging(get_string('nopermissiontoshow','error'));
+            debugging(get_string('nopermissiontoshow', 'error'));
             return '';
         }
     }
@@ -170,7 +166,7 @@ function blog_rss_get_feed($context, $args) {
     }
 
     $type  = clean_param($args[3], PARAM_ALPHA);
-    $id = clean_param($args[4], PARAM_INT);  // could be groupid / courseid  / userid  depending on $type
+    $id = clean_param($args[4], PARAM_INT);  // Could be groupid / courseid  / userid  depending on $type.
 
     $tagid=0;
     if ($args[5] != 'rss.xml') {
@@ -183,14 +179,13 @@ function blog_rss_get_feed($context, $args) {
 
     if (file_exists($filename)) {
         if (filemtime($filename) + 3600 > time()) {
-            return $filename;   // It's already done so we return cached version
+            return $filename;   // It's already done so we return cached version.
         }
     }
 
     $courseid = $groupid = $userid = null;
     switch ($type) {
         case 'site':
-            //$siteid = $id;
             break;
         case 'course':
             $courseid = $id;
@@ -203,26 +198,26 @@ function blog_rss_get_feed($context, $args) {
             break;
     }
 
-    // Get all the entries from the database
+    // Get all the entries from the database.
     require_once($CFG->dirroot .'/blog/locallib.php');
     $blogheaders = blog_get_headers($courseid, $groupid, $userid, $tagid);
 
     $bloglisting = new blog_listing($blogheaders['filters']);
     $blogentries = $bloglisting->get_entries();
 
-    // Now generate an array of RSS items
+    // Now generate an array of RSS items.
     if ($blogentries) {
         $items = array();
-        foreach ($blogentries as $blog_entry) {
-            $item = NULL;
-            $item->author = fullname($DB->get_record('user', array('id'=>$blog_entry->userid))); // TODO: this is slow
-            $item->title = $blog_entry->subject;
-            $item->pubdate = $blog_entry->lastmodified;
-            $item->link = $CFG->wwwroot.'/blog/index.php?entryid='.$blog_entry->id;
-            $summary = file_rewrite_pluginfile_urls($blog_entry->summary, 'pluginfile.php',
-                $sitecontext->id, 'blog', 'post', $blog_entry->id);
-            $item->description = format_text($summary, $blog_entry->format);
-            if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_array('post', $blog_entry->id)) ) {
+        foreach ($blogentries as $blogentry) {
+            $item = null;
+            $item->author = fullname($DB->get_record('user', array('id' => $blogentry->userid))); // TODO: this is slow.
+            $item->title = $blogentry->subject;
+            $item->pubdate = $blogentry->lastmodified;
+            $item->link = $CFG->wwwroot.'/blog/index.php?entryid='.$blogentry->id;
+            $summary = file_rewrite_pluginfile_urls($blogentry->summary, 'pluginfile.php',
+                $sitecontext->id, 'blog', 'post', $blogentry->id);
+            $item->description = format_text($summary, $blogentry->format);
+            if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_array('post', $blogentry->id)) ) {
                 if ($blogtags) {
                     $item->tags = $blogtags;
                 }
@@ -230,12 +225,12 @@ function blog_rss_get_feed($context, $args) {
             }
             $items[] = $item;
         }
-        $articles = rss_add_items($items);   /// Change structure to XML
+        $articles = rss_add_items($items);   // Change structure to XML.
     } else {
         $articles = '';
     }
 
-/// Get header and footer information
+    // Get header and footer information.
 
     switch ($type) {
         case 'user':
@@ -250,7 +245,7 @@ function blog_rss_get_feed($context, $args) {
             break;
         case 'group':
             $group = groups_get_group($id);
-            $info = $group->name; //TODO: $DB->get_field('groups', 'name', array('id'=>$id))
+            $info = $group->name; // TODO: $DB->get_field('groups', 'name', array('id'=>$id)).
             break;
         default:
             $info = '';
@@ -261,18 +256,18 @@ function blog_rss_get_feed($context, $args) {
         $info .= ': '.$DB->get_field('tags', 'text', array('id'=>$tagid));
     }
 
-    $header = rss_standard_header(get_string($type.'blog','blog', $info),
+    $header = rss_standard_header(get_string($type.'blog', 'blog', $info),
                                   $CFG->wwwroot.'/blog/index.php',
-                                  get_string('intro','blog'));
+                                  get_string('intro', 'blog'));
 
     $footer = rss_standard_footer();
 
     // Save the XML contents to file.
     $rssdata = $header.$articles.$footer;
-    if (blog_rss_save_file($type,$id,$tagid,$rssdata)) {
+    if (blog_rss_save_file($type, $id, $tagid, $rssdata)) {
         return $filename;
     } else {
-        return false;   // Couldn't find it or make it
+        return false;   // Couldn't find it or make it.
     }
 }
 
@@ -308,12 +303,12 @@ function blog_rss_save_file($type, $id, $tagid=0, $contents='') {
 
     $status = true;
 
-    //blog creates some additional dirs within the rss cache so make sure they all exist
+    // Blog creates some additional dirs within the rss cache so make sure they all exist.
     make_cache_directory('rss/blog');
     make_cache_directory('rss/blog/'.$type);
 
     $filename = blog_rss_file_name($type, $id, $tagid);
-    $expandfilename = false; //we're supplying a full file path
+    $expandfilename = false; // We are supplying a full file path.
     $status = rss_save_file('blog', $filename, $contents, $expandfilename);
 
     return $status;
index 2db2265..06b1f07 100644 (file)
@@ -32,7 +32,7 @@ require_once($CFG->dirroot . '/blog/lib.php');
  */
 class core_bloglib_testcase extends advanced_testcase {
 
-    private $courseid; // To store important ids to be used in tests
+    private $courseid;
     private $cmid;
     private $groupid;
     private $userid;
@@ -45,22 +45,22 @@ class core_bloglib_testcase extends advanced_testcase {
 
         $this->resetAfterTest();
 
-        // Create default course
+        // Create default course.
         $course = $this->getDataGenerator()->create_course(array('category'=>1, 'shortname'=>'ANON'));
         $this->assertNotEmpty($course);
         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
         $this->assertNotEmpty($page);
 
-        // Create default group
+        // Create default group.
         $group = new stdClass();
         $group->courseid = $course->id;
         $group->name = 'ANON';
         $group->id = $DB->insert_record('groups', $group);
 
-        // Create default user
+        // Create default user.
         $user = $this->getDataGenerator()->create_user(array('username'=>'testuser', 'firstname'=>'Jimmy', 'lastname'=>'Kinnon'));
 
-        // Create default tag
+        // Create default tag.
         $tag = new stdClass();
         $tag->userid = $user->id;
         $tag->name = 'testtagname';
@@ -68,14 +68,14 @@ class core_bloglib_testcase extends advanced_testcase {
         $tag->tagtype = 'official';
         $tag->id = $DB->insert_record('tag', $tag);
 
-        // Create default post
+        // Create default post.
         $post = new stdClass();
         $post->userid = $user->id;
         $post->groupid = $group->id;
         $post->content = 'test post content text';
         $post->id = $DB->insert_record('post', $post);
 
-        // Grab important ids
+        // Grab important ids.
         $this->courseid = $course->id;
         $this->cmid = $page->cmid;
         $this->groupid  = $group->id;
@@ -88,7 +88,7 @@ class core_bloglib_testcase extends advanced_testcase {
     public function test_overrides() {
         global $SITE;
 
-        // Try all the filters at once: Only the entry filter is active
+        // Try all the filters at once: Only the entry filter is active.
         $filters = array('site' => $SITE->id, 'course' => $this->courseid, 'module' => $this->cmid,
             'group' => $this->groupid, 'user' => $this->userid, 'tag' => $this->tagid, 'entry' => $this->postid);
         $blog_listing = new blog_listing($filters);
@@ -100,7 +100,7 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertFalse(array_key_exists('tag', $blog_listing->filters));
         $this->assertTrue(array_key_exists('entry', $blog_listing->filters));
 
-        // Again, but without the entry filter: This time, the tag, user and module filters are active
+        // Again, but without the entry filter: This time, the tag, user and module filters are active.
         $filters = array('site' => $SITE->id, 'course' => $this->courseid, 'module' => $this->cmid,
             'group' => $this->groupid, 'user' => $this->userid, 'tag' => $this->postid);
         $blog_listing = new blog_listing($filters);
@@ -111,7 +111,7 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertTrue(array_key_exists('user', $blog_listing->filters));
         $this->assertTrue(array_key_exists('tag', $blog_listing->filters));
 
-        // We should get the same result by removing the 3 inactive filters: site, course and group:
+        // We should get the same result by removing the 3 inactive filters: site, course and group.
         $filters = array('module' => $this->cmid, 'user' => $this->userid, 'tag' => $this->tagid);
         $blog_listing = new blog_listing($filters);
         $this->assertFalse(array_key_exists('site', $blog_listing->filters));
@@ -128,26 +128,26 @@ class core_bloglib_testcase extends advanced_testcase {
 
     public function test_blog_get_headers_case_1() {
         global $CFG, $PAGE, $OUTPUT;
-        $blog_headers = blog_get_headers();
-        $this->assertEquals($blog_headers['heading'], get_string('siteblog', 'blog', 'phpunit'));
+        $blogheaders = blog_get_headers();
+        $this->assertEquals($blogheaders['heading'], get_string('siteblog', 'blog', 'phpunit'));
     }
 
     public function test_blog_get_headers_case_6() {
         global $CFG, $PAGE, $OUTPUT;
-        $blog_headers = blog_get_headers($this->courseid, NULL, $this->userid);
-        $this->assertNotEquals($blog_headers['heading'], '');
+        $blogheaders = blog_get_headers($this->courseid, null, $this->userid);
+        $this->assertNotEquals($blogheaders['heading'], '');
     }
 
     public function test_blog_get_headers_case_7() {
         global $CFG, $PAGE, $OUTPUT;
-        $blog_headers = blog_get_headers(NULL, $this->groupid);
-        $this->assertNotEquals($blog_headers['heading'], '');
+        $blogheaders = blog_get_headers(null, $this->groupid);
+        $this->assertNotEquals($blogheaders['heading'], '');
     }
 
     public function test_blog_get_headers_case_10() {
         global $CFG, $PAGE, $OUTPUT;
-        $blog_headers = blog_get_headers($this->courseid);
-        $this->assertNotEquals($blog_headers['heading'], '');
+        $blogheaders = blog_get_headers($this->courseid);
+        $this->assertNotEquals($blogheaders['heading'], '');
     }
 
     /**
index c9c35db..827b137 100644 (file)
@@ -52,7 +52,7 @@ $locks = cache_administration_helper::get_lock_summaries();
 
 $title = new lang_string('cacheadmin', 'cache');
 $mform = null;
-$notification = null;
+$notifications = array();
 $notifysuccess = true;
 
 if (!empty($action) && confirm_sesskey()) {
@@ -110,10 +110,10 @@ if (!empty($action) && confirm_sesskey()) {
 
             if (!array_key_exists($store, $stores)) {
                 $notifysuccess = false;
-                $notification = get_string('invalidstore', 'cache');
+                $notifications[] = array(get_string('invalidstore', 'cache'), false);
             } else if ($stores[$store]['mappings'] > 0) {
                 $notifysuccess = false;
-                $notification = get_string('deletestorehasmappings', 'cache');
+                $notifications[] = array(get_string('deletestorehasmappings', 'cache'), false);
             }
 
             if ($notifysuccess) {
@@ -250,10 +250,10 @@ if (!empty($action) && confirm_sesskey()) {
             $confirm = optional_param('confirm', false, PARAM_BOOL);
             if (!array_key_exists($lock, $locks)) {
                 $notifysuccess = false;
-                $notification = get_string('invalidlock', 'cache');
+                $notifications[] = array(get_string('invalidlock', 'cache'), false);
             } else if ($locks[$lock]['uses'] > 0) {
                 $notifysuccess = false;
-                $notification = get_string('deletelockhasuses', 'cache');
+                $notifications[] = array(get_string('deletelockhasuses', 'cache'), false);
             }
             if ($notifysuccess) {
                 if (!$confirm) {
@@ -280,6 +280,8 @@ if (!empty($action) && confirm_sesskey()) {
     }
 }
 
+$notifications = array_merge($notifications, cache_helper::warnings($stores));
+
 $PAGE->set_title($title);
 $PAGE->set_heading($SITE->fullname);
 /* @var core_cache_renderer $renderer */
@@ -287,10 +289,7 @@ $renderer = $PAGE->get_renderer('core_cache');
 
 echo $renderer->header();
 echo $renderer->heading($title);
-
-if (!is_null($notification)) {
-    echo $renderer->notification($notification, ($notifysuccess)?'notifysuccess' : 'notifyproblem');
-}
+echo $renderer->notifications($notifications);
 
 if ($mform instanceof moodleform) {
     $mform->display();
index 02bd243..dd150b7 100644 (file)
@@ -736,4 +736,28 @@ class cache_helper {
         }
         return $stores;
     }
+
+    /**
+     * Returns an array of warnings from the cache API.
+     *
+     * The warning returned here are for things like conflicting store instance configurations etc.
+     * These get shown on the admin notifications page for example.
+     *
+     * @param array|null $stores An array of stores to get warnings for, or null for all.
+     * @return string[]
+     */
+    public static function warnings(array $stores = null) {
+        global $CFG;
+        if ($stores === null) {
+            require_once($CFG->dirroot.'/cache/locallib.php');
+            $stores = cache_administration_helper::get_store_instance_summaries();
+        }
+        $warnings = array();
+        foreach ($stores as $store) {
+            if (!empty($store['warnings'])) {
+                $warnings = array_merge($warnings, $store['warnings']);
+            }
+        }
+        return $warnings;
+    }
 }
index b5cfcc1..464df84 100644 (file)
@@ -365,4 +365,20 @@ abstract class cache_store implements cache_store_interface {
     public static function initialise_unit_test_instance(cache_definition $definition) {
         return static::initialise_test_instance($definition);
     }
+
+    /**
+     * Can be overridden to return any warnings this store instance should make to the admin.
+     *
+     * This should be used to notify things like configuration conflicts etc.
+     * The warnings returned here will be displayed on the cache configuration screen.
+     *
+     * @return array[] Returns an array of arrays with the format:
+     *     $notifications = array(
+     *         array('This is a success message', true),
+     *         array('This is a failure message', false),
+     *     );
+     */
+    public function get_warnings() {
+        return array();
+    }
 }
index 042b891..58dcdd8 100644 (file)
@@ -709,7 +709,8 @@ abstract class cache_administration_helper extends cache_helper {
                     'nativelocking' => ($store instanceof cache_is_lockable),
                     'keyawareness' => ($store instanceof cache_is_key_aware),
                     'searchable' => ($store instanceof cache_is_searchable)
-                )
+                ),
+                'warnings' => $store->get_warnings()
             );
             if (empty($details['default'])) {
                 $return[$name] = $record;
index 685b838..1ccb4bb 100644 (file)
@@ -235,6 +235,8 @@ class core_cache_renderer extends plugin_renderer_base {
         );
         $table->data = array();
 
+        core_collator::asort_array_of_arrays_by_key($definitions, 'name');
+
         $none = new lang_string('none', 'cache');
         foreach ($definitions as $id => $definition) {
             $actions = cache_administration_helper::get_definition_actions($context, $definition);
@@ -372,4 +374,30 @@ class core_cache_renderer extends plugin_renderer_base {
         $html .= html_writer::end_tag('div');
         return $html;
     }
+
+    /**
+     * Renders an array of notifications for the cache configuration screen.
+     *
+     * Takes an array of notifications with the form:
+     * $notifications = array(
+     *     array('This is a success message', true),
+     *     array('This is a failure message', false),
+     * );
+     *
+     * @param array $notifications
+     * @return string
+     */
+    public function notifications(array $notifications = array()) {
+        if (count($notifications) === 0) {
+            // There are no notifications to render.
+            return '';
+        }
+        $html = html_writer::start_div('notifications');
+        foreach ($notifications as $notification) {
+            list($message, $notifysuccess) = $notification;
+            $html .= $this->notification($message, ($notifysuccess) ? 'notifysuccess' : 'notifyproblem');
+        }
+        $html .= html_writer::end_div();
+        return $html;
+    }
 }
\ No newline at end of file
index 78d3947..26f1c9e 100644 (file)
@@ -64,6 +64,7 @@ For example:
 server.url.com
 ipaddress:port
 </pre>';
+$string['sessionhandlerconflict'] = 'Warning: A memcache instance ({$a}) has being configured to use the same memcached server as sessions. Purging all caches will lead to sessions also being purged.';
 $string['testservers'] = 'Test servers';
 $string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
 If no port is provided then the default port (11211) is used.';
\ No newline at end of file
index 3a7da5f..036da56 100644 (file)
@@ -573,4 +573,28 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
     public function my_name() {
         return $this->name;
     }
+
+    /**
+     * Used to notify of configuration conflicts.
+     *
+     * The warnings returned here will be displayed on the cache configuration screen.
+     *
+     * @return string[] Returns an array of warnings (strings)
+     */
+    public function get_warnings() {
+        global $CFG;
+        $warnings = array();
+        if (isset($CFG->session_memcached_save_path) && count($this->servers)) {
+            $bits = explode(':', $CFG->session_memcached_save_path, 3);
+            $host = array_shift($bits);
+            $port = (count($bits)) ? array_shift($bits) : '11211';
+            foreach ($this->servers as $server) {
+                if ($server[0] === $host && $server[1] === $port) {
+                    $warnings[] = get_string('sessionhandlerconflict', 'cachestore_memcache', $this->my_name());
+                    break;
+                }
+            }
+        }
+        return $warnings;
+    }
 }
index 5d35c1c..97fe5b6 100644 (file)
@@ -78,6 +78,7 @@ For example:
 server.url.com
 ipaddress:port
 </pre>';
+$string['sessionhandlerconflict'] = 'Warning: A memcached instance ({$a}) has being configured to use the same memcached server as sessions. Purging all caches will lead to sessions also being purged.';
 $string['testservers'] = 'Test servers';
 $string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
 If no port is provided then the default port (11211) is used.';
index 284f3c8..b379b26 100644 (file)
@@ -668,4 +668,29 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
     public function my_name() {
         return $this->name;
     }
+
+    /**
+     * Used to notify of configuration conflicts.
+     *
+     * The warnings returned here will be displayed on the cache configuration screen.
+     *
+     * @return string[] Returns an array of warnings (strings)
+     */
+    public function get_warnings() {
+        global $CFG;
+        $warnings = array();
+        if (isset($CFG->session_memcached_save_path) && count($this->servers)) {
+            $bits = explode(':', $CFG->session_memcached_save_path, 3);
+            $host = array_shift($bits);
+            $port = (count($bits)) ? array_shift($bits) : '11211';
+
+            foreach ($this->servers as $server) {
+                if ((string)$server[0] === $host && (string)$server[1] === $port) {
+                    $warnings[] = get_string('sessionhandlerconflict', 'cachestore_memcached', $this->my_name());
+                    break;
+                }
+            }
+        }
+        return $warnings;
+    }
 }
index 69a8924..6a1bdf9 100644 (file)
@@ -120,7 +120,7 @@ class event_form extends moodleform {
         $group[] =& $mform->createElement('radio', 'duration', null, get_string('durationuntil', 'calendar'), 1);
         $group[] =& $mform->createElement('date_time_selector', 'timedurationuntil', '');
         $group[] =& $mform->createElement('radio', 'duration', null, get_string('durationminutes', 'calendar'), 2);
-        $group[] =& $mform->createElement('text', 'timedurationminutes', null);
+        $group[] =& $mform->createElement('text', 'timedurationminutes', get_string('durationminutes', 'calendar'));
 
         $mform->addGroup($group, 'durationgroup', '', '<br />', false);
 
index c3304fa..dcedc54 100644 (file)
@@ -3201,10 +3201,10 @@ function calendar_cron() {
         mtrace("Updating calendar subscription {$sub->name} in course {$sub->courseid}");
         try {
             $log = calendar_update_subscription_events($sub->id);
+            mtrace(trim(strip_tags($log)));
         } catch (moodle_exception $ex) {
-
+            mtrace('Error updating calendar subscription: ' . $ex->getMessage());
         }
-        mtrace(trim(strip_tags($log)));
     }
 
     mtrace('Finished updating calendar subscriptions.');
index c405b96..2a61b5e 100644 (file)
@@ -35,9 +35,11 @@ require_once($CFG->dirroot . '/calendar/lib.php');
  */
 class core_calendar_lib_testcase extends advanced_testcase {
 
-    public function test_calendar_get_course_cached() {
+    protected function setUp() {
         $this->resetAfterTest(true);
+    }
 
+    public function test_calendar_get_course_cached() {
         // Setup some test courses.
         $course1 = $this->getDataGenerator()->create_course();
         $course2 = $this->getDataGenerator()->create_course();
@@ -68,4 +70,50 @@ class core_calendar_lib_testcase extends advanced_testcase {
         $this->assertEquals($course3->shortname, $cachedcourse3->shortname);
         $this->assertEquals($course3->fullname, $cachedcourse3->fullname);
     }
+
+    /**
+     * Test calendar cron with a working subscription URL.
+     */
+    public function test_calendar_cron_working_url() {
+        global $CFG;
+        require_once($CFG->dirroot . '/lib/cronlib.php');
+
+        // Moodle ICal URL (moodle.org events).
+        $presetwhat = 'all';
+        $presettime = 'recentupcoming';
+        $userid = 1;
+        $authtoken = 'a8bcfee2fb868a05357f650bd65dc0699b026524';
+        $subscriptionurl = 'https://moodle.org/calendar/export_execute.php'
+                . '?preset_what='.$presetwhat.'&preset_time='.$presettime.'&userid='.$userid.'&authtoken='.$authtoken;
+
+        $subscription = new stdClass();
+        $subscription->eventtype = 'site';
+        $subscription->name = 'test';
+        $subscription->url = $subscriptionurl;
+        $subscription->pollinterval = 86400;
+        $subscription->lastupdated = 0;
+        calendar_add_subscription($subscription);
+
+        $this->expectOutputRegex('/Events imported: .* Events updated:/');
+        calendar_cron();
+    }
+
+    /**
+     * Test calendar cron with a broken subscription URL.
+     */
+    public function test_calendar_cron_broken_url() {
+        global $CFG;
+        require_once($CFG->dirroot . '/lib/cronlib.php');
+
+        $subscription = new stdClass();
+        $subscription->eventtype = 'site';
+        $subscription->name = 'test';
+        $subscription->url = 'brokenurl';
+        $subscription->pollinterval = 86400;
+        $subscription->lastupdated = 0;
+        calendar_add_subscription($subscription);
+
+        $this->expectOutputRegex('/Error updating calendar subscription: The given iCal URL is invalid/');
+        calendar_cron();
+    }
 }
index 9a02d19..14de49b 100644 (file)
@@ -221,12 +221,22 @@ class helper {
             $actions['resortbyname'] = array(
                 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'name')),
                 'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
-                'string' => new \lang_string('resortsubcategoriesbyname', 'moodle')
+                'string' => new \lang_string('resortsubcategoriesby', 'moodle' , get_string('categoryname'))
+            );
+            $actions['resortbynamedesc'] = array(
+                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'namedesc')),
+                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
+                'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('categoryname'))
             );
             $actions['resortbyidnumber'] = array(
                 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumber')),
                 'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
-                'string' => new \lang_string('resortsubcategoriesbyidnumber', 'moodle')
+                'string' => new \lang_string('resortsubcategoriesby', 'moodle', get_string('idnumbercoursecategory'))
+            );
+            $actions['resortbyidnumberdesc'] = array(
+                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumberdesc')),
+                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
+                'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('idnumbercoursecategory'))
             );
         }
 
index 688e304..6ed18dd 100644 (file)
@@ -407,8 +407,10 @@ class core_course_management_renderer extends plugin_renderer_base {
             $form .= html_writer::div(
                 html_writer::select(
                     array(
-                        'name' => get_string('sortcategoriesbyname'),
-                        'idnumber' => get_string('sortcategoriesbyidnumber'),
+                        'name' => get_string('sortbyx', 'moodle', get_string('categoryname')),
+                        'namedesc' => get_string('sortbyxreverse', 'moodle', get_string('categoryname')),
+                        'idnumber' => get_string('sortbyx', 'moodle', get_string('idnumbercoursecategory')),
+                        'idnumberdesc' => get_string('sortbyxreverse' , 'moodle' , get_string('idnumbercoursecategory')),
                         'none' => get_string('dontsortcategories')
                     ),
                     'resortcategoriesby',
@@ -420,9 +422,14 @@ class core_course_management_renderer extends plugin_renderer_base {
             $form .= html_writer::div(
                 html_writer::select(
                     array(
-                        'fullname' => get_string('sortcoursesbyfullname'),
-                        'shortname' => get_string('sortcoursesbyshortname'),
-                        'idnumber' => get_string('sortcoursesbyidnumber'),
+                        'fullname' => get_string('sortbyx', 'moodle', get_string('fullnamecourse')),
+                        'fullnamedesc' => get_string('sortbyxreverse', 'moodle', get_string('fullnamecourse')),
+                        'shortname' => get_string('sortbyx', 'moodle', get_string('shortnamecourse')),
+                        'shortnamedesc' => get_string('sortbyxreverse', 'moodle', get_string('shortnamecourse')),
+                        'idnumber' => get_string('sortbyx', 'moodle', get_string('idnumbercourse')),
+                        'idnumberdesc' => get_string('sortbyxreverse', 'moodle', get_string('idnumbercourse')),
+                        'timecreated' => get_string('sortbyx', 'moodle', get_string('timecreatedcourse')),
+                        'timecreateddesc' => get_string('sortbyxreverse', 'moodle', get_string('timecreatedcourse')),
                         'none' => get_string('dontsortcourses')
                     ),
                     'resortcoursesby',
@@ -676,12 +683,38 @@ class core_course_management_renderer extends plugin_renderer_base {
             $params['sesskey'] = sesskey();
             $baseurl = new moodle_url('/course/management.php', $params);
             $fullnameurl = new moodle_url($baseurl, array('resort' => 'fullname'));
+            $fullnameurldesc = new moodle_url($baseurl, array('resort' => 'fullnamedesc'));
             $shortnameurl = new moodle_url($baseurl, array('resort' => 'shortname'));
+            $shortnameurldesc = new moodle_url($baseurl, array('resort' => 'shortnamedesc'));
             $idnumberurl = new moodle_url($baseurl, array('resort' => 'idnumber'));
+            $idnumberdescurl = new moodle_url($baseurl, array('resort' => 'idnumberdesc'));
+            $timecreatedurl = new moodle_url($baseurl, array('resort' => 'timecreated'));
+            $timecreateddescurl = new moodle_url($baseurl, array('resort' => 'timecreateddesc'));
             $menu = new action_menu(array(
-                new action_menu_link_secondary($fullnameurl, null, get_string('resortbyfullname')),
-                new action_menu_link_secondary($shortnameurl, null, get_string('resortbyshortname')),
-                new action_menu_link_secondary($idnumberurl, null, get_string('resortbyidnumber'))
+                new action_menu_link_secondary($fullnameurl,
+                                               null,
+                                               get_string('sortbyx', 'moodle', get_string('fullnamecourse'))),
+                new action_menu_link_secondary($fullnameurldesc,
+                                               null,
+                                               get_string('sortbyxreverse', 'moodle', get_string('fullnamecourse'))),
+                new action_menu_link_secondary($shortnameurl,
+                                               null,
+                                               get_string('sortbyx', 'moodle', get_string('shortnamecourse'))),
+                new action_menu_link_secondary($shortnameurldesc,
+                                               null,
+                                               get_string('sortbyxreverse', 'moodle', get_string('shortnamecourse'))),
+                new action_menu_link_secondary($idnumberurl,
+                                               null,
+                                               get_string('sortbyx', 'moodle', get_string('idnumbercourse'))),
+                new action_menu_link_secondary($idnumberdescurl,
+                                               null,
+                                               get_string('sortbyxreverse', 'moodle', get_string('idnumbercourse'))),
+                new action_menu_link_secondary($timecreatedurl,
+                                               null,
+                                               get_string('sortbyx', 'moodle', get_string('timecreatedcourse'))),
+                new action_menu_link_secondary($timecreateddescurl,
+                                               null,
+                                               get_string('sortbyxreverse', 'moodle', get_string('timecreatedcourse')))
             ));
             $menu->set_menu_trigger(get_string('resortcourses'));
             $actions[] = $this->render($menu);
@@ -1203,6 +1236,51 @@ class core_course_management_renderer extends plugin_renderer_base {
         return html_writer::span(join('', $actions), 'course-item-actions item-actions');
     }
 
+    /**
+     * Renders html to display a course search form
+     *
+     * @param string $value default value to populate the search field
+     * @param string $format display format - 'plain' (default), 'short' or 'navbar'
+     * @return string
+     */
+    public function course_search_form($value = '', $format = 'plain') {
+        static $count = 0;
+        $formid = 'coursesearch';
+        if ((++$count) > 1) {
+            $formid .= $count;
+        }
+
+        switch ($format) {
+            case 'navbar' :
+                $formid = 'coursesearchnavbar';
+                $inputid = 'navsearchbox';
+                $inputsize = 20;
+                break;
+            case 'short' :
+                $inputid = 'shortsearchbox';
+                $inputsize = 12;
+                break;
+            default :
+                $inputid = 'coursesearchbox';
+                $inputsize = 30;
+        }
+
+        $strsearchcourses = get_string("searchcourses");
+        $searchurl = new moodle_url('/course/management.php');
+
+        $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get'));
+        $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset'));
+        $output .= html_writer::tag('label', $strsearchcourses.': ', array('for' => $inputid));
+        $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid,
+            'size' => $inputsize, 'name' => 'search', 'value' => s($value)));
+        $output .= html_writer::empty_tag('input', array('type' => 'submit',
+            'value' => get_string('go')));
+        $output .= html_writer::end_tag('fieldset');
+        $output .= html_writer::end_tag('form');
+
+        return $output;
+    }
+
     /**
      * Creates access hidden skip to links for the displayed sections.
      *
index ffde87b..1915299 100644 (file)
@@ -2517,7 +2517,8 @@ function create_course($data, $editoroptions = NULL) {
         }
     }
 
-    $data->timecreated  = time();
+    // Check if timecreated is given.
+    $data->timecreated  = !empty($data->timecreated) ? $data->timecreated : time();
     $data->timemodified = $data->timecreated;
 
     // place at beginning of any category
index c48158d..85a2494 100644 (file)
@@ -354,10 +354,14 @@ if ($action !== false && confirm_sesskey()) {
                     // They're not sorting anything.
                     break;
                 }
-                if (!in_array($sortcategoriesby, array('idnumber', 'name'))) {
+                if (!in_array($sortcategoriesby, array('idnumber', 'idnumberdesc',
+                                                       'name', 'namedesc'))) {
                     $sortcategoriesby = false;
                 }
-                if (!in_array($sortcoursesby, array('idnumber', 'fullname', 'shortname'))) {
+                if (!in_array($sortcoursesby, array('timecreated', 'timecreateddesc',
+                                                    'idnumber', 'idnumberdesc',
+                                                    'fullname', 'fullnamedesc',
+                                                    'shortname', 'shortnamedesc'))) {
                     $sortcoursesby = false;
                 }
 
@@ -508,4 +512,6 @@ echo $renderer->grid_end();
 
 // End of the management form.
 echo $renderer->management_form_end();
+echo $renderer->course_search_form($search);
+
 echo $renderer->footer();
index 96f7359..55d242f 100644 (file)
@@ -1098,8 +1098,9 @@ class core_course_renderer extends plugin_renderer_base {
             foreach ($moduleshtml as $modnumber => $modulehtml) {
                 if ($ismoving) {
                     $movingurl = new moodle_url('/course/mod.php', array('moveto' => $modnumber, 'sesskey' => sesskey()));
-                    $sectionoutput .= html_writer::tag('li', html_writer::link($movingurl, $this->output->render($movingpix)),
-                            array('class' => 'movehere', 'title' => $strmovefull));
+                    $sectionoutput .= html_writer::tag('li',
+                            html_writer::link($movingurl, $this->output->render($movingpix), array('title' => $strmovefull)),
+                            array('class' => 'movehere'));
                 }
 
                 $sectionoutput .= $modulehtml;
@@ -1107,8 +1108,9 @@ class core_course_renderer extends plugin_renderer_base {
 
             if ($ismoving) {
                 $movingurl = new moodle_url('/course/mod.php', array('movetosection' => $section->id, 'sesskey' => sesskey()));
-                $sectionoutput .= html_writer::tag('li', html_writer::link($movingurl, $this->output->render($movingpix)),
-                        array('class' => 'movehere', 'title' => $strmovefull));
+                $sectionoutput .= html_writer::tag('li',
+                        html_writer::link($movingurl, $this->output->render($movingpix), array('title' => $strmovefull)),
+                        array('class' => 'movehere'));
             }
         }
 
index 01d6857..781186a 100644 (file)
@@ -25,8 +25,10 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Sort categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "Sort categories by ID number"   | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Sort by Category name ascending"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Category name descending"      | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Category ID number ascending"  | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Category ID number descending" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   Scenario Outline: Test bulk sorting current category.
     Given the following "categories" exist:
@@ -52,8 +54,10 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Sort categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "Sort categories by ID number"   | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Sort by Category name ascending"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Category name descending"      | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Category ID number ascending"  | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Category ID number descending" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   Scenario Outline: Test resorting subcategories.
     Given the following "categories" exist:
@@ -77,8 +81,10 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"         | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbynamedesc"     | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "resortbyidnumber"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "resortbyidnumberdesc" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   @javascript
   Scenario Outline: Test resorting subcategories with JS enabled.
@@ -103,8 +109,10 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"         | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbynamedesc"     | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "resortbyidnumber"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "resortbyidnumberdesc" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   # The scenario below this is the same but with JS enabled.
   Scenario: Test moving categories up and down by one.
index 818619b..a07af93 100644 (file)
@@ -255,8 +255,10 @@ Feature: Course category management interface performs as expected
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Sort categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "Sort categories by ID number"   | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Sort by Category name ascending"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Category name descending"      | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Category ID number ascending"  | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Category ID number descending" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   @javascript
   Scenario Outline: Sub categories are displayed correctly when resorted
@@ -281,8 +283,10 @@ Feature: Course category management interface performs as expected
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"         | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbynamedesc"     | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "resortbyidnumber"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "resortbyidnumberdesc" | "Applied sciences"        | "Social studies"          | "Extended social studies" |
 
   @javascript
   Scenario Outline: Test courses are displayed correctly after being resorted.
@@ -290,10 +294,10 @@ Feature: Course category management interface performs as expected
       | name | category 0| idnumber |
       | Cat 1 | 0 | CAT1 |
     And the following "courses" exist:
-      | category | fullname | shortname | idnumber | sortorder |
-      | CAT1 | Social studies | Senior school | Ext003 | 1 |
-      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 |
-      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 |
+      | category | fullname | shortname | idnumber | sortorder | timecreated |
+      | CAT1 | Social studies | Senior school | Ext003 | 1 | 10000000001 |
+      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 | 10000000002 |
+      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 | 10000000003 |
 
     And I log in as "admin"
     And I go to the courses management page
@@ -302,9 +306,14 @@ Feature: Course category management interface performs as expected
   # Redirect.
     And I should see the "Course categories and courses" management page
     And I click on "Sort courses" "link"
-    And I should see "By fullname" in the ".course-listing-actions" "css_element"
-    And I should see "By shortname" in the ".course-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created descending" in the ".course-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".course-listing-actions" "css_element"
   # Redirect.
     And I should see the "Course categories and courses" management page
@@ -313,9 +322,14 @@ Feature: Course category management interface performs as expected
 
   Examples:
     | sortby | course1 | course2 | course3 |
-    | "By fullname"        | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By shortname"       | "Extended social studies" | "Applied sciences"        | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course full name ascending"     | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Course full name descending"    | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Course short name ascending"    | "Extended social studies" | "Applied sciences"        | "Social studies" |
+    | "Sort by Course short name descending"   | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course ID number ascending"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course ID number descending"    | "Applied sciences"        | "Social studies"          | "Extended social studies" |
+    | "Sort by Course time created ascending"  | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course time created descending" | "Extended social studies" | "Applied sciences"        | "Social studies" |
 
   @javascript
   Scenario: Test course pagination
@@ -344,7 +358,7 @@ Feature: Course category management interface performs as expected
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I click on "Sort courses" "link"
-    And I click on "By idnumber" "link" in the ".course-listing-actions" "css_element"
+    And I click on "Sort by Course ID number ascending" "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see "Per page: 20" in the ".course-listing-actions" "css_element"
     And I should see course listing "Course 1" before "Course 2"
@@ -527,7 +541,7 @@ Feature: Course category management interface performs as expected
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I click on "Sort courses" "link"
-    And I click on "By idnumber" "link" in the ".course-listing-actions" "css_element"
+    And I click on "Sort by Course ID number ascending" "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see "Per page: 20" in the ".course-listing-actions" "css_element"
     And I should see course listing "Course 1" before "Course 2"
@@ -592,7 +606,7 @@ Feature: Course category management interface performs as expected
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I click on "Sort courses" "link"
-    And I click on "By idnumber" "link" in the ".course-listing-actions" "css_element"
+    And I click on "Sort by Course ID number ascending" "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see "Per page: 20" in the ".course-listing-actions" "css_element"
index b530e5b..9cb31a9 100644 (file)
@@ -10,10 +10,10 @@ Feature: Test we can resort course in the management interface.
       | name | category 0| idnumber |
       | Cat 1 | 0 | CAT1 |
     And the following "courses" exist:
-      | category | fullname | shortname | idnumber | sortorder |
-      | CAT1 | Social studies | Senior school | Ext003 | 1 |
-      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 |
-      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 |
+      | category | fullname | shortname | idnumber | sortorder | timecreated |
+      | CAT1 | Social studies | Senior school | Ext003 | 1 | 10000000001 |
+      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 | 10000000002 |
+      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 | 10000000003 |
 
     And I log in as "admin"
     And I go to the courses management page
@@ -22,9 +22,14 @@ Feature: Test we can resort course in the management interface.
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see "Sort courses" in the ".course-listing-actions" "css_element"
-    And I should see "By fullname" in the ".course-listing-actions" "css_element"
-    And I should see "By shortname" in the ".course-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created descending" in the ".course-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories and courses" management page
@@ -33,9 +38,14 @@ Feature: Test we can resort course in the management interface.
 
   Examples:
     | sortby | course1 | course2 | course3 |
-    | "By fullname"        | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By shortname"       | "Extended social studies" | "Applied sciences"        | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course full name ascending"     | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Course full name descending"    | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Course short name ascending"    | "Extended social studies" | "Applied sciences"        | "Social studies" |
+    | "Sort by Course short name descending"   | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course ID number ascending"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course ID number descending"    | "Applied sciences"        | "Social studies"          | "Extended social studies" |
+    | "Sort by Course time created ascending"  | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course time created descending" | "Extended social studies" | "Applied sciences"        | "Social studies" |
 
   @javascript
   Scenario Outline: Resort courses with JavaScript enabled.
@@ -43,10 +53,10 @@ Feature: Test we can resort course in the management interface.
       | name | category 0| idnumber |
       | Cat 1 | 0 | CAT1 |
     And the following "courses" exist:
-      | category | fullname | shortname | idnumber | sortorder |
-      | CAT1 | Social studies | Senior school | Ext003 | 1 |
-      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 |
-      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 |
+      | category | fullname | shortname | idnumber | sortorder | timecreated |
+      | CAT1 | Social studies | Senior school | Ext003 | 1 | 10000000001 |
+      | CAT1 | Applied sciences  | Middle school | Sci001 | 2 | 10000000002 |
+      | CAT1 | Extended social studies  | Junior school | Ext002 | 3 | 10000000003 |
 
     And I log in as "admin"
     And I go to the courses management page
@@ -55,13 +65,23 @@ Feature: Test we can resort course in the management interface.
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see "Sort courses" in the ".course-listing-actions" "css_element"
-    And I should not see "By fullname" in the ".course-listing-actions" "css_element"
-    And I should not see "By shortname" in the ".course-listing-actions" "css_element"
-    And I should not see "By idnumber" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course full name ascending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course full name descending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course short name ascending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course short name descending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course ID number ascending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course ID number descending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course time created ascending" in the ".course-listing-actions" "css_element"
+    And I should not see "Sort by Course time created descending" in the ".course-listing-actions" "css_element"
     And I click on "Sort courses" "link"
-    And I should see "By fullname" in the ".course-listing-actions" "css_element"
-    And I should see "By shortname" in the ".course-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course full name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course short name descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course ID number descending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created ascending" in the ".course-listing-actions" "css_element"
+    And I should see "Sort by Course time created descending" in the ".course-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories and courses" management page
@@ -70,9 +90,14 @@ Feature: Test we can resort course in the management interface.
 
   Examples:
     | sortby | course1 | course2 | course3 |
-    | "By fullname"        | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By shortname"       | "Extended social studies" | "Applied sciences"        | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course full name ascending"     | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Sort by Course full name descending"    | "Social studies"          | "Extended social studies" | "Applied sciences" |
+    | "Sort by Course short name ascending"    | "Extended social studies" | "Applied sciences"        | "Social studies" |
+    | "Sort by Course short name descending"   | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course ID number ascending"     | "Extended social studies" | "Social studies"          | "Applied sciences" |
+    | "Sort by Course ID number descending"    | "Applied sciences"        | "Social studies"          | "Extended social studies" |
+    | "Sort by Course time created ascending"  | "Social studies"          | "Applied sciences"        | "Extended social studies" |
+    | "Sort by Course time created descending" | "Extended social studies" | "Applied sciences"        | "Social studies" |
 
   Scenario: Test moving courses up and down by one.
     Given the following "categories" exist:
@@ -93,7 +118,7 @@ Feature: Test we can resort course in the management interface.
     And I should see "Course categories" in the "#category-listing h3" "css_element"
     And I should see "Cat 1" in the "#category-listing" "css_element"
     And I click on "Sort courses" "link"
-    And I click on "By idnumber" "link" in the ".course-listing-actions" "css_element"
+    And I click on "Sort by Course ID number ascending" "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see course listing "Course 1" before "Course 2"
@@ -130,7 +155,7 @@ Feature: Test we can resort course in the management interface.
     And I should see "Course categories" in the "#category-listing h3" "css_element"
     And I should see "Cat 1" in the "#category-listing" "css_element"
     And I click on "Sort courses" "link"
-    And I click on "By idnumber" "link" in the ".course-listing-actions" "css_element"
+    And I click on "Sort by Course ID number ascending" "link" in the ".course-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see course listing "Course 1" before "Course 2"
index 6db0851..f6d2d9b 100644 (file)
@@ -270,13 +270,16 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
      * @param string $tagcontents The raw contents of the XML element
      */
     protected function process_group_tag($tagcontents) {
-        global $DB;
+        global $DB, $CFG;
 
         // Get configs.
         $truncatecoursecodes    = $this->get_config('truncatecoursecodes');
         $createnewcourses       = $this->get_config('createnewcourses');
         $createnewcategories    = $this->get_config('createnewcategories');
 
+        if ($createnewcourses) {
+            require_once("$CFG->dirroot/course/lib.php");
+        }
         // Process tag contents.
         $group = new stdClass();
         if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
index 7623b14..b497034 100644 (file)
@@ -602,13 +602,13 @@ class core_files_renderer extends plugin_renderer_base {
                     <div class="fp-tb-message"></div>
                 </div>
                 <div class="fp-viewbar">
-                    <a title="'. get_string('displayicons', 'repository') .'" class="fp-vb-icons" href="#">
+                    <a role="button" title="'. get_string('displayicons', 'repository') .'" class="fp-vb-icons" href="#">
                         <img alt="" src="'. $this->pix_url('fp/view_icon_active', 'theme') .'" />
                     </a>
-                    <a title="'. get_string('displaydetails', 'repository') .'" class="fp-vb-details" href="#">
+                    <a role="button" title="'. get_string('displaydetails', 'repository') .'" class="fp-vb-details" href="#">
                         <img alt="" src="'. $this->pix_url('fp/view_list_active', 'theme') .'" />
                     </a>
-                    <a title="'. get_string('displaytree', 'repository') .'" class="fp-vb-tree" href="#">
+                    <a role="button" title="'. get_string('displaytree', 'repository') .'" class="fp-vb-tree" href="#">
                         <img alt="" src="'. $this->pix_url('fp/view_tree_active', 'theme') .'" />
                     </a>
                 </div>
index 0f46959..d9ce6f7 100644 (file)
@@ -74,11 +74,12 @@ class filter_activitynames extends moodle_text_filter {
                             'name' => $cm->name,
                             'url' => $cm->url,
                             'id' => $cm->id,
-                            'namelen' => strlen($cm->name),
+                            'namelen' => -strlen($cm->name), // Negative value for reverse sorting.
                         );
                     }
                 }
-                core_collator::asort_objects_by_property($sortedactivities, 'namelen', SORT_NUMERIC);
+                // Sort activities by the length of the activity name in reverse order.
+                core_collator::asort_objects_by_property($sortedactivities, 'namelen', core_collator::SORT_NUMERIC);
 
                 foreach ($sortedactivities as $cm) {
                     $title = s(trim(strip_tags($cm->name)));
index 0a4b562..6c957c8 100644 (file)
@@ -85,7 +85,7 @@ if ($editform->is_cancelled()) {
         default:
             print_error('unknoworder');
     }
-    $users = groups_get_potential_members($data->courseid, $data->roleid, $data->cohortid, $orderby);
+    $users = groups_get_potential_members($data->courseid, $data->roleid, $data->cohortid, $orderby, !empty($data->notingroup));
     $usercnt = count($users);
 
     if ($data->allocateby == 'random') {
index c62c390..d874911 100644 (file)
@@ -110,6 +110,8 @@ class autogroup_form extends moodleform {
         $mform->addElement('checkbox', 'nosmallgroups', get_string('nosmallgroups', 'group'));
         $mform->disabledIf('nosmallgroups', 'groupby', 'noteq', 'members');
 
+        $mform->addElement('checkbox', 'notingroup', get_string('notingroup', 'group'));
+
         $mform->addElement('header', 'groupinghdr', get_string('grouping', 'group'));
 
         $options = array('0' => get_string('nogrouping', 'group'),
index 970ce78..7d44a43 100644 (file)
@@ -709,23 +709,42 @@ function groups_get_possible_roles($context) {
  * @param int $roleid The role to select users from
  * @param int $cohortid restrict to cohort id
  * @param string $orderby The column to sort users by
+ * @param int $notingroup restrict to users not in existing groups
  * @return array An array of the users
  */
-function groups_get_potential_members($courseid, $roleid = null, $cohortid = null, $orderby = 'lastname ASC, firstname ASC') {
+function groups_get_potential_members($courseid, $roleid = null, $cohortid = null,
+                                      $orderby = 'lastname ASC, firstname ASC',
+                                      $notingroup = null) {
     global $DB;
 
     $context = context_course::instance($courseid);
 
     list($esql, $params) = get_enrolled_sql($context);
 
+    $notingroupsql = "";
+    if ($notingroup) {
+        // We want to eliminate users that are already associated with a course group.
+        $notingroupsql = "u.id NOT IN (SELECT userid
+                                         FROM {groups_members}
+                                        WHERE groupid IN (SELECT id
+                                                            FROM {groups}
+                                                           WHERE courseid = :courseid))";
+        $params['courseid'] = $courseid;
+    }
+
     if ($roleid) {
         // We are looking for all users with this role assigned in this context or higher.
-        list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx');
+        list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true),
+                                                                       SQL_PARAMS_NAMED,
+                                                                       'relatedctx');
 
         $params = array_merge($params, $relatedctxparams, array('roleid' => $roleid));
         $where = "WHERE u.id IN (SELECT userid
                                    FROM {role_assignments}
                                   WHERE roleid = :roleid AND contextid $relatedctxsql)";
+        $where .= $notingroup ? "AND $notingroupsql" : "";
+    } else if ($notingroup) {
+        $where = "WHERE $notingroupsql";
     } else {
         $where = "";
     }
index a49d13f..ee7f6b0 100644 (file)
@@ -84,3 +84,28 @@ Feature: Automatic creation of groups
     And I should see "Group B" in the ".generaltable" "css_element"
     And I should see "5" in the "Group A" "table_row"
     And I should see "5" in the "Group B" "table_row"
+
+  @javascript
+  Scenario: Split automatically the course users in groups that are not in groups
+    Given I press "Cancel"
+    And I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group 1 |
+    And I press "Save changes"
+    And I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group 2 |
+    And I press "Save changes"
+    When I add "Student 0" user to "Group 1" group members
+    And I add "Student 1" user to "Group 1" group members
+    And I add "Student 2" user to "Group 2" group members
+    And I add "Student 3" user to "Group 2" group members
+    And I press "Auto-create groups"
+    And I expand all fieldsets
+    And I set the field "Auto create based on" to "Number of groups"
+    And I set the field "Group/member count" to "2"
+    And I set the field "Grouping of auto-created groups" to "No grouping"
+    And I set the field "Ignore users in groups" to "1"
+    And I press "Submit"
+    And the "groups" select box should contain "Group A (3)"
+    And the "groups" select box should contain "Group B (3)"
\ No newline at end of file
diff --git a/install/lang/af/moodle.php b/install/lang/af/moodle.php
new file mode 100644 (file)
index 0000000..e9fb8fd
--- /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['language'] = 'Taal';
index b9211b2..d6a74dc 100644 (file)
@@ -34,8 +34,8 @@ $string['admindirname'] = 'Каталог администратора';
 $string['availablelangs'] = 'Доступные языковые пакеты';
 $string['chooselanguagehead'] = 'Выберите язык';
 $string['chooselanguagesub'] = 'Сейчас необходимо выбрать язык ТОЛЬКО для сообщений во время установки. Язык сайта и пользовательских интерфейсов можно будет указать далее в процессе установки.';
-$string['clialreadyconfigured'] = 'Файл config.php уже существует. Если Вы хотите установить этот сайт, используйте admin/cli/install_database.php.';
-$string['clialreadyinstalled'] = 'Файл config.php уже существует. Если Вы хотите обновить сайт, то используйте скрипт admin/cli/upgrade.php.';
+$string['clialreadyconfigured'] = 'Файл config.php уже существует. Если Вы хотите установить Moodle на этот сайт, используйте admin/cli/install_database.php.';
+$string['clialreadyinstalled'] = 'Файл config.php уже существует. Если Вы хотите обновить сайт Moodle, то используйте скрипт admin/cli/upgrade.php.';
 $string['cliinstallheader'] = 'Программа установки Moodle {$a} в режиме командной строки';
 $string['databasehost'] = 'Сервер баз данных';
 $string['databasename'] = 'Название базы данных';
index eaaccaf..0d61dab 100644 (file)
@@ -1124,6 +1124,7 @@ $string['useexternalyui'] = 'Use online YUI libraries';
 $string['user'] = 'User';
 $string['userbulk'] = 'Bulk user actions';
 $string['userlist'] = 'Browse list of users';
+$string['userdefaultpreferences'] = 'User default preferences';
 $string['userpreference'] = 'User preference';
 $string['userpolicies'] = 'User policies';
 $string['users'] = 'Users';
index ac1316a..f640d22 100644 (file)
@@ -157,6 +157,7 @@ $string['nogroups'] = 'There are no groups set up in this course yet';
 $string['nogroupsassigned'] = 'No groups assigned';
 $string['nopermissionforcreation'] = 'Can\'t create group "{$a}" as you don\'t have the required permissions';
 $string['nosmallgroups'] = 'Prevent last small group';
+$string['notingroup'] = 'Ignore users in groups';
 $string['notingrouping'] = '[Not in a grouping]';
 $string['nousersinrole'] = 'There are no suitable users in the selected role';
 $string['number'] = 'Group/member count';
index 2b46997..61130db 100644 (file)
@@ -1513,12 +1513,9 @@ $string['resetstartdate'] = 'Reset start date';
 $string['resetstatus'] = 'Status';
 $string['resettask'] = 'Task';
 $string['resettodefaults'] = 'Reset to defaults';
-$string['resortsubcategoriesbyname'] = 'Sort subcategories by name';
-$string['resortsubcategoriesbyidnumber'] = 'Sort subcategories by idnumber';
+$string['resortsubcategoriesby'] = 'Sort subcategories by {$a} ascending';
+$string['resortsubcategoriesbyreverse'] = 'Sort subcategories by {$a} descending';
 $string['resortcourses'] = 'Sort courses';
-$string['resortbyshortname'] = 'By shortname';
-$string['resortbyfullname'] = 'By fullname';
-$string['resortbyidnumber'] = 'By idnumber';
 $string['resource'] = 'Resource';
 $string['resourcedisplayauto'] = 'Automatic';
 $string['resourcedisplaydownload'] = 'Force download';
@@ -1724,11 +1721,6 @@ $string['sort'] = 'Sort';
 $string['sortby'] = 'Sort by';
 $string['sortbyx'] = 'Sort by {$a} ascending';
 $string['sortbyxreverse'] = 'Sort by {$a} descending';
-$string['sortcategoriesbyname'] = 'Sort categories by name';
-$string['sortcategoriesbyidnumber'] = 'Sort categories by ID number';
-$string['sortcoursesbyfullname'] = 'Sort courses by full name';
-$string['sortcoursesbyshortname'] = 'Sort courses by short name';
-$string['sortcoursesbyidnumber'] = 'Sort courses by ID number';
 $string['sorting'] = 'Sorting';
 $string['sourcerole'] = 'Source role';
 $string['specifyname'] = 'You must specify a name.';
@@ -1822,6 +1814,7 @@ $string['therearecourses'] = 'There are {$a} courses';
 $string['thiscategory'] = 'This category';
 $string['thiscategorycontains'] = 'This category contains';
 $string['time'] = 'Time';
+$string['timecreatedcourse'] = 'Course time created';
 $string['timezone'] = 'Timezone';
 $string['to'] = 'To';
 $string['tocreatenewaccount'] = 'Skip to create new account';
@@ -1848,6 +1841,7 @@ $string['trackforumsyes'] = 'Yes: highlight new posts for me';
 $string['trysearching'] = 'Try searching instead.';
 $string['turneditingoff'] = 'Turn editing off';
 $string['turneditingon'] = 'Turn editing on';
+$string['unauthorisedlogin'] = 'The user account "{$a}" is not available on this site';
 $string['undecided'] = 'Undecided';
 $string['unfinished'] = 'Unfinished';
 $string['unknowncategory'] = 'Unknown category';
index 6740fe2..50470a8 100644 (file)
@@ -76,6 +76,8 @@ define('AUTH_LOGIN_FAILED', 3);
 /** Can not login because user is locked out. */
 define('AUTH_LOGIN_LOCKOUT', 4);
 
+/** Can not login becauser user is not authorised. */
+define('AUTH_LOGIN_UNAUTHORISED', 5);
 
 /**
  * Abstract authentication plugin.
index ad779f2..398ff67 100644 (file)
@@ -284,6 +284,46 @@ class core_collator {
         return $result;
     }
 
+    /**
+     * Locale aware sort of array of arrays.
+     *
+     * Given an array like:
+     * $array = array(
+     *     array('name' => 'bravo'),
+     *     array('name' => 'charlie'),
+     *     array('name' => 'alpha')
+     * );
+     *
+     * If you call:
+     * core_collator::asort_array_of_arrays_by_key($array, 'name')
+     *
+     * You will be returned $array sorted by the name key of the subarrays. e.g.
+     * $array = array(
+     *     array('name' => 'alpha'),
+     *     array('name' => 'bravo'),
+     *     array('name' => 'charlie')
+     * );
+     *
+     * @param array $array An array of objects to sort (handled by reference)
+     * @param string $key The key to use for comparison
+     * @param int $sortflag One of
+     *          core_collator::SORT_NUMERIC,
+     *          core_collator::SORT_STRING,
+     *          core_collator::SORT_NATURAL,
+     *          core_collator::SORT_REGULAR
+     *      optionally "|" core_collator::CASE_SENSITIVE
+     * @return bool True on success
+     */
+    public static function asort_array_of_arrays_by_key(array &$array, $key, $sortflag = core_collator::SORT_STRING) {
+        $original = $array;
+        foreach ($array as $initkey => $item) {
+            $array[$initkey] = $item[$key];
+        }
+        $result = self::asort($array, $sortflag);
+        self::restore_array($array, $original);
+        return $result;
+    }
+
     /**
      * Locale aware sorting, the key associations are kept, keys are sorted alphabetically.
      *
index 17fd3bb..2c85687 100644 (file)
@@ -43,7 +43,7 @@ class recent_activity_viewed extends base {
      */
     protected function init() {
         $this->data['crud'] = 'r';
-        $this->data['edulevel'] = self::LEVEL_OTHER;
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
     }
 
     /**
index 916b92b..30ebc73 100644 (file)
@@ -64,6 +64,14 @@ abstract class adhoc_task extends task_base {
         $this->customdata = json_encode($customdata);
     }
 
+    /**
+     * Alternate setter for $customdata. Expects the data as a json_encoded string.
+     * @param string $customdata json_encoded string
+     */
+    public function set_custom_data_as_string($customdata) {
+        $this->customdata = $customdata;
+    }
+
     /**
      * Getter for $customdata.
      * @return mixed (anything that can be handled by json_decode).
@@ -72,4 +80,13 @@ abstract class adhoc_task extends task_base {
         return json_decode($this->customdata);
     }
 
+    /**
+     * Alternate getter for $customdata.
+     * @return string this is the raw json encoded version.
+     */
+    public function get_custom_data_as_string() {
+        return $this->customdata;
+    }
+
+
 }
index be60ee2..e2ec497 100644 (file)
@@ -220,7 +220,7 @@ class manager {
         $record->blocking = $task->is_blocking();
         $record->nextruntime = $task->get_next_run_time();
         $record->faildelay = $task->get_fail_delay();
-        $record->customdata = $task->get_custom_data();
+        $record->customdata = $task->get_custom_data_as_string();
 
         return $record;
     }
@@ -254,7 +254,7 @@ class manager {
             $task->set_fail_delay($record->faildelay);
         }
         if (isset($record->customdata)) {
-            $task->set_custom_data($record->customdata);
+            $task->set_custom_data_as_string($record->customdata);
         }
 
         return $task;
index 3ff0c28..2e83745 100644 (file)
@@ -477,9 +477,12 @@ class completion_info {
     /**
      * Get incomplete course completion criteria
      *
+     * @deprecated since Moodle 2.8 MDL-46290.
+     * @todo MDL-46294 This will be deleted in Moodle 3.0.
      * @return array
      */
     public function get_incomplete_criteria() {
+        debugging('completion_info->get_incomplete_criteria() is deprecated.', DEBUG_DEVELOPER);
         $incomplete = array();
 
         foreach ($this->get_criteria() as $criteria) {
index d96f582..0bf08f6 100644 (file)
@@ -258,7 +258,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             return $coursecat;
         } else {
             if ($strictness == MUST_EXIST) {
-                throw new moodle_exception('unknowcategory');
+                throw new moodle_exception('unknowncategory');
             }
         }
         return null;
@@ -2441,18 +2441,26 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
     /**
      * Resorts the sub categories of this category by the given field.
      *
-     * @param string $field
+     * @param string $field One of name, idnumber or descending values of each (appended desc)
      * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later.
      * @return bool True on success.
      * @throws coding_exception
      */
     public function resort_subcategories($field, $cleanup = true) {
         global $DB;
+        $desc = false;
+        if (substr($field, -4) === "desc") {
+            $desc = true;
+            $field = substr($field, 0, -4);  // Remove "desc" from field name.
+        }
         if ($field !== 'name' && $field !== 'idnumber') {
             throw new coding_exception('Invalid field requested');
         }
         $children = $this->get_children();
         core_collator::asort_objects_by_property($children, $field, core_collator::SORT_NATURAL);
+        if (!empty($desc)) {
+            $children = array_reverse($children);
+        }
         $i = 1;
         foreach ($children as $cat) {
             $i++;
@@ -2481,14 +2489,19 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
     /**
      * Resort the courses within this category by the given field.
      *
-     * @param string $field One of fullname, shortname or idnumber
+     * @param string $field One of fullname, shortname, idnumber or descending values of each (appended desc)
      * @param bool $cleanup
      * @return bool True for success.
      * @throws coding_exception
      */
     public function resort_courses($field, $cleanup = true) {
         global $DB;
-        if ($field !== 'fullname' && $field !== 'shortname' && $field !== 'idnumber') {
+        $desc = false;
+        if (substr($field, -4) === "desc") {
+            $desc = true;
+            $field = substr($field, 0, -4);  // Remove "desc" from field name.
+        }
+        if ($field !== 'fullname' && $field !== 'shortname' && $field !== 'idnumber' && $field !== 'timecreated') {
             // This is ultra important as we use $field in an SQL statement below this.
             throw new coding_exception('Invalid field requested');
         }
@@ -2497,8 +2510,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
                   FROM {course} c
              LEFT JOIN {context} ctx ON ctx.instanceid = c.id
                  WHERE ctx.contextlevel = :ctxlevel AND
-                       c.category = :categoryid
-              ORDER BY c.{$field}, c.sortorder";
+                       c.category = :categoryid";
         $params = array(
             'ctxlevel' => CONTEXT_COURSE,
             'categoryid' => $this->id
@@ -2527,6 +2539,9 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             }
             // Sort the courses.
             core_collator::asort_objects_by_property($courses, 'sortby', core_collator::SORT_NATURAL);
+            if (!empty($desc)) {
+                $courses = array_reverse($courses);
+            }
             $i = 1;
             foreach ($courses as $course) {
                 $DB->set_field('course', 'sortorder', $this->sortorder + $i, array('id' => $course->id));
index f6a3316..9450aba 100644 (file)
@@ -1166,7 +1166,8 @@ function format_postdata_for_curlcall($postdata) {
  * @param string $tofile store the downloaded content to file instead of returning it.
  * @param bool $calctimeout false by default, true enables an extra head request to try and determine
  *   filesize and appropriately larger timeout based on $CFG->curltimeoutkbitrate
- * @return mixed false if request failed or content of the file as string if ok. True if file downloaded into $tofile successfully.
+ * @return stdClass|string|bool stdClass object if $fullresponse is true, false if request failed, true
+ *   if file downloaded into $tofile successfully or the file content as a string.
  */
 function download_file_content($url, $headers=null, $postdata=null, $fullresponse=false, $timeout=300, $connecttimeout=20, $skipcertverify=false, $tofile=NULL, $calctimeout=false) {
     global $CFG;
index a2c7cd7..166e5a3 100644 (file)
@@ -111,7 +111,7 @@ M.form_filemanager.init = function(Y, options) {
             var labelid = 'fm-dialog-label_'+ this.selectnode.get('id');
             this.selectui = new M.core.dialogue({
                 draggable    : true,
-                headerContent: '<span id="' + labelid +'">' + M.str.moodle.edit + '</span>',
+                headerContent: '<h3 id="' + labelid +'">' + M.str.moodle.edit + '</h3>',
                 bodyContent  : this.selectnode,
                 centered     : true,
                 width        : '480px',
index 66e4e97..b949b74 100644 (file)
@@ -41,6 +41,22 @@ class HTMLPurifier_URIScheme_rtsp extends HTMLPurifier_URIScheme {
 }
 
 
+/**
+ * Validates RTMP defined by Adobe
+ */
+class HTMLPurifier_URIScheme_rtmp extends HTMLPurifier_URIScheme {
+
+    public $browsable = false;
+    public $hierarchical = true;
+
+    public function doValidate(&$uri, $config, $context) {
+        $uri->userinfo = null;
+        return true;
+    }
+
+}
+
+
 /**
  * Validates IRC defined by IETF Draft
  */
index 17c85d9..6018d1a 100644 (file)
@@ -1329,6 +1329,8 @@ function html_is_blank($string) {
  *
  * A NULL value will delete the entry.
  *
+ * NOTE: this function is called from lib/db/upgrade.php
+ *
  * @param string $name the key to set
  * @param string $value the value to set (without magic quotes)
  * @param string $plugin (optional) the plugin scope, default null
@@ -1399,6 +1401,8 @@ function set_config($name, $value, $plugin=null) {
  * If called with 2 parameters it will return a string single
  * value or false if the value is not found.
  *
+ * NOTE: this function is called from lib/db/upgrade.php
+ *
  * @static string|false $siteidentifier The site identifier is not cached. We use this static cache so
  *     that we need only fetch it once per request.
  * @param string $plugin full component name
@@ -1485,6 +1489,8 @@ function get_config($plugin, $name = null) {
 /**
  * Removes a key from global configuration.
  *
+ * NOTE: this function is called from lib/db/upgrade.php
+ *
  * @param string $name the key to set
  * @param string $plugin (optional) the plugin scope
  * @return boolean whether the operation succeeded.
@@ -4426,20 +4432,6 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
             return false;
         }
 
-        // Do not try to authenticate non-existent accounts when user creation is disabled.
-        if (!empty($CFG->authpreventaccountcreation)) {
-            $failurereason = AUTH_LOGIN_NOUSER;
-
-            // Trigger login failed event.
-            $event = \core\event\user_login_failed::create(array('other' => array('username' => $username,
-                    'reason' => $failurereason)));
-            $event->trigger();
-
-            error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Unknown user, can not create new accounts:  $username  ".
-                    $_SERVER['HTTP_USER_AGENT']);
-            return false;
-        }
-
         // User does not exist.
         $auths = $authsenabled;
         $user = new stdClass();
@@ -4492,8 +4484,21 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
                 $user = update_user_record_by_id($user->id);
             }
         } else {
-            // Create account, we verified above that user creation is allowed.
-            $user = create_user_record($username, $password, $auth);
+            // The user is authenticated but user creation may be disabled.
+            if (!empty($CFG->authpreventaccountcreation)) {
+                $failurereason = AUTH_LOGIN_UNAUTHORISED;
+
+                // Trigger login failed event.
+                $event = \core\event\user_login_failed::create(array('other' => array('username' => $username,
+                        'reason' => $failurereason)));
+                $event->trigger();
+
+                error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Unknown user, can not create new accounts:  $username  ".
+                        $_SERVER['HTTP_USER_AGENT']);
+                return false;
+            } else {
+                $user = create_user_record($username, $password, $auth);
+            }
         }
 
         $authplugin->sync_roles($user);
@@ -8752,17 +8757,19 @@ function message_popup_window() {
 
     // A quick query to check whether the user has new messages.
     $messagecount = $DB->count_records('message', array('useridto' => $USER->id));
-    if ($messagecount<1) {
+    if ($messagecount < 1) {
         return;
     }
 
-    // Got unread messages so now do another query that joins with the user table.
+    // There are unread messages so now do a more complex but slower query.
     $namefields = get_all_user_name_fields(true, 'u');
-    $messagesql = "SELECT m.id, m.smallmessage, m.fullmessageformat, m.notification, $namefields
+    $messagesql = "SELECT m.id, m.smallmessage, m.fullmessageformat, m.notification, m.useridto, m.useridfrom, $namefields, c.blocked
                      FROM {message} m
                      JOIN {message_working} mw ON m.id=mw.unreadmessageid
                      JOIN {message_processors} p ON mw.processorid=p.id
                      JOIN {user} u ON m.useridfrom=u.id
+                     LEFT JOIN {message_contacts} c ON c.contactid = m.useridfrom
+                                                   AND c.userid = m.useridto
                     WHERE m.useridto = :userid
                       AND p.name='popup'";
 
@@ -8775,10 +8782,18 @@ function message_popup_window() {
 
     $messageusers = $DB->get_records_sql($messagesql, array('userid' => $USER->id, 'lastpopuptime' => $USER->message_lastpopup));
 
-    // If we have new messages to notify the user about.
-    if (!empty($messageusers)) {
+    $validmessages = 0;
+    foreach($messageusers as $message) {
+        if ($message->blocked) {
+            // Message is from a user who has since been blocked so just mark it read.
+            message_mark_message_read($message, time());
+        } else {
+            $validmessages++;
+        }
+    }
 
-        $strmessages = get_string('unreadnewmessages', 'message', count($messageusers));
+    if ($validmessages > 0) {
+        $strmessages = get_string('unreadnewmessages', 'message', $validmessages);
         $strgomessage = get_string('gotomessages', 'message');
         $strstaymessage = get_string('ignore', 'admin');
 
index 76b0c83..9bf8408 100644 (file)
@@ -174,6 +174,11 @@ class user_picture implements renderable {
      */
     public $class = 'userpicture';
 
+    /**
+     * @var bool Whether to be visible to screen readers.
+     */
+    public $visibletoscreenreaders = true;
+
     /**
      * User picture constructor.
      *
index 9da96cb..82938bc 100644 (file)
@@ -2288,6 +2288,7 @@ class core_renderer extends renderer_base {
      *     - popup=false (open in popup)
      *     - alttext=true (add image alt attribute)
      *     - class = image class attribute (default 'userpicture')
+     *     - visibletoscreenreaders=true (whether to be visible to screen readers)
      * @return string HTML fragment
      */
     public function user_picture(stdClass $user, array $options = null) {
@@ -2338,6 +2339,9 @@ class core_renderer extends renderer_base {
         $src = $userpicture->get_url($this->page, $this);
 
         $attributes = array('src'=>$src, 'alt'=>$alt, 'title'=>$alt, 'class'=>$class, 'width'=>$size, 'height'=>$size);
+        if (!$userpicture->visibletoscreenreaders) {
+            $attributes['role'] = 'presentation';
+        }
 
         // get the image html output fisrt
         $output = html_writer::empty_tag('img', $attributes);
@@ -2360,6 +2364,11 @@ class core_renderer extends renderer_base {
         }
 
         $attributes = array('href'=>$url);
+        if (!$userpicture->visibletoscreenreaders) {
+            $attributes['role'] = 'presentation';
+            $attributes['tabindex'] = '-1';
+            $attributes['aria-hidden'] = 'true';
+        }
 
         if ($userpicture->popup) {
             $id = html_writer::random_id('userpicture');
index 67e44be..6a15a68 100644 (file)
@@ -219,7 +219,23 @@ EOD;
         }
 
         if (!isset($record['maildisplay'])) {
-            $record['maildisplay'] = 1;
+            $record['maildisplay'] = $CFG->defaultpreference_maildisplay;
+        }
+
+        if (!isset($record['mailformat'])) {
+            $record['mailformat'] = $CFG->defaultpreference_mailformat;
+        }
+
+        if (!isset($record['maildigest'])) {
+            $record['maildigest'] = $CFG->defaultpreference_maildigest;
+        }
+
+        if (!isset($record['autosubscribe'])) {
+            $record['autosubscribe'] = $CFG->defaultpreference_autosubscribe;
+        }
+
+        if (!isset($record['trackforums'])) {
+            $record['trackforums'] = $CFG->defaultpreference_trackforums;
         }
 
         if (!isset($record['deleted'])) {
index 5711157..f962480 100644 (file)
@@ -198,6 +198,48 @@ class core_collator_testcase extends advanced_testcase {
         $this->assertTrue($result);
     }
 
+    /**
+     * Tests the sorting of an array of arrays by key.
+     */
+    public function test_asort_array_of_arrays_by_key() {
+        $array = array(
+            'a' => array('name' => 'bravo'),
+            'b' => array('name' => 'charlie'),
+            'c' => array('name' => 'alpha')
+        );
+        $this->assertSame(array('a', 'b', 'c'), array_keys($array));
+        $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
+        $this->assertSame(array('c', 'a', 'b'), array_keys($array));
+
+        $array = array(
+            'a' => array('name' => 'b'),
+            'b' => array('name' => 1),
+            'c' => array('name' => 0)
+        );
+        $this->assertSame(array('a', 'b', 'c'), array_keys($array));
+        $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
+        $this->assertSame(array('c', 'b', 'a'), array_keys($array));
+
+        $array = array(
+            'a' => array('name' => 'áb'),
+            'b' => array('name' => 'ab'),
+            1   => array('name' => 'aa'),
+            'd' => array('name' => 'cc'),
+            0   => array('name' => 'Áb')
+        );
+        $this->assertSame(array('a', 'b', 1, 'd', 0), array_keys($array));
+        $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
+        $this->assertSame(array(1, 'b', 'a', 0, 'd'), array_keys($array));
+        $this->assertSame(array(
+            1   => array('name' => 'aa'),
+            'b' => array('name' => 'ab'),
+            'a' => array('name' => 'áb'),
+            0   => array('name' => 'Áb'),
+            'd' => array('name' => 'cc')
+        ), $array);
+
+    }
+
     /**
      * Returns an array of sorted names.
      * @param array $objects
index b4b8180..0aa637b 100644 (file)
@@ -395,25 +395,29 @@ class core_coursecatlib_testcase extends advanced_testcase {
             'category' => $category->id,
             'idnumber' => '006-01',
             'shortname' => 'Biome Study',
-            'fullname' => '<span lang="ar" class="multilang">'.'دراسة منطقة إحيائية'.'</span><span lang="en" class="multilang">Biome Study</span>'
+            'fullname' => '<span lang="ar" class="multilang">'.'دراسة منطقة إحيائية'.'</span><span lang="en" class="multilang">Biome Study</span>',
+            'timecreated' => '10000000001'
         ));
         $course2 = $generator->create_course(array(
             'category' => $category->id,
             'idnumber' => '007-02',
             'shortname' => 'Chemistry Revision',
-            'fullname' => 'Chemistry Revision'
+            'fullname' => 'Chemistry Revision',
+            'timecreated' => '10000000002'
         ));
         $course3 = $generator->create_course(array(
             'category' => $category->id,
             'idnumber' => '007-03',
             'shortname' => 'Swiss Rolls and Sunflowers',
-            'fullname' => 'Aarkvarks guide to Swiss Rolls and Sunflowers'
+            'fullname' => 'Aarkvarks guide to Swiss Rolls and Sunflowers',
+            'timecreated' => '10000000003'
         ));
         $course4 = $generator->create_course(array(
             'category' => $category->id,
             'idnumber' => '006-04',
             'shortname' => 'Scratch',
-            'fullname' => '<a href="test.php">Basic Scratch</a>'
+            'fullname' => '<a href="test.php">Basic Scratch</a>',
+            'timecreated' => '10000000004'
         ));
         $c1 = (int)$course1->id;
         $c2 = (int)$course2->id;
@@ -427,6 +431,8 @@ class core_coursecatlib_testcase extends advanced_testcase {
         $this->assertTrue($coursecat->resort_courses('shortname'));
         $this->assertSame(array($c1, $c2, $c4, $c3), array_keys($coursecat->get_courses()));
 
+        $this->assertTrue($coursecat->resort_courses('timecreated'));
+        $this->assertSame(array($c1, $c2, $c3, $c4), array_keys($coursecat->get_courses()));
 
         try {
             // Enable the multilang filter and set it to apply to headings and content.
index 88a1644..51d22c8 100644 (file)
@@ -295,6 +295,9 @@ class core_htmlpurifier_testcase extends basic_testcase {
         $text = '<a href="rtsp://www.example.com/movie.mov">link</a>';
         $this->assertSame($text, purify_html($text));
 
+        $text = '<a href="rtmp://www.example.com/video.f4v">link</a>';
+        $this->assertSame($text, purify_html($text));
+
         $text = '<a href="teamspeak://speak.example.com/?par=val?par2=val2">link</a>';
         $this->assertSame($text, purify_html($text));
 
index 04a7c18..9e067cf 100644 (file)
@@ -8,7 +8,11 @@ information provided here is intended especially for developers.
   If you have a renderable class named like "blah_renderable" and have a method on a renderer named "render_blah_renderable"
   you will need to change the name of your render method to "render_blah" instead, as renderable at the end is no longer accepted.
 
+DEPRECATIONS:
+* completion_info->get_incomplete_criteria() is deprecated and will be removed in Moodle 3.0.
+
 === 2.6.4 / 2.7.1 ===
+
 * setnew_password_and_mail() and update_internal_user_password() will trigger
   \core\event\user_password_updated. Previously they used to generate
   \core\event\user_updated event.
index 55ae5f7..c082abb 100644 (file)
@@ -1680,6 +1680,7 @@ function purify_html($text, $options = array()) {
             'nntp' => true,
             'news' => true,
             'rtsp' => true,
+            'rtmp' => true,
             'teamspeak' => true,
             'gopher' => true,
             'mms' => true,
index a759287..0d80195 100644 (file)
Binary files a/lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js and b/lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js differ
index 9ee43d1..65fff2d 100644 (file)
Binary files a/lib/yui/build/moodle-core-dock/moodle-core-dock-min.js and b/lib/yui/build/moodle-core-dock/moodle-core-dock-min.js differ
index 528e73b..90dc406 100644 (file)
Binary files a/lib/yui/build/moodle-core-dock/moodle-core-dock.js and b/lib/yui/build/moodle-core-dock/moodle-core-dock.js differ
index 1a7edfc..50ad9a9 100644 (file)
@@ -965,7 +965,7 @@ DOCK.prototype = {
         }
 
         if (this.get('position') === 'right') {
-            panel.get('node').setStyle('left', -panel.get('offsetWidth')+'px');
+            panel.get('node').setStyle('left', '-' + panel.get('node').get('offsetWidth') + 'px');
 
         } else if (this.get('position') === 'top') {
             dockx = this.get('dockNode').getX();
index a4e29fc..587eb0e 100644 (file)
@@ -137,7 +137,7 @@ if ($frm and isset($frm->username)) {                             // Login WITH
         $frm = false;
     } else {
         if (empty($errormsg)) {
-            $user = authenticate_user_login($frm->username, $frm->password);
+            $user = authenticate_user_login($frm->username, $frm->password, false, $errorcode);
         }
     }
 
@@ -233,8 +233,12 @@ if ($frm and isset($frm->username)) {                             // Login WITH
 
     } else {
         if (empty($errormsg)) {
-            $errormsg = get_string("invalidlogin");
-            $errorcode = 3;
+            if ($errorcode == AUTH_LOGIN_UNAUTHORISED) {
+                $errormsg = get_string("unauthorisedlogin", "", $frm->username);
+            } else {
+                $errormsg = get_string("invalidlogin");
+                $errorcode = 3;
+            }
         }
     }
 }
index bae2d7c..7f212c3 100644 (file)
@@ -43,7 +43,6 @@ if (empty($CFG->authloginviaemail)) {
             <div class="form-label"><label for="password"><?php print_string("password") ?></label></div>
             <div class="form-input">
               <input type="password" name="password" id="password" size="15" value="" <?php echo $autocomplete; ?> />
-              <input type="submit" id="loginbtn" value="<?php print_string("login") ?>" />
             </div>
           </div>
             <div class="clearer"><!-- --></div>
@@ -54,6 +53,7 @@ if (empty($CFG->authloginviaemail)) {
               </div>
               <?php } ?>
           <div class="clearer"><!-- --></div>
+          <input type="submit" id="loginbtn" value="<?php print_string("login") ?>" />
           <div class="forgetpass"><a href="forgot_password.php"><?php print_string("forgotten") ?></a></div>
         </form>
         <div class="desc">
index a3bb7cb..9408c07 100644 (file)
@@ -704,70 +704,56 @@ function message_print_search($advancedsearch = false, $user1=null) {
 function message_get_recent_conversations($user, $limitfrom=0, $limitto=100) {
     global $DB;
 
-    $userfields = user_picture::fields('u', array('lastaccess'));
-    //This query retrieves the last message received from and sent to each user
-    //It unions that data then, within that set, it finds the most recent message you've exchanged with each user over all
-    //It then joins with some other tables to get some additional data we need
-
-    //message ID is used instead of timecreated as it should sort the same and will be much faster
-
-    //There is a separate query for read and unread queries as they are stored in different tables
-    //They were originally retrieved in one query but it was so large that it was difficult to be confident in its correctness
-    $sql = "SELECT $userfields, mr.id as mid, mr.notification, mr.smallmessage, mr.fullmessage, mr.fullmessagehtml, mr.fullmessageformat, mr.timecreated, mc.id as contactlistid, mc.blocked
-              FROM {message_read} mr
-              JOIN (
-                    SELECT messages.userid AS userid, MAX(messages.mid) AS mid
-                      FROM (
-                           SELECT mr1.useridto AS userid, MAX(mr1.id) AS mid
-                             FROM {message_read} mr1
-                            WHERE mr1.useridfrom = :userid1
-                                  AND mr1.notification = 0
-                         GROUP BY mr1.useridto
-                                  UNION
-                           SELECT mr2.useridfrom AS userid, MAX(mr2.id) AS mid
-                             FROM {message_read} mr2
-                            WHERE mr2.useridto = :userid2
-                                  AND mr2.notification = 0
-                         GROUP BY mr2.useridfrom
-                           ) messages
-                  GROUP BY messages.userid
-                   ) messages2 ON mr.id = messages2.mid AND (mr.useridto = messages2.userid OR mr.useridfrom = messages2.userid)
-              JOIN {user} u ON u.id = messages2.userid
-         LEFT JOIN {message_contacts} mc ON mc.userid = :userid3 AND mc.contactid = u.id
-             WHERE u.deleted = '0'
-          ORDER BY mr.id DESC";
-    $params = array('userid1' => $user->id, 'userid2' => $user->id, 'userid3' => $user->id);
-    $read =  $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
-
-    $sql = "SELECT $userfields, m.id as mid, m.notification, m.smallmessage, m.fullmessage, m.fullmessagehtml, m.fullmessageformat, m.timecreated, mc.id as contactlistid, mc.blocked
-              FROM {message} m
-              JOIN (
-                    SELECT messages.userid AS userid, MAX(messages.mid) AS mid
-                      FROM (
-                           SELECT m1.useridto AS userid, MAX(m1.id) AS mid
-                             FROM {message} m1
-                            WHERE m1.useridfrom = :userid1
-                                  AND m1.notification = 0
-                         GROUP BY m1.useridto
-                                  UNION
-                           SELECT m2.useridfrom AS userid, MAX(m2.id) AS mid
-                             FROM {message} m2
-                            WHERE m2.useridto = :userid2
-                                  AND m2.notification = 0
-                         GROUP BY m2.useridfrom
-                           ) messages
-                  GROUP BY messages.userid
-                   ) messages2 ON m.id = messages2.mid AND (m.useridto = messages2.userid OR m.useridfrom = messages2.userid)
-              JOIN {user} u ON u.id = messages2.userid
-         LEFT JOIN {message_contacts} mc ON mc.userid = :userid3 AND mc.contactid = u.id
-             WHERE u.deleted = '0'
-             ORDER BY m.id DESC";
-    $unread =  $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
+    $userfields = user_picture::fields('otheruser', array('lastaccess'));
+
+    // This query retrieves the most recent message received from or sent to
+    // seach other user.
+    //
+    // If two messages have the same timecreated, we take the one with the
+    // larger id.
+    //
+    // There is a separate query for read and unread messages as they are stored
+    // in different tables. They were originally retrieved in one query but it
+    // was so large that it was difficult to be confident in its correctness.
+    $sql = "SELECT $userfields,
+                   message.id as mid, message.notification, message.smallmessage, message.fullmessage,
+                   message.fullmessagehtml, message.fullmessageformat, message.timecreated,
+                   contact.id as contactlistid, contact.blocked
+
+              FROM {message_read} message
+              JOIN {user} otheruser ON otheruser.id = CASE
+                                WHEN message.useridto = :userid1 THEN message.useridfrom
+                                                                 ELSE message.useridto END
+         LEFT JOIN {message_contacts} contact ON contact.userid = :userid2 AND contact.contactid = otheruser.id
+
+             WHERE otheruser.deleted = 0
+               AND (message.useridto = :userid3 OR message.useridfrom = :userid4)
+               AND message.notification = 0
+               AND NOT EXISTS (
+                        SELECT 1
+                          FROM {message_read} othermessage
+                         WHERE ((othermessage.useridto = :userid5 AND othermessage.useridfrom = otheruser.id) OR
+                                (othermessage.useridfrom = :userid6 AND othermessage.useridto = otheruser.id))
+                           AND (othermessage.timecreated > message.timecreated OR (
+                                othermessage.timecreated = message.timecreated AND othermessage.id > message.id))
+                   )
+
+          ORDER BY message.timecreated DESC";
+    $params = array('userid1' => $user->id, 'userid2' => $user->id, 'userid3' => $user->id,
+            'userid4' => $user->id, 'userid5' => $user->id, 'userid6' => $user->id);
+    $read = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
+
+    // We want to get the messages that have not been read. These are stored in the 'message' table. It is the
+    // exact same query as the one above, except for the table we are querying. So, simply replace references to
+    // the 'message_read' table with the 'message' table.
+    $sql = str_replace('{message_read}', '{message}', $sql);
+    $unread = $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
 
     $conversations = array();
 
-    //Union the 2 result sets together looking for the message with the most recent timecreated for each other user
-    //$conversation->id (the array key) is the other user's ID
+    // Union the 2 result sets together looking for the message with the most
+    // recent timecreated for each other user.
+    // $conversation->id (the array key) is the other user's ID.
     $conversation_arrays = array($unread, $read);
     foreach ($conversation_arrays as $conversation_array) {
         foreach ($conversation_array as $conversation) {
@@ -802,7 +788,7 @@ function message_get_recent_notifications($user, $limitfrom=0, $limitto=100) {
               FROM {message_read} mr
                    JOIN {user} u ON u.id=mr.useridfrom
              WHERE mr.useridto = :userid1 AND u.deleted = '0' AND mr.notification = :notification
-             ORDER BY mr.id DESC";//ordering by id should give the same result as ordering by timecreated but will be faster
+             ORDER BY mr.timecreated DESC";
     $params = array('userid1' => $user->id, 'notification' => 1);
 
     $notifications =  $DB->get_records_sql($sql, $params, $limitfrom, $limitto);
@@ -2352,7 +2338,7 @@ function message_move_userfrom_unread2read($userid) {
  * @param int $fromuserid the id of the message sender
  * @return void
  */
-function message_mark_messages_read($touserid, $fromuserid){
+function message_mark_messages_read($touserid, $fromuserid) {
     global $DB;
 
     $sql = 'SELECT m.* FROM {message} m WHERE m.useridto=:useridto AND m.useridfrom=:useridfrom';
index 126e6af..0259d57 100644 (file)
@@ -62,6 +62,7 @@ class core_message_messagelib_testcase extends advanced_testcase {
      * @param stdClass $userfrom user object of the one sending the message.
      * @param stdClass $userto user object of the one receiving the message.
      * @param string $message message to send.
+     * @return int the id of the message
      */
     protected function send_fake_message($userfrom, $userto, $message = 'Hello world!') {
         global $DB;
@@ -72,7 +73,8 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $record->subject = 'No subject';
         $record->fullmessage = $message;
         $record->timecreated = time();
-        $insert = $DB->insert_record('message', $record);
+
+        return $DB->insert_record('message', $record);
     }
 
     /**
@@ -358,4 +360,81 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $this->assertEquals(false, message_search(array('Message'), true, true, 2));
         $this->assertCount(5, message_search(array('Message'), true, true, SITEID));
     }
+
+    /**
+     * Test message_get_recent_conversations.
+     */
+    public function test_message_get_recent_conversations() {
+        global $DB, $USER;
+
+        // Set this user as the admin.
+        $this->setAdminUser();
+
+        // Create user's to send messages to/from.
+        $user1 = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'user1'));
+        $user2 = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'user2'));
+
+        // Add a few messages that have been read and some that are unread.
+        $m1 = $this->send_fake_message($USER, $user1, 'Message 1'); // An unread message.
+        $m2 = $this->send_fake_message($user1, $USER, 'Message 2'); // An unread message.
+        $m3 = $this->send_fake_message($USER, $user1, 'Message 3'); // An unread message.
+        $m4 = message_post_message($USER, $user2, 'Message 4', FORMAT_PLAIN);
+        $m5 = message_post_message($user2, $USER, 'Message 5', FORMAT_PLAIN);
+        $m6 = message_post_message($USER, $user2, 'Message 6', FORMAT_PLAIN);
+
+        // We want to alter the timecreated values so we can ensure message_get_recent_conversations orders by timecreated.
+        $updatemessage = new stdClass();
+        $updatemessage->id = $m3;
+        $updatemessage->timecreated = 0;
+        $DB->update_record('message', $updatemessage);
+
+        $updatemessage->id = $m6;
+        $DB->update_record('message_read', $updatemessage);
+
+        // Get the recent conversations for the current user.
+        $conversations = message_get_recent_conversations($USER);
+
+        // Confirm that we have received the messages with the maximum timecreated, rather than the max id.
+        $this->assertEquals('Message 2', $conversations[0]->fullmessage);
+        $this->assertEquals('Message 5', $conversations[1]->smallmessage);
+    }
+
+    /**
+     * Test message_get_recent_notifications.
+     */
+    public function test_message_get_recent_notifications() {
+        global $DB, $USER;
+
+        // Set this user as the admin.
+        $this->setAdminUser();
+
+        // Create a user to send messages from.
+        $user1 = $this->getDataGenerator()->create_user(array('firstname' => 'Test1', 'lastname' => 'user1'));
+
+        // Add two messages - will mark them as notifications later.
+        $m1 = message_post_message($user1, $USER, 'Message 1', FORMAT_PLAIN);
+        $m2 = message_post_message($user1, $USER, 'Message 2', FORMAT_PLAIN);
+
+        // Mark the second message as a notification.
+        $updatemessage = new stdClass();
+        $updatemessage->id = $m2;
+        $updatemessage->notification = 1;
+        $DB->update_record('message_read', $updatemessage);
+
+        // Mark the first message as a notification and change the timecreated to 0.
+        $updatemessage->id = $m1;
+        $updatemessage->notification = 1;
+        $updatemessage->timecreated = 0;
+        $DB->update_record('message_read', $updatemessage);
+
+        $notifications = message_get_recent_notifications($USER);
+
+        // Get the messages.
+        $firstmessage = array_shift($notifications);
+        $secondmessage = array_shift($notifications);
+
+        // Confirm that we have received the notifications with the maximum timecreated, rather than the max id.
+        $this->assertEquals('Message 2', $firstmessage->smallmessage);
+        $this->assertEquals('Message 1', $secondmessage->smallmessage);
+    }
 }
index 5506a6b..115a04e 100644 (file)
@@ -294,9 +294,6 @@ class assign_grading_table extends table_sql implements renderable {
         if ($assignment->get_instance()->teamsubmission) {
             $columns[] = 'team';
             $headers[] = get_string('submissionteam', 'assign');
-
-            $columns[] = 'teamstatus';
-            $headers[] = get_string('teamsubmissionstatus', 'assign');
         }
         // Allocated marker.
         if ($this->assignment->get_instance()->markingallocation &&
@@ -417,7 +414,6 @@ class assign_grading_table extends table_sql implements renderable {
 
         if ($assignment->get_instance()->teamsubmission) {
             $this->no_sorting('team');
-            $this->no_sorting('teamstatus');
         }
 
         $plugincolumnindex = 0;
@@ -557,7 +553,11 @@ class assign_grading_table extends table_sql implements renderable {
             return '';
         }
         if ($this->is_downloading()) {
-            return $markers[$row->allocatedmarker];
+            if (isset($markers[$row->allocatedmarker])) {
+                return fullname($markers[$row->allocatedmarker]);
+            } else {
+                return '';
+            }
         }
 
         if ($this->quickgrading && has_capability('mod/assign:manageallocations', $this->assignment->get_context()) &&
@@ -673,26 +673,6 @@ class assign_grading_table extends table_sql implements renderable {
         }
     }
 
-
-    /**
-     * Get the team status for this user.
-     *
-     * @param stdClass $row
-     * @return string The team name
-     */
-    public function col_teamstatus(stdClass $row) {
-        $submission = false;
-        $group = false;
-        $this->get_group_and_submission($row->id, $group, $submission, -1);
-
-        $status = '';
-        if ($submission) {
-            $status = $submission->status;
-        }
-        return get_string('submissionstatus_' . $status, 'assign');
-    }
-
-
     /**
      * Format a list of outcomes.
      *
@@ -906,7 +886,12 @@ class assign_grading_table extends table_sql implements renderable {
     public function col_timesubmitted(stdClass $row) {
         $o = '-';
 
-        if ($row->timesubmitted) {
+        $group = false;
+        $submission = false;
+        $this->get_group_and_submission($row->id, $group, $submission, -1);
+        if ($group && $submission && $submission->timemodified) {
+            $o = userdate($submission->timemodified);
+        } else if ($row->timesubmitted) {
             $o = userdate($row->timesubmitted);
         }
 
@@ -929,12 +914,23 @@ class assign_grading_table extends table_sql implements renderable {
             $due = $row->extensionduedate;
         }
 
+        $group = false;
+        $submission = false;
+        $this->get_group_and_submission($row->id, $group, $submission, -1);
+        if ($group && $submission) {
+            $timesubmitted = $submission->timemodified;
+            $status = $submission->status;
+        } else {
+            $timesubmitted = $row->timesubmitted;
+            $status = $row->status;
+        }
+
         if ($this->assignment->is_any_submission_plugin_enabled()) {
 
-            $o .= $this->output->container(get_string('submissionstatus_' . $row->status, 'assign'),
-                                           array('class'=>'submissionstatus' .$row->status));
-            if ($due && $row->timesubmitted > $due) {
-                $usertime = format_time($row->timesubmitted - $due);
+            $o .= $this->output->container(get_string('submissionstatus_' . $status, 'assign'),
+                                           array('class'=>'submissionstatus' .$status));
+            if ($due && $timesubmitted > $due) {
+                $usertime = format_time($timesubmitted - $due);
                 $latemessage = get_string('submittedlateshort',
                                           'assign',
                                           $usertime);
@@ -950,15 +946,14 @@ class assign_grading_table extends table_sql implements renderable {
                 $o .= $this->col_workflowstatus($row);
             } else if ($row->grade !== null && $row->grade >= 0) {
                 $o .= $this->output->container(get_string('graded', 'assign'), 'submissiongraded');
-            }
-
-            if (!$row->timesubmitted) {
+            } else if (!$timesubmitted) {
                 $now = time();
                 if ($due && ($now > $due)) {
                     $overduestr = get_string('overdue', 'assign', format_time($now - $due));
                     $o .= $this->output->container($overduestr, 'overduesubmission');
                 }
             }
+
             if ($row->extensionduedate) {
                 $userdate = userdate($row->extensionduedate);
                 $extensionstr = get_string('userextensiondate', 'assign', $userdate);
index 3c0e43b..2d37dd2 100644 (file)
@@ -313,6 +313,7 @@ $string['quickgrading'] = 'Quick grading';
 $string['quickgradingresult'] = 'Quick grading';
 $string['quickgradingchangessaved'] = 'The grade changes were saved';
 $string['quickgrading_help'] = 'Quick grading allows you to assign grades (and outcomes) directly in the submissions table. Quick grading is not compatible with advanced grading and is not recommended when there are multiple markers.';
+$string['reopenuntilpassincompatiblewithblindmarking'] = 'Reopen until pass option is incompatible with blind marking, because the grades are not released to the gradebook until the student identities are revealed.';
 $string['requiresubmissionstatement'] = 'Require that students accept the submission statement';
 $string['requiresubmissionstatement_help'] = 'Require that students accept the submission statement for all submissions to this assignment.';
 $string['requireallteammemberssubmit'] = 'Require all group members submit';
@@ -423,7 +424,6 @@ $string['teamsubmission'] = 'Students submit in groups';
 $string['teamsubmission_help'] = 'If enabled students will be divided into groups based on the default set of groups or a custom grouping. A group submission will be shared among group members and all members of the group will see each others changes to the submission.';
 $string['teamsubmissiongroupingid'] = 'Grouping for student groups';
 $string['teamsubmissiongroupingid_help'] = 'This is the grouping that the assignment will use to find groups for student groups. If not set - the default set of groups will be used.';
-$string['teamsubmissionstatus'] = 'Group submission status';
 $string['textinstructions'] = 'Assignment instructions';
 $string['timemodified'] = 'Last modified';
 $string['timeremaining'] = 'Time remaining';
index 53413e9..a1776c5 100644 (file)
@@ -2303,9 +2303,9 @@ class assign {
     }
 
     /**
-     * Display a continue page.
+     * Display a continue page after grading.
      *
-     * @param string $message - The message to display
+     * @param string $message - The message to display.
      * @return string
      */
     protected function view_savegrading_result($message) {
@@ -2323,9 +2323,9 @@ class assign {
         return $o;
     }
     /**
-     * Display a grading error.
+     * Display a continue page after quickgrading.
      *
-     * @param string $message - The description of the result
+     * @param string $message - The message to display.
      * @return string
      */
     protected function view_quickgrading_result($message) {
@@ -6451,31 +6451,12 @@ class assign {
         $shouldreopen = false;
         if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) {
             // Check the gradetopass from the gradebook.
-            $gradinginfo = grade_get_grades($this->get_course()->id,
-                                            'mod',
-                                            'assign',
-                                            $instance->id,
-                                            $userid);
+            $gradeitem = $this->get_grade_item();
+            if ($gradeitem) {
+                $gradegrade = grade_grade::fetch(array('userid' => $userid, 'itemid' => $gradeitem->id));
 
-            // What do we do if the grade has not been added to the gradebook (e.g. blind marking)?
-            $gradingitem = null;
-            $gradebookgrade = null;
-            if (isset($gradinginfo->items[0])) {
-                $gradingitem = $gradinginfo->items[0];
-                $gradebookgrade = $gradingitem->grades[$userid];
-            }
-
-            if ($gradebookgrade) {
-                // TODO: This code should call grade_grade->is_passed().
-                $shouldreopen = true;
-                if (is_null($gradebookgrade->grade)) {
-                    $shouldreopen = false;
-                }
-                if (empty($gradingitem->gradepass) || $gradingitem->gradepass == $gradingitem->grademin) {
-                    $shouldreopen = false;
-                }
-                if ($gradebookgrade->grade >= $gradingitem->gradepass) {
-                    $shouldreopen = false;
+                if ($gradegrade && !$gradegrade->is_passed()) {
+                    $shouldreopen = true;
                 }
             }
         }
index eead2ae..5fd4ccd 100644 (file)
@@ -241,6 +241,9 @@ class mod_assign_mod_form extends moodleform_mod {
                 $errors['cutoffdate'] = get_string('cutoffdatefromdatevalidation', 'assign');
             }
         }
+        if ($data['blindmarking'] && $data['attemptreopenmethod'] == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) {
+            $errors['attemptreopenmethod'] = get_string('reopenuntilpassincompatiblewithblindmarking', 'assign');
+        }
 
         return $errors;
     }
index c4e2598..3758f30 100644 (file)
@@ -65,17 +65,21 @@ class assign_gradingmessage implements renderable {
     public $message = '';
     /** @var int $coursemoduleid */
     public $coursemoduleid = 0;
+    /** @var int $gradingerror should be set true if there was a problem grading */
+    public $gradingerror = null;
 
     /**
      * Constructor
      * @param string $heading This is the heading to display
      * @param string $message This is the message to display
+     * @param bool $gradingerror Set to true to display the message as an error.
      * @param int $coursemoduleid
      */
-    public function __construct($heading, $message, $coursemoduleid) {
+    public function __construct($heading, $message, $coursemoduleid, $gradingerror = false) {
         $this->heading = $heading;
         $this->message = $message;
         $this->coursemoduleid = $coursemoduleid;
+        $this->gradingerror = $gradingerror;
     }
 
 }
index 593494e..f6037a4 100644 (file)
@@ -92,10 +92,11 @@ class mod_assign_renderer extends plugin_renderer_base {
     public function render_assign_gradingmessage(assign_gradingmessage $result) {
         $urlparams = array('id' => $result->coursemoduleid, 'action'=>'grading');
         $url = new moodle_url('/mod/assign/view.php', $urlparams);
+        $classes = $result->gradingerror ? 'notifyproblem' : 'notifysuccess';
 
         $o = '';
         $o .= $this->output->heading($result->heading, 4);
-        $o .= $this->output->notification($result->message);
+        $o .= $this->output->notification($result->message, $classes);
         $o .= $this->output->continue_button($url);
         return $o;
     }
@@ -558,6 +559,7 @@ class mod_assign_renderer extends plugin_renderer_base {
         $row->cells = array($cell1, $cell2);
         $t->data[] = $row;
 
+        $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
         $duedate = $status->duedate;
         if ($duedate > 0) {
             // Due date.
@@ -592,8 +594,8 @@ class mod_assign_renderer extends plugin_renderer_base {
             $row = new html_table_row();
             $cell1 = new html_table_cell(get_string('timeremaining', 'assign'));
             if ($duedate - $time <= 0) {
-                if (!$status->submission ||
-                        $status->submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
+                if (!$submission ||
+                        $submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
                     if ($status->submissionsenabled) {
                         $overduestr = get_string('overdue', 'assign', format_time($time - $duedate));
                         $cell2 = new html_table_cell($overduestr);
@@ -602,16 +604,16 @@ class mod_assign_renderer extends plugin_renderer_base {
                         $cell2 = new html_table_cell(get_string('duedatereached', 'assign'));
                     }
                 } else {
-                    if ($status->submission->timemodified > $duedate) {
+                    if ($submission->timemodified > $duedate) {
                         $latestr = get_string('submittedlate',
                                               'assign',
-                                              format_time($status->submission->timemodified - $duedate));
+                                              format_time($submission->timemodified - $duedate));
                         $cell2 = new html_table_cell($latestr);
                         $cell2->attributes = array('class'=>'latesubmission');
                     } else {
                         $earlystr = get_string('submittedearly',
                                                'assign',
-                                               format_time($status->submission->timemodified - $duedate));
+                                               format_time($submission->timemodified - $duedate));
                         $cell2 = new html_table_cell($earlystr);
                         $cell2->attributes = array('class'=>'earlysubmission');
                     }
@@ -648,7 +650,6 @@ class mod_assign_renderer extends plugin_renderer_base {
         }
 
         // Last modified.
-        $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
         if ($submission) {
             $row = new html_table_row();
             $cell1 = new html_table_cell(get_string('timemodified', 'assign'));
index f43b0f6..d1cbbbc 100644 (file)
@@ -118,6 +118,173 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
         $this->editingteachers[0]->ignoresesskey = false;
     }
 
+    /**
+     * Test submissions with extension date.
+     */
+    public function test_gradingtable_extension_due_date() {
+        global $PAGE;
+
+        // Setup the assignment.
+        $this->create_extra_users();
+        $this->setUser($this->editingteachers[0]);
+        $assign = $this->create_instance(array(
+            'assignsubmission_onlinetext_enabled'=>1,
+            'duedate' => time() - 4 * 24 * 60 * 60,
+         ));
+        $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
+            'id' => $assign->get_course_module()->id,
+            'action' => 'grading',
+        )));
+
+        // Check that the assignment is late.
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
+        $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
+
+        // Grant an extension.
+        $assign->testable_save_user_extension($this->students[0]->id, time() + 2 * 24 * 60 * 60);
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
+        $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() + 2*24*60*60)), $output);
+
+        // Simulate a submission.
+        $this->setUser($this->students[0]);
+        $submission = $assign->get_user_submission($this->students[0]->id, true);
+        $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+        $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
+        $data = new stdClass();
+        $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+                                         'text'=>'Submission text',
+                                         'format'=>FORMAT_MOODLE);
+        $plugin = $assign->get_submission_plugin_by_type('onlinetext');
+        $plugin->save($submission, $data);
+
+        // Verify output.
+        $this->setUser($this->editingteachers[0]);
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
+        $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() + 2*24*60*60)), $output);
+    }
+
+    /**
+     * Test that late submissions with extension date calculate correctly.
+     */
+    public function test_gradingtable_extension_date_calculation_for_lateness() {
+        global $PAGE;
+
+        // Setup the assignment.
+        $this->create_extra_users();
+        $this->setUser($this->editingteachers[0]);
+        $assign = $this->create_instance(array(
+            'assignsubmission_onlinetext_enabled'=>1,
+            'duedate' => time() - 4 * 24 * 60 * 60,
+         ));
+        $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
+            'id' => $assign->get_course_module()->id,
+            'action' => 'grading',
+        )));
+
+        // Check that the assignment is late.
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
+        $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
+
+        // Grant an extension that is in the past.
+        $assign->testable_save_user_extension($this->students[0]->id, time() - 2 * 24 * 60 * 60);
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
+        $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() - 2*24*60*60)), $output);
+        $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60)), $output);
+
+        // Simulate a submission.
+        $this->setUser($this->students[0]);
+        $submission = $assign->get_user_submission($this->students[0]->id, true);
+        $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+        $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
+        $data = new stdClass();
+        $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+                                         'text'=>'Submission text',
+                                         'format'=>FORMAT_MOODLE);
+        $plugin = $assign->get_submission_plugin_by_type('onlinetext');
+        $plugin->save($submission, $data);
+
+        // Verify output.
+        $this->setUser($this->editingteachers[0]);
+        $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
+        $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() - 2*24*60*60)), $output);
+        $this->assertContains(get_string('submittedlateshort', 'assign', format_time(2*24*60*60)), $output);
+    }
+
+    /**
+     * Check that group submission information is rendered correctly in the
+     * grading table.
+     */
+    public function test_gradingtable_group_submissions_rendering() {
+        global $PAGE;
+
+        $this->create_extra_users();
+        // Now verify group assignments.
+        $this->setUser($this->teachers[0]);
+        $assign = $this->create_instance(array(
+            'teamsubmission' => 1,
+            'assignsubmission_onlinetext_enabled' => 1,
+            'submissiondrafts' => 1,
+            'requireallteammemberssubmit' => 0,
+        ));
+        $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
+            'id' => $assign->get_course_module()->id,
+            'action' => 'grading',
+        )));
+
+        // Add a submission.
+        $this->setUser($this->extrastudents[0]);
+        $data = new stdClass();
+        $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+                                         'text'=>'Submission text',
+                                         'format'=>FORMAT_MOODLE);
+        $notices = array();
+        $assign->save_submission($data, $notices);
+
+        $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
+        $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+        $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
+
+        // Check output.
+        $this->setUser($this->teachers[0]);
+        $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
+        $output = $assign->get_renderer()->render($gradingtable);
+        $document = new DOMDocument();
+        $document->loadHTML($output);
+        $xpath = new DOMXPath($document);
+
+        // Check status.
+        $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
+        $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
+
+        // Check submission last modified date
+        $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
+        $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
+
+        // Check group.
+        $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
+        $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
+
+        // Check submission text.
+        $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
+        $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
+
+        // Check comments can be made.
+        $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
+        $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
+    }
+
     public function test_show_intro() {
         // Test whether we are showing the intro at the correct times.
         $this->setUser($this->editingteachers[0]);
@@ -734,7 +901,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
     }
 
-    public function test_group_submissions_submit_for_marking() {
+    public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
         global $PAGE;
 
         $this->create_extra_users();
@@ -746,8 +913,8 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
                                                'requireallteammemberssubmit'=>1));
         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
 
-        $this->setUser($this->extrastudents[0]);
         // Add a submission.
+        $this->setUser($this->extrastudents[0]);
         $data = new stdClass();
         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
                                          'text'=>'Submission text',
@@ -780,6 +947,59 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
     }
 
+    public function test_group_submissions_submit_for_marking() {
+        global $PAGE;
+
+        $this->create_extra_users();
+        // Now verify group assignments.
+        $this->setUser($this->editingteachers[0]);
+        $assign = $this->create_instance(array('teamsubmission'=>1,
+                                               'assignsubmission_onlinetext_enabled'=>1,
+                                               'submissiondrafts'=>1,
+                                               'requireallteammemberssubmit'=>0,
+                                               'duedate' => time() - 2*24*60*60));
+        $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
+
+        $this->setUser($this->extrastudents[0]);
+        // Add a submission.
+        $data = new stdClass();
+        $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+                                         'text'=>'Submission text',
+                                         'format'=>FORMAT_MOODLE);
+
+        $notices = array();
+        $assign->save_submission($data, $notices);
+
+        // Check we can see the submit button.
+        $output = $assign->view_student_summary($this->extrastudents[0], true);
+        $this->assertContains(get_string('submitassignment', 'assign'), $output);
+        $this->assertContains(get_string('timeremaining', 'assign'), $output);
+        $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60)), $output);
+
+        $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
+        $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+        $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
+
+        // Check that the student does not see "Submit" button.
+        $output = $assign->view_student_summary($this->extrastudents[0], true);
+        $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
+
+        // Change to another user in the same group.
+        $this->setUser($this->extrastudents[self::GROUP_COUNT]);
+        $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
+        $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
+
+        // Check that time remaining is not overdue.
+        $this->assertContains(get_string('timeremaining', 'assign'), $output);
+        $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60)), $output);
+
+        $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
+        $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+        $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
+        $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
+        $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
+    }
+
     public function test_submissions_open() {
         $this->setUser($this->editingteachers[0]);
 
index 304e5d7..e086e12 100644 (file)
@@ -177,12 +177,12 @@ class feedback_item_textarea extends feedback_item_base {
         $data = $analysed_item->data;
         if (is_array($data)) {
             if (isset($data[0])) {
-                $worksheet->write_string($row_offset, 2, $data[0], $xls_formats->value_bold);
+                $worksheet->write_string($row_offset, 2, htmlspecialchars_decode($data[0], ENT_QUOTES), $xls_formats->value_bold);
             }
             $row_offset++;
             $sizeofdata = count($data);
             for ($i = 1; $i < $sizeofdata; $i++) {
-                $worksheet->write_string($row_offset, 2, $data[$i], $xls_formats->default);
+                $worksheet->write_string($row_offset, 2, htmlspecialchars_decode($data[$i], ENT_QUOTES), $xls_formats->default);
                 $row_offset++;
             }
         }
index ccbf806..77027c1 100644 (file)
@@ -167,11 +167,11 @@ class feedback_item_textfield extends feedback_item_base {
         $worksheet->write_string($row_offset, 1, $item->name, $xls_formats->head2);
         $data = $analysed_item->data;
         if (is_array($data)) {
-            $worksheet->write_string($row_offset, 2, $data[0], $xls_formats->value_bold);
+            $worksheet->write_string($row_offset, 2, htmlspecialchars_decode($data[0], ENT_QUOTES), $xls_formats->value_bold);
             $row_offset++;
             $sizeofdata = count($data);
             for ($i = 1; $i < $sizeofdata; $i++) {
-                $worksheet->write_string($row_offset, 2, $data[$i], $xls_formats->default);
+                $worksheet->write_string($row_offset, 2, htmlspecialchars_decode($data[$i], ENT_QUOTES), $xls_formats->default);
                 $row_offset++;
             }
         }
index 96eeb59..711ea10 100644 (file)
@@ -49,7 +49,7 @@ class user_report_viewed extends \core\event\base {
      */
     protected function init() {
         $this->data['crud'] = 'r';
-        $this->data['edulevel'] = self::LEVEL_OTHER;
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
     }
 
     /**
index 45cda24..024db75 100644 (file)
@@ -1358,10 +1358,68 @@ function forum_user_complete($course, $user, $mod, $forum) {
     }
 }
 
+/**
+ * Filters the forum discussions according to groups membership and config.
+ *
+ * @since  Moodle 2.8, 2.7.1, 2.6.4
+ * @param  array $discussions Discussions with new posts array
+ * @return array Forums with the number of new posts
+ */
+function forum_filter_user_groups_discussions($discussions) {
+
+    // Group the remaining discussions posts by their forumid.
+    $filteredforums = array();
+
+    // Discard not visible groups.
+    foreach ($discussions as $discussion) {
+
+        // Course data is already cached.
+        $instances = get_fast_modinfo($discussion->course)->get_instances();
+        $forum = $instances['forum'][$discussion->forum];
+
+        // Continue if the user should not see this discussion.
+        if (!forum_is_user_group_discussion($forum, $discussion->groupid)) {
+            continue;
+        }
+
+        // Grouping results by forum.
+        if (empty($filteredforums[$forum->instance])) {
+            $filteredforums[$forum->instance] = new stdClass();
+            $filteredforums[$forum->instance]->id = $forum->id;
+            $filteredforums[$forum->instance]->count = 0;
+        }
+        $filteredforums[$forum->instance]->count += $discussion->count;
 
+    }
+
+    return $filteredforums;
+}
+
+/**
+ * Returns whether the discussion group is visible by the current user or not.
+ *
+ * @since Moodle 2.8, 2.7.1, 2.6.4
+ * @param cm_info $cm The discussion course module
+ * @param int $discussiongroupid The discussion groupid
+ * @return bool
+ */
+function forum_is_user_group_discussion(cm_info $cm, $discussiongroupid) {
+
+    if ($discussiongroupid == -1 || $cm->effectivegroupmode != SEPARATEGROUPS) {
+        return true;
+    }
 
+    if (isguestuser()) {
+        return false;
+    }
 
+    if (has_capability('moodle/site:accessallgroups', context_module::instance($cm->id)) ||
+            in_array($discussiongroupid, $cm->get_modinfo()->get_groups($cm->groupingid))) {
+        return true;
+    }
 
+    return false;
+}
 
 /**
  * @global object
@@ -1388,12 +1446,12 @@ function forum_print_overview($courses,&$htmlarray) {
 
         // If the user has never entered into the course all posts are pending
         if ($course->lastaccess == 0) {
-            $coursessqls[] = '(f.course = ?)';
+            $coursessqls[] = '(d.course = ?)';
             $params[] = $course->id;
 
         // Only posts created after the course last access
         } else {
-            $coursessqls[] = '(f.course = ? AND p.created > ?)';
+            $coursessqls[] = '(d.course = ? AND p.created > ?)';
             $params[] = $course->id;
             $params[] = $course->lastaccess;
         }
@@ -1401,18 +1459,20 @@ function forum_print_overview($courses,&$htmlarray) {
     $params[] = $USER->id;
     $coursessql = implode(' OR ', $coursessqls);
 
-    $sql = "SELECT f.id, COUNT(*) as count "
-                .'FROM {forum} f '
-                .'JOIN {forum_discussions} d ON d.forum  = f.id '
+    $sql = "SELECT d.id, d.forum, d.course, d.groupid, COUNT(*) as count "
+                .'FROM {forum_discussions} d '
                 .'JOIN {forum_posts} p ON p.discussion = d.id '
                 ."WHERE ($coursessql) "
                 .'AND p.userid != ? '
-                .'GROUP BY f.id';
+                .'GROUP BY d.id, d.forum, d.course, d.groupid';
 
-    if (!$new = $DB->get_records_sql($sql, $params)) {
-        $new = array(); // avoid warnings
+    // Avoid warnings.
+    if (!$discussions = $DB->get_records_sql($sql, $params)) {
+        $discussions = array();
     }
 
+    $forumsnewposts = forum_filter_user_groups_discussions($discussions);
+
     // also get all forum tracking stuff ONCE.
     $trackingforums = array();
     foreach ($forums as $forum) {
@@ -1459,7 +1519,7 @@ function forum_print_overview($courses,&$htmlarray) {
         $unread = array();
     }
 
-    if (empty($unread) and empty($new)) {
+    if (empty($unread) and empty($forumsnewposts)) {
         return;
     }
 
@@ -1471,8 +1531,8 @@ function forum_print_overview($courses,&$htmlarray) {
         $thisunread = 0;
         $showunread = false;
         // either we have something from logs, or trackposts, or nothing.
-        if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
-            $count = $new[$forum->id]->count;
+        if (array_key_exists($forum->id, $forumsnewposts) && !empty($forumsnewposts[$forum->id])) {
+            $count = $forumsnewposts[$forum->id]->count;
         }
         if (array_key_exists($forum->id,$unread)) {
             $thisunread = $unread[$forum->id]->count;
@@ -1561,25 +1621,11 @@ function forum_print_recent_activity($course, $viewfullnames, $timestart) {
             }
         }
 
-        $groupmode = groups_get_activity_groupmode($cm, $course);
-
-        if ($groupmode) {
-            if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
-                // oki (Open discussions have groupid -1)
-            } else {
-                // separate mode
-                if (isguestuser()) {
-                    // shortcut
-                    continue;
-                }
-
-                if (!in_array($post->groupid, $modinfo->get_groups($cm->groupingid))) {
-                    continue;
-                }
-            }
+        // Check that the user can see the discussion.
+        if (forum_is_user_group_discussion($cm, $post->groupid)) {
+            $printposts[] = $post;
         }
 
-        $printposts[] = $post;
     }
     unset($posts);