Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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.
120
        $CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0, 'backup_auto_files' => 1];
121
 
122
        // Backup the course.
123
        $user = get_admin();
124
        $controller = new \backup_controller(
125
            \backup::TYPE_1COURSE,
126
            $course->id,
127
            \backup::FORMAT_MOODLE,
128
            \backup::INTERACTIVE_NO,
129
            \backup::MODE_AUTOMATED,
130
            $user->id
131
        );
132
        $controller->execute_plan();
133
 
134
        // We don't need the forced setting anymore, hence unsetting it.
135
        // TODO: Remove this as part of MDL-65228.
136
        unset($CFG->forced_plugin_settings['backup']);
137
 
138
        // Grab the result.
139
        $result = $controller->get_results();
140
        if (!isset($result['backup_destination'])) {
141
            throw new \moodle_exception('Failed to backup activity prior to deletion.');
142
        }
143
 
144
        // Have finished with the controller, let's destroy it, freeing mem and resources.
145
        $controller->destroy();
146
 
147
        // Grab the filename.
148
        $file = $result['backup_destination'];
149
        if (!$file->get_contenthash()) {
150
            throw new \moodle_exception('Failed to backup activity prior to deletion (invalid file).');
151
        }
152
 
153
        // Record the activity, get an ID.
154
        $item = new \stdClass();
155
        $item->categoryid = $course->category;
156
        $item->shortname = $course->shortname;
157
        $item->fullname = $course->fullname;
158
        $item->timecreated = time();
159
        $binid = $DB->insert_record('tool_recyclebin_category', $item);
160
 
161
        // Create the location we want to copy this file to.
162
        $filerecord = array(
163
            'contextid' => \context_coursecat::instance($course->category)->id,
164
            'component' => 'tool_recyclebin',
165
            'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
166
            'itemid' => $binid,
167
            'timemodified' => time()
168
        );
169
 
170
        // Move the file to our own special little place.
171
        $fs = get_file_storage();
172
        if (!$fs->create_file_from_storedfile($filerecord, $file)) {
173
            // Failed, cleanup first.
174
            $DB->delete_records('tool_recyclebin_category', array(
175
                'id' => $binid
176
            ));
177
 
178
            throw new \moodle_exception("Failed to copy backup file to recyclebin.");
179
        }
180
 
181
        // Delete the old file.
182
        $file->delete();
183
 
184
        // Fire event.
185
        $event = \tool_recyclebin\event\category_bin_item_created::create(array(
186
            'objectid' => $binid,
187
            'context' => \context_coursecat::instance($course->category)
188
        ));
189
        $event->trigger();
190
    }
191
 
192
    /**
193
     * Restore an item from the recycle bin.
194
     *
195
     * @param \stdClass $item The item database record
196
     * @throws \moodle_exception
197
     */
198
    public function restore_item($item) {
199
        global $CFG, $OUTPUT, $PAGE;
200
 
201
        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
202
        require_once($CFG->dirroot . '/course/lib.php');
203
 
204
        $user = get_admin();
205
 
206
        // Grab the course category context.
207
        $context = \context_coursecat::instance($this->_categoryid);
208
 
209
        // Get the backup file.
210
        $fs = get_file_storage();
211
        $files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id,
212
            'itemid, filepath, filename', false);
213
 
214
        if (empty($files)) {
215
            throw new \moodle_exception('Invalid recycle bin item!');
216
        }
217
 
218
        if (count($files) > 1) {
219
            throw new \moodle_exception('Too many files found!');
220
        }
221
 
222
        // Get the backup file.
223
        $file = reset($files);
224
 
225
        // Get a backup temp directory name and create it.
226
        $tempdir = \restore_controller::get_tempdir_name($context->id, $user->id);
227
        $fulltempdir = make_backup_temp_directory($tempdir);
228
 
229
        // Extract the backup to tmpdir.
230
        $fb = get_file_packer('application/vnd.moodle.backup');
231
        $fb->extract_to_pathname($file, $fulltempdir);
232
 
233
        // Build a course.
234
        $course = new \stdClass();
235
        $course->category = $this->_categoryid;
236
        $course->shortname = $item->shortname;
237
        $course->fullname = $item->fullname;
238
        $course->summary = '';
239
 
240
        // Create a new course.
241
        $course = create_course($course);
242
        if (!$course) {
243
            throw new \moodle_exception("Could not create course to restore into.");
244
        }
245
 
246
        // As far as recycle bin is using MODE_AUTOMATED, it observes the General restore settings.
247
        // For recycle bin we want to ensure that backup files are always restore the users and groups information.
248
        // In order to achieve that, we hack the setting here via $CFG->forced_plugin_settings,
249
        // so it won't interfere other operations.
250
        // See MDL-65218 and MDL-35773 for more information.
