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
 * Data provider.
19
 *
20
 * @package    mod_chat
21
 * @copyright  2018 Frédéric Massart
22
 * @author     Frédéric Massart <fred@branchup.tech>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace mod_chat\privacy;
27
defined('MOODLE_INTERNAL') || die();
28
 
29
use context;
30
use context_helper;
31
use context_module;
32
use moodle_recordset;
33
use stdClass;
34
use core_privacy\local\metadata\collection;
35
use core_privacy\local\request\approved_contextlist;
36
use core_privacy\local\request\approved_userlist;
37
use core_privacy\local\request\contextlist;
38
use core_privacy\local\request\helper;
39
use core_privacy\local\request\transform;
40
use core_privacy\local\request\userlist;
41
use core_privacy\local\request\writer;
42
 
43
/**
44
 * Data provider class.
45
 *
46
 * @package    mod_chat
47
 * @copyright  2018 Frédéric Massart
48
 * @author     Frédéric Massart <fred@branchup.tech>
49
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
50
 */
51
class provider implements
52
    \core_privacy\local\metadata\provider,
53
    \core_privacy\local\request\core_userlist_provider,
54
    \core_privacy\local\request\plugin\provider {
55
 
56
    /**
57
     * Returns metadata.
58
     *
59
     * @param collection $collection The initialised collection to add items to.
60
     * @return collection A listing of user data stored through this system.
61
     */
62
    public static function get_metadata(collection $collection): collection {
63
 
64
        $collection->add_database_table('chat_messages', [
65
            'userid' => 'privacy:metadata:messages:userid',
66
            'message' => 'privacy:metadata:messages:message',
67
            'issystem' => 'privacy:metadata:messages:issystem',
68
            'timestamp' => 'privacy:metadata:messages:timestamp',
69
        ], 'privacy:metadata:messages');
70
 
71
        // The tables chat_messages_current and chat_users are not exported/deleted
72
        // because they are considered as short-lived data and are deleted on a
73
        // regular basis by cron, or during normal requests. TODO MDL-62006.
74
 
75
        $collection->add_database_table('chat_messages_current', [
76
            'userid' => 'privacy:metadata:messages:userid',
77
            'message' => 'privacy:metadata:messages:message',
78
            'issystem' => 'privacy:metadata:messages:issystem',
79
            'timestamp' => 'privacy:metadata:messages:timestamp'
80
        ], 'privacy:metadata:chat_messages_current');
81
 
82
        $collection->add_database_table('chat_users', [
83
            'userid' => 'privacy:metadata:chat_users:userid',
84
            'version' => 'privacy:metadata:chat_users:version',
85
            'ip' => 'privacy:metadata:chat_users:ip',
86
            'firstping' => 'privacy:metadata:chat_users:firstping',
87
            'lastping' => 'privacy:metadata:chat_users:lastping',
88
            'lastmessageping' => 'privacy:metadata:chat_users:lastmessageping',
89
            'lang' => 'privacy:metadata:chat_users:lang'
90
        ], 'privacy:metadata:chat_users');
91
 
92
        return $collection;
93
    }
94
 
95
    /**
96
     * Get the list of contexts that contain user information for the specified user.
97
     *
98
     * @param int $userid The user to search.
99
     * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
100
     */
101
    public static function get_contexts_for_userid(int $userid): \core_privacy\local\request\contextlist {
102
        $contextlist = new \core_privacy\local\request\contextlist();
103
 
104
        $sql = "
105
            SELECT DISTINCT ctx.id
106
              FROM {chat} c
107
              JOIN {modules} m
108
                ON m.name = :chat
109
              JOIN {course_modules} cm
110
                ON cm.instance = c.id
111
               AND cm.module = m.id
112
              JOIN {context} ctx
113
                ON ctx.instanceid = cm.id
114
               AND ctx.contextlevel = :modulelevel
115
              JOIN {chat_messages} chm
116
                ON chm.chatid = c.id
117
             WHERE chm.userid = :userid";
118
 
119
        $params = [
120
            'chat' => 'chat',
121
            'modulelevel' => CONTEXT_MODULE,
122
            'userid' => $userid,
123
        ];
124
        $contextlist->add_from_sql($sql, $params);
125
 
126
        return $contextlist;
127
    }
128
 
129
    /**
130
     * Get the list of users who have data within a context.
131
     *
132
     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
133
     */
134
    public static function get_users_in_context(userlist $userlist) {
135
        $context = $userlist->get_context();
136
 
137
        if (!is_a($context, \context_module::class)) {
138
            return;
139
        }
140
 
141
        $params = [
142
            'instanceid'    => $context->instanceid,
143
            'modulename'    => 'chat',
144
        ];
145
 
146
        $sql = "SELECT chm.userid
147
                  FROM {course_modules} cm
148
                  JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
149
                  JOIN {chat} c ON c.id = cm.instance
150
                  JOIN {chat_messages} chm ON chm.chatid = c.id
151
                 WHERE cm.id = :instanceid";
152
 
153
        $userlist->add_from_sql('userid', $sql, $params);
154
    }
155
 
156
    /**
157
     * Export all user data for the specified user, in the specified contexts.
158
     *
159
     * @param approved_contextlist $contextlist The approved contexts to export information for.
160
     */
161
    public static function export_user_data(approved_contextlist $contextlist) {
162
        global $DB;
163
 
164
        $user = $contextlist->get_user();
165
        $userid = $user->id;
166
        $cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
167
            if ($context->contextlevel == CONTEXT_MODULE) {
168
                $carry[] = $context->instanceid;
169
            }
170
            return $carry;
171
        }, []);
