Proyectos de Subversion Moodle

Rev

| 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
 * Course and category management helper class.
19
 *
20
 * @package    core_course
21
 * @copyright  2013 Sam Hemelryk
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_course\management;
26
 
27
defined('MOODLE_INTERNAL') || die;
28
 
29
require_once($CFG->dirroot . '/course/lib.php');
30
 
31
/**
32
 * Course and category management interface helper class.
33
 *
34
 * This class provides methods useful to the course and category management interfaces.
35
 * Many of the methods on this class are static and serve one of two purposes.
36
 *  1.  encapsulate functionality in an effort to ensure minimal changes between the different
37
 *      methods of interaction. Specifically browser, AJAX and webservice.
38
 *  2.  abstract logic for acquiring actions away from output so that renderers may use them without
39
 *      having to include any logic or capability checks.
40
 *
41
 * @package    core_course
42
 * @copyright  2013 Sam Hemelryk
43
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44
 */
45
class helper {
46
 
47
    /**
48
     * The expanded category structure if its already being loaded from the cache.
49
     * @var null|array
50
     */
51
    protected static $expandedcategories = null;
52
 
53
    /**
54
     * Returns course details in an array ready to be printed.
55
     *
56
     * @global \moodle_database $DB
57
     * @param \core_course_list_element $course
58
     * @return array
59
     */
60
    public static function get_course_detail_array(\core_course_list_element $course) {
61
        global $DB;
62
 
63
        $canaccess = $course->can_access();
64
 
65
        $format = \course_get_format($course->id);
66
        $modinfo = \get_fast_modinfo($course->id);
67
        $modules = $modinfo->get_used_module_names();
68
        $sections = array();
69
        if ($format->uses_sections()) {
70
            foreach ($modinfo->get_section_info_all() as $section) {
71
                if ($section->uservisible) {
72
                    $sections[] = $format->get_section_name($section);
73
                }
74
            }
75
        }
76
 
77
        $category = \core_course_category::get($course->category);
78
        $categoryurl = new \moodle_url('/course/management.php', array('categoryid' => $course->category));
79
        $categoryname = $category->get_formatted_name();
80
 
81
        $details = array(
82
            'fullname' => array(
83
                'key' => \get_string('fullname'),
84
                'value' => $course->get_formatted_fullname()
85
            ),
86
            'shortname' => array(
87
                'key' => \get_string('shortname'),
88
                'value' => $course->get_formatted_shortname()
89
            ),
90
            'idnumber' => array(
91
                'key' => \get_string('idnumber'),
92
                'value' => s($course->idnumber)
93
            ),
94
            'category' => array(
95
                'key' => \get_string('category'),
96
                'value' => \html_writer::link($categoryurl, $categoryname)
97
            )
98
        );
99
        if (has_capability('moodle/site:accessallgroups', $course->get_context())) {
100
            $groups = \groups_get_course_data($course->id);
101
            $details += array(
102
                'groupings' => array(
103
                    'key' => \get_string('groupings', 'group'),
104
                    'value' => count($groups->groupings)
105
                ),
106
                'groups' => array(
107
                    'key' => \get_string('groups'),
108
                    'value' => count($groups->groups)
109
                )
110
            );
111
        }
112
        if ($canaccess) {
113
            $names = \role_get_names($course->get_context());
114
            $sql = 'SELECT ra.roleid, COUNT(ra.id) AS rolecount
115
                      FROM {role_assignments} ra
116
                     WHERE ra.contextid = :contextid
117
                  GROUP BY ra.roleid';
118
            $rolecounts = $DB->get_records_sql($sql, array('contextid' => $course->get_context()->id));
119
            $roledetails = array();
120
            foreach ($rolecounts as $result) {
121
                $a = new \stdClass;
122
                $a->role = $names[$result->roleid]->localname;
123
                $a->count = $result->rolecount;
124
                $roledetails[] = \get_string('assignedrolecount', 'moodle', $a);
125
            }
126
 
127
            $details['roleassignments'] = array(
128
                'key' => \get_string('roleassignments'),
129
                'value' => join('<br />', $roledetails)
130
            );
131
        }
132
        if ($course->can_review_enrolments()) {
133
            $enrolmentlines = array();
134
            $instances = \enrol_get_instances($course->id, true);
135
            $plugins = \enrol_get_plugins(true);
136
            foreach ($instances as $instance) {
137
                if (!isset($plugins[$instance->enrol])) {
138
                    // Weird.
139
                    continue;
140
                }
141
                $plugin = $plugins[$instance->enrol];
142
                $enrolmentlines[] = $plugin->get_instance_name($instance);
143
            }
144
            $details['enrolmentmethods'] = array(
145
                'key' => \get_string('enrolmentmethods'),
146
                'value' => join('<br />', $enrolmentlines)
147
            );
148
        }
149
        if ($canaccess) {
150
            $details['format'] = array(
151
                'key' => \get_string('format'),
152
                'value' => \course_get_format($course)->get_format_name()
153
            );
154
            $details['sections'] = array(
155
                'key' => \get_string('sections'),
156
                'value' => join('<br />', $sections)
157
            );
158
            $details['modulesused'] = array(
159
                'key' => \get_string('modulesused'),
160
                'value' =>  join('<br />', $modules)
161
            );
162
        }
163
        return $details;
164
    }
165
 
166
    /**
167
     * Returns an array of actions that can be performed upon a category being shown in a list.
168
     *
169
     * @param \core_course_category $category
170
     * @return array
171
     */
172
    public static function get_category_listitem_actions(\core_course_category $category) {
173
        global $CFG;
174
 
175
        $manageurl = new \moodle_url('/course/management.php', array('categoryid' => $category->id));
176
        $baseurl = new \moodle_url($manageurl, array('sesskey' => \sesskey()));
177
        $actions = array();
178
 
179
        // View link.
180
        $actions['view'] = [
181
            'url' => new \moodle_url('/course/index.php', ['categoryid' => $category->id]),
182
            'icon' => null,
183
            'string' => get_string('view')
184
        ];
185
 
186
        // Edit.
187
        if ($category->can_edit()) {
188
            $actions['edit'] = array(
189
                'url' => new \moodle_url('/course/editcategory.php', array('id' => $category->id)),
190
                'icon' => new \pix_icon('t/edit', new \lang_string('edit')),
191
                'string' => new \lang_string('edit')
192
            );
193
        }
194
 
195
        // Show/Hide.
196
        if ($category->can_change_visibility()) {
197
            // We always show both icons and then just toggle the display of the invalid option with CSS.
198
            $actions['hide'] = array(
199
                'url' => new \moodle_url($baseurl, array('action' => 'hidecategory')),
200
                'icon' => new \pix_icon('t/hide', new \lang_string('hide')),
201
                'string' => new \lang_string('hide')
202
            );
203
            $actions['show'] = array(
204
                'url' => new \moodle_url($baseurl, array('action' => 'showcategory')),
205
                'icon' => new \pix_icon('t/show', new \lang_string('show')),
206
                'string' => new \lang_string('show')
207
            );
208
        }
209
 
210
        // Move up/down.
211
        if ($category->can_change_sortorder()) {
212
            $actions['moveup'] = array(
213
                'url' => new \moodle_url($baseurl, array('action' => 'movecategoryup')),
214
                'icon' => new \pix_icon('t/up', new \lang_string('moveup')),
215
                'string' => new \lang_string('moveup')
216
            );
217
            $actions['movedown'] = array(
218
                'url' => new \moodle_url($baseurl, array('action' => 'movecategorydown')),
219
                'icon' => new \pix_icon('t/down', new \lang_string('movedown')),
220
                'string' => new \lang_string('movedown')
221
            );
222
        }
223
 
224
        if ($category->can_create_subcategory()) {
225
            $actions['createnewsubcategory'] = array(
226
                'url' => new \moodle_url('/course/editcategory.php', array('parent' => $category->id)),
227
                'icon' => new \pix_icon('i/withsubcat', new \lang_string('createnewsubcategory')),
228
                'string' => new \lang_string('createnewsubcategory')
229
            );
230
        }
231
 
232
        // Resort.
233
        if ($category->can_resort_subcategories() && $category->has_children()) {
234
            $actions['resortbyname'] = array(
235
                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'name')),
236
                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
237
                'string' => new \lang_string('resortsubcategoriesby', 'moodle' , get_string('categoryname'))
238
            );
239
            $actions['resortbynamedesc'] = array(
240
                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'namedesc')),
