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
// 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
 * The main interface for recycle bin methods.
19
 *
20
 * @package    tool_recyclebin
21
 * @copyright  2015 University of Kent
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace tool_recyclebin;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
define('TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA', 'recyclebin_coursecat');
30
 
31
/**
32
 * Represents a category's recyclebin.
33
 *
34
 * @package    tool_recyclebin
35
 * @copyright  2015 University of Kent
36
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class category_bin extends base_bin {
39
 
40
    /**
41
     * @var int The category id.
42
     */
43
    protected $_categoryid;
44
 
45
    /**
46
     * Constructor.
47
     *
48
     * @param int $categoryid The category id.
49
     */
50
    public function __construct($categoryid) {
51
        $this->_categoryid = $categoryid;
52
    }
53
 
54
    /**
55
     * Is this recyclebin enabled?
56
     *
57
     * @return bool true if enabled, false if not.
58
     */
59
    public static function is_enabled() {
60
        return get_config('tool_recyclebin', 'categorybinenable');
61
    }
62
 
63
    /**
64
     * Returns an item from the recycle bin.
65
     *
66
     * @param int $itemid Item ID to retrieve.
67
     * @return \stdClass the item.
68
     */
69
    public function get_item($itemid) {
70
        global $DB;
71
 
72
        $item = $DB->get_record('tool_recyclebin_category', array(
73
            'id' => $itemid
74
        ), '*', MUST_EXIST);
75
 
76
        $item->name = get_course_display_name_for_list($item);
77
 
78
        return $item;
79
    }
80
 
81
    /**
82
     * Returns a list of items in the recycle bin for this course.
83
     *
84
     * @return array the list of items.
85
     */
86
    public function get_items() {
87
        global $DB;
88
 
89
        $items = $DB->get_records('tool_recyclebin_category', array(
90
            'categoryid' => $this->_categoryid
91
        ));
92
 
93
        foreach ($items as $item) {
94
            $item->name = get_course_display_name_for_list($item);
95
        }
96
 
97
        return $items;
98
    }
99
 
100
    /**
101
     * Store a course in the recycle bin.
102
     *
103
     * @param \stdClass $course Course
104
     * @throws \moodle_exception
105
     */
106
    public function store_item($course) {
107
        global $CFG, $DB;
108
 
109
        require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
110
 
111
        // As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
112
        // settings (storing backups @ real location and potentially not including files).
113
        // For recycle bin we want to ensure that backup files are always stored in Moodle file
114
        // area and always contain the users' files. In order to achieve that, we hack the
115
        // setting here via $CFG->forced_plugin_settings, so it won't interfere other operations.
116
        // See MDL-65218 and MDL-35773 for more information.
117
        // This hack will be removed once recycle bin switches to use its own backup mode, with
118
        // own preferences and 100% separate from MOODLE_AUTOMATED.
119
        // TODO: Remove this as part of MDL-65228.
11 efrain 120
        $forcedbackupsettings = $CFG->forced_plugin_settings['backup'] ?? null;
121
        $CFG->forced_plugin_settings['backup']['backup_auto_storage'] = 0;
122
        $CFG->forced_plugin_settings['backup']['backup_auto_files'] = 1;
1 efrain 123
 
124
        // Backup the course.
125
        $user = get_admin();
126
        $controller = new \backup_controller(
127
            \backup::TYPE_1COURSE,
128
            $course->id,
129
            \backup::FORMAT_MOODLE,
130
            \backup::INTERACTIVE_NO,
131
            \backup::MODE_AUTOMATED,
132
            $user->id
133
        );
134
        $controller->execute_plan();
135
 
11 efrain 136
        // We don't need the forced setting anymore, hence restore previous settings.
1 efrain 137
        // TODO: Remove this as part of MDL-65228.
11 efrain 138
        $CFG->forced_plugin_settings['backup'] = $forcedbackupsettings;
1 efrain 139
 
140
        // Grab the result.
141
        $result = $controller->get_results();
142
        if (!isset($result['backup_destination'])) {
143
            throw new \moodle_exception('Failed to backup activity prior to deletion.');
144
        }
145
 
146
        // Have finished with the controller, let's destroy it, freeing mem and resources.
147
        $controller->destroy();
148
 
149
        // Grab the filename.
150
        $file = $result['backup_destination'];
151
        if (!$file->get_contenthash()) {
152
            throw new \moodle_exception('Failed to backup activity prior to deletion (invalid file).');
153
        }
154
 
155
        // Record the activity, get an ID.
156
        $item = new \stdClass();
157
        $item->categoryid = $course->category;
158
        $item->shortname = $course->shortname;
159
        $item->fullname = $course->fullname;
160
        $item->timecreated = time();
161
        $binid = $DB->insert_record('tool_recyclebin_category', $item);
162
 
163
        // Create the location we want to copy this file to.
164
        $filerecord = array(
165
            'contextid' => \context_coursecat::instance($course->category)->id,
166
            'component' => 'tool_recyclebin',
167
            'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
168
            'itemid' => $binid,
169
            'timemodified' => time()
170
        );
171
 
172
        // Move the file to our own special little place.
173
        $fs = get_file_storage();
174
        if (!$fs->create_file_from_storedfile($filerecord, $file)) {
175
            // Failed, cleanup first.
176
            $DB->delete_records('tool_recyclebin_category', array(
177
                'id' => $binid
178
            ));
179
 
180
            throw new \moodle_exception("Failed to copy backup file to recyclebin.");
181
        }
182
 
183
        // Delete the old file.
184
        $file->delete();
185
 
186
        // Fire event.
187
        $event = \tool_recyclebin\event\category_bin_item_created::create(array(
188
            'objectid' => $binid,
189
            'context' => \context_coursecat::instance($course->category)
190
        ));
191
        $event->trigger();
192
    }