251
        // This hack will be removed once recycle bin switches to use its own backup mode, with
252
        // own preferences and 100% separate from MOODLE_AUTOMATED.
253
        // TODO: Remove this as part of MDL-65228.
254
        $CFG->forced_plugin_settings['restore'] = ['restore_general_users' => 1, 'restore_general_groups' => 1];
255
 
256
        // Define the import.
257
        $controller = new \restore_controller(
258
            $tempdir,
259
            $course->id,
260
            \backup::INTERACTIVE_NO,
261
            \backup::MODE_AUTOMATED,
262
            $user->id,
263
            \backup::TARGET_NEW_COURSE
264
        );
265
 
266
        // Prechecks.
267
        if (!$controller->execute_precheck()) {
268
            $results = $controller->get_precheck_results();
269
 
270
            // Check if errors have been found.
271
            if (!empty($results['errors'])) {
272
                // Delete the temporary file we created.
273
                fulldelete($fulltempdir);
274
 
275
                // Delete the course we created.
276
                delete_course($course, false);
277
 
278
                echo $OUTPUT->header();
279
                $backuprenderer = $PAGE->get_renderer('core', 'backup');
280
                echo $backuprenderer->precheck_notices($results);
281
                echo $OUTPUT->continue_button(new \moodle_url('/course/index.php', array('categoryid' => $this->_categoryid)));
282
                echo $OUTPUT->footer();
283
                exit();
284
            }
285
        }
286
 
287
        // Run the import.
288
        $controller->execute_plan();
289
 
290
        // We don't need the forced setting anymore, hence unsetting it.
291
        // TODO: Remove this as part of MDL-65228.
292
        unset($CFG->forced_plugin_settings['restore']);
293
 
294
        // Have finished with the controller, let's destroy it, freeing mem and resources.
295
        $controller->destroy();
296
 
297
        // Fire event.
298
        $event = \tool_recyclebin\event\category_bin_item_restored::create(array(
299
            'objectid' => $item->id,
300
            'context' => $context
301
        ));
302
        $event->add_record_snapshot('tool_recyclebin_category', $item);
303
        $event->trigger();
304
 
305
        // Cleanup.
306
        fulldelete($fulltempdir);
307
        $this->delete_item($item);
308
    }
309
 
310
    /**
311
     * Delete an item from the recycle bin.
312
     *
313
     * @param \stdClass $item The item database record
314
     * @throws \coding_exception
315
     */
316
    public function delete_item($item) {
317
        global $DB;
318
 
319
        // Grab the course category context.
320
        $context = \context_coursecat::instance($this->_categoryid, IGNORE_MISSING);
321
        if (!empty($context)) {
322
            // Delete the files.
323
            $fs = get_file_storage();
324
            $fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
325
        } else {
326
            // Course category has been deleted. Find records using $item->id as this is unique for coursecat recylebin.
327
            $files = $DB->get_recordset('files', [
328
                'component' => 'tool_recyclebin',
329
                'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
330
                'itemid' => $item->id,
331
            ]);
332
            $fs = get_file_storage();
333
            foreach ($files as $filer) {
334
                $file = $fs->get_file_instance($filer);
335
                $file->delete();
336
            }
337
            $files->close();
338
        }
339
 
340
        // Delete the record.
341
        $DB->delete_records('tool_recyclebin_category', array(
342
            'id' => $item->id
343
        ));
344
 
345
        // The coursecat might have been deleted, check we have a context before triggering event.
346
        if (!$context) {
347
            return;
348
        }
349
 
350
        // Fire event.
351
        $event = \tool_recyclebin\event\category_bin_item_deleted::create(array(
352
            'objectid' => $item->id,
353
            'context' => \context_coursecat::instance($item->categoryid)
354
        ));
355
        $event->add_record_snapshot('tool_recyclebin_category', $item);
356
        $event->trigger();
357
    }
358
 
359
    /**
360
     * Can we view items in this recycle bin?
361
     *
362
     * @return bool returns true if they can view, false if not
363
     */
364
    public function can_view() {
365
        $context = \context_coursecat::instance($this->_categoryid);
366
        return has_capability('tool/recyclebin:viewitems', $context);
367
    }
368
 
369
    /**
370
     * Can we restore items in this recycle bin?
371
     *
372
     * @return bool returns true if they can restore, false if not
373
     */
374
    public function can_restore() {
375
        $context = \context_coursecat::instance($this->_categoryid);
376
        return has_capability('tool/recyclebin:restoreitems', $context);
377
    }
378
 
379
    /**
380
     * Can we delete items in this recycle bin?
381
     *
382
     * @return bool returns true if they can delete, false if not
383
     */
384
    public function can_delete() {
385
        $context = \context_coursecat::instance($this->_categoryid);
386
        return has_capability('tool/recyclebin:deleteitems', $context);
387
    }
388
}