241
                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
242
                'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('categoryname'))
243
            );
244
            $actions['resortbyidnumber'] = array(
245
                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumber')),
246
                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
247
                'string' => new \lang_string('resortsubcategoriesby', 'moodle', get_string('idnumbercoursecategory'))
248
            );
249
            $actions['resortbyidnumberdesc'] = array(
250
                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumberdesc')),
251
                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
252
                'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('idnumbercoursecategory'))
253
            );
254
        }
255
 
256
        // Delete.
257
        if (!empty($category->move_content_targets_list()) || $category->can_delete_full()) {
258
            $actions['delete'] = array(
259
                'url' => new \moodle_url($baseurl, array('action' => 'deletecategory')),
260
                'icon' => new \pix_icon('t/delete', new \lang_string('delete')),
261
                'string' => new \lang_string('delete')
262
            );
263
        }
264
 
265
        // Permissions.
266
        if ($category->can_review_permissions()) {
267
            $actions['permissions'] = array(
268
                'url' => new \moodle_url('/admin/roles/permissions.php', ['contextid' => $category->get_context()->id]),
269
                'icon' => new \pix_icon('i/permissions', new \lang_string('permissions', 'role')),
270
                'string' => new \lang_string('permissions', 'role')
271
            );
272
        }
273
 
