Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
// This file is part of Moodle - http://moodle.org/
4
//
5
// Moodle is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// Moodle is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
/**
19
 * @package    moodlecore
20
 * @subpackage backup-dbops
21
 * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
/**
26
 * Non instantiable helper class providing DB support to the @backup_controller
27
 *
28
 * This class contains various static methods available for all the DB operations
29
 * performed by the backup_controller class
30
 *
31
 * TODO: Finish phpdocs
32
 */
33
abstract class backup_controller_dbops extends backup_dbops {
34
 
35
    /**
36
     * @var string Backup id for cached backup_includes_files result.
37
     */
38
    protected static $includesfilescachebackupid;
39
 
40
    /**
41
     * @var int Cached backup_includes_files result
42
     */
43
    protected static $includesfilescache;
44
 
45
    /**
46
     * Send one backup controller to DB
47
     *
48
     * @param backup_controller $controller controller to send to DB
49
     * @param string $checksum hash of the controller to be checked
50
     * @param bool $includeobj to decide if the object itself must be updated (true) or no (false)
51
     * @param bool $cleanobj to decide if the object itself must be cleaned (true) or no (false)
52
     * @return int id of the controller record in the DB
53
     * @throws backup_controller_exception|backup_dbops_exception
54
     */
55
    public static function save_controller($controller, $checksum, $includeobj = true, $cleanobj = false) {
56
        global $DB;
57
        // Check we are going to save one backup_controller
58
        if (! $controller instanceof backup_controller) {
59
            throw new backup_controller_exception('backup_controller_expected');
60
        }
61
        // Check checksum is ok. Only if we are including object info. Sounds silly but it isn't ;-).
62
        if ($includeobj and !$controller->is_checksum_correct($checksum)) {
63
            throw new backup_dbops_exception('backup_controller_dbops_saving_checksum_mismatch');
64
        }
65
        // Cannot request to $includeobj and $cleanobj at the same time.
66
        if ($includeobj and $cleanobj) {
67
            throw new backup_dbops_exception('backup_controller_dbops_saving_cannot_include_and_delete');
68
        }
69
        // Get all the columns
70
        $rec = new stdclass();
71
        $rec->backupid     = $controller->get_backupid();
72
        $rec->operation    = $controller->get_operation();
73
        $rec->type         = $controller->get_type();
74
        $rec->itemid       = $controller->get_id();
75
        $rec->format       = $controller->get_format();
76
        $rec->interactive  = $controller->get_interactive();
77
        $rec->purpose      = $controller->get_mode();
78
        $rec->userid       = $controller->get_userid();
79
        $rec->status       = $controller->get_status();
80
        $rec->execution    = $controller->get_execution();
81
        $rec->executiontime= $controller->get_executiontime();
82
        $rec->checksum     = $checksum;
83
        // Serialize information
84
        if ($includeobj) {
85
            $rec->controller = base64_encode(serialize($controller));
86
        } else if ($cleanobj) {
87
            $rec->controller = '';
88
        }
89
        // Send it to DB
90
        if ($recexists = $DB->get_record('backup_controllers', array('backupid' => $rec->backupid))) {
91
            $rec->id = $recexists->id;
92
            $rec->timemodified = time();
93
            $DB->update_record('backup_controllers', $rec);
94
        } else {
95
            $rec->timecreated = time();
96
            $rec->timemodified = 0;
97
            $rec->id = $DB->insert_record('backup_controllers', $rec);
98
        }
99
        return $rec->id;
100
    }
101
 
102
    public static function load_controller($backupid) {
103
        global $DB;
104
        if (! $controllerrec = $DB->get_record('backup_controllers', array('backupid' => $backupid))) {
105
            throw new backup_dbops_exception('backup_controller_dbops_nonexisting');
106
        }
107
        $controller = unserialize(base64_decode($controllerrec->controller));
108
        if (!is_object($controller)) {
109
            // The controller field of the table did not contain a serialized object.
110
            // It is made empty after it has been used successfully, it is likely that
111
            // the user has pressed the browser back button at some point.
112
            throw new backup_dbops_exception('backup_controller_dbops_loading_invalid_controller');
113
        }
114
        // Check checksum is ok. Sounds silly but it isn't ;-)
115
        if (!$controller->is_checksum_correct($controllerrec->checksum)) {
116
            throw new backup_dbops_exception('backup_controller_dbops_loading_checksum_mismatch');
117
        }
118
        return $controller;
119
    }
120
 
121
    public static function create_backup_ids_temp_table($backupid) {
122
        global $CFG, $DB;
123
        $dbman = $DB->get_manager(); // We are going to use database_manager services
124
 
125
        $xmldb_table = new xmldb_table('backup_ids_temp');
126
        $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
127
        // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
128
        $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
129
        $xmldb_table->add_field('itemname', XMLDB_TYPE_CHAR, 160, null, XMLDB_NOTNULL, null, null);
130
        $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
131
        $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
132
        $xmldb_table->add_field('parentitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
133
        $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
134
        $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
135
        $xmldb_table->add_key('backupid_itemname_itemid_uk', XMLDB_KEY_UNIQUE, array('backupid','itemname','itemid'));
136
        $xmldb_table->add_index('backupid_parentitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','parentitemid'));
137
        $xmldb_table->add_index('backupid_itemname_newitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','newitemid'));
138
 
139
        $dbman->create_temp_table($xmldb_table); // And create it
140
 
141
    }