172
        if (empty($cmids)) {
173
            return;
174
        }
175
 
176
        $chatidstocmids = static::get_chat_ids_to_cmids_from_cmids($cmids);
177
        $chatids = array_keys($chatidstocmids);
178
 
179
        // Export the messages.
180
        list($insql, $inparams) = $DB->get_in_or_equal($chatids, SQL_PARAMS_NAMED);
181
        $params = array_merge($inparams, ['userid' => $userid]);
182
        $recordset = $DB->get_recordset_select('chat_messages', "chatid $insql AND userid = :userid", $params, 'timestamp, id');
183
        static::recordset_loop_and_export($recordset, 'chatid', [], function($carry, $record) use ($user, $chatidstocmids) {
184
            $message = $record->message;
185
            if ($record->issystem) {
186
                $message = get_string('message' . $record->message, 'mod_chat', fullname($user));
187
            }
188
            $carry[] = [
189
                'message' => $message,
190
                'sent_at' => transform::datetime($record->timestamp),
191
                'is_system_generated' => transform::yesno($record->issystem),
192
            ];
193
            return $carry;
194
 
195
        }, function($chatid, $data) use ($user, $chatidstocmids) {
196
            $context = context_module::instance($chatidstocmids[$chatid]);
197
            $contextdata = helper::get_context_data($context, $user);
198
            $finaldata = (object) array_merge((array) $contextdata, ['messages' => $data]);
199
            helper::export_context_files($context, $user);
200
            writer::with_context($context)->export_data([], $finaldata);
201
        });
202
    }
203
 
204
    /**
205
     * Delete all data for all users in the specified context.
206
     *
207
     * @param context $context The specific context to delete data for.
208
     */
209
    public static function delete_data_for_all_users_in_context(context $context) {
210
        global $DB;
211
 
212
        if ($context->contextlevel != CONTEXT_MODULE) {
213
            return;
214
        }
215
 
216
        $cm = get_coursemodule_from_id('chat', $context->instanceid);
217
        if (!$cm) {
218
            return;
219
        }
220
 
221
        $chatid = $cm->instance;
222
        $DB->delete_records_select('chat_messages', 'chatid = :chatid', ['chatid' => $chatid]);
223
        $DB->delete_records_select('chat_messages_current', 'chatid = :chatid', ['chatid' => $chatid]);
224
        $DB->delete_records_select('chat_users', 'chatid = :chatid', ['chatid' => $chatid]);
225
    }