274
        // Context locking.
275
        if (!empty($CFG->contextlocking) && has_capability('moodle/site:managecontextlocks', $category->get_context())) {
276
            $parentcontext = $category->get_context()->get_parent_context();
277
            if (empty($parentcontext) || !$parentcontext->locked) {
278
                if ($category->get_context()->locked) {
279
                    $lockicon = 'i/unlock';
280
                    $lockstring = get_string('managecontextunlock', 'admin');
281
                } else {
282
                    $lockicon = 'i/lock';
283
                    $lockstring = get_string('managecontextlock', 'admin');
284
                }
285
                $actions['managecontextlock'] = [
286
                    'url' => new \moodle_url('/admin/lock.php', [
287
                            'id' => $category->get_context()->id,
288
                            'returnurl' => $manageurl->out_as_local_url(false),
289
                        ]),
290
                    'icon' => new \pix_icon($lockicon, $lockstring),
291
                    'string' => $lockstring,
292
                ];
293
            }
294
        }
295
 
296
        // Cohorts.
297
        if ($category->can_review_cohorts()) {
298
            $actions['cohorts'] = array(
299
                'url' => new \moodle_url('/cohort/index.php', array('contextid' => $category->get_context()->id)),
300
                'icon' => new \pix_icon('t/cohort', new \lang_string('cohorts', 'cohort')),
301
                'string' => new \lang_string('cohorts', 'cohort')
302
            );
303
        }
304
 
305
        // Filters.
306
        if ($category->can_review_filters()) {
307
            $actions['filters'] = array(
308
                'url' => new \moodle_url('/filter/manage.php', array('contextid' => $category->get_context()->id,
309
                    'return' => 'management')),
310
                'icon' => new \pix_icon('i/filter', new \lang_string('filters', 'admin')),
311
                'string' => new \lang_string('filters', 'admin')
312
            );
313
        }
314
 
315
        if ($category->can_restore_courses_into()) {
316
            $actions['restore'] = array(
317
                'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $category->get_context()->id)),
318
                'icon' => new \pix_icon('i/restore', new \lang_string('restorecourse', 'admin')),
319
                'string' => new \lang_string('restorecourse', 'admin')
320
            );
321
        }
322
        // Recyclebyn.
323
        if (\tool_recyclebin\category_bin::is_enabled()) {
324
            $categorybin = new \tool_recyclebin\category_bin($category->id);
325
            if ($categorybin->can_view()) {
326
                $autohide = get_config('tool_recyclebin', 'autohide');
327
                if ($autohide) {
328
                    $items = $categorybin->get_items();
329
                } else {
330
                    $items = [];
331
                }
332
                if (!$autohide || !empty($items)) {
333
                    $pluginname = get_string('pluginname', 'tool_recyclebin');
334
                    $actions['recyclebin'] = [
335
                       'url' => new \moodle_url('/admin/tool/recyclebin/index.php', ['contextid' => $category->get_context()->id]),
336
                       'icon' => new \pix_icon('trash', $pluginname, 'tool_recyclebin'),
337
                       'string' => $pluginname
338
                    ];
339
                }
340
            }
341
        }
342
 
343
        // Content bank.
344
        if ($category->has_contentbank()) {
345
            $url = new \moodle_url('/contentbank/index.php', ['contextid' => $category->get_context()->id]);
346
            $actions['contentbank'] = [
347
                'url' => $url,
348
                'icon' => new \pix_icon('i/contentbank', ''),
349
                'string' => get_string('contentbank')
350
            ];
351
        }
352
 
353
        return $actions;
354
    }
355
 
356
    /**
357
     * Returns an array of actions for a course listitem.
358
     *
359
     * @param \core_course_category $category
360
     * @param \core_course_list_element $course
361
     * @return array
362
     */
363
    public static function get_course_listitem_actions(\core_course_category $category, \core_course_list_element $course) {
364
        $baseurl = new \moodle_url(
365
            '/course/management.php',
366
            array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey())
367
        );
368
        $actions = array();
369
        // Edit.
370
        if ($course->can_edit()) {
371
            $actions[] = array(
372
                'url' => new \moodle_url('/course/edit.php', array('id' => $course->id, 'returnto' => 'catmanage')),
373
                'icon' => new \pix_icon('t/edit', \get_string('edit')),
374
                'attributes' => array('class' => 'action-edit')
375
            );
376
        }
