MDL-62709 core_message: convert NULL format types in task
[moodle.git] / message / classes / task / migrate_message_data.php
CommitLineData
41bc5aa5
MN
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/>.
16
17/**
18 * Adhoc task handling migrating data to the new messaging table schema.
19 *
20 * @package core_message
21 * @copyright 2018 Mark Nelson <markn@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace core_message\task;
26
27defined('MOODLE_INTERNAL') || die();
28
29/**
30 * Class handling migrating data to the new messaging table schema.
31 *
32 * @package core_message
33 * @copyright 2018 Mark Nelson <markn@moodle.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36class migrate_message_data extends \core\task\adhoc_task {
37
38 /**
39 * Run the migration task.
40 */
41 public function execute() {
42 global $DB;
43
44 $userid = $this->get_custom_data()->userid;
45
46 // Get the user's preference.
47 $hasbeenmigrated = get_user_preferences('core_message_migrate_data', false, $userid);
48
49 if (!$hasbeenmigrated) {
50 // To determine if we should update the preference.
51 $updatepreference = true;
52
53 // Get all the users the current user has received a message from.
54 $sql = "SELECT DISTINCT(useridfrom)
55 FROM {message} m
56 WHERE useridto = ?
57 UNION
58 SELECT DISTINCT(useridfrom)
59 FROM {message_read} m
60 WHERE useridto = ?";
61 $users = $DB->get_records_sql($sql, [$userid, $userid]);
62
63 // Get all the users the current user has messaged.
64 $sql = "SELECT DISTINCT(useridto)
65 FROM {message} m
66 WHERE useridfrom = ?
67 UNION
68 SELECT DISTINCT(useridto)
69 FROM {message_read} m
70 WHERE useridfrom = ?";
71 $users = $users + $DB->get_records_sql($sql, [$userid, $userid]);
72 if (!empty($users)) {
73 // Loop through each user and migrate the data.
74 foreach ($users as $otheruserid => $user) {
75 $ids = [$userid, $otheruserid];
76 sort($ids);
77 $key = implode('_', $ids);
78
79 // Set the lock data.
80 $timeout = 5; // In seconds.
81 $locktype = 'core_message_migrate_data';
82
83 // Get an instance of the currently configured lock factory.
84 $lockfactory = \core\lock\lock_config::get_lock_factory($locktype);
85
86 // See if we can grab this lock.
87 if ($lock = $lockfactory->get_lock($key, $timeout)) {
88 try {
89 $transaction = $DB->start_delegated_transaction();
90 $this->migrate_data($userid, $otheruserid);
91 $transaction->allow_commit();
92 } catch (\Throwable $e) {
8791c50d
DP
93 throw $e;
94 } finally {
95 $lock->release();
41bc5aa5 96 }
41bc5aa5
MN
97 } else {
98 // Couldn't get a lock, move on to next user but make sure we don't update user preference so
99 // we still try again.
100 $updatepreference = false;
101 continue;
102 }
103 }
104 }
105
106 if ($updatepreference) {
107 set_user_preference('core_message_migrate_data', true, $userid);
108 } else {
109 // Throwing an exception in the task will mean that it isn't removed from the queue and is tried again.
110 throw new \moodle_exception('Task failed.');
111 }
112 }
113 }
114
115 /**
116 * Helper function to deal with migrating the data.
117 *
118 * @param int $userid The current user id.
119 * @param int $otheruserid The user id of the other user in the conversation.
120 * @throws \dml_exception
121 */
122 private function migrate_data($userid, $otheruserid) {
123 global $DB;
124
125 if (!$conversationid = \core_message\api::get_conversation_between_users([$userid, $otheruserid])) {
126 $conversationid = \core_message\api::create_conversation_between_users([$userid, $otheruserid]);
127 }
128
129 // First, get the rows from the 'message' table.
130 $select = "(useridfrom = ? AND useridto = ?) OR (useridfrom = ? AND useridto = ?)";
131 $params = [$userid, $otheruserid, $otheruserid, $userid];
132 $messages = $DB->get_recordset_select('message', $select, $params, 'id ASC');
133 foreach ($messages as $message) {
134 if ($message->notification) {
84f6a716 135 $this->migrate_notification($message, false);
41bc5aa5
MN
136 } else {
137 $this->migrate_message($conversationid, $message);
138 }
139 }
140 $messages->close();
141
142 // Ok, all done, delete the records from the 'message' table.
143 $DB->delete_records_select('message', $select, $params);
144
145 // Now, get the rows from the 'message_read' table.
146 $messages = $DB->get_recordset_select('message_read', $select, $params, 'id ASC');
147 foreach ($messages as $message) {
148 if ($message->notification) {
84f6a716 149 $this->migrate_notification($message, true);
41bc5aa5
MN
150 } else {
151 $this->migrate_message($conversationid, $message);
152 }
153 }
154 $messages->close();
155
156 // Ok, all done, delete the records from the 'message_read' table.
157 $DB->delete_records_select('message_read', $select, $params);
158 }
159
160 /**
161 * Helper function to deal with migrating an individual notification.
162 *
163 * @param \stdClass $notification
84f6a716 164 * @param bool $isread Was the notification read?
41bc5aa5
MN
165 * @throws \dml_exception
166 */
84f6a716 167 private function migrate_notification($notification, $isread) {
41bc5aa5
MN
168 global $DB;
169
170 $tabledata = new \stdClass();
171 $tabledata->useridfrom = $notification->useridfrom;
172 $tabledata->useridto = $notification->useridto;
173 $tabledata->subject = $notification->subject;
174 $tabledata->fullmessage = $notification->fullmessage;
cd9ecbd8 175 $tabledata->fullmessageformat = $notification->fullmessageformat ?? FORMAT_MOODLE;
41bc5aa5
MN
176 $tabledata->fullmessagehtml = $notification->fullmessagehtml;
177 $tabledata->smallmessage = $notification->smallmessage;
178 $tabledata->component = $notification->component;
179 $tabledata->eventtype = $notification->eventtype;
180 $tabledata->contexturl = $notification->contexturl;
181 $tabledata->contexturlname = $notification->contexturlname;
182 $tabledata->timeread = $notification->timeread ?? null;
183 $tabledata->timecreated = $notification->timecreated;
184
84f6a716
MN
185 $newid = $DB->insert_record('notifications', $tabledata);
186
187 // Check if there is a record to move to the new 'message_popup_notifications' table.
188 if ($mp = $DB->get_record('message_popup', ['messageid' => $notification->id, 'isread' => (int) $isread])) {
189 $mpn = new \stdClass();
190 $mpn->notificationid = $newid;
191 $DB->insert_record('message_popup_notifications', $mpn);
192
193 $DB->delete_records('message_popup', ['id' => $mp->id]);
194 }
41bc5aa5
MN
195 }
196
197 /**
198 * Helper function to deal with migrating an individual message.
199 *
200 * @param int $conversationid The conversation between the two users.
201 * @param \stdClass $message The message from either the 'message' or 'message_read' table
202 * @throws \dml_exception
203 */
204 private function migrate_message($conversationid, $message) {
205 global $DB;
206
207 // Create the object we will be inserting into the database.
208 $tabledata = new \stdClass();
209 $tabledata->useridfrom = $message->useridfrom;
210 $tabledata->conversationid = $conversationid;
211 $tabledata->subject = $message->subject;
212 $tabledata->fullmessage = $message->fullmessage;
cd9ecbd8 213 $tabledata->fullmessageformat = $message->fullmessageformat ?? FORMAT_MOODLE;
41bc5aa5
MN
214 $tabledata->fullmessagehtml = $message->fullmessagehtml;
215 $tabledata->smallmessage = $message->smallmessage;
216 $tabledata->timecreated = $message->timecreated;
217
218 $messageid = $DB->insert_record('messages', $tabledata);
219
220 // Check if we need to mark this message as deleted for the user from.
221 if ($message->timeuserfromdeleted) {
222 $mua = new \stdClass();
223 $mua->userid = $message->useridfrom;
224 $mua->messageid = $messageid;
225 $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
226 $mua->timecreated = $message->timeuserfromdeleted;
227
228 $DB->insert_record('message_user_actions', $mua);
229 }
230
231 // Check if we need to mark this message as deleted for the user to.
232 if ($message->timeusertodeleted) {
233 $mua = new \stdClass();
234 $mua->userid = $message->useridto;
235 $mua->messageid = $messageid;
236 $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
237 $mua->timecreated = $message->timeusertodeleted;
238
239 $DB->insert_record('message_user_actions', $mua);
240 }
241
242 // Check if we need to mark this message as read for the user to (it is always read by the user from).
243 // Note - we do an isset() check here because this column only exists in the 'message_read' table.
244 if (isset($message->timeread)) {
245 $mua = new \stdClass();
246 $mua->userid = $message->useridto;
247 $mua->messageid = $messageid;
248 $mua->action = \core_message\api::MESSAGE_ACTION_READ;
249 $mua->timecreated = $message->timeread;
250
251 $DB->insert_record('message_user_actions', $mua);
252 }
253 }
254
255 /**
256 * Queues the task.
257 *
258 * @param int $userid
259 */
260 public static function queue_task($userid) {
261 // Let's set up the adhoc task.
262 $task = new \core_message\task\migrate_message_data();
263 $task->set_custom_data(
264 [
265 'userid' => $userid
266 ]
267 );
268
269 // Queue it.
270 \core\task\manager::queue_adhoc_task($task, true);
271 }
272}