MDL-64017 message_email: fixed incorrect unread count
[moodle.git] / message / output / email / classes / task / send_email_task.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Contains the class responsible for sending emails as a digest.
19  *
20  * @package    message_email
21  * @copyright  2019 Mark Nelson <markn@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 namespace message_email\task;
26 use core\task\scheduled_task;
27 use moodle_recordset;
29 defined('MOODLE_INTERNAL') || die();
31 /**
32  * Class responsible for sending emails as a digest.
33  *
34  * @package    message_email
35  * @copyright  2019 Mark Nelson <markn@moodle.com>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class send_email_task extends scheduled_task {
40     /**
41      * @var int $maxid This is the maximum id of the message in 'message_email_messages'.
42      *                 We use this so we know what records to process, as more records may be added
43      *                 while this task runs.
44      */
45     private $maxid;
47     /**
48      * Get a descriptive name for this task (shown to admins).
49      *
50      * @return string
51      */
52     public function get_name() {
53         return get_string('tasksendemail', 'message_email');
54     }
56     /**
57      * Send out emails.
58      */
59     public function execute() {
60         global $DB, $PAGE;
62         // Get the maximum id we are going to use.
63         // We use this as records may be added to the table while this task runs.
64         $this->maxid = $DB->get_field_sql("SELECT MAX(id) FROM {message_email_messages}");
66         // We are going to send these emails from 'noreplyaddress'.
67         $noreplyuser = \core_user::get_noreply_user();
69         // The renderers used for sending emails.
70         $htmlrenderer = $PAGE->get_renderer('message_email', 'email', 'htmlemail');
71         $textrenderer = $PAGE->get_renderer('message_email', 'email', 'textemail');
73         // Keep track of which emails failed to send.
74         $users = $this->get_unique_users();
75         foreach ($users as $user) {
76             $hascontent = false;
77             $renderable = new \message_email\output\email_digest($user);
78             $conversations = $this->get_conversations_for_user($user->id);
79             foreach ($conversations as $conversation) {
80                 $renderable->add_conversation($conversation);
81                 $messages = $this->get_users_messages_for_conversation($conversation->id, $user->id);
82                 if ($messages->valid()) {
83                     $hascontent = true;
84                     foreach ($messages as $message) {
85                         $renderable->add_message($message);
86                     }
87                 }
88                 $messages->close();
89             }
90             $conversations->close();
91             if ($hascontent) {
92                 $subject = get_string('emaildigestsubject', 'message_email');
93                 $message = $textrenderer->render($renderable);
94                 $messagehtml = $htmlrenderer->render($renderable);
95                 if (email_to_user($user, $noreplyuser, $subject, $message, $messagehtml)) {
96                     $DB->delete_records_select('message_email_messages', 'useridto = ? AND id <= ?', [$user->id, $this->maxid]);
97                 }
98             }
99         }
100         $users->close();
101     }
103     /**
104      * Returns an array of users in the given conversation.
105      *
106      * @return moodle_recordset A moodle_recordset instance.
107      */
108     private function get_unique_users() : moodle_recordset {
109         global $DB;
111         $subsql = 'SELECT DISTINCT(useridto) as id
112                      FROM {message_email_messages}
113                     WHERE id <= ?';
115         $sql = "SELECT *
116                   FROM {user} u
117                  WHERE id IN ($subsql)";
119         return $DB->get_recordset_sql($sql, [$this->maxid]);
120     }
122     /**
123      * Returns an array of unique conversations that require processing.
124      *
125      * @param int $userid The ID of the user we are sending a digest to.
126      * @return moodle_recordset A moodle_recordset instance.
127      */
128     private function get_conversations_for_user(int $userid) : moodle_recordset {
129         global $DB;
131         // We shouldn't be joining directly on the group table as group
132         // conversations may (in the future) be something created that
133         // isn't related to an actual group in a course. However, for
134         // now this will have to do before 3.7 code freeze.
135         // See related MDL-63814.
136         $sql = "SELECT DISTINCT mc.id, mc.name, c.id as courseid, c.fullname as coursename, g.id as groupid, 
137                                 g.picture, g.hidepicture
138                   FROM {message_conversations} mc
139                   JOIN {groups} g
140                     ON mc.itemid = g.id
141                   JOIN {course} c
142                     ON g.courseid = c.id
143                   JOIN {message_email_messages} mem
144                     ON mem.conversationid = mc.id  
145                  WHERE mem.useridto = ?
146                    AND mem.id <= ?";
148         return $DB->get_recordset_sql($sql, [$userid, $this->maxid]);
149     }
151     /**
152      * Returns the messages to send to a user for a given conversation
153      *
154      * @param int $conversationid
155      * @param int $userid
156      * @return moodle_recordset A moodle_recordset instance.
157      */
158     protected function get_users_messages_for_conversation(int $conversationid, int $userid) : moodle_recordset {
159         global $DB;
161         $usernamefields = \user_picture::fields('u');
162         $sql = "SELECT $usernamefields, m.*
163                   FROM {messages} m
164                   JOIN {user} u
165                     ON u.id = m.useridfrom
166                   JOIN {message_email_messages} mem
167                     ON mem.messageid = m.id
168                  WHERE mem.useridto = ?
169                    AND mem.conversationid = ?
170                    AND mem.id <= ?";
172         return $DB->get_recordset_sql($sql, [$userid, $conversationid, $this->maxid]);
173     }