377
        // Copy.
378
        if (self::can_copy_course($course->id)) {
379
            $actions[] = array(
380
                'url' => new \moodle_url('/backup/copy.php', array('id' => $course->id, 'returnto' => 'catmanage')),
381
                'icon' => new \pix_icon('t/copy', \get_string('copycourse')),
382
                'attributes' => array('class' => 'action-copy')
383
            );
384
        }
385
        // Delete.
386
        if ($course->can_delete()) {
387
            $actions[] = array(
388
                'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)),
389
                'icon' => new \pix_icon('t/delete', \get_string('delete')),
390
                'attributes' => array('class' => 'action-delete')
391
            );
392
        }
393
        // Show/Hide.
394
        if ($course->can_change_visibility()) {
395
            $actions[] = array(
396
                'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')),
397
                'icon' => new \pix_icon('t/hide', \get_string('hide')),
398
                'attributes' => array('data-action' => 'hide', 'class' => 'action-hide')
399
            );
400
            $actions[] = array(
401
                'url' => new \moodle_url($baseurl, array('action' => 'showcourse')),
402
                'icon' => new \pix_icon('t/show', \get_string('show')),
403
                'attributes' => array('data-action' => 'show', 'class' => 'action-show')
404
            );
405
        }
406
        // Move up/down.
407
        if ($category->can_resort_courses()) {
408
            $actions[] = array(
409
                'url' => new \moodle_url($baseurl, array('action' => 'movecourseup')),
410
                'icon' => new \pix_icon('t/up', \get_string('moveup')),
411
                'attributes' => array('data-action' => 'moveup', 'class' => 'action-moveup')
412
            );
413
            $actions[] = array(
414
                'url' => new \moodle_url($baseurl, array('action' => 'movecoursedown')),
415
                'icon' => new \pix_icon('t/down', \get_string('movedown')),
416
                'attributes' => array('data-action' => 'movedown', 'class' => 'action-movedown')
417
            );
418
        }
419
        return $actions;
420
    }
421
 
422
    /**
423
     * Returns an array of actions that can be performed on the course being displayed.
424
     *
425
     * @param \core_course_list_element $course
426
     * @return array
427
     */
428
    public static function get_course_detail_actions(\core_course_list_element $course) {
429
        $params = array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey());
430
        $baseurl = new \moodle_url('/course/management.php', $params);
431
        $actions = array();
432
        // View.
433
        $actions['view'] = array(
434
            'url' => new \moodle_url('/course/view.php', array('id' => $course->id)),
435
            'string' => \get_string('view')
436
        );
437
        // Edit.
438
        if ($course->can_edit()) {
439
            $actions['edit'] = array(
440
                'url' => new \moodle_url('/course/edit.php', array('id' => $course->id)),
441
                'string' => \get_string('edit')
442
            );
443
        }
444
        // Permissions.
445
        if ($course->can_review_enrolments()) {
446
            $actions['enrolledusers'] = array(
447
                'url' => new \moodle_url('/user/index.php', array('id' => $course->id)),
448
                'string' => \get_string('enrolledusers', 'enrol')
449
            );
450
        }
451
        // Delete.
452
        if ($course->can_delete()) {
453
            $actions['delete'] = array(
454
                'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)),
455
                'string' => \get_string('delete')
456
            );
457
        }
458
        // Show/Hide.
459
        if ($course->can_change_visibility()) {
460
            if ($course->visible) {
461
                $actions['hide'] = array(
462
                    'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')),
463
                    'string' => \get_string('hide')
464
                );
465
            } else {
466
                $actions['show'] = array(
467
                    'url' => new \moodle_url($baseurl, array('action' => 'showcourse')),
468
                    'string' => \get_string('show')
469
                );
470
            }
471
        }
472
        // Backup.
473
        if ($course->can_backup()) {
474
            $actions['backup'] = array(
475
                'url' => new \moodle_url('/backup/backup.php', array('id' => $course->id)),
476
                'string' => \get_string('backup')
477
            );
478
        }
479
        // Restore.
480
        if ($course->can_restore()) {
481
            $actions['restore'] = array(
482
                'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $course->get_context()->id)),
483
                'string' => \get_string('restore')
484
            );
485
        }
486
        return $actions;
487
    }
488
 
489
    /**
490
     * Resorts the courses within a category moving the given course up by one.
491
     *
492
     * @param \core_course_list_element $course
493
     * @param \core_course_category $category
494
     * @return bool
495
     * @throws \moodle_exception
496
     */
497
    public static function action_course_change_sortorder_up_one(\core_course_list_element $course,
498
                                                                 \core_course_category $category) {
499
        if (!$category->can_resort_courses()) {
500
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort');
501
        }
502
        return \course_change_sortorder_by_one($course, true);
503
    }