142
 
143
    public static function create_backup_files_temp_table($backupid) {
144
        global $CFG, $DB;
145
        $dbman = $DB->get_manager(); // We are going to use database_manager services
146
 
147
        $xmldb_table = new xmldb_table('backup_files_temp');
148
        $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
149
        // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
150
        $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
151
        $xmldb_table->add_field('contextid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
152
        $xmldb_table->add_field('component', XMLDB_TYPE_CHAR, 100, null, XMLDB_NOTNULL, null, null);
153
        $xmldb_table->add_field('filearea', XMLDB_TYPE_CHAR, 50, null, XMLDB_NOTNULL, null, null);
154
        $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
155
        $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
156
        $xmldb_table->add_field('newcontextid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
157
        $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
158
        $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
159
        $xmldb_table->add_index('backupid_contextid_component_filearea_itemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','contextid','component','filearea','itemid'));
160
 
161
        $dbman->create_temp_table($xmldb_table); // And create it
162
    }
163
 
1441 ariadna 164
    /**
165
     * Create temporary tables to store a partial copies of question category data from the backup_ids_temp table.
166
     *
167
     * This is to work around MySQL's restruction on joining the same temporary table multiple times in the same query. It isn't
168
     * necessary on PostgreSQL (which can join temporary tables multiple times) or MSSQL (which doesn't really use temporary tables)
169
     * but doing it this way keeps things database-agnostic.
170
     */
171
    public static function create_question_category_temp_tables(): void {
172
        global $DB;
173
        $dbman = $DB->get_manager();
174
 
175
        foreach (['question_category_complete_temp', 'question_category_partial_temp'] as $tablename) {
176
            $xmldbtable = new xmldb_table($tablename);
177
            $xmldbtable->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
178
            $xmldbtable->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL);
179
            $xmldbtable->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL);
180
            $xmldbtable->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
181
 
182
            $dbman->create_temp_table($xmldbtable);
183
        }
184
    }
185
 
1 efrain 186
    public static function drop_backup_ids_temp_table($backupid) {
187
        global $DB;
188
        $dbman = $DB->get_manager(); // We are going to use database_manager services
189
 
190
        $targettablename = 'backup_ids_temp';
191
        if ($dbman->table_exists($targettablename)) {
192
            $table = new xmldb_table($targettablename);
193
            $dbman->drop_table($table); // And drop it
194
        }
195
    }
196
 
197
    /**
1441 ariadna 198
     * Dispose of the additional temporary tables.
199
     */
