MDL-4908 Forum: per-forum digests
authorAndrew Nicols <andrew@nicols.co.uk>
Fri, 5 Jul 2013 00:12:36 +0000 (01:12 +0100)
committerAndrew Nicols <andrew@nicols.co.uk>
Tue, 13 Aug 2013 13:46:10 +0000 (14:46 +0100)
mod/forum/db/install.xml
mod/forum/db/upgrade.php [changed mode: 0644->0755]
mod/forum/index.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/maildigest.php [new file with mode: 0644]
mod/forum/version.php

index 17ab229..76dcc03 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/forum/db" VERSION="20130205" COMMENT="XMLDB file for Moodle mod/forum"
+<XMLDB PATH="mod/forum/db" VERSION="20130710" COMMENT="XMLDB file for Moodle mod/forum"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
         <INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
       </INDEXES>
     </TABLE>
+    <TABLE NAME="forum_digests" COMMENT="Keeps track of user mail delivery preferences for each forum">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="forum" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="maildigest" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="-1" SEQUENCE="false"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+        <KEY NAME="forum" TYPE="foreign" FIELDS="forum" REFTABLE="forum" REFFIELDS="id"/>
+        <KEY NAME="forumdigest" TYPE="unique" FIELDS="forum, userid, maildigest"/>
+      </KEYS>
+    </TABLE>
     <TABLE NAME="forum_read" COMMENT="Tracks each users read posts">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
       </INDEXES>
     </TABLE>
   </TABLES>
-</XMLDB>
\ No newline at end of file
+</XMLDB>
old mode 100644 (file)
new mode 100755 (executable)
index 7603626..703e93f
@@ -84,9 +84,30 @@ function xmldb_forum_upgrade($oldversion) {
 
     // Moodle v2.5.0 release upgrade line.
     // Put any upgrade step following this.
+    if ($oldversion < 2013071000) {
+        // Define table forum_digests to be created.
+        $table = new xmldb_table('forum_digests');
+
+        // Adding fields to table forum_digests.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('maildigest', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '-1');
+
+        // Adding keys to table forum_digests.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
+        $table->add_key('forumdigest', XMLDB_KEY_UNIQUE, array('forum', 'userid', 'maildigest'));
+
+        // Conditionally launch create table for forum_digests.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
 
+        // Forum savepoint reached.
+        upgrade_mod_savepoint(true, 2013071000, 'forum');
+    }
 
     return true;
 }
-
-
index fc9903d..93b1656 100644 (file)
@@ -68,9 +68,21 @@ $strunsubscribe  = get_string('unsubscribe', 'forum');
 $stryes          = get_string('yes');
 $strno           = get_string('no');
 $strrss          = get_string('rss');
+$stremaildigest  = get_string('emaildigest');
 
 $searchform = forum_search_form($course);
 
+// Retrieve the list of forum digest options for later.
+$digestoptions = forum_get_user_digest_options();
+$digestoptions_selector = new single_select(new moodle_url('/mod/forum/maildigest.php',
+    array(
+        'backtoindex' => 1,
+    )),
+    'maildigest',
+    $digestoptions,
+    null,
+    '');
+$digestoptions_selector->method = 'post';
 
 // Start of the table for General Forums
 