504
 
505
    /**
506
     * Resorts the courses within a category moving the given course down by one.
507
     *
508
     * @param \core_course_list_element $course
509
     * @param \core_course_category $category
510
     * @return bool
511
     * @throws \moodle_exception
512
     */
513
    public static function action_course_change_sortorder_down_one(\core_course_list_element $course,
514
                                                                   \core_course_category $category) {
515
        if (!$category->can_resort_courses()) {
516
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort');
517
        }
518
        return \course_change_sortorder_by_one($course, false);
519
    }
520
 
521
    /**
522
     * Resorts the courses within a category moving the given course up by one.
523
     *
524
     * @global \moodle_database $DB
525
     * @param int|\stdClass $courserecordorid
526
     * @return bool
527
     */
528
    public static function action_course_change_sortorder_up_one_by_record($courserecordorid) {
529
        if (is_int($courserecordorid)) {
530
            $courserecordorid = get_course($courserecordorid);
531
        }
532
        $course = new \core_course_list_element($courserecordorid);
533
        $category = \core_course_category::get($course->category);
534
        return self::action_course_change_sortorder_up_one($course, $category);
535
    }
536
 
537
    /**
538
     * Resorts the courses within a category moving the given course down by one.
539
     *
540
     * @global \moodle_database $DB
541
     * @param int|\stdClass $courserecordorid
542
     * @return bool
543
     */
544
    public static function action_course_change_sortorder_down_one_by_record($courserecordorid) {
545
        if (is_int($courserecordorid)) {
546
            $courserecordorid = get_course($courserecordorid);
547
        }
548
        $course = new \core_course_list_element($courserecordorid);
549
        $category = \core_course_category::get($course->category);
550
        return self::action_course_change_sortorder_down_one($course, $category);
551
    }
552
 
553
    /**
554
     * Changes the sort order so that the first course appears after the second course.
555
     *
556
     * @param int|\stdClass $courserecordorid
557
     * @param int $moveaftercourseid
558
     * @return bool
559
     * @throws \moodle_exception
560
     */
561
    public static function action_course_change_sortorder_after_course($courserecordorid, $moveaftercourseid) {
562
        $course = \get_course($courserecordorid);
563
        $category = \core_course_category::get($course->category);
564
        if (!$category->can_resort_courses()) {
565
            $url = '/course/management.php?categoryid='.$course->category;
566
            throw new \moodle_exception('nopermissions', 'error', $url, \get_string('resortcourses', 'moodle'));
567
        }
568
        return \course_change_sortorder_after_course($course, $moveaftercourseid);
569
    }
570
 
571
    /**
572
     * Makes a course visible given a \core_course_list_element object.
573
     *
574
     * @param \core_course_list_element $course
575
     * @return bool
576
     * @throws \moodle_exception
577
     */
578
    public static function action_course_show(\core_course_list_element $course) {
579
        if (!$course->can_change_visibility()) {
580
            throw new \moodle_exception('permissiondenied', 'error', '', null,
581
                'core_course_list_element::can_change_visbility');
582
        }
583
        return course_change_visibility($course->id, true);
584
    }
585
 
586
    /**
587
     * Makes a course hidden given a \core_course_list_element object.
588
     *
589
     * @param \core_course_list_element $course
590
     * @return bool
591
     * @throws \moodle_exception
592
     */
593
    public static function action_course_hide(\core_course_list_element $course) {
594
        if (!$course->can_change_visibility()) {
595
            throw new \moodle_exception('permissiondenied', 'error', '', null,
596
                'core_course_list_element::can_change_visbility');
597
        }
598
        return course_change_visibility($course->id, false);
599
    }
600
 
601
    /**
602
     * Makes a course visible given a course id or a database record.
603
     *
604
     * @global \moodle_database $DB
605
     * @param int|\stdClass $courserecordorid
606
     * @return bool
607
     */
608
    public static function action_course_show_by_record($courserecordorid) {
609
        if (is_int($courserecordorid)) {
610
            $courserecordorid = get_course($courserecordorid);
611
        }
612
        $course = new \core_course_list_element($courserecordorid);
613
        return self::action_course_show($course);
614
    }
615
 
616
    /**
617
     * Makes a course hidden given a course id or a database record.
618
     *
619
     * @global \moodle_database $DB
620
     * @param int|\stdClass $courserecordorid
621
     * @return bool
622
     */
623
    public static function action_course_hide_by_record($courserecordorid) {
624
        if (is_int($courserecordorid)) {
625
            $courserecordorid = get_course($courserecordorid);
626
        }
627
        $course = new \core_course_list_element($courserecordorid);
628
        return self::action_course_hide($course);
629
    }