200
    public static function drop_question_category_temp_tables(): void {
201
        global $DB;
202
        $dbman = $DB->get_manager();
203
 
204
        foreach (['question_category_complete_temp', 'question_category_partial_temp'] as $tablename) {
205
            if ($dbman->table_exists($tablename)) {
206
                $xmldbtable = new xmldb_table($tablename);
207
                $dbman->drop_table($xmldbtable);
208
            }
209
        }
210
    }
211
 
212
    /**
1 efrain 213
     * Decode the info field from backup_ids_temp or backup_files_temp.
214
     *
215
     * @param mixed $info The info field data to decode, may be an object or a simple integer.
216
     * @return mixed The decoded information.  For simple types it returns, for complex ones we decode.
217
     */
218
    public static function decode_backup_temp_info($info) {
219
        // We encode all data except null.
220
        if ($info != null) {
221
            return unserialize(gzuncompress(base64_decode($info)));
222
        }
223
        return $info;
224
    }
225
 
226
    /**
227
     * Encode the info field for backup_ids_temp or backup_files_temp.
228
     *
229
     * @param mixed $info string The info field data to encode.
230
     * @return string An encoded string of data or null if the input is null.
231
     */
232
    public static function encode_backup_temp_info($info) {
233
        // We encode if there is any information to keep the translations simpler.
234
        if ($info != null) {
235
            // We compress if possible. It reduces db, network and memory storage. The saving is greater than CPU compression cost.
236
            // Compression level 1 is chosen has it produces good compression with the smallest possible overhead, see MDL-40618.
237
            return base64_encode(gzcompress(serialize($info), 1));
238
        }
239
        return $info;
240
    }
241
 
242
    /**
243
     * Given one type and id from controller, return the corresponding courseid
244
     */
245
    public static function get_courseid_from_type_id($type, $id) {
246
        global $DB;
247
        if ($type == backup::TYPE_1COURSE) {
248
            return $id; // id is the course id
249
 
250
        } else if ($type == backup::TYPE_1SECTION) {
251
            if (! $courseid = $DB->get_field('course_sections', 'course', array('id' => $id))) {
252
                throw new backup_dbops_exception('course_not_found_for_section', $id);
253
            }
254
            return $courseid;
255
        } else if ($type == backup::TYPE_1ACTIVITY) {
256
            if (! $courseid = $DB->get_field('course_modules', 'course', array('id' => $id))) {
257
                throw new backup_dbops_exception('course_not_found_for_moduleid', $id);
258
            }
259
            return $courseid;
260
        }
261
    }
262
 
263
    /**
264
     * Given one activity task, return the activity information and related settings
265
     * Used by get_moodle_backup_information()
266
     */
267
    private static function get_activity_backup_information($task) {
268
 
269
        $contentinfo = array(
270
            'moduleid'   => $task->get_moduleid(),
271
            'sectionid'  => $task->get_sectionid(),
272
            'modulename' => $task->get_modulename(),
273
            'title'      => $task->get_name(),
1441 ariadna 274
            'directory'  => 'activities/' . $task->get_modulename() . '_' . $task->get_moduleid(),
275
            'insubsection' => ($task->is_in_subsection()) ? 1 : '',
276
        );
1 efrain 277
 
278
        // Now get activity settings
279
        // Calculate prefix to find valid settings
280
        $prefix = basename($contentinfo['directory']);
281
        $settingsinfo = array();
282
        foreach ($task->get_settings() as $setting) {
283
            // Discard ones without valid prefix
284
            if (strpos($setting->get_name(), $prefix) !== 0) {
285
                continue;
286
            }
287
            // Validate level is correct (activity)
1441 ariadna 288
            if (!in_array($setting->get_level(), [backup_setting::ACTIVITY_LEVEL, backup_setting::SUBACTIVITY_LEVEL])) {
1 efrain 289
                throw new backup_controller_exception('setting_not_activity_level', $setting);
290
            }
291
            $settinginfo = array(
292
                'level'    => 'activity',
293
                'activity' => $prefix,
294
                'name'     => $setting->get_name(),
295
                'value'    => $setting->get_value());
296
            $settingsinfo[$setting->get_name()] = (object)$settinginfo;
297
        }
298
        return array($contentinfo, $settingsinfo);
299
    }
