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