630
 
631
    /**
632
     * Resort a categories subcategories shifting the given category up one.
633
     *
634
     * @param \core_course_category $category
635
     * @return bool
636
     * @throws \moodle_exception
637
     */
638
    public static function action_category_change_sortorder_up_one(\core_course_category $category) {
639
        if (!$category->can_change_sortorder()) {
640
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_sortorder');
641
        }
642
        return $category->change_sortorder_by_one(true);
643
    }
644
 
645
    /**
646
     * Resort a categories subcategories shifting the given category down one.
647
     *
648
     * @param \core_course_category $category
649
     * @return bool
650
     * @throws \moodle_exception
651
     */
652
    public static function action_category_change_sortorder_down_one(\core_course_category $category) {
653
        if (!$category->can_change_sortorder()) {
654
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_sortorder');
655
        }
656
        return $category->change_sortorder_by_one(false);
657
    }
658
 
659
    /**
660
     * Resort a categories subcategories shifting the given category up one.
661
     *
662
     * @param int $categoryid
663
     * @return bool
664
     */
665
    public static function action_category_change_sortorder_up_one_by_id($categoryid) {
666
        $category = \core_course_category::get($categoryid);
667
        return self::action_category_change_sortorder_up_one($category);
668
    }
669
 
670
    /**
671
     * Resort a categories subcategories shifting the given category down one.
672
     *
673
     * @param int $categoryid
674
     * @return bool
675
     */
676
    public static function action_category_change_sortorder_down_one_by_id($categoryid) {
677
        $category = \core_course_category::get($categoryid);
678
        return self::action_category_change_sortorder_down_one($category);
679
    }
680
 
681
    /**
682
     * Makes a category hidden given a core_course_category object.
683
     *
684
     * @param \core_course_category $category
685
     * @return bool
686
     * @throws \moodle_exception
687
     */
688
    public static function action_category_hide(\core_course_category $category) {
689
        if (!$category->can_change_visibility()) {
690
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_visbility');
691
        }
692
        $category->hide();
693
        return true;
694
    }
695
 
696
    /**
697
     * Makes a category visible given a core_course_category object.
698
     *
699
     * @param \core_course_category $category
700
     * @return bool
701
     * @throws \moodle_exception
702
     */
703
    public static function action_category_show(\core_course_category $category) {
704
        if (!$category->can_change_visibility()) {
705
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_visbility');
706
        }
707
        $category->show();
708
        return true;
709
    }
710
 
711
    /**
712
     * Makes a category visible given a course category id or database record.
713
     *
714
     * @param int|\stdClass $categoryid
715
     * @return bool
716
     */
717
    public static function action_category_show_by_id($categoryid) {
718
        return self::action_category_show(\core_course_category::get($categoryid));
719
    }
720
 
721
    /**
722
     * Makes a category hidden given a course category id or database record.
723
     *
724
     * @param int|\stdClass $categoryid
725
     * @return bool
726
     */
727
    public static function action_category_hide_by_id($categoryid) {
728
        return self::action_category_hide(\core_course_category::get($categoryid));
729
    }
730
 
731
    /**
732
     * Resorts the sub categories of the given category.
733
     *
734
     * @param \core_course_category $category
735
     * @param string $sort One of idnumber or name.
736
     * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later.
737
     * @return bool
738
     * @throws \moodle_exception
739
     */
740
    public static function action_category_resort_subcategories(\core_course_category $category, $sort, $cleanup = true) {
741
        if (!$category->can_resort_subcategories()) {
742
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort');
743
        }
744
        return $category->resort_subcategories($sort, $cleanup);
745
    }
746
 
747
    /**
748
     * Resorts the courses within the given category.
749
     *
750
     * @param \core_course_category $category
751
     * @param string $sort One of fullname, shortname or idnumber
752
     * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later.
753
     * @return bool
754
     * @throws \moodle_exception
755
     */
756
    public static function action_category_resort_courses(\core_course_category $category, $sort, $cleanup = true) {
757
        if (!$category->can_resort_courses()) {
758
            throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort');
759
        }
760
        return $category->resort_courses($sort, $cleanup);
761
    }
762
 
763
    /**
764
     * Moves courses out of one category and into a new category.
765
     *
766
     * @param \core_course_category $oldcategory The category we are moving courses out of.
767
     * @param \core_course_category $newcategory The category we are moving courses into.
768
     * @param array $courseids The ID's of the courses we want to move.
769
     * @return bool True on success.
770
     * @throws \moodle_exception
771
     */