226
 
227
    /**
228
     * Delete all user data for the specified user, in the specified contexts.
229
     *
230
     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
231
     */
232
    public static function delete_data_for_user(approved_contextlist $contextlist) {
233
        global $DB;
234
 
235
        $userid = $contextlist->get_user()->id;
236
        $cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
237
            if ($context->contextlevel == CONTEXT_MODULE) {
238
                $carry[] = $context->instanceid;
239
            }
240
            return $carry;
241
        }, []);
242
        if (empty($cmids)) {
243
            return;
244
        }
245
 
246
        $chatidstocmids = static::get_chat_ids_to_cmids_from_cmids($cmids);
247
        $chatids = array_keys($chatidstocmids);
248
 
249
        list($insql, $inparams) = $DB->get_in_or_equal($chatids, SQL_PARAMS_NAMED);
250
        $sql = "chatid $insql AND userid = :userid";
251
        $params = array_merge($inparams, ['userid' => $userid]);
252
 
253
        $DB->delete_records_select('chat_messages', $sql, $params);
254
        $DB->delete_records_select('chat_messages_current', $sql, $params);
255
        $DB->delete_records_select('chat_users', $sql, $params);
256
    }
257
 
258
 
259
    /**
260
     * Delete multiple users within a single context.
261
     *
262
     * @param   approved_userlist       $userlist The approved context and user information to delete information for.
263
     */
264
    public static function delete_data_for_users(approved_userlist $userlist) {
265
        global $DB;
266
 
267
        $context = $userlist->get_context();
268
        $cm = $DB->get_record('course_modules', ['id' => $context->instanceid]);
269
        $chat = $DB->get_record('chat', ['id' => $cm->instance]);
270
 
271
        list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
272
        $params = array_merge(['chatid' => $chat->id], $userinparams);
273
        $sql = "chatid = :chatid AND userid {$userinsql}";
274
 
275
        $DB->delete_records_select('chat_messages', $sql, $params);
276
        $DB->delete_records_select('chat_messages_current', $sql, $params);
277
        $DB->delete_records_select('chat_users', $sql, $params);
278
    }
279
 
280
    /**
281
     * Return a dict of chat IDs mapped to their course module ID.
282
     *
283
     * @param array $cmids The course module IDs.
284
     * @return array In the form of [$chatid => $cmid].
285
     */
286
    protected static function get_chat_ids_to_cmids_from_cmids(array $cmids) {
287
        global $DB;
288
        list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED);
289
        $sql = "
290
            SELECT c.id, cm.id AS cmid
291
              FROM {chat} c
292
              JOIN {modules} m
293
                ON m.name = :chat
294
              JOIN {course_modules} cm
295
                ON cm.instance = c.id
296
               AND cm.module = m.id
297
             WHERE cm.id $insql";
298
        $params = array_merge($inparams, ['chat' => 'chat']);
299
        return $DB->get_records_sql_menu($sql, $params);
300
    }
301
 
302
    /**
303
     * Loop and export from a recordset.
304
     *
305
     * @param moodle_recordset $recordset The recordset.
306
     * @param string $splitkey The record key to determine when to export.
307
     * @param mixed $initial The initial data to reduce from.
308
     * @param callable $reducer The function to return the dataset, receives current dataset, and the current record.
309
     * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.
310
     * @return void
311
     */
312
    protected static function recordset_loop_and_export(moodle_recordset $recordset, $splitkey, $initial,
313
            callable $reducer, callable $export) {
314
 
315
        $data = $initial;
316
        $lastid = null;
317
 
318
        foreach ($recordset as $record) {
319
            if ($lastid && $record->{$splitkey} != $lastid) {
320
                $export($lastid, $data);
321
                $data = $initial;
322
            }
323
            $data = $reducer($data, $record);
324
            $lastid = $record->{$splitkey};
325
        }
326
        $recordset->close();
327
 
328
        if (!empty($lastid)) {
329
            $export($lastid, $data);
330
        }
331
    }
332
 
333
}