300
 
301
    /**
302
     * Given one section task, return the section information and related settings
303
     * Used by get_moodle_backup_information()
304
     */
305
    private static function get_section_backup_information($task) {
306
 
307
        $contentinfo = array(
308
            'sectionid'  => $task->get_sectionid(),
309
            'title'      => $task->get_name(),
1441 ariadna 310
            'directory'  => 'sections/' . 'section_' . $task->get_sectionid(),
311
            'parentcmid' => $task->get_delegated_cm() ?? '',
312
            'modname' => $task->get_modname() ?? '',
313
        );
1 efrain 314
 
315
        // Now get section settings
316
        // Calculate prefix to find valid settings
317
        $prefix = basename($contentinfo['directory']);
318
        $settingsinfo = array();
319
        foreach ($task->get_settings() as $setting) {
320
            // Discard ones without valid prefix
321
            if (strpos($setting->get_name(), $prefix) !== 0) {
322
                continue;
323
            }
324
            // Validate level is correct (section)
1441 ariadna 325
            if (!in_array($setting->get_level(), [backup_setting::SECTION_LEVEL, backup_setting::SUBSECTION_LEVEL])) {
1 efrain 326
                throw new backup_controller_exception('setting_not_section_level', $setting);
327
            }
328
            $settinginfo = array(
329
                'level'    => 'section',
330
                'section'  => $prefix,
331
                'name'     => $setting->get_name(),
332
                'value'    => $setting->get_value());
333
            $settingsinfo[$setting->get_name()] = (object)$settinginfo;
334
        }
335
        return array($contentinfo, $settingsinfo);
336
    }
337
 
338
    /**
339
     * Given one course task, return the course information and related settings
340
     * Used by get_moodle_backup_information()
341
     */
342
    private static function get_course_backup_information($task) {
343
 
344
        $contentinfo = array(
345
            'courseid'   => $task->get_courseid(),
346
            'title'      => $task->get_name(),
347
            'directory'  => 'course');
348
 
349
        // Now get course settings
350
        // Calculate prefix to find valid settings
351
        $prefix = basename($contentinfo['directory']);
352
        $settingsinfo = array();
353
        foreach ($task->get_settings() as $setting) {
354
            // Discard ones without valid prefix
355
            if (strpos($setting->get_name(), $prefix) !== 0) {
356
                continue;
357
            }
358
            // Validate level is correct (course)
359
            if ($setting->get_level() != backup_setting::COURSE_LEVEL) {
360
                throw new backup_controller_exception('setting_not_course_level', $setting);
361
            }
362
            $settinginfo = array(
363
                'level'    => 'course',
364
                'name'     => $setting->get_name(),
365
                'value'    => $setting->get_value());
366
            $settingsinfo[$setting->get_name()] = (object)$settinginfo;
367
        }
368
        return array($contentinfo, $settingsinfo);
369
    }
370
 
371
    /**
372
     * Given one root task, return the course information and related settings
373
     * Used by get_moodle_backup_information()
374
     */
375
    private static function get_root_backup_information($task) {
376
 
377
        // Now get root settings
378
        $settingsinfo = array();
379
        foreach ($task->get_settings() as $setting) {
380
            // Validate level is correct (root)
381
            if ($setting->get_level() != backup_setting::ROOT_LEVEL) {
382
                throw new backup_controller_exception('setting_not_root_level', $setting);
383
            }
384
            $settinginfo = array(
385
                'level'    => 'root',
386
                'name'     => $setting->get_name(),
387
                'value'    => $setting->get_value());
388
            $settingsinfo[$setting->get_name()] = (object)$settinginfo;
389
        }
390
        return array(null, $settingsinfo);
391
    }
392
 