772
    public static function action_category_move_courses_into(\core_course_category $oldcategory,
773
                                                             \core_course_category $newcategory, array $courseids) {
774
        global $DB;
775
 
776
        list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
777
        $params['categoryid'] = $oldcategory->id;
778
        $sql = "SELECT c.id FROM {course} c WHERE c.id {$where} AND c.category <> :categoryid";
779
        if ($DB->record_exists_sql($sql, $params)) {
780
            // Likely being tinkered with.
781
            throw new \moodle_exception('coursedoesnotbelongtocategory');
782
        }
783
 
784
        // All courses are currently within the old category.
785
        return self::move_courses_into_category($newcategory, $courseids);
786
    }
787
 
788
    /**
789
     * Returns the view modes for the management interface.
790
     * @return array
791
     */
792
    public static function get_management_viewmodes() {
793
        return array(
794
            'combined' => new \lang_string('categoriesandcourses'),
795
            'categories' => new \lang_string('categories'),
796
            'courses' => new \lang_string('courses')
797
        );
798
    }
799
 
800
    /**
801
     * Search for courses with matching params.
802
     *
803
     * Please note that only one of search, blocklist, or modulelist can be specified at a time.
804
     * Specifying more than one will result in only the first being used.
805
     *
806
     * @param string $search Words to search for. We search fullname, shortname, idnumber and summary.
807
     * @param int $blocklist The ID of a block, courses will only be returned if they use this block.
808
     * @param string $modulelist The name of a module (relates to database table name). Only courses containing this module
809
     *      will be returned.
810
     * @param int $page The page number to display, starting at 0.
811
     * @param int $perpage The number of courses to display per page.
812
     * @return array
813
     */
814
    public static function search_courses($search, $blocklist, $modulelist, $page = 0, $perpage = null) {
815
        global $CFG;
816
 
817
        if ($perpage === null) {
818
            $perpage = $CFG->coursesperpage;
819
        }
820
 
821
        $searchcriteria = array();
822
        if (!empty($search)) {
823
            $searchcriteria = array('search' => $search);
824
        } else if (!empty($blocklist)) {
825
            $searchcriteria = array('blocklist' => $blocklist);
826
        } else if (!empty($modulelist)) {
827
            $searchcriteria = array('modulelist' => $modulelist);
828
        }
829
 
830
        $topcat = \core_course_category::top();
831
        $courses = $topcat->search_courses($searchcriteria, array(
832
            'recursive' => true,
833
            'offset' => $page * $perpage,
834
            'limit' => $perpage,
835
            'sort' => array('fullname' => 1)
836
        ));
837
        $totalcount = $topcat->search_courses_count($searchcriteria, array('recursive' => true));
838
 
839
        return array($courses, \count($courses), $totalcount);
840
    }
841
 
842
    /**
843
     * Moves one or more courses out of the category they are currently in and into a new category.
844
     *
845
     * This function works much the same way as action_category_move_courses_into however it allows courses from multiple
846
     * categories to be moved into a single category.
847
     *
848
     * @param int|\core_course_category $categoryorid The category to move them into.
849
     * @param array|int $courseids An array of course id's or optionally just a single course id.
850
     * @return bool True on success or false on failure.
851
     * @throws \moodle_exception
852
     */
853
    public static function move_courses_into_category($categoryorid, $courseids = array()) {
854
        global $DB;
855
        if (!is_array($courseids)) {
856
            // Just a single course ID.
857
            $courseids = array($courseids);
858
        }
859
        // Bulk move courses from one category to another.
860
        if (count($courseids) === 0) {
861
            return false;
862
        }
863
        if ($categoryorid instanceof \core_course_category) {
864
            $moveto = $categoryorid;
865
        } else {
866
            $moveto = \core_course_category::get($categoryorid);
867
        }
868
        if (!$moveto->can_move_courses_out_of() || !$moveto->can_move_courses_into()) {
869
            throw new \moodle_exception('cannotmovecourses');
870
        }
871
 
872
        list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
873
        $sql = "SELECT c.id, c.category FROM {course} c WHERE c.id {$where}";
874
        $courses = $DB->get_records_sql($sql, $params);
875
        $checks = array();
876
        foreach ($courseids as $id) {
877
            if (!isset($courses[$id])) {
878
                throw new \moodle_exception('invalidcourseid');
879
            }
880
            $catid = $courses[$id]->category;
881
            if (!isset($checks[$catid])) {
882
                $coursecat = \core_course_category::get($catid);
883
                $checks[$catid] = $coursecat->can_move_courses_out_of() && $coursecat->can_move_courses_into();
884
            }
885
            if (!$checks[$catid]) {
886
                throw new \moodle_exception('cannotmovecourses');
887
            }
888
        }
889
        return \move_courses($courseids, $moveto->id);
890
    }
891
 
892
    /**
893
     * Returns an array of courseids and visiblity for all courses within the given category.
894
     * @param int $categoryid
895
     * @return array
896
     */
