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
 * Contains the default activity control menu.
19
 *
20
 * @package   core_courseformat
21
 * @copyright 2020 Ferran Recio <ferran@moodle.com>
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_courseformat\output\local\content\cm;
26
 
27
use cm_info;
1441 ariadna 28
use core\context\module as context_module;
29
use core\output\action_menu;
30
use core\output\action_menu\link;
31
use core\output\action_menu\link_secondary;
32
use core\output\action_menu\subpanel;
33
use core\output\pix_icon;
34
use core\output\renderer_base;
1 efrain 35
use core_courseformat\base as course_format;
1441 ariadna 36
use core_courseformat\output\local\content\basecontrolmenu;
37
use core_courseformat\sectiondelegate;
38
use core\url;
1 efrain 39
use section_info;
40
use stdClass;
41
 
42
/**
43
 * Base class to render a course module menu inside a course format.
44
 *
45
 * @package   core_courseformat
46
 * @copyright 2020 Ferran Recio <ferran@moodle.com>
47
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
48
 */
1441 ariadna 49
class controlmenu extends basecontrolmenu {
1 efrain 50
 
1441 ariadna 51
    /** @var array optional display options */
52
    protected $displayoptions;
1 efrain 53
 
1441 ariadna 54
    /** @var context_module|null modcontext the module context if any */
55
    protected ?context_module $modcontext = null;
1 efrain 56
 
1441 ariadna 57
    /** @var bool $canmanageactivities Optimization to know if the user can manage activities */
58
    protected bool $canmanageactivities;
1 efrain 59
 
1441 ariadna 60
    /** @var url $basemodurl the base mod.php url */
61
    protected url $basemodurl;
1 efrain 62
 
63
    /**
64
     * Constructor.
65
     *
66
     * @param course_format $format the course format
67
     * @param section_info $section the section info
68
     * @param cm_info $mod the course module info
69
     * @param array $displayoptions optional extra display options
70
     */
71
    public function __construct(course_format $format, section_info $section, cm_info $mod, array $displayoptions = []) {
1441 ariadna 72
        parent::__construct($format, $section, $mod, $mod->id);
1 efrain 73
        $this->displayoptions = $displayoptions;
1441 ariadna 74
 
75
        $this->modcontext = context_module::instance($mod->id);
76
        $this->canmanageactivities = has_capability('moodle/course:manageactivities', $this->modcontext);
77
 
78
        $this->basemodurl = new url('/course/mod.php');
79
        $sectionnumreturn = $format->get_sectionnum();
80
        if ($sectionnumreturn !== null) {
81
            $this->basemodurl->param('sr', $sectionnumreturn);
82
        }
1 efrain 83
    }
84
 
85
    /**
86
     * Export this data so it can be used as the context for a mustache template.
87
     *
1441 ariadna 88
     * @param renderer_base $output typically, the renderer that's calling this function
89
     * @return stdClass|null data context for a mustache template
1 efrain 90
     */
1441 ariadna 91
    public function export_for_template(renderer_base $output): ?stdClass {
1 efrain 92
 
93
        $mod = $this->mod;
94
 
1441 ariadna 95
        if (!$this->format->show_activity_editor_options($this->mod)) {
96
            return null;
97
        }
98
 
1 efrain 99
        $menu = $this->get_action_menu($output);
100
 
101
        if (empty($menu)) {
102
            return new stdClass();
103
        }
104
 
105
        $data = (object)[
106
            'menu' => $menu->export_for_template($output),
107
            'hasmenu' => true,
1441 ariadna 108
            'id' => $this->menuid,
1 efrain 109
        ];
110
 
111
        // After icons.
112
        if (!empty($mod->afterediticons)) {
113
            $data->afterediticons = $mod->afterediticons;
114
        }
115
 
116
        return $data;
117
    }
118
 
119
    /**
120
     * Generate the action menu element.
121
     *
122
     * This method is public in case some block needs to modify the menu before output it.
1441 ariadna 123
     * @param renderer_base $output typically, the renderer that's calling this function
1 efrain 124
     * @return action_menu|null the activity action menu
125
     */
1441 ariadna 126
    public function get_action_menu(renderer_base $output): ?action_menu {
1 efrain 127
 
128
        if (!empty($this->menu)) {
129
            return $this->menu;
130
        }
131
 
1441 ariadna 132
        // In case module is delegating a section, we should return delegated section action menu.
133
        if ($delegated = $this->mod->get_delegated_section_info()) {
134
            $controlmenuclass = $this->format->get_output_classname('content\\cm\\delegatedcontrolmenu');
135
            $controlmenu = new $controlmenuclass($this->format, $delegated, $this->mod);
136
            return $controlmenu->get_action_menu($output);
137
        }
138
 
139
        // TODO remove this if as part of MDL-83530.
140
        if (!$this->format->supports_components()) {
141
            $this->menu = $this->get_action_menu_legacy($output);
142
            return $this->menu;
143
        }
144
 
145
        $controls = $this->get_cm_control_items();
146
        return $this->format_controls($controls);
147
    }
148
 
149
    /**
150
     * Generate the edit control items of a course module.
151
     *
152
     * This method uses course_get_cm_edit_actions function to get the cm actions.
153
     * However, format plugins can override the method to add or remove elements
154
     * from the menu.
155
     *
156
     * @return array of edit control items
157
     */
158
    public function get_cm_control_items(): ?array {
159
        $controls = [];
160
 
161
        $controls['update'] = $this->get_cm_edit_item();
162
        $controls['move'] = $this->get_cm_move_item();
163
        $controls['moveright'] = $this->get_cm_moveend_item();
164
        $controls['moveleft'] = $this->get_cm_movestart_item();
165
        $controls['availability'] = $this->get_cm_visibility_item();
166
        $controls['duplicate'] = $this->get_cm_duplicate_item();
167
        $controls['assign'] = $this->get_cm_assign_item();
168
        $controls['groupmode'] = $this->get_cm_groupmode_item();
169
        $controls['delete'] = $this->get_cm_delete_item();
170
 
171
        return $controls;
172
    }
173
 
174
    /**
175
     * Generates the edit settings item for a course module.
176
     *
177
     * @return link|null The menu item if applicable, otherwise null.
178
     */
179
    protected function get_cm_edit_item(): ?link {
180
        if (!$this->canmanageactivities) {
181
            return null;
182
        }
183
 
184
        $url = new url($this->basemodurl, ['update' => $this->mod->id]);
185
 
186
        return new link_secondary(
187
            url: $url,
188
            icon: new pix_icon('i/settings', ''),
189
            text: get_string('editsettings'),
190
            attributes: [
191
                'class' => 'editing_update',
192
            ],
193
        );
194
    }
195
 
196
    /**
197
     * Generates the move item for a course module.
198
     *
199
     * @return link|null The menu item if applicable, otherwise null.
200
     */
201
    protected function get_cm_move_item(): ?link {
202
        // Only show the move link if we are not already in the section view page.
203
        if (!$this->canmanageactivities) {
204
            return null;
205
        }
206
 
207
        $url = new url($this->basemodurl);
208
 
209
        return new link_secondary(
210
            url: $url,
211
            icon: new pix_icon('i/dragdrop', ''),
212
            text: get_string('move'),
213
            attributes: [
214
                // This tool requires ajax and will appear only when the frontend state is ready.
215
                'class' => 'editing_movecm waitstate',
216
                'data-action' => 'moveCm',
217
                'data-id' => $this->mod->id,
218
            ],
219
        );
220
    }
221
 
222
    /**
223
     * Check if the course module can be indented.
224
     *
225
     * @return bool
226
     */
227
    protected function can_indent_cm(): bool {
228
        return $this->canmanageactivities
229
            && !sectiondelegate::has_delegate_class('mod_'.$this->mod->modname)
230
            && empty($this->displayoptions['disableindentation'])
231
            && $this->format->uses_indentation();
232
    }
233
 
234
    /**
235
     * Generates the move right item for a course module.
236
     *
237
     * @return link|null The menu item if applicable, otherwise null.
238
     */
239
    protected function get_cm_moveend_item(): ?link {
240
        if (!$this->can_indent_cm() || $this->mod->indent > 0) {
241
            return null;
242
        }
243
 
244
        $url = $this->format->get_update_url(
245
            action: 'cm_moveright',
246
            ids: [$this->mod->id],
247
            returnurl: $this->baseurl,
248
        );
249
 
250
        $icon = (right_to_left()) ? 't/left' : 't/right';
251
 
252
        return new link_secondary(
253
            url: $url,
254
            icon: new pix_icon($icon, ''),
255
            text: get_string('moveright'),
256
            attributes: [
257
                'class' => 'editing_moveright',
258
                'data-action' => 'cmMoveRight',
259
                'data-keepopen' => true,
260
                'data-sectionreturn' => $this->format->get_sectionnum(),
261
                'data-id' => $this->mod->id,
262
            ],
263
        );
264
    }
265
 
266
    /**
267
     * Generates the move left item for a course module.
268
     *
269
     * @return link|null The menu item if applicable, otherwise null.
270
     */
271
    protected function get_cm_movestart_item(): ?link {
272
        if (!$this->can_indent_cm() || $this->mod->indent <= 0) {
273
            return null;
274
        }
275
 
276
        $url = $this->format->get_update_url(
277
            action: 'cm_moveleft',
278
            ids: [$this->mod->id],
279
            returnurl: $this->baseurl,
280
        );
281
 
282
        $icon = (right_to_left()) ? 't/right' : 't/left';
283
 
284
        return new link_secondary(
285
            url: $url,
286
            icon: new pix_icon($icon, ''),
287
            text: get_string('moveleft'),
288
            attributes: [
289
                'class' => 'editing_moveleft',
290
                'data-action' => 'cmMoveLeft',
291
                'data-keepopen' => true,
292
                'data-sectionreturn' => $this->format->get_sectionnum(),
293
                'data-id' => $this->mod->id,
294
            ],
295
        );
296
    }
297
 
298
    /**
299
     * Generates the visibility item for a course module.
300
     *
301
     * @return link|null The menu item if applicable, otherwise null.
302
     */
303
    protected function get_cm_visibility_item(): link_secondary|subpanel|null {
304
        if (!has_capability('moodle/course:activityvisibility', $this->modcontext)) {
305
            return null;
306
        }
307
        $outputclass = $this->format->get_output_classname('content\\cm\\visibility');
308
        /** @var \core_courseformat\output\local\content\cm\visibility $output */
309
        $output = new $outputclass($this->format, $this->section, $this->mod);
310
        return $output->get_menu_item();
311
    }
312
 
313
    /**
314
     * Generates the duplicate item for a course module.
315
     *
316
     * @return link|null The menu item if applicable, otherwise null.
317
     */
318
    protected function get_cm_duplicate_item(): ?link {
319
        if (
320
            !has_all_capabilities(
321
                ['moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport'],
322
                $this->coursecontext
323
            )
324
            || !plugin_supports('mod', $this->mod->modname, FEATURE_BACKUP_MOODLE2)
325
            || !course_allowed_module($this->mod->get_course(), $this->mod->modname)
326
        ) {
327
                return null;
328
        }
329
 
330
        $url = $this->format->get_update_url(
331
            action: 'cm_duplicate',
332
            ids: [$this->mod->id],
333
            returnurl: $this->baseurl,
334
        );
335
 
336
        return new link_secondary(
337
            url: $url,
338
            icon: new pix_icon('t/copy', ''),
339
            text: get_string('duplicate'),
340
            attributes: [
341
                'class' => 'editing_duplicate',
342
                'data-action' => 'cmDuplicate',
343
                'data-sectionreturn' => $this->format->get_sectionnum(),
344
                'data-id' => $this->mod->id,
345
            ],
346
        );
347
    }
348
 
349
    /**
350
     * Generates the assign roles item for a course module.
351
     *
352
     * @return link|null The menu item if applicable, otherwise null.
353
     */
354
    protected function get_cm_assign_item(): ?link {
355
        if (
356
            !has_capability('moodle/role:assign', $this->modcontext)
357
            || sectiondelegate::has_delegate_class('mod_'.$this->mod->modname)
358
        ) {
359
            return null;
360
        }
361
 
362
        return new link_secondary(
363
            url: new url('/admin/roles/assign.php', ['contextid' => $this->modcontext->id]),
364
            icon: new pix_icon('t/assignroles', ''),
365
            text: get_string('assignroles', 'role'),
366
            attributes: [
367
                'class' => 'editing_assign',
368
                'data-sectionreturn' => $this->format->get_sectionnum(),
369
            ],
370
        );
371
    }
372
 
373
    /**
374
     * Generates the group mode item for a course module.
375
     *
376
     * @return subpanel|null The menu item if applicable, otherwise null.
377
     */
378
    protected function get_cm_groupmode_item(): ?subpanel {
379
        if (
380
            !$this->format->show_groupmode($this->mod)
381
            || $this->mod->coursegroupmodeforce
382
        ) {
383
            return null;
384
        }
385
 
386
        $groupmodeclass = $this->format->get_output_classname('content\\cm\\groupmode');
387
        /** @var \core_courseformat\output\local\content\cm\groupmode $groupmode */
388
        $groupmode = new $groupmodeclass($this->format, $this->section, $this->mod);
389
        return new subpanel(
390
            text: get_string('groupmode', 'group'),
391
            subpanel: $groupmode->get_choice_list(),
392
            attributes: ['class' => 'editing_groupmode'],
393
            icon: new pix_icon('t/groupv', '', 'moodle', ['class' => 'iconsmall']),
394
        );
395
    }
396
 
397
    /**
398
     * Generates the delete item for a course module.
399
     *
400
     * @return link|null The menu item if applicable, otherwise null.
401
     */
402
    protected function get_cm_delete_item(): ?link {
403
        if (!$this->canmanageactivities) {
404
            return null;
405
        }
406
 
407
        $url = $this->format->get_update_url(
408
            action: 'cm_delete',
409
            ids: [$this->mod->id],
410
            returnurl: $this->baseurl,
411
        );
412
 
413
        return new link_secondary(
414
            url: $url,
415
            icon: new pix_icon('t/delete', ''),
416
            text: get_string('delete'),
417
            attributes: [
418
                'class' => 'editing_delete text-danger',
419
                'data-action' => 'cmDelete',
420
                'data-sectionreturn' => $this->format->get_sectionnum(),
421
                'data-id' => $this->mod->id,
422
            ],
423
        );
424
    }
425
 
426
    /**
427
     * Generate the action menu element for old course formats.
428
     *
429
     * This method is public in case some block needs to modify the menu before output it.
430
     *
431
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
432
     * @param \renderer_base $output typically, the renderer that's calling this function
433
     * @return action_menu|null the activity action menu
434
     */
435
    private function get_action_menu_legacy(\renderer_base $output): ?action_menu {
1 efrain 436
        $mod = $this->mod;
437
 
438
        $controls = $this->cm_control_items();
439
 
440
        if (empty($controls)) {
441
            return null;
442
        }
443
 
444
        // Convert control array into an action_menu.
445
        $menu = new action_menu();
446
        $menu->set_kebab_trigger(get_string('edit'));
447
        $menu->attributes['class'] .= ' section-cm-edit-actions commands';
448
 
449
        // Prioritise the menu ahead of all other actions.
450
        $menu->prioritise = true;
451
 
452
        $ownerselector = $this->displayoptions['ownerselector'] ?? '#module-' . $mod->id;
453
        $menu->set_owner_selector($ownerselector);
454
 
455
        foreach ($controls as $control) {
1441 ariadna 456
            if ($control instanceof link) {
1 efrain 457
                $control->add_class('cm-edit-action');
458
            }
459
            $menu->add($control);
460
        }
461
 
462
        $this->menu = $menu;
463
 
464
        return $menu;
465
    }
466
 
467
    /**
468
     * Generate the edit control items of a course module.
469
     *
470
     * This method uses course_get_cm_edit_actions function to get the cm actions.
471
     * However, format plugins can override the method to add or remove elements
472
     * from the menu.
473
     *
1441 ariadna 474
     * @deprecated since Moodle 5.0
475
     * @todo Remove this method in Moodle 6.0 (MDL-83530).
1 efrain 476
     * @return array of edit control items
477
     */
1441 ariadna 478
    #[\core\attribute\deprecated(
479
        replacement: 'get_cm_control_items',
480
        since: '5.0',
481
        mdl: 'MDL-83527',
482
    )]
1 efrain 483
    protected function cm_control_items() {
1441 ariadna 484
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
1 efrain 485
        $format = $this->format;
486
        $mod = $this->mod;
487
        $sectionreturn = $format->get_sectionnum();
488
        if (!empty($this->displayoptions['disableindentation']) || !$format->uses_indentation()) {
489
            $indent = -1;
490
        } else {
491
            $indent = $mod->indent;
492
        }
493
        return course_get_cm_edit_actions($mod, $indent, $sectionreturn);
494
    }
495
}