Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
25
namespace core_message\task;
26
 
27
defined('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
 */
36
class 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) {
93
                            throw $e;
94
                        } finally {
95
                            $lock->release();
96
                        }
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 ($userid == $otheruserid) {
126
            // Since 3.7, pending self-conversations should be migrated during the upgrading process so shouldn't be any
127
            // self-conversations on the legacy tables. However, this extra-check has been added just in case.
128
            $conversation = \core_message\api::get_self_conversation($userid);
129
            if (empty($conversation)) {
130
                $conversation = \core_message\api::create_conversation(
131
                    \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
132
                    [$userid]
133
                );
134
            }
135
            $conversationid = $conversation->id;
136
        } else if (!$conversationid = \core_message\api::get_conversation_between_users([$userid, $otheruserid])) {
137
            $conversation = \core_message\api::create_conversation(
138
                \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
139
                [
140
                    $userid,
141
                    $otheruserid
142
                ]
143
            );
144
            $conversationid = $conversation->id;
145
        }
146
 
147
        // First, get the rows from the 'message' table.
148
        $select = "(useridfrom = ? AND useridto = ?) OR (useridfrom = ? AND useridto = ?)";
149
        $params = [$userid, $otheruserid, $otheruserid, $userid];
150
        $messages = $DB->get_recordset_select('message', $select, $params, 'id ASC');
151
        foreach ($messages as $message) {
152
            if ($message->notification) {
153
                $this->migrate_notification($message, false);
154
            } else {
155
                $this->migrate_message($conversationid, $message);
156
            }
157
        }
158
        $messages->close();
159
 
160
        // Ok, all done, delete the records from the 'message' table.
161
        $DB->delete_records_select('message', $select, $params);
162
 
163
        // Now, get the rows from the 'message_read' table.
164
        $messages = $DB->get_recordset_select('message_read', $select, $params, 'id ASC');
165
        foreach ($messages as $message) {
166
            if ($message->notification) {
167
                $this->migrate_notification($message, true);
168
            } else {
169
                $this->migrate_message($conversationid, $message);
170
            }
171
        }
172
        $messages->close();
173
 
174
        // Ok, all done, delete the records from the 'message_read' table.
175
        $DB->delete_records_select('message_read', $select, $params);
176
    }
177
 
178
    /**
179
     * Helper function to deal with migrating an individual notification.
180
     *
181
     * @param \stdClass $notification
182
     * @param bool $isread Was the notification read?
183
     * @throws \dml_exception
184
     */
185
    private function migrate_notification($notification, $isread) {
186
        global $DB;
187
 
188
        $tabledata = new \stdClass();
189
        $tabledata->useridfrom = $notification->useridfrom;
190
        $tabledata->useridto = $notification->useridto;
191
        $tabledata->subject = $notification->subject;
192
        $tabledata->fullmessage = $notification->fullmessage;
193
        $tabledata->fullmessageformat = $notification->fullmessageformat ?? FORMAT_MOODLE;
194
        $tabledata->fullmessagehtml = $notification->fullmessagehtml;
195
        $tabledata->smallmessage = $notification->smallmessage;
196
        $tabledata->component = $notification->component;
197
        $tabledata->eventtype = $notification->eventtype;
198
        $tabledata->contexturl = $notification->contexturl;
199
        $tabledata->contexturlname = $notification->contexturlname;
200
        $tabledata->timeread = $notification->timeread ?? null;
201
        $tabledata->timecreated = $notification->timecreated;
202
 
203
        $newid = $DB->insert_record('notifications', $tabledata);
204
 
205
        // Check if there is a record to move to the new 'message_popup_notifications' table.
206
        if ($mp = $DB->get_record('message_popup', ['messageid' => $notification->id, 'isread' => (int) $isread])) {
207
            $mpn = new \stdClass();
208
            $mpn->notificationid = $newid;
209
            $DB->insert_record('message_popup_notifications', $mpn);
210
 
211
            $DB->delete_records('message_popup', ['id' => $mp->id]);
212
        }
213
    }
214
 
215
    /**
216
     * Helper function to deal with migrating an individual message.
217
     *
218
     * @param int $conversationid The conversation between the two users.
219
     * @param \stdClass $message The message from either the 'message' or 'message_read' table
220
     * @throws \dml_exception
221
     */
222
    private function migrate_message($conversationid, $message) {
223
        global $DB;
224
 
225
        // Create the object we will be inserting into the database.
226
        $tabledata = new \stdClass();
227
        $tabledata->useridfrom = $message->useridfrom;
228
        $tabledata->conversationid = $conversationid;
229
        $tabledata->subject = $message->subject;
230
        $tabledata->fullmessage = $message->fullmessage;
231
        $tabledata->fullmessageformat = $message->fullmessageformat ?? FORMAT_MOODLE;
232
        $tabledata->fullmessagehtml = $message->fullmessagehtml;
233
        $tabledata->smallmessage = $message->smallmessage;
234
        $tabledata->timecreated = $message->timecreated;
235
 
236
        $messageid = $DB->insert_record('messages', $tabledata);
237
 
238
        // Check if we need to mark this message as deleted for the user from.
239
        if ($message->timeuserfromdeleted) {
240
            $mua = new \stdClass();
241
            $mua->userid = $message->useridfrom;
242
            $mua->messageid = $messageid;
243
            $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
244
            $mua->timecreated = $message->timeuserfromdeleted;
245
 
246
            $DB->insert_record('message_user_actions', $mua);
247
        }
248
 
249
        // Check if we need to mark this message as deleted for the user to.
250
        if ($message->timeusertodeleted and ($message->useridfrom != $message->useridto)) {
251
            $mua = new \stdClass();
252
            $mua->userid = $message->useridto;
253
            $mua->messageid = $messageid;
254
            $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
255
            $mua->timecreated = $message->timeusertodeleted;
256
 
257
            $DB->insert_record('message_user_actions', $mua);
258
        }
259
 
260
        // Check if we need to mark this message as read for the user to (it is always read by the user from).
261
        // Note - we do an isset() check here because this column only exists in the 'message_read' table.
262
        if (isset($message->timeread)) {
263
            $mua = new \stdClass();
264
            $mua->userid = $message->useridto;
265
            $mua->messageid = $messageid;
266
            $mua->action = \core_message\api::MESSAGE_ACTION_READ;
267
            $mua->timecreated = $message->timeread;
268
 
269
            $DB->insert_record('message_user_actions', $mua);
270
        }
271
    }
272
 
273
    /**
274
     * Queues the task.
275
     *
276
     * @param int $userid
277
     */
278
    public static function queue_task($userid) {
279
        // Let's set up the adhoc task.
280
        $task = new \core_message\task\migrate_message_data();
281
        $task->set_custom_data(
282
            [
283
                'userid' => $userid
284
            ]
285
        );
286
 
287
        // Queue it.
288
        \core\task\manager::queue_adhoc_task($task, true);
289
    }
290
}