897
    public static function get_category_courses_visibility($categoryid) {
898
        global $DB;
899
        $sql = "SELECT c.id, c.visible
900
                  FROM {course} c
901
                 WHERE c.category = :category";
902
        $params = array('category' => (int)$categoryid);
903
        return $DB->get_records_sql($sql, $params);
904
    }
905
 
906
    /**
907
     * Returns an array of all categoryids that have the given category as a parent and their visible value.
908
     * @param int $categoryid
909
     * @return array
910
     */
911
    public static function get_category_children_visibility($categoryid) {
912
        global $DB;
913
        $category = \core_course_category::get($categoryid);
914
        $select = $DB->sql_like('path', ':path');
915
        $path = $category->path . '/%';
916
 
917
        $sql = "SELECT c.id, c.visible
918
                  FROM {course_categories} c
919
                 WHERE ".$select;
920
        $params = array('path' => $path);
921
        return $DB->get_records_sql($sql, $params);
922
    }
923
 
924
    /**
925
     * Records when a category is expanded or collapsed so that when the user
926
     *
927
     * @param \core_course_category $coursecat The category we're working with.
928
     * @param bool $expanded True if the category is expanded now.
929
     */
930
    public static function record_expanded_category(\core_course_category $coursecat, $expanded = true) {
931
        // If this ever changes we are going to reset it and reload the categories as required.
932
        self::$expandedcategories = null;
933
        $categoryid = $coursecat->id;
934
        $path = $coursecat->get_parents();
935
        /* @var \cache_session $cache */
936
        $cache = \cache::make('core', 'userselections');
937
        $categories = $cache->get('categorymanagementexpanded');
938
        if (!is_array($categories)) {
939
            if (!$expanded) {
940
                // No categories recorded, nothing to remove.
941
                return;
942
            }
943
            $categories = array();
944
        }
945
        if ($expanded) {
946
            $ref =& $categories;
947
            foreach ($coursecat->get_parents() as $path) {
948
                if (!isset($ref[$path]) || !is_array($ref[$path])) {
949
                    $ref[$path] = array();
950
                }
951
                $ref =& $ref[$path];
952
            }
953
            if (!isset($ref[$categoryid])) {
954
                $ref[$categoryid] = true;
955
            }
956
        } else {
957
            $found = true;
958
            $ref =& $categories;
959
            foreach ($coursecat->get_parents() as $path) {
960
                if (!isset($ref[$path])) {
961
                    $found = false;
962
                    break;
963
                }
964
                $ref =& $ref[$path];
965
            }
966
            if ($found) {
967
                $ref[$categoryid] = null;
968
                unset($ref[$categoryid]);
969
            }
970
        }
971
        $cache->set('categorymanagementexpanded', $categories);
972
    }
973
 
974
    /**
975
     * Returns the categories that should be expanded when displaying the interface.
976
     *
977
     * @param int|null $withpath If specified a path to require as the parent.
978
     * @return \core_course_category[] An array of Category ID's to expand.
979
     */
980
    public static function get_expanded_categories($withpath = null) {
981
        if (self::$expandedcategories === null) {
982
            /* @var \cache_session $cache */
983
            $cache = \cache::make('core', 'userselections');
984
            self::$expandedcategories = $cache->get('categorymanagementexpanded');
985
            if (self::$expandedcategories === false) {
986
                self::$expandedcategories = array();
987
            }
988
        }
989
        if (empty($withpath)) {
990
            return array_keys(self::$expandedcategories);
991
        }
992
        $parents = explode('/', trim($withpath, '/'));
993
        $ref =& self::$expandedcategories;
994
        foreach ($parents as $parent) {
995
            if (!isset($ref[$parent])) {
996
                return array();
997
            }
998
            $ref =& $ref[$parent];
999
        }
1000
        if (is_array($ref)) {
1001
            return array_keys($ref);
1002
        } else {
1003
            return array($parent);
1004
        }
1005
    }
1006
 
1007
    /**
1008
     * Get an array of the capabilities required to copy a course.
1009
     *
1010
     * @return array
1011
     */
1012
    public static function get_course_copy_capabilities(): array {
1013
        return array('moodle/backup:backupcourse', 'moodle/restore:restorecourse', 'moodle/course:create');
1014
    }
1015
 
1016
    /**
1017
     * Returns true if the current user can copy this course.
1018
     *
1019
     * @param int $courseid
1020
     * @return bool
1021
     */
1022
    public static function can_copy_course(int $courseid): bool {
1023
        $coursecontext = \context_course::instance($courseid);
1024
        return has_all_capabilities(self::get_course_copy_capabilities(), $coursecontext);
1025
    }
1026
}