@@ -94,6 +106,9 @@ $can_subscribe = is_enrolled($coursecontext);
 if ($can_subscribe) {
     $generaltable->head[] = $strsubscribed;
     $generaltable->align[] = 'center';
+
+    $generaltable->head[] = $stremaildigest . ' ' . $OUTPUT->help_icon('emaildigesttype', 'mod_forum');
+    $generaltable->align[] = 'center';
 }
 
 if ($show_rss = (($can_subscribe || $course->id == SITEID) &&
@@ -111,7 +126,13 @@ $table = new html_table();
 // some special ones are not.  These get placed in the general forums
 // category with the forums in section 0.
 
-$forums = $DB->get_records('forum', array('course' => $course->id));
+$forums = $DB->get_records_sql("
+    SELECT f.*,
+           d.maildigest
+      FROM {forum} f
+ LEFT JOIN {forum_digests} d ON d.forum = f.id AND d.userid = ?
+     WHERE f.course = ?
+    ", array($USER->id, $course->id));
 
 $generalforums  = array();
 $learningforums = array();
@@ -252,6 +273,14 @@ if ($generalforums) {
             } else {
                 $row[] = '-';
             }
+
+            $digestoptions_selector->url->param('id', $forum->id);
+            if ($forum->maildigest === null) {
+                $digestoptions_selector->selected = -1;
+            } else {
+                $digestoptions_selector->selected = $forum->maildigest;
+            }
+            $row[] = $OUTPUT->render($digestoptions_selector);
         }
 
         //If this forum has RSS activated, calculate it
@@ -297,6 +326,9 @@ if ($usetracking) {
 if ($can_subscribe) {
     $learningtable->head[] = $strsubscribed;
     $learningtable->align[] = 'center';
+
+    $learningtable->head[] = $stremaildigest . ' ' . $OUTPUT->help_icon('emaildigesttype', 'mod_forum');
+    $learningtable->align[] = 'center';
 }
 
 if ($show_rss = (($can_subscribe || $course->id == SITEID) &&
@@ -390,6 +422,14 @@ if ($course->id != SITEID) {    // Only real courses have learning forums
                 } else {
                     $row[] = '-';
                 }
+
+                $digestoptions_selector->url->param('id', $forum->id);
+                if ($forum->maildigest === null) {
+                    $digestoptions_selector->selected = -1;
+                } else {
+                    $digestoptions_selector->selected = $forum->maildigest;
+                }
+                $row[] = $OUTPUT->render($digestoptions_selector);
             }
 
             //If this forum has RSS activated, calculate it
index bbf4ffb..db3eabf 100644 (file)
@@ -142,6 +142,23 @@ $string['edit'] = 'Edit';
 $string['editedby'] = 'Edited by {$a->name} - original submission {$a->date}';
 $string['editedpostupdated'] = '{$a}\'s post was updated';
 $string['editing'] = 'Editing';
+$string['emaildigestcompleteshort'] = 'Complete posts';
+$string['emaildigestdefault'] = 'Default ({$a})';
+$string['emaildigestoffshort'] = 'No digest';
+$string['emaildigestsubjectsshort'] = 'Subjects only';
+$string['emaildigesttype'] = 'Email digest options';
+$string['emaildigesttype_help'] = 'The type of notification that you will receive for each forum.
+
+* Default - follow the digest setting found in your user profile. If you update your profile, then that change will be reflected here too;
+* No digest - you will receive one e-mail per forum post;
+* Digest - complete posts - you will receive one digest e-mail per day containing the complete contents of each forum post;
+* Digest - subjects only - you will receive one digest e-mail per day containing just the subject of each forum post.
+';
+$string['emaildigestupdated'] = 'The e-mail digest option was changed to \'{$a->maildigesttitle}\' for the forum \'{$a->forum}\'. {$a->maildigestdescription}';
+$string['emaildigestupdated_default'] = 'Your default profile setting of \'{$a->maildigesttitle}\' was used for the forum \'{$a->forum}\'. {$a->maildigestdescription}.';
+$string['emaildigest_0'] = 'You will receive one e-mail per forum post.';
+$string['emaildigest_1'] = 'You will receive one digest e-mail per day containing the  complete contents of each forum post.';
+$string['emaildigest_2'] = 'You will receive one digest e-mail per day containing the subject of each forum post.';
 $string['emptymessage'] = 'Something was wrong with your post. Perhaps you left it blank, or the attachment was too big. Your changes have NOT been saved.';
 $string['erroremptymessage'] = 'Post message cannot be empty';
 $string['erroremptysubject'] = 'Post subject cannot be empty.';
@@ -208,6 +225,7 @@ $string['introsocial'] = 'An open forum for chatting about anything you want to'
 $string['introteacher'] = 'A forum for teacher-only notes and discussion';
 $string['invalidaccess'] = 'This page was not accessed correctly';
 $string['invaliddiscussionid'] = 'Discussion ID was incorrect or no longer exists';
+$string['invaliddigestsetting'] = 'An invalid mail digest setting was provided';
 $string['invalidforcesubscribe'] = 'Invalid force subscription mode';
 $string['invalidforumid'] = 'Forum ID was incorrect';
 $string['invalidparentpostid'] = 'Parent post ID was incorrect';
@@ -346,6 +364,7 @@ $string['replyforum'] = 'Reply to forum';
 $string['replytouser'] = 'Use email address in reply';
 $string['resetforums'] = 'Delete posts from';
 $string['resetforumsall'] = 'Delete all posts';
+$string['resetdigests'] = 'Delete all per-user forum digest preferences';
 $string['resetsubscriptions'] = 'Delete all forum subscriptions';
 $string['resettrackprefs'] = 'Delete all forum tracking preferences';
 $string['rsssubscriberssdiscussions'] = 'RSS feed of discussions';
index 245179d..47352b5 100644 (file)
@@ -264,6 +264,10 @@ function forum_delete_instance($id) {
         }
     }
 
+    if (!$DB->delete_records('forum_digests', array('forum' => $forum->id))) {
+        $result = false;
+    }
+
     if (!$DB->delete_records('forum_subscriptions', array('forum'=>$forum->id))) {
         $result = false;
     }
@@ -457,6 +461,17 @@ function forum_cron() {
     $endtime   = $timenow - $CFG->maxeditingtime;
     $starttime = $endtime - 48 * 3600;   // Two days earlier
 
+    // Get the list of forum subscriptions for per-user per-forum maildigest settings.
+    $digestsset = $DB->get_recordset('forum_digests', null, '', 'id, userid, forum, maildigest');
+    $digests = array();
+    foreach ($digestsset as $thisrow) {
+        if (!isset($digests[$thisrow->forum])) {
+            $digests[$thisrow->forum] = array();
+        }
+        $digests[$thisrow->forum][$thisrow->userid] = $thisrow->maildigest;
+    }
+    $digestsset->close();
+
     if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
         // Mark them all now as being mailed.  It's unlikely but possible there
         // might be an error later so that a post is NOT actually mailed out,
@@ -666,7 +681,9 @@ function forum_cron() {
                 // OK so we need to send the email.
 
                 // Does the user want this post in a digest?  If so postpone it for now.
-                if ($userto->maildigest > 0) {
+                $maildigest = forum_get_user_maildigest_bulk($digests, $userto, $forum->id);
+
+                if ($maildigest > 0) {
                     // This user wants the mails to be in digest form
                     $queue = new stdClass();
                     $queue->userid       = $userto->id;
@@ -991,7 +1008,8 @@ function forum_cron() {
 
                         $userfrom->customheaders = array ("Precedence: Bulk");
 
-                        if ($userto->maildigest == 2) {
+                        $maildigest = forum_get_user_maildigest_bulk($digests, $userto, $forum->id);
+                        if ($maildigest == 2) {
                             // Subjects and link only
                             $posttext .= "\n";
                             $posttext .= $CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id;
@@ -4820,7 +4838,8 @@ function forum_subscribe($userid, $forumid) {
  */
 function forum_unsubscribe($userid, $forumid) {
     global $DB;
-    return $DB->delete_records("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid));
+    return ($DB->delete_records('forum_digests', array('userid' => $userid, 'forum' => $forumid))
+            && $DB->delete_records('forum_subscriptions', array('userid' => $userid, 'forum' => $forumid)));
 }
 
 /**
@@ -6278,6 +6297,7 @@ function forum_user_unenrolled($cp) {
         $params = array('userid'=>$cp->userid, 'courseid'=>$cp->courseid);
         $forumselect = "IN (SELECT f.id FROM {forum} f WHERE f.course = :courseid)";
 
+        $DB->delete_records_select('forum_digests',       "userid = :userid AND forum $forumselect", $params);
         $DB->delete_records_select('forum_subscriptions', "userid = :userid AND forum $forumselect", $params);
         $DB->delete_records_select('forum_track_prefs',   "userid = :userid AND forumid $forumselect", $params);
         $DB->delete_records_select('forum_read',          "userid = :userid AND forumid $forumselect", $params);
@@ -7321,6 +7341,12 @@ function forum_reset_userdata($data) {
         }
     }
 
+    // remove all digest settings unconditionally - even for users still enrolled in course.
+    if (!empty($data->reset_forum_digests)) {
+        $DB->delete_records_select('forum_digests', "forum IN ($allforumssql)", $params);
+        $status[] = array('component' => $componentstr, 'item' => get_string('resetdigests', 'forum'), 'error' => false);
+    }
+
     // remove all subscriptions unconditionally - even for users still enrolled in course
     if (!empty($data->reset_forum_subscriptions)) {
         $DB->delete_records_select('forum_subscriptions', "forum IN ($allforumssql)", $params);
@@ -7356,6 +7382,9 @@ function forum_reset_course_form_definition(&$mform) {
     $mform->setAdvanced('reset_forum_types');
     $mform->disabledIf('reset_forum_types', 'reset_forum_all', 'checked');
 
+    $mform->addElement('checkbox', 'reset_forum_digests', get_string('resetdigests','forum'));
+    $mform->setAdvanced('reset_forum_digests');
+
     $mform->addElement('checkbox', 'reset_forum_subscriptions', get_string('resetsubscriptions','forum'));
     $mform->setAdvanced('reset_forum_subscriptions');
 
@@ -7372,7 +7401,7 @@ function forum_reset_course_form_definition(&$mform) {
  * @return array
  */
 function forum_reset_course_form_defaults($course) {
-    return array('reset_forum_all'=>1, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1);
+    return array('reset_forum_all'=>1, 'reset_forum_digests' => 0, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1);
 }
 
 /**
@@ -8444,3 +8473,118 @@ function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false,
 
     return $return;
 }
+
+/**
+ * Set the per-forum maildigest option for the specified user.
+ *
+ * @param stdClass $forum The forum to set the option for.
+ * @param int $maildigest The maildigest option.
+ * @param stdClass $user The user object. This defaults to the global $USER object.
+ * @throws invalid_digest_setting thrown if an invalid maildigest option is provided.
+ */
+function forum_set_user_maildigest($forum, $maildigest, $user = null) {
+    global $DB, $USER;
+
+    if (is_number($forum)) {
+        $forum = $DB->get_record('forum', array('id' => $forum));
+    }
+
+    if ($user === null) {
+        $user = $USER;
+    }
+
+    $course  = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
+    $cm      = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
+    $context = context_module::instance($cm->id);
+
+    // User must be allowed to see this forum.
+    require_capability('mod/forum:viewdiscussion', $context, $user->id);
+
+    // Validate the maildigest setting.
+    $digestoptions = forum_get_user_digest_options($user);
+
+    if (!isset($digestoptions[$maildigest])) {
+        throw new moodle_exception('invaliddigestsetting', 'mod_forum');
+    }
+
+    // Attempt to retrieve any existing forum digest record.
+    $subscription = $DB->get_record('forum_digests', array(
+        'userid' => $user->id,
+        'forum' => $forum->id,
+    ));
+
+    // Create or Update the existing maildigest setting.
+    if ($subscription) {
+        if ($maildigest == -1) {
+            $DB->delete_records('forum_digests', array('forum' => $forum->id, 'userid' => $user->id));
+        } else if ($maildigest !== $subscription->maildigest) {
+            // Only update the maildigest setting if it's changed.
+
+            $subscription->maildigest = $maildigest;
+            $DB->update_record('forum_digests', $subscription);
+        }
+    } else {
+        if ($maildigest != -1) {
+            // Only insert the maildigest setting if it's non-default.
+
+            $subscription = new stdClass();
+            $subscription->forum = $forum->id;
+            $subscription->userid = $user->id;
+            $subscription->maildigest = $maildigest;
+            $subscription->id = $DB->insert_record('forum_digests', $subscription);
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Determine the maildigest setting for the specified user against the
+ * specified forum.
+ *
+ * @param Array $digests An array of forums and user digest settings.
+ * @param stdClass $user The user object containing the id and maildigest default.
+ * @param int $forumid The ID of the forum to check.
+ * @return int The calculated maildigest setting for this user and forum.
+ */
+function forum_get_user_maildigest_bulk($digests, $user, $forumid) {
+    if (isset($digests[$forumid]) && isset($digests[$forumid][$user->id])) {
+        $maildigest = $digests[$forumid][$user->id];
+        if ($maildigest === -1) {
+            $maildigest = $user->maildigest;
+        }
+    } else {
+        $maildigest = $user->maildigest;
+    }
+    return $maildigest;
+}
+
+/**
+ * Retrieve the list of available user digest options.
+ *
+ * @param stdClass $user The user object. This defaults to the global $USER object.
+ * @return array The mapping of values to digest options.
+ */
+function forum_get_user_digest_options($user = null) {
+    global $USER;
+
+    // Revert to the global user object.
+    if ($user === null) {
+        $user = $USER;
+    }
+
+    $digestoptions = array();
+    $digestoptions['0']  = get_string('emaildigestoffshort', 'mod_forum');
+    $digestoptions['1']  = get_string('emaildigestcompleteshort', 'mod_forum');
+    $digestoptions['2']  = get_string('emaildigestsubjectsshort', 'mod_forum');
+
+    // We need to add the default digest option at the end - it relies on
+    // the contents of the existing values.
+    $digestoptions['-1'] = get_string('emaildigestdefault', 'mod_forum',
+            $digestoptions[$user->maildigest]);
+
+    // Resort the options to be in a sensible order.
+    ksort($digestoptions);
+
+    return $digestoptions;
+}
diff --git a/mod/forum/maildigest.php b/mod/forum/maildigest.php
new file mode 100644 (file)
index 0000000..e7fbd88
--- /dev/null
@@ -0,0 +1,77 @@
+<?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/>.
+
+/**
+ * Set the mail digest option in a specific forum for a user.
+ *
+ * @copyright 2013 Andrew Nicols
+ * @package   mod_forum
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(__DIR__)) . '/config.php');
+require_once($CFG->dirroot.'/mod/forum/lib.php');
+
+$id = required_param('id', PARAM_INT);
+$maildigest = required_param('maildigest', PARAM_INT);
+$backtoindex = optional_param('backtoindex', 0, PARAM_INT);
+
+// We must have a valid session key.
+require_sesskey();
+
+$forum = $DB->get_record('forum', array('id' => $id));
+$course  = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
+$cm      = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
+$context = context_module::instance($cm->id);
+
+require_course_login($course);
+
+$url = new moodle_url('/mod/forum/maildigest.php', array(
+    'id' => $id,
+    'maildigest' => $maildigest,
+));
+$PAGE->set_url($url);
+$PAGE->set_context($context);
+
+$digestoptions = forum_get_user_digest_options();
+
+$info = new stdClass();
+$info->name  = fullname($USER);
+$info->forum = format_string($forum->name);
+$info->maildigest = forum_set_user_maildigest($forum, $maildigest);
+
+if ($maildigest === -1) {
+    // Get the default maildigest options.
+    $info->maildigest = $USER->maildigest;
+    $info->maildigesttitle = $digestoptions[$info->maildigest];
+    $info->maildigestdescription = get_string('emaildigest_' . $info->maildigest,
+        'mod_forum', $info);
+    $updatemessage = get_string('emaildigestupdated_default', 'forum', $info);
+} else {
+    $info->maildigesttitle = $digestoptions[$info->maildigest];
+    $info->maildigestdescription = get_string('emaildigest_' . $info->maildigest,
+        'mod_forum', $info);
+    $updatemessage = get_string('emaildigestupdated', 'forum', $info);
+}
+
+if ($backtoindex) {
+    $returnto = "index.php?id={$course->id}";
+} else {
+    $returnto = "view.php?f={$id}";
+}
+
+redirect($returnto, $updatemessage, 1);
index cbd7595..4c62e97 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version   = 2013050100;       // The current module version (Date: YYYYMMDDXX)
+$module->version   = 2013071000;       // The current module version (Date: YYYYMMDDXX)
 $module->requires  = 2013050100;       // Requires this Moodle version
 $module->component = 'mod_forum';      // Full name of the plugin (used for diagnostics)
 $module->cron      = 60;