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