193
 
194
    /**
195
     * Restore an item from the recycle bin.
196
     *
197
     * @param \stdClass $item The item database record
198
     * @throws \moodle_exception
199
     */
200
    public function restore_item($item) {
201
        global $CFG, $OUTPUT, $PAGE;
202
 
203
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
204
        require_once($CFG->dirroot . '/course/lib.php');
205
 
206
        $user = get_admin();
207
 
208
        // Grab the course category context.
209
        $context = \context_coursecat::instance($this->_categoryid);
210
 
211
        // Get the backup file.
212
        $fs = get_file_storage();
213
        $files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id,
214
            'itemid, filepath, filename', false);
215
 
216
        if (empty($files)) {
217
            throw new \moodle_exception('Invalid recycle bin item!');
218
        }
219
 
220
        if (count($files) > 1) {
221
            throw new \moodle_exception('Too many files found!');
222
        }
223
 
224
        // Get the backup file.
225
        $file = reset($files);
226
 
227
        // Get a backup temp directory name and create it.
228
        $tempdir = \restore_controller::get_tempdir_name($context->id, $user->id);
229
        $fulltempdir = make_backup_temp_directory($tempdir);
230
 
231
        // Extract the backup to tmpdir.
232
        $fb = get_file_packer('application/vnd.moodle.backup');
233
        $fb->extract_to_pathname($file, $fulltempdir);
234
 
235
        // Build a course.
236
        $course = new \stdClass();
237
        $course->category = $this->_categoryid;
238
        $course->shortname = $item->shortname;
239
        $course->fullname = $item->fullname;
240
        $course->summary = '';
241
 
242
        // Create a new course.
243
        $course = create_course($course);
244
        if (!$course) {
245
            throw new \moodle_exception("Could not create course to restore into.");
246
        }
247
 
248
        // As far as recycle bin is using MODE_AUTOMATED, it observes the General restore settings.
249
        // For recycle bin we want to ensure that backup files are always restore the users and groups information.
250
        // In order to achieve that, we hack the setting here via $CFG->forced_plugin_settings,
251
        // so it won't interfere other operations.
252
        // See MDL-65218 and MDL-35773 for more information.
253
        // This hack will be removed once recycle bin switches to use its own backup mode, with
254
        // own preferences and 100% separate from MOODLE_AUTOMATED.
255
        // TODO: Remove this as part of MDL-65228.
11 efrain 256
        $forcedrestoresettings = $CFG->forced_plugin_settings['restore'] ?? null;
257
        $CFG->forced_plugin_settings['restore']['restore_general_users'] = 1;
258
        $CFG->forced_plugin_settings['restore']['restore_general_groups'] = 1;
1 efrain 259
 
260
        // Define the import.
261
        $controller = new \restore_controller(
262
            $tempdir,
263
            $course->id,
264
            \backup::INTERACTIVE_NO,
265
            \backup::MODE_AUTOMATED,
266
            $user->id,
267
            \backup::TARGET_NEW_COURSE
268
        );