393
    /**
394
     * Get details information for main moodle_backup.xml file, extracting it from
395
     * the specified controller.
396
     *
397
     * If you specify the progress monitor, this will start a new progress section
398
     * to track progress in processing (in case this task takes a long time).
399
     *
400
     * @param string $backupid Backup ID
401
     * @param \core\progress\base $progress Optional progress monitor
402
     */
403
    public static function get_moodle_backup_information($backupid,
1441 ariadna 404
            ?\core\progress\base $progress = null) {
1 efrain 405
 
406
        // Start tracking progress if required (for load_controller).
407
        if ($progress) {
408
            $progress->start_progress('get_moodle_backup_information', 2);
409
        }
410
 
411
        $detailsinfo = array(); // Information details
412
        $contentsinfo= array(); // Information about backup contents
413
        $settingsinfo= array(); // Information about backup settings
414
        $bc = self::load_controller($backupid); // Load controller
415
 
416
        // Note that we have loaded controller.
417
        if ($progress) {
418
            $progress->progress(1);
419
        }
420
 
421
        // Details info
422
        $detailsinfo['id'] = $bc->get_id();
423
        $detailsinfo['backup_id'] = $bc->get_backupid();
424
        $detailsinfo['type'] = $bc->get_type();
425
        $detailsinfo['format'] = $bc->get_format();
426
        $detailsinfo['interactive'] = $bc->get_interactive();
427
        $detailsinfo['mode'] = $bc->get_mode();
428
        $detailsinfo['execution'] = $bc->get_execution();
429
        $detailsinfo['executiontime'] = $bc->get_executiontime();
430
        $detailsinfo['userid'] = $bc->get_userid();
431
        $detailsinfo['courseid'] = $bc->get_courseid();
432
 
433
 
434
        // Init content placeholders
435
        $contentsinfo['activities'] = array();
436
        $contentsinfo['sections']   = array();
437
        $contentsinfo['course']     = array();
438
 
439
        // Get tasks and start nested progress.
440
        $tasks = $bc->get_plan()->get_tasks();
441
        if ($progress) {
442
            $progress->start_progress('get_moodle_backup_information', count($tasks));
443
            $done = 1;
444
        }
445
 
446
        // Contents info (extract information from tasks)
447
        foreach ($tasks as $task) {
448
 
449
            if ($task instanceof backup_activity_task) { // Activity task
450
 
451
                if ($task->get_setting_value('included')) { // Only return info about included activities
452
                    list($contentinfo, $settings) = self::get_activity_backup_information($task);
453
                    $contentsinfo['activities'][] = $contentinfo;
454
                    $settingsinfo = array_merge($settingsinfo, $settings);
455
                }
456
 
457
            } else if ($task instanceof backup_section_task) { // Section task
458
 
459
                if ($task->get_setting_value('included')) { // Only return info about included sections
460
                    list($contentinfo, $settings) = self::get_section_backup_information($task);
461
                    $contentsinfo['sections'][] = $contentinfo;
462
                    $settingsinfo = array_merge($settingsinfo, $settings);
463
                }
464
 
465
            } else if ($task instanceof backup_course_task) { // Course task
466
 
467
                list($contentinfo, $settings) = self::get_course_backup_information($task);
468
                $contentsinfo['course'][] = $contentinfo;
469
                $settingsinfo = array_merge($settingsinfo, $settings);
470
 
471
            } else if ($task instanceof backup_root_task) { // Root task
472
 
473
                list($contentinfo, $settings) = self::get_root_backup_information($task);
474
                $settingsinfo = array_merge($settingsinfo, $settings);
475
            }
476
 
477
            // Report task handled.
478
            if ($progress) {
479
                $progress->progress($done++);
480
            }
481
        }
482
 
483
        $bc->destroy(); // Always need to destroy controller to handle circular references
484
 
485
        // Finish progress reporting.
486
        if ($progress) {
487
            $progress->end_progress();
488
            $progress->end_progress();
489
        }
490
 
491
        return array(array((object)$detailsinfo), $contentsinfo, $settingsinfo);
492
    }