269
 
270
        // Prechecks.
271
        if (!$controller->execute_precheck()) {
272
            $results = $controller->get_precheck_results();
273
 
274
            // Check if errors have been found.
275
            if (!empty($results['errors'])) {
276
                // Delete the temporary file we created.
277
                fulldelete($fulltempdir);
278
 
279
                // Delete the course we created.
280
                delete_course($course, false);
281
 
282
                echo $OUTPUT->header();
283
                $backuprenderer = $PAGE->get_renderer('core', 'backup');
284
                echo $backuprenderer->precheck_notices($results);
285
                echo $OUTPUT->continue_button(new \moodle_url('/course/index.php', array('categoryid' => $this->_categoryid)));
286
                echo $OUTPUT->footer();
287
                exit();
288
            }
289
        }
290
 
291
        // Run the import.
292
        $controller->execute_plan();
293
 
11 efrain 294
        // We don't need the forced setting anymore, hence restore previous settings.
1 efrain 295
        // TODO: Remove this as part of MDL-65228.
11 efrain 296
        $CFG->forced_plugin_settings['restore'] = $forcedrestoresettings;
1 efrain 297
 
298
        // Have finished with the controller, let's destroy it, freeing mem and resources.
299
        $controller->destroy();
300
 
301
        // Fire event.
302
        $event = \tool_recyclebin\event\category_bin_item_restored::create(array(
303
            'objectid' => $item->id,
304
            'context' => $context
305
        ));
306
        $event->add_record_snapshot('tool_recyclebin_category', $item);
307
        $event->trigger();
308
 
309
        // Cleanup.
310
        fulldelete($fulltempdir);
311
        $this->delete_item($item);
312
    }
313
 
314
    /**
315
     * Delete an item from the recycle bin.
316
     *
317
     * @param \stdClass $item The item database record
318
     * @throws \coding_exception
319
     */
320
    public function delete_item($item) {
321
        global $DB;
322
 
323
        // Grab the course category context.
324
        $context = \context_coursecat::instance($this->_categoryid, IGNORE_MISSING);
325
        if (!empty($context)) {
326
            // Delete the files.
327
            $fs = get_file_storage();
328
            $fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
329
        } else {
330
            // Course category has been deleted. Find records using $item->id as this is unique for coursecat recylebin.
331
            $files = $DB->get_recordset('files', [
332
                'component' => 'tool_recyclebin',
333
                'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
334
                'itemid' => $item->id,
335
            ]);
336
            $fs = get_file_storage();
337
            foreach ($files as $filer) {
338
                $file = $fs->get_file_instance($filer);
339
                $file->delete();
340
            }
341
            $files->close();
342
        }
343
 
344
        // Delete the record.
345
        $DB->delete_records('tool_recyclebin_category', array(
346
            'id' => $item->id
347
        ));
348
 
349
        // The coursecat might have been deleted, check we have a context before triggering event.
350
        if (!$context) {
351
            return;
352
        }
353
 
354
        // Fire event.
355
        $event = \tool_recyclebin\event\category_bin_item_deleted::create(array(
356
            'objectid' => $item->id,
357
            'context' => \context_coursecat::instance($item->categoryid)
358
        ));
359
        $event->add_record_snapshot('tool_recyclebin_category', $item);
360
        $event->trigger();
361
    }
362
 
363
    /**
364
     * Can we view items in this recycle bin?
365
     *
366
     * @return bool returns true if they can view, false if not
367
     */
368
    public function can_view() {
369
        $context = \context_coursecat::instance($this->_categoryid);
370
        return has_capability('tool/recyclebin:viewitems', $context);
371
    }
372
 
373
    /**
374
     * Can we restore items in this recycle bin?
375
     *
376
     * @return bool returns true if they can restore, false if not
377
     */
378
    public function can_restore() {
379
        $context = \context_coursecat::instance($this->_categoryid);
380
        return has_capability('tool/recyclebin:restoreitems', $context);
381
    }
382
 
383
    /**
384
     * Can we delete items in this recycle bin?
385
     *
386
     * @return bool returns true if they can delete, false if not
387
     */
388
    public function can_delete() {
389
        $context = \context_coursecat::instance($this->_categoryid);
390
        return has_capability('tool/recyclebin:deleteitems', $context);
391
    }
392
}