493
 
494
    /**
495
     * Update CFG->backup_version and CFG->backup_release if change in
496
     * version is detected.
497
     */
498
    public static function apply_version_and_release() {
499
        global $CFG;
500
 
501
        if ($CFG->backup_version < backup::VERSION) {
502
            set_config('backup_version', backup::VERSION);
503
            set_config('backup_release', backup::RELEASE);
504
        }
505
    }
506
 
507
    /**
508
     * Given the backupid, detect if the backup includes "mnet" remote users or no
509
     */
510
    public static function backup_includes_mnet_remote_users($backupid) {
511
        global $CFG, $DB;
512
 
513
        $sql = "SELECT COUNT(*)
514
                  FROM {backup_ids_temp} b
515
                  JOIN {user} u ON u.id = b.itemid
516
                 WHERE b.backupid = ?
517
                   AND b.itemname = 'userfinal'
518
                   AND u.mnethostid != ?";
519
        $count = $DB->count_records_sql($sql, array($backupid, $CFG->mnet_localhost_id));
520
        return (int)(bool)$count;
521
    }
522
 
523
    /**
524
     * Given the backupid, determine whether this backup should include
525
     * files from the moodle file storage system.
526
     *
527
     * @param string $backupid The ID of the backup.
528
     * @return int Indicates whether files should be included in backups.
529
     */
530
    public static function backup_includes_files($backupid) {
531
        // This function is called repeatedly in a backup with many files.
532
        // Loading the controller is a nontrivial operation (in a large test
533
        // backup it took 0.3 seconds), so we do a temporary cache of it within
534
        // this request.
535
        if (self::$includesfilescachebackupid === $backupid) {
536
            return self::$includesfilescache;
537
        }
538
 
539
        // Load controller, get value, then destroy controller and return result.
540
        self::$includesfilescachebackupid = $backupid;
541
        $bc = self::load_controller($backupid);
542
        self::$includesfilescache = $bc->get_include_files();
543
        $bc->destroy();
544
        return self::$includesfilescache;
545
    }
546
 
547
    /**
548
     * Given the backupid, detect if the backup contains references to external contents
549
     *
550
     * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
551
     * @return int
552
     */
553
    public static function backup_includes_file_references($backupid) {
554
        global $CFG, $DB;
555
 
556
        $sql = "SELECT count(r.repositoryid)
557
                  FROM {files} f
558
                  LEFT JOIN {files_reference} r
559
                       ON r.id = f.referencefileid
560
                  JOIN {backup_ids_temp} bi
561
                       ON f.id = bi.itemid
562
                 WHERE bi.backupid = ?
563
                       AND bi.itemname = 'filefinal'";
564
        $count = $DB->count_records_sql($sql, array($backupid));
565
        return (int)(bool)$count;
566
    }
567
 
568
    /**
569
     * Given the courseid, return some course related information we want to transport
570
     *
571
     * @param int $course the id of the course this backup belongs to
572
     */
573
    public static function backup_get_original_course_info($courseid) {
574
        global $DB;
575
        return $DB->get_record('course', array('id' => $courseid), 'fullname, shortname, startdate, enddate, format');
576
    }
577
 
578
    /**
579
     * Sets the default values for the settings in a backup operation
580
     *
581
     * Based on the mode of the backup it will load proper defaults
582
     * using {@link apply_admin_config_defaults}.
583
     *
584
     * @param backup_controller $controller
585
     */
586
    public static function apply_config_defaults(backup_controller $controller) {
587
        // Based on the mode of the backup (general, automated, import, hub...)
588
        // decide the action to perform to get defaults loaded
589
        $mode = $controller->get_mode();
590
 
591
        switch ($mode) {
592
            case backup::MODE_GENERAL:
593
            case backup::MODE_ASYNC:
594
                // Load the general defaults
595
                $settings = array(
596
                        'backup_general_users'              => 'users',
597
                        'backup_general_anonymize'          => 'anonymize',
598
                        'backup_general_role_assignments'   => 'role_assignments',
599
                        'backup_general_activities'         => 'activities',
600
                        'backup_general_blocks'             => 'blocks',
1441 ariadna 601
                        'backup_general_files'              => 'files',
1 efrain 602
                        'backup_general_filters'            => 'filters',
603
                        'backup_general_comments'           => 'comments',
604
                        'backup_general_badges'             => 'badges',
605
                        'backup_general_calendarevents'     => 'calendarevents',
606
                        'backup_general_userscompletion'    => 'userscompletion',
607
                        'backup_general_logs'               => 'logs',
608
                        'backup_general_histories'          => 'grade_histories',
609
                        'backup_general_groups'             => 'groups',
610
                        'backup_general_competencies'       => 'competencies',
1441 ariadna 611
                        'backup_general_customfield'        => 'customfield',
1 efrain 612
                        'backup_general_contentbankcontent' => 'contentbankcontent',
613
                        'backup_general_xapistate'          => 'xapistate',
614
                        'backup_general_legacyfiles'        => 'legacyfiles'
615
                );
616
                self::apply_admin_config_defaults($controller, $settings, true);
617
                break;
618
            case backup::MODE_IMPORT:
619
                // Load the import defaults.
620
                $settings = array(
621
                        'backup_import_activities'         => 'activities',
622
                        'backup_import_blocks'             => 'blocks',
623
                        'backup_import_filters'            => 'filters',
1441 ariadna 624
                        'backup_import_badges'             => 'badges',
1 efrain 625
                        'backup_import_calendarevents'     => 'calendarevents',
626
                        'backup_import_permissions'        => 'permissions',
627
                        'backup_import_groups'             => 'groups',
628
                        'backup_import_competencies'       => 'competencies',
1441 ariadna 629
                        'backup_import_customfield'        => 'customfield',
1 efrain 630
                        'backup_import_contentbankcontent' => 'contentbankcontent',
631
                        'backup_import_legacyfiles'        => 'legacyfiles'
632
                );
633
                self::apply_admin_config_defaults($controller, $settings, true);
634
                if ((!$controller->get_interactive()) &&
635
                        $controller->get_type() == backup::TYPE_1ACTIVITY) {
636
                    // This is duplicate - there is no concept of defaults - these settings must be on.
637
                    $settings = array(
638
                         'activities',
639
                         'blocks',
640
                         'filters',
641
                    );
642
                    self::force_enable_settings($controller, $settings);
1441 ariadna 643
                    // Badges are not included by default when duplicating activities.
644
                    self::force_settings($controller, ['badges'], false);
1 efrain 645
                }
646
                break;
647
            case backup::MODE_AUTOMATED:
648
                // Load the automated defaults.
649
                $settings = array(
650
                        'backup_auto_users'              => 'users',
651
                        'backup_auto_role_assignments'   => 'role_assignments',
652
                        'backup_auto_activities'         => 'activities',
653
                        'backup_auto_blocks'             => 'blocks',
1441 ariadna 654
                        'backup_auto_files'              => 'files',
1 efrain 655
                        'backup_auto_filters'            => 'filters',
656
                        'backup_auto_comments'           => 'comments',
657
                        'backup_auto_badges'             => 'badges',
658
                        'backup_auto_calendarevents'     => 'calendarevents',
659
                        'backup_auto_userscompletion'    => 'userscompletion',
660
                        'backup_auto_logs'               => 'logs',
661
                        'backup_auto_histories'          => 'grade_histories',
662
                        'backup_auto_groups'             => 'groups',
663
                        'backup_auto_competencies'       => 'competencies',
1441 ariadna 664
                        'backup_auto_customfield'        => 'customfield',
1 efrain 665
                        'backup_auto_contentbankcontent' => 'contentbankcontent',
666
                        'backup_auto_xapistate'          => 'xapistate',
667
                        'backup_auto_legacyfiles'        => 'legacyfiles'
668
                );
669
                self::apply_admin_config_defaults($controller, $settings, false);
670
                break;
671
            default:
672
                // Nothing to do for other modes (HUB...). Some day we
673
                // can define defaults (admin UI...) for them if we want to
674
        }
675
    }
676
 
677
    /**
678
     * Turn these settings on. No defaults from admin settings.
679
     *
680
     * @param backup_controller $controller
681
     * @param array $settings a map from admin config names to setting names (Config name => Setting name)
682
     */
683
    private static function force_enable_settings(backup_controller $controller, array $settings) {
1441 ariadna 684
        self::force_settings($controller, $settings, true);
685
    }
686
 
687
    /**
688
     * Set these settings to the given $value. No defaults from admin settings.
689
     *
690
     * @param backup_controller $controller The backup controller.
691
     * @param array $settings a map from admin config names to setting names (Config name => Setting name).
692
     * @param mixed $value the value to set the settings to.
693
     */
694
    private static function force_settings(backup_controller $controller, array $settings, $value) {
1 efrain 695
        $plan = $controller->get_plan();
696
        foreach ($settings as $config => $settingname) {
697
            if ($plan->setting_exists($settingname)) {
698
                $setting = $plan->get_setting($settingname);
699
                // We do not allow this setting to be locked for a duplicate function.
700
                if ($setting->get_status() !== base_setting::NOT_LOCKED) {
701
                    $setting->set_status(base_setting::NOT_LOCKED);
702
                }
703
                $setting->set_value($value);
704
                $setting->set_status(base_setting::LOCKED_BY_CONFIG);
705
            } else {
706
                $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
707
            }
708
        }
709
    }
710
 
711
    /**
712
     * Sets the controller settings default values from the admin config.
713
     *
714
     * @param backup_controller $controller
715
     * @param array $settings a map from admin config names to setting names (Config name => Setting name)
716
     * @param boolean $uselocks whether "locked" admin settings should be honoured
717
     */
718
    private static function apply_admin_config_defaults(backup_controller $controller, array $settings, $uselocks) {
719
        $plan = $controller->get_plan();
720
        foreach ($settings as $config=>$settingname) {
721
            $value = get_config('backup', $config);
722
            if ($value === false) {
723
                // Ignore this because the config has not been set. get_config
724
                // returns false if a setting doesn't exist, '0' is returned when
725
                // the configuration is set to false.
726
                $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG);
727
                continue;
728
            }
729
            $locked = $uselocks && (get_config('backup', $config.'_locked') == true);
730
            if ($plan->setting_exists($settingname)) {
731
                $setting = $plan->get_setting($settingname);
732
                // We can only update the setting if it isn't already locked by config or permission.
733
                if ($setting->get_status() !== base_setting::LOCKED_BY_CONFIG
734
                        && $setting->get_status() !== base_setting::LOCKED_BY_PERMISSION) {
735
                    $setting->set_value($value);
736
                    if ($locked) {
737
                        $setting->set_status(base_setting::LOCKED_BY_CONFIG);
738
                    }
739
                }
740
            } else {
741
                $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
742
            }
743
        }
744
    }
745
 
746
    /**
747
     * Get the progress details of a backup operation.
748
     * Get backup records directly from database, if the backup has successfully completed
749
     * there will be no controller object to load.
750
     *
751
     * @param string $backupid The backup id to query.
752
     * @return array $progress The backup progress details.
753
     */
754
    public static function get_progress($backupid) {
755
        global $DB;
756
 
757
        $progress = array();
758
        $backuprecord = $DB->get_record(
759
            'backup_controllers',
760
            array('backupid' => $backupid),
761
            'status, progress, operation',
762
            MUST_EXIST);
763
 
764
        $status = $backuprecord->status;
765
        $progress = $backuprecord->progress;
766
        $operation = $backuprecord->operation;
767
 
768
        $progress = array('status' => $status, 'progress' => $progress, 'backupid' => $backupid, 'operation' => $operation);
769
 
770
        return $progress;
771
    }
772
}