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
 * This file contains a renderer for the assignment class
19
 *
20
 * @package   mod_assign
21
 * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace mod_assign\output;
26
 
27
use assign_files;
28
use html_writer;
29
use mod_assign\output\grading_app;
30
use portfolio_add_button;
31
use stored_file;
32
 
33
defined('MOODLE_INTERNAL') || die();
34
 
35
require_once($CFG->dirroot . '/mod/assign/locallib.php');
36
 
37
/**
38
 * A custom renderer class that extends the plugin_renderer_base and is used by the assign module.
39
 *
40
 * @package mod_assign
41
 * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
42
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43
 */
44
class renderer extends \plugin_renderer_base {
45
 
46
    /** @var string a unique ID. */
47
    public $htmlid;
48
 
49
    /**
50
     * Rendering assignment files
51
     *
52
     * @param \context $context
53
     * @param int $userid
54
     * @param string $filearea
55
     * @param string $component
56
     * @param \stdClass $course
57
     * @param \stdClass $coursemodule
58
     * @return string
59
     */
60
    public function assign_files(\context $context, $userid, $filearea, $component, $course = null, $coursemodule = null) {
61
        return $this->render(new \assign_files($context, $userid, $filearea, $component, $course, $coursemodule));
62
    }
63
 
64
    /**
65
     * Rendering assignment files
66
     *
67
     * @param \assign_files $tree
68
     * @return string
69
     */
70
    public function render_assign_files(\assign_files $tree) {
71
        $this->htmlid = \html_writer::random_id('assign_files_tree');
72
        $this->page->requires->js_init_call('M.mod_assign.init_tree', array(true, $this->htmlid));
73
        $html = '<div id="'.$this->htmlid.'">';
74
        $html .= $this->htmllize_tree($tree, $tree->dir);
75
        $html .= '</div>';
76
 
77
        if ($tree->portfolioform) {
78
            $html .= $tree->portfolioform;
79
        }
80
        return $html;
81
    }
82
 
83
    /**
84
     * Utility function to add a row of data to a table with 2 columns where the first column is the table's header.
85
     * Modified the table param and does not return a value.
86
     *
87
     * @param \html_table $table The table to append the row of data to
88
     * @param string $first The first column text
89
     * @param string $second The second column text
90
     * @param array $firstattributes The first column attributes (optional)
91
     * @param array $secondattributes The second column attributes (optional)
92
     * @return void
93
     */
94
    private function add_table_row_tuple(\html_table $table, $first, $second, $firstattributes = [],
95
            $secondattributes = []) {
96
        $row = new \html_table_row();
97
        $cell1 = new \html_table_cell($first);
98
        $cell1->header = true;
99
        if (!empty($firstattributes)) {
100
            $cell1->attributes = $firstattributes;
101
        }
102
        $cell2 = new \html_table_cell($second);
103
        if (!empty($secondattributes)) {
104
            $cell2->attributes = $secondattributes;
105
        }
106
        $row->cells = array($cell1, $cell2);
107
        $table->data[] = $row;
108
    }
109
 
110
    /**
111
     * Render a grading message notification
112
     * @param \assign_gradingmessage $result The result to render
113
     * @return string
114
     */
115
    public function render_assign_gradingmessage(\assign_gradingmessage $result) {
116
        $urlparams = array('id' => $result->coursemoduleid, 'action'=>'grading');
117
        if (!empty($result->page)) {
118
            $urlparams['page'] = $result->page;
119
        }
120
        $url = new \moodle_url('/mod/assign/view.php', $urlparams);
121
        $classes = $result->gradingerror ? 'notifyproblem' : 'notifysuccess';
122
 
123
        $o = '';
124
        $o .= $this->output->heading($result->heading, 4);
125
        $o .= $this->output->notification($result->message, $classes);
126
        $o .= $this->output->continue_button($url);
127
        return $o;
128
    }
129
 
130
    /**
131
     * Render the generic form
132
     * @param \assign_form $form The form to render
133
     * @return string
134
     */
135
    public function render_assign_form(\assign_form $form) {
136
        $o = '';
137
        if ($form->jsinitfunction) {
138
            $this->page->requires->js_init_call($form->jsinitfunction, array());
139
        }
140
        $o .= $this->output->box_start('boxaligncenter ' . $form->classname);
141
        $o .= $this->moodleform($form->form);
142
        $o .= $this->output->box_end();
143
        return $o;
144
    }
145
 
146
    /**
147
     * Render the user summary
148
     *
149
     * @param \assign_user_summary $summary The user summary to render
150
     * @return string
151
     */
152
    public function render_assign_user_summary(\assign_user_summary $summary) {
153
        $o = '';
154
        $supendedclass = '';
155
        $suspendedicon = '';
156
 
157
        if (!$summary->user) {
158
            return;
159
        }
160
 
161
        if ($summary->suspendeduser) {
162
            $supendedclass = ' usersuspended';
163
            $suspendedstring = get_string('userenrolmentsuspended', 'grades');
164
            $suspendedicon = ' ' . $this->pix_icon('i/enrolmentsuspended', $suspendedstring);
165
        }
166
        $o .= $this->output->container_start('usersummary');
167
        $o .= $this->output->box_start('boxaligncenter usersummarysection'.$supendedclass);
168
        if ($summary->blindmarking) {
169
            $o .= get_string('hiddenuser', 'assign') . $summary->uniqueidforuser.$suspendedicon;
170
        } else {
171
            $o .= $this->output->user_picture($summary->user);
172
            $o .= $this->output->spacer(array('width'=>30));
173
            $urlparams = array('id' => $summary->user->id, 'course'=>$summary->courseid);
174
            $url = new \moodle_url('/user/view.php', $urlparams);
175
            $fullname = fullname($summary->user, $summary->viewfullnames);
176
            $extrainfo = array();
177
            foreach ($summary->extrauserfields as $extrafield) {
178
                $extrainfo[] = s($summary->user->$extrafield);
179
            }
180
            if (count($extrainfo)) {
181
                $fullname .= ' (' . implode(', ', $extrainfo) . ')';
182
            }
183
            $fullname .= $suspendedicon;
184
            $o .= $this->output->action_link($url, $fullname);
185
        }
186
        $o .= $this->output->box_end();
187
        $o .= $this->output->container_end();
188
 
189
        return $o;
190
    }
191
 
192
    /**
193
     * Render the submit for grading page
194
     *
195
     * @param \assign_submit_for_grading_page $page
196
     * @return string
197
     */
198
    public function render_assign_submit_for_grading_page($page) {
199
        $o = '';
200
 
201
        $o .= $this->output->container_start('submitforgrading');
202
        $o .= $this->output->heading(get_string('confirmsubmissionheading', 'assign'), 3);
203
 
204
        $cancelurl = new \moodle_url('/mod/assign/view.php', array('id' => $page->coursemoduleid));
205
        if (count($page->notifications)) {
206
            // At least one of the submission plugins is not ready for submission.
207
 
208
            $o .= $this->output->heading(get_string('submissionnotready', 'assign'), 4);
209
 
210
            foreach ($page->notifications as $notification) {
211
                $o .= $this->output->notification($notification);
212
            }
213
 
214
            $o .= $this->output->continue_button($cancelurl);
215
        } else {
216
            // All submission plugins ready - show the confirmation form.
217
            $o .= $this->moodleform($page->confirmform);
218
        }
219
        $o .= $this->output->container_end();
220
 
221
        return $o;
222
    }
223
 
224
    /**
225
     * Page is done - render the footer.
226
     *
227
     * @return void
228
     */
229
    public function render_footer() {
230
        return $this->output->footer();
231
    }
232
 
233
    /**
234
     * Render the header.
235
     *
236
     * @param assign_header $header
237
     * @return string
238
     */
239
    public function render_assign_header(assign_header $header) {
240
        if ($header->subpage) {
241
            $this->page->navbar->add($header->subpage, $header->subpageurl);
242
            $args = ['contextname' => $header->context->get_context_name(false, true), 'subpage' => $header->subpage];
243
            $title = get_string('subpagetitle', 'assign', $args);
244
        } else {
245
            $title = $header->context->get_context_name(false, true);
246
        }
247
        $courseshortname = $header->context->get_course_context()->get_context_name(false, true);
248
        $title = $courseshortname . ': ' . $title;
249
        $heading = format_string($header->assign->name, false, array('context' => $header->context));
250
 
251
        $this->page->set_title($title);
252
        $this->page->set_heading($this->page->course->fullname);
253
 
254
        $description = $header->preface;
255
        if ($header->showintro || $header->activity) {
256
            $description = $this->output->box_start('generalbox boxaligncenter');
257
            if ($header->showintro) {
258
                $description .= format_module_intro('assign', $header->assign, $header->coursemoduleid);
259
            }
260
            if ($header->activity) {
261
                $description .= $this->format_activity_text($header->assign, $header->coursemoduleid);
262
            }
263
            $description .= $header->postfix;
264
            $description .= $this->output->box_end();
265
        }
266
 
267
        $activityheader = $this->page->activityheader;
268
        $activityheader->set_attrs([
269
            'title' => $activityheader->is_title_allowed() ? $heading : '',
270
            'description' => $description
271
        ]);
272
 
273
        return $this->output->header();
274
    }
275
 
276
    /**
277
     * Render the header for an individual plugin.
278
     *
279
     * @param \assign_plugin_header $header
280
     * @return string
281
     */
282
    public function render_assign_plugin_header(\assign_plugin_header $header) {
283
        $o = $header->plugin->view_header();
284
        return $o;
285
    }
286
 
287
    /**
288
     * Render a table containing the current status of the grading process.
289
     *
290
     * @param \assign_grading_summary $summary
291
     * @return string
292
     */
293
    public function render_assign_grading_summary(\assign_grading_summary $summary) {
294
        // Create a table for the data.
295
        $o = '';
296
        $o .= $this->output->container_start('gradingsummary');
297
        $o .= $this->output->heading(get_string('gradingsummary', 'assign'), 3);
298
 
299
        if (isset($summary->cm)) {
300
            $currenturl = new \moodle_url('/mod/assign/view.php', array('id' => $summary->cm->id));
301
            $o .= groups_print_activity_menu($summary->cm, $currenturl->out(), true);
302
        }
303
 
304
        $o .= $this->output->box_start('boxaligncenter gradingsummarytable');
305
        $t = new \html_table();
306
        $t->attributes['class'] = 'generaltable table-bordered';
307
 
308
        // Visibility Status.
309
        $cell1content = get_string('hiddenfromstudents');
310
        $cell2content = (!$summary->isvisible) ? get_string('yes') : get_string('no');
311
        $this->add_table_row_tuple($t, $cell1content, $cell2content);
312
 
313
        // Status.
314
        if ($summary->teamsubmission) {
315
            if ($summary->warnofungroupedusers === \assign_grading_summary::WARN_GROUPS_REQUIRED) {
316
                $o .= $this->output->notification(get_string('ungroupedusers', 'assign'));
317
            } else if ($summary->warnofungroupedusers === \assign_grading_summary::WARN_GROUPS_OPTIONAL) {
318
                $o .= $this->output->notification(get_string('ungroupedusersoptional', 'assign'));
319
            }
320
            $cell1content = get_string('numberofteams', 'assign');
321
        } else {
322
            $cell1content = get_string('numberofparticipants', 'assign');
323
        }
324
 
325
        $cell2content = $summary->participantcount;
326
        $this->add_table_row_tuple($t, $cell1content, $cell2content);
327
 
328
        // Drafts count and dont show drafts count when using offline assignment.
329
        if ($summary->submissiondraftsenabled && $summary->submissionsenabled) {
330
            $cell1content = get_string('numberofdraftsubmissions', 'assign');
331
            $cell2content = $summary->submissiondraftscount;
332
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
333
        }
334
 
335
        // Submitted for grading.
336
        if ($summary->submissionsenabled) {
337
            $cell1content = get_string('numberofsubmittedassignments', 'assign');
338
            $cell2content = $summary->submissionssubmittedcount;
339
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
340
 
341
            if (!$summary->teamsubmission) {
342
                $cell1content = get_string('numberofsubmissionsneedgrading', 'assign');
343
                $cell2content = $summary->submissionsneedgradingcount;
344
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
345
            }
346
        }
347
 
348
        $time = time();
349
        if ($summary->duedate) {
350
            // Time remaining.
351
            $duedate = $summary->duedate;
352
            $cell1content = get_string('timeremaining', 'assign');
353
            if ($summary->courserelativedatesmode) {
354
                $cell2content = get_string('relativedatessubmissiontimeleft', 'mod_assign');
355
            } else {
356
                if ($duedate - $time <= 0) {
357
                    $cell2content = get_string('assignmentisdue', 'assign');
358
                } else {
359
                    $cell2content = format_time($duedate - $time);
360
                }
361
            }
362
 
363
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
364
 
365
            if ($duedate < $time) {
366
                $cell1content = get_string('latesubmissions', 'assign');
367
                $cutoffdate = $summary->cutoffdate;
368
                if ($cutoffdate) {
369
                    if ($cutoffdate > $time) {
370
                        $cell2content = get_string('latesubmissionsaccepted', 'assign', userdate($summary->cutoffdate));
371
                    } else {
372
                        $cell2content = get_string('nomoresubmissionsaccepted', 'assign');
373
                    }
374
 
375
                    $this->add_table_row_tuple($t, $cell1content, $cell2content);
376
                }
377
            }
378
 
379
        }
380
 
381
        // Add time limit info if there is one.
382
        $timelimitenabled = get_config('assign', 'enabletimelimit');
383
        if ($timelimitenabled && $summary->timelimit > 0) {
384
            $cell1content = get_string('timelimit', 'assign');
385
            $cell2content = format_time($summary->timelimit);
386
            $this->add_table_row_tuple($t, $cell1content, $cell2content, [], []);
387
        }
388
 
389
        // All done - write the table.
390
        $o .= \html_writer::table($t);
391
        $o .= $this->output->box_end();
392
 
393
        // Close the container and insert a spacer.
394
        $o .= $this->output->container_end();
395
        $o .= \html_writer::end_tag('center');
396
 
397
        return $o;
398
    }
399
 
400
    /**
401
     * Render a table containing all the current grades and feedback.
402
     *
403
     * @param \assign_feedback_status $status
404
     * @return string
405
     */
406
    public function render_assign_feedback_status(\assign_feedback_status $status) {
407
        $o = '';
408
 
409
        $o .= $this->output->container_start('feedback');
410
        $o .= $this->output->heading(get_string('feedback', 'assign'), 3);
411
        $o .= $this->output->box_start('boxaligncenter feedbacktable');
412
        $t = new \html_table();
413
 
414
        // Grade.
415
        if (isset($status->gradefordisplay)) {
416
            $cell1content = get_string('gradenoun');
417
            $cell2content = $status->gradefordisplay;
418
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
419
 
420
            // Grade date.
421
            $cell1content = get_string('gradedon', 'assign');
422
            $cell2content = userdate($status->gradeddate);
423
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
424
        }
425
 
426
        if ($status->grader) {
427
            // Grader.
428
            $cell1content = get_string('gradedby', 'assign');
429
            $cell2content = $this->output->user_picture($status->grader) .
430
                            $this->output->spacer(array('width' => 30)) .
431
                            fullname($status->grader, $status->canviewfullnames);
432
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
433
        }
434
 
435
        foreach ($status->feedbackplugins as $plugin) {
436
            if ($plugin->is_enabled() &&
437
                    $plugin->is_visible() &&
438
                    $plugin->has_user_summary() &&
439
                    !empty($status->grade) &&
440
                    !$plugin->is_empty($status->grade)) {
441
 
442
                $displaymode = \assign_feedback_plugin_feedback::SUMMARY;
443
                $pluginfeedback = new \assign_feedback_plugin_feedback($plugin,
444
                                                                      $status->grade,
445
                                                                      $displaymode,
446
                                                                      $status->coursemoduleid,
447
                                                                      $status->returnaction,
448
                                                                      $status->returnparams);
449
                $cell1content = $plugin->get_name();
450
                $cell2content = $this->render($pluginfeedback);
451
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
452
            }
453
        }
454
 
455
        $o .= \html_writer::table($t);
456
        $o .= $this->output->box_end();
457
 
458
        if (!empty($status->gradingcontrollergrade)) {
459
            $o .= $this->output->heading(get_string('gradebreakdown', 'assign'), 4);
460
            $o .= $status->gradingcontrollergrade;
461
        }
462
 
463
        $o .= $this->output->container_end();
464
        return $o;
465
    }
466
 
467
    /**
468
     * Render a compact view of the current status of the submission.
469
     *
470
     * @param \assign_submission_status_compact $status
471
     * @return string
472
     */
473
    public function render_assign_submission_status_compact(\assign_submission_status_compact $status) {
474
        $o = '';
475
        $o .= $this->output->container_start('submissionstatustable');
476
        $o .= $this->output->heading(get_string('submission', 'assign'), 3);
477
 
478
        if ($status->teamsubmissionenabled) {
479
            $group = $status->submissiongroup;
480
            if ($group) {
481
                $team = format_string($group->name, false, ['context' => $status->context]);
482
            } else if ($status->preventsubmissionnotingroup) {
483
                if (count($status->usergroups) == 0) {
484
                    $team = '<span class="alert alert-error">' . get_string('noteam', 'assign') . '</span>';
485
                } else if (count($status->usergroups) > 1) {
486
                    $team = '<span class="alert alert-error">' . get_string('multipleteams', 'assign') . '</span>';
487
                }
488
            } else {
489
                $team = get_string('defaultteam', 'assign');
490
            }
491
            $o .= $this->output->container(get_string('teamname', 'assign', $team), 'teamname');
492
        }
493
 
494
        if (!$status->teamsubmissionenabled) {
495
            if ($status->submission && $status->submission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
496
                $statusstr = get_string('submissionstatus_' . $status->submission->status, 'assign');
497
                $o .= $this->output->container($statusstr, 'submissionstatus' . $status->submission->status);
498
            } else {
499
                if (!$status->submissionsenabled) {
500
                    $o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus');
501
                } else {
502
                    $o .= $this->output->container(get_string('noattempt', 'assign'), 'submissionstatus');
503
                }
504
            }
505
        } else {
506
            $group = $status->submissiongroup;
507
            if (!$group && $status->preventsubmissionnotingroup) {
508
                $o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus');
509
            } else if ($status->teamsubmission && $status->teamsubmission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
510
                $teamstatus = $status->teamsubmission->status;
511
                $submissionsummary = get_string('submissionstatus_' . $teamstatus, 'assign');
512
                $groupid = 0;
513
                if ($status->submissiongroup) {
514
                    $groupid = $status->submissiongroup->id;
515
                }
516
 
517
                $members = $status->submissiongroupmemberswhoneedtosubmit;
518
                $userslist = array();
519
                foreach ($members as $member) {
520
                    $urlparams = array('id' => $member->id, 'course' => $status->courseid);
521
                    $url = new \moodle_url('/user/view.php', $urlparams);
522
                    if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) {
523
                        $userslist[] = $member->alias;
524
                    } else {
525
                        $fullname = fullname($member, $status->canviewfullnames);
526
                        $userslist[] = $this->output->action_link($url, $fullname);
527
                    }
528
                }
529
                if (count($userslist) > 0) {
530
                    $userstr = join(', ', $userslist);
531
                    $formatteduserstr = get_string('userswhoneedtosubmit', 'assign', $userstr);
532
                    $submissionsummary .= $this->output->container($formatteduserstr);
533
                }
534
                $o .= $this->output->container($submissionsummary, 'submissionstatus' . $status->teamsubmission->status);
535
            } else {
536
                if (!$status->submissionsenabled) {
537
                    $o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus');
538
                } else {
539
                    $o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus');
540
                }
541
            }
542
        }
543
 
544
        // Is locked?
545
        if ($status->locked) {
546
            $o .= $this->output->container(get_string('submissionslocked', 'assign'), 'submissionlocked');
547
        }
548
 
549
        // Grading status.
550
        $statusstr = '';
551
        $classname = 'gradingstatus';
552
        if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
553
            $status->gradingstatus == ASSIGN_GRADING_STATUS_NOT_GRADED) {
554
            $statusstr = get_string($status->gradingstatus, 'assign');
555
        } else {
556
            $gradingstatus = 'markingworkflowstate' . $status->gradingstatus;
557
            $statusstr = get_string($gradingstatus, 'assign');
558
        }
559
        if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
560
            $status->gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
561
            $classname = 'submissiongraded';
562
        } else {
563
            $classname = 'submissionnotgraded';
564
        }
565
 
566
        $o .= $this->output->container($statusstr, $classname);
567
 
568
        $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
569
        $duedate = $status->duedate;
570
        if ($duedate > 0) {
571
 
572
            if ($status->extensionduedate) {
573
                // Extension date.
574
                $duedate = $status->extensionduedate;
575
            }
576
        }
577
 
578
        // Time remaining.
579
        // Only add the row if there is a due date, or a countdown.
580
        if ($status->duedate > 0 || !empty($submission->timestarted)) {
581
            [$remaining, $classname] = $this->get_time_remaining($status);
582
 
583
            // If the assignment is not submitted, and there is a submission in progress,
584
            // Add a heading for the time limit.
585
            if (!empty($submission) &&
586
                $submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED &&
587
                !empty($submission->timestarted)
588
            ) {
589
                $o .= $this->output->container(get_string('timeremaining', 'assign'));
590
            }
591
            $o .= $this->output->container($remaining, $classname);
592
        }
593
 
594
        // Show graders whether this submission is editable by students.
595
        if ($status->view == assign_submission_status::GRADER_VIEW) {
596
            if ($status->canedit) {
597
                $o .= $this->output->container(get_string('submissioneditable', 'assign'), 'submissioneditable');
598
            } else {
599
                $o .= $this->output->container(get_string('submissionnoteditable', 'assign'), 'submissionnoteditable');
600
            }
601
        }
602
 
603
        // Grading criteria preview.
604
        if (!empty($status->gradingcontrollerpreview)) {
605
            $o .= $this->output->container($status->gradingcontrollerpreview, 'gradingmethodpreview');
606
        }
607
 
608
        if ($submission) {
609
 
610
            if (!$status->teamsubmission || $status->submissiongroup != false || !$status->preventsubmissionnotingroup) {
611
                foreach ($status->submissionplugins as $plugin) {
612
                    $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
613
                    if ($plugin->is_enabled() &&
614
                        $plugin->is_visible() &&
615
                        $plugin->has_user_summary() &&
616
                        $pluginshowsummary
617
                    ) {
618
 
619
                        $displaymode = \assign_submission_plugin_submission::SUMMARY;
620
                        $pluginsubmission = new \assign_submission_plugin_submission($plugin,
621
                            $submission,
622
                            $displaymode,
623
                            $status->coursemoduleid,
624
                            $status->returnaction,
625
                            $status->returnparams);
626
                        $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
627
                        $o .= $this->output->container($this->render($pluginsubmission), 'assignsubmission ' . $plugincomponent);
628
                    }
629
                }
630
            }
631
        }
632
 
633
        $o .= $this->output->container_end();
634
        return $o;
635
    }
636
 
637
    /**
638
     * Render a table containing the current status of the submission.
639
     *
640
     * @param assign_submission_status $status
641
     * @return string
642
     */
643
    public function render_assign_submission_status(assign_submission_status $status) {
644
        $o = '';
645
        $o .= $this->output->container_start('submissionstatustable');
646
        $o .= $this->output->heading(get_string('submissionstatusheading', 'assign'), 3);
647
        $time = time();
648
 
649
        $o .= $this->output->box_start('boxaligncenter submissionsummarytable');
650
 
651
        $t = new \html_table();
652
        $t->attributes['class'] = 'generaltable table-bordered';
653
 
654
        $warningmsg = '';
655
        if ($status->teamsubmissionenabled) {
656
            $cell1content = get_string('submissionteam', 'assign');
657
            $group = $status->submissiongroup;
658
            if ($group) {
659
                $cell2content = format_string($group->name, false, ['context' => $status->context]);
660
            } else if ($status->preventsubmissionnotingroup) {
661
                if (count($status->usergroups) == 0) {
662
                    $notification = new \core\output\notification(get_string('noteam', 'assign'), 'error');
663
                    $notification->set_show_closebutton(false);
664
                    $warningmsg = $this->output->notification(get_string('noteam_desc', 'assign'), 'error');
665
                } else if (count($status->usergroups) > 1) {
666
                    $notification = new \core\output\notification(get_string('multipleteams', 'assign'), 'error');
667
                    $notification->set_show_closebutton(false);
668
                    $warningmsg = $this->output->notification(get_string('multipleteams_desc', 'assign'), 'error');
669
                }
670
                $cell2content = $this->output->render($notification);
671
            } else {
672
                $cell2content = get_string('defaultteam', 'assign');
673
            }
674
 
675
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
676
        }
677
 
678
        if ($status->attemptreopenmethod != ASSIGN_ATTEMPT_REOPEN_METHOD_NONE) {
679
            $currentattempt = 1;
680
            if (!$status->teamsubmissionenabled) {
681
                if ($status->submission) {
682
                    $currentattempt = $status->submission->attemptnumber + 1;
683
                }
684
            } else {
685
                if ($status->teamsubmission) {
686
                    $currentattempt = $status->teamsubmission->attemptnumber + 1;
687
                }
688
            }
689
 
690
            $cell1content = get_string('attemptnumber', 'assign');
691
            $maxattempts = $status->maxattempts;
692
            if ($maxattempts == ASSIGN_UNLIMITED_ATTEMPTS) {
693
                $cell2content = get_string('currentattempt', 'assign', $currentattempt);
694
            } else {
695
                $cell2content = get_string('currentattemptof', 'assign',
696
                    array('attemptnumber' => $currentattempt, 'maxattempts' => $maxattempts));
697
            }
698
 
699
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
700
        }
701
 
702
        $cell1content = get_string('submissionstatus', 'assign');
703
        $cell2attributes = [];
704
        if (!$status->teamsubmissionenabled) {
705
            if ($status->submission && $status->submission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
706
                $cell2content = get_string('submissionstatus_' . $status->submission->status, 'assign');
707
                $cell2attributes = array('class' => 'submissionstatus' . $status->submission->status);
708
            } else {
709
                if (!$status->submissionsenabled) {
710
                    $cell2content = get_string('noonlinesubmissions', 'assign');
711
                } else {
712
                    $cell2content = get_string('nosubmissionyet', 'assign');
713
                }
714
            }
715
        } else {
716
            $group = $status->submissiongroup;
717
            if (!$group && $status->preventsubmissionnotingroup) {
718
                $cell2content = get_string('nosubmission', 'assign');
719
            } else if ($status->teamsubmission && $status->teamsubmission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
720
                $teamstatus = $status->teamsubmission->status;
721
                $cell2content = get_string('submissionstatus_' . $teamstatus, 'assign');
722
 
723
                $members = $status->submissiongroupmemberswhoneedtosubmit;
724
                $userslist = array();
725
                foreach ($members as $member) {
726
                    $urlparams = array('id' => $member->id, 'course'=>$status->courseid);
727
                    $url = new \moodle_url('/user/view.php', $urlparams);
728
                    if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) {
729
                        $userslist[] = $member->alias;
730
                    } else {
731
                        $fullname = fullname($member, $status->canviewfullnames);
732
                        $userslist[] = $this->output->action_link($url, $fullname);
733
                    }
734
                }
735
                if (count($userslist) > 0) {
736
                    $userstr = join(', ', $userslist);
737
                    $formatteduserstr = get_string('userswhoneedtosubmit', 'assign', $userstr);
738
                    $cell2content .= $this->output->container($formatteduserstr);
739
                }
740
 
741
                $cell2attributes = array('class' => 'submissionstatus' . $status->teamsubmission->status);
742
            } else {
743
                if (!$status->submissionsenabled) {
744
                    $cell2content = get_string('noonlinesubmissions', 'assign');
745
                } else {
746
                    $cell2content = get_string('nosubmission', 'assign');
747
                }
748
            }
749
        }
750
 
751
        $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
752
 
753
        // Is locked?
754
        if ($status->locked) {
755
            $cell1content = '';
756
            $cell2content = get_string('submissionslocked', 'assign');
757
            $cell2attributes = array('class' => 'submissionlocked');
758
            $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
759
        }
760
 
761
        // Grading status.
762
        $cell1content = get_string('gradingstatus', 'assign');
763
        if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
764
            $status->gradingstatus == ASSIGN_GRADING_STATUS_NOT_GRADED) {
765
            $cell2content = get_string($status->gradingstatus, 'assign');
766
        } else {
767
            $gradingstatus = 'markingworkflowstate' . $status->gradingstatus;
768
            $cell2content = get_string($gradingstatus, 'assign');
769
        }
770
        if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
771
            $status->gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
772
            $cell2attributes = array('class' => 'submissiongraded');
773
        } else {
774
            $cell2attributes = array('class' => 'submissionnotgraded');
775
        }
776
        $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
777
 
778
        $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
779
        $duedate = $status->duedate;
780
        if ($duedate > 0) {
781
            if ($status->view == assign_submission_status::GRADER_VIEW) {
782
                if ($status->cutoffdate) {
783
                    // Cut off date.
784
                    $cell1content = get_string('cutoffdate', 'assign');
785
                    $cell2content = userdate($status->cutoffdate);
786
                    $this->add_table_row_tuple($t, $cell1content, $cell2content);
787
                }
788
            }
789
 
790
            if ($status->extensionduedate) {
791
                // Extension date.
792
                $cell1content = get_string('extensionduedate', 'assign');
793
                $cell2content = userdate($status->extensionduedate);
794
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
795
                $duedate = $status->extensionduedate;
796
            }
797
        }
798
 
799
        // Time remaining.
800
        // Only add the row if there is a due date, or a countdown.
801
        if ($status->duedate > 0 || !empty($submission->timestarted)) {
802
            $cell1content = get_string('timeremaining', 'assign');
803
            [$cell2content, $cell2attributes] = $this->get_time_remaining($status);
804
            $this->add_table_row_tuple($t, $cell1content, $cell2content, [], ['class' => $cell2attributes]);
805
        }
806
 
807
        // Add time limit info if there is one.
808
        $timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0;
809
        if ($timelimitenabled && $status->timelimit > 0) {
810
            $cell1content = get_string('timelimit', 'assign');
811
            $cell2content = format_time($status->timelimit);
812
            $this->add_table_row_tuple($t, $cell1content, $cell2content, [], []);
813
        }
814
 
815
        // Show graders whether this submission is editable by students.
816
        if ($status->view == assign_submission_status::GRADER_VIEW) {
817
            $cell1content = get_string('editingstatus', 'assign');
818
            if ($status->canedit) {
819
                $cell2content = get_string('submissioneditable', 'assign');
820
                $cell2attributes = array('class' => 'submissioneditable');
821
            } else {
822
                $cell2content = get_string('submissionnoteditable', 'assign');
823
                $cell2attributes = array('class' => 'submissionnoteditable');
824
            }
825
            $this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
826
        }
827
 
828
        // Last modified.
829
        if ($submission) {
830
            $cell1content = get_string('timemodified', 'assign');
831
 
832
            if ($submission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
833
                $cell2content = userdate($submission->timemodified);
834
            } else {
835
                $cell2content = "-";
836
            }
837
 
838
            $this->add_table_row_tuple($t, $cell1content, $cell2content);
839
 
840
            if (!$status->teamsubmission || $status->submissiongroup != false || !$status->preventsubmissionnotingroup) {
841
                foreach ($status->submissionplugins as $plugin) {
842
                    $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
843
                    if ($plugin->is_enabled() &&
844
                        $plugin->is_visible() &&
845
                        $plugin->has_user_summary() &&
846
                        $pluginshowsummary
847
                    ) {
848
 
849
                        $cell1content = $plugin->get_name();
850
                        $displaymode = \assign_submission_plugin_submission::SUMMARY;
851
                        $pluginsubmission = new \assign_submission_plugin_submission($plugin,
852
                            $submission,
853
                            $displaymode,
854
                            $status->coursemoduleid,
855
                            $status->returnaction,
856
                            $status->returnparams);
857
                        $cell2content = $this->render($pluginsubmission);
858
                        $this->add_table_row_tuple($t, $cell1content, $cell2content);
859
                    }
860
                }
861
            }
862
        }
863
 
864
        $o .= $warningmsg;
865
        $o .= \html_writer::table($t);
866
        $o .= $this->output->box_end();
867
 
868
        // Grading criteria preview.
869
        if (!empty($status->gradingcontrollerpreview)) {
870
            $o .= $this->output->heading(get_string('gradingmethodpreview', 'assign'), 4);
871
            $o .= $status->gradingcontrollerpreview;
872
        }
873
 
874
        $o .= $this->output->container_end();
875
        return $o;
876
    }
877
 
878
    /**
879
     * Output the attempt history chooser for this assignment
880
     *
881
     * @param \assign_attempt_history_chooser $history
882
     * @return string
883
     */
884
    public function render_assign_attempt_history_chooser(\assign_attempt_history_chooser $history) {
885
        $o = '';
886
 
887
        $context = $history->export_for_template($this);
888
        $o .= $this->render_from_template('mod_assign/attempt_history_chooser', $context);
889
 
890
        return $o;
891
    }
892
 
893
    /**
894
     * Output the attempt history for this assignment
895
     *
896
     * @param \assign_attempt_history $history
897
     * @return string
898
     */
899
    public function render_assign_attempt_history(\assign_attempt_history $history) {
900
        $o = '';
901
 
902
        // Don't show the last one because it is the current submission.
903
        array_pop($history->submissions);
904
 
905
        // Show newest to oldest.
906
        $history->submissions = array_reverse($history->submissions);
907
 
908
        if (empty($history->submissions)) {
909
            return '';
910
        }
911
 
912
        $containerid = 'attempthistory' . uniqid();
913
        $o .= $this->output->heading(get_string('attempthistory', 'assign'), 3);
914
        $o .= $this->box_start('attempthistory', $containerid);
915
 
916
        foreach ($history->submissions as $i => $submission) {
917
            $grade = null;
918
            foreach ($history->grades as $onegrade) {
919
                if ($onegrade->attemptnumber == $submission->attemptnumber) {
920
                    if ($onegrade->grade != ASSIGN_GRADE_NOT_SET) {
921
                        $grade = $onegrade;
922
                    }
923
                    break;
924
                }
925
            }
926
 
927
            if ($submission) {
928
                $submissionsummary = userdate($submission->timemodified);
929
            } else {
930
                $submissionsummary = get_string('nosubmission', 'assign');
931
            }
932
 
933
            $attemptsummaryparams = array('attemptnumber'=>$submission->attemptnumber+1,
934
                                          'submissionsummary'=>$submissionsummary);
935
            $o .= $this->heading(get_string('attemptheading', 'assign', $attemptsummaryparams), 4);
936
 
937
            $t = new \html_table();
938
 
939
            if ($submission) {
940
                $cell1content = get_string('submissionstatus', 'assign');
941
                $cell2content = get_string('submissionstatus_' . $submission->status, 'assign');
942
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
943
 
944
                foreach ($history->submissionplugins as $plugin) {
945
                    $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
946
                    if ($plugin->is_enabled() &&
947
                            $plugin->is_visible() &&
948
                            $plugin->has_user_summary() &&
949
                            $pluginshowsummary) {
950
 
951
                        $cell1content = $plugin->get_name();
952
                        $pluginsubmission = new \assign_submission_plugin_submission($plugin,
953
                                                                                    $submission,
954
                                                                                    \assign_submission_plugin_submission::SUMMARY,
955
                                                                                    $history->coursemoduleid,
956
                                                                                    $history->returnaction,
957
                                                                                    $history->returnparams);
958
                        $cell2content = $this->render($pluginsubmission);
959
                        $this->add_table_row_tuple($t, $cell1content, $cell2content);
960
                    }
961
                }
962
            }
963
 
964
            if ($grade) {
965
                // Heading 'feedback'.
966
                $title = get_string('feedback', 'assign', $i);
967
                $title .= $this->output->spacer(array('width'=>10));
968
                if ($history->cangrade) {
969
                    // Edit previous feedback.
970
                    $returnparams = http_build_query($history->returnparams);
971
                    $urlparams = array('id' => $history->coursemoduleid,
972
                                   'rownum'=>$history->rownum,
973
                                   'useridlistid'=>$history->useridlistid,
974
                                   'attemptnumber'=>$grade->attemptnumber,
975
                                   'action'=>'grade',
976
                                   'returnaction'=>$history->returnaction,
977
                                   'returnparams'=>$returnparams);
978
                    $url = new \moodle_url('/mod/assign/view.php', $urlparams);
979
                    $icon = new \pix_icon('gradefeedback',
980
                                            get_string('editattemptfeedback', 'assign', $grade->attemptnumber+1),
981
                                            'mod_assign');
982
                    $title .= $this->output->action_icon($url, $icon);
983
                }
984
                $cell = new \html_table_cell($title);
985
                $cell->attributes['class'] = 'feedbacktitle';
986
                $cell->colspan = 2;
987
                $t->data[] = new \html_table_row(array($cell));
988
 
989
                // Grade.
990
                $cell1content = get_string('gradenoun');
991
                $cell2content = $grade->gradefordisplay;
992
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
993
 
994
                // Graded on.
995
                $cell1content = get_string('gradedon', 'assign');
996
                $cell2content = userdate($grade->timemodified);
997
                $this->add_table_row_tuple($t, $cell1content, $cell2content);
998
 
999
                // Graded by set to a real user. Not set can be empty or -1.
1000
                if (!empty($grade->grader) && is_object($grade->grader)) {
1001
                    $cell1content = get_string('gradedby', 'assign');
1002
                    $cell2content = $this->output->user_picture($grade->grader) .
1003
                                    $this->output->spacer(array('width' => 30)) . fullname($grade->grader);
1004
                    $this->add_table_row_tuple($t, $cell1content, $cell2content);
1005
                }
1006
 
1007
                // Feedback from plugins.
1008
                foreach ($history->feedbackplugins as $plugin) {
1009
                    if ($plugin->is_enabled() &&
1010
                        $plugin->is_visible() &&
1011
                        $plugin->has_user_summary() &&
1012
                        !$plugin->is_empty($grade)) {
1013
 
1014
                        $pluginfeedback = new \assign_feedback_plugin_feedback(
1015
                            $plugin, $grade, \assign_feedback_plugin_feedback::SUMMARY, $history->coursemoduleid,
1016
                            $history->returnaction, $history->returnparams
1017
                        );
1018
 
1019
                        $cell1content = $plugin->get_name();
1020
                        $cell2content = $this->render($pluginfeedback);
1021
                        $this->add_table_row_tuple($t, $cell1content, $cell2content);
1022
                    }
1023
 
1024
                }
1025
 
1026
            }
1027
 
1028
            $o .= \html_writer::table($t);
1029
        }
1030
        $o .= $this->box_end();
1031
 
1032
        $this->page->requires->yui_module('moodle-mod_assign-history', 'Y.one("#' . $containerid . '").history');
1033
 
1034
        return $o;
1035
    }
1036
 
1037
    /**
1038
     * Render a submission plugin submission
1039
     *
1040
     * @param \assign_submission_plugin_submission $submissionplugin
1041
     * @return string
1042
     */
1043
    public function render_assign_submission_plugin_submission(\assign_submission_plugin_submission $submissionplugin) {
1044
        $o = '';
1045
 
1046
        if ($submissionplugin->view == \assign_submission_plugin_submission::SUMMARY) {
1047
            $showviewlink = false;
1048
            $summary = $submissionplugin->plugin->view_summary($submissionplugin->submission,
1049
                                                               $showviewlink);
1050
 
1051
            $classsuffix = $submissionplugin->plugin->get_subtype() .
1052
                           '_' .
1053
                           $submissionplugin->plugin->get_type() .
1054
                           '_' .
1055
                           $submissionplugin->submission->id;
1056
 
1057
            $o .= $this->output->box_start('boxaligncenter plugincontentsummary summary_' . $classsuffix);
1058
 
1059
            $link = '';
1060
            if ($showviewlink) {
1061
                $previewstr = get_string('viewsubmission', 'assign');
1062
                $icon = $this->output->pix_icon('t/preview', $previewstr);
1063
 
1064
                $expandstr = get_string('viewfull', 'assign');
1065
                $expandicon = $this->output->pix_icon('t/switch_plus', $expandstr);
1066
                $options = array(
1067
                    'class' => 'expandsummaryicon expand_' . $classsuffix,
1068
                    'aria-label' => $expandstr,
1069
                    'role' => 'button',
1070
                    'aria-expanded' => 'false'
1071
                );
1072
                $o .= \html_writer::link('', $expandicon, $options);
1073
 
1074
                $jsparams = array($submissionplugin->plugin->get_subtype(),
1075
                                  $submissionplugin->plugin->get_type(),
1076
                                  $submissionplugin->submission->id);
1077
 
1078
                $this->page->requires->js_init_call('M.mod_assign.init_plugin_summary', $jsparams);
1079
 
1080
                $action = 'viewplugin' . $submissionplugin->plugin->get_subtype();
1081
                $returnparams = http_build_query($submissionplugin->returnparams);
1082
                $link .= '<noscript>';
1083
                $urlparams = array('id' => $submissionplugin->coursemoduleid,
1084
                                   'sid'=>$submissionplugin->submission->id,
1085
                                   'plugin'=>$submissionplugin->plugin->get_type(),
1086
                                   'action'=>$action,
1087
                                   'returnaction'=>$submissionplugin->returnaction,
1088
                                   'returnparams'=>$returnparams);
1089
                $url = new \moodle_url('/mod/assign/view.php', $urlparams);
1090
                $link .= $this->output->action_link($url, $icon);
1091
                $link .= '</noscript>';
1092
 
1093
                $link .= $this->output->spacer(array('width'=>15));
1094
            }
1095
 
1096
            $o .= $link . $summary;
1097
            $o .= $this->output->box_end();
1098
            if ($showviewlink) {
1099
                $o .= $this->output->box_start('boxaligncenter hidefull full_' . $classsuffix);
1100
                $collapsestr = get_string('viewsummary', 'assign');
1101
                $options = array(
1102
                    'class' => 'expandsummaryicon contract_' . $classsuffix,
1103
                    'aria-label' => $collapsestr,
1104
                    'role' => 'button',
1105
                    'aria-expanded' => 'true'
1106
                );
1107
                $collapseicon = $this->output->pix_icon('t/switch_minus', $collapsestr);
1108
                $o .= \html_writer::link('', $collapseicon, $options);
1109
 
1110
                $o .= $submissionplugin->plugin->view($submissionplugin->submission);
1111
                $o .= $this->output->box_end();
1112
            }
1113
        } else if ($submissionplugin->view == \assign_submission_plugin_submission::FULL) {
1114
            $o .= $this->output->box_start('boxaligncenter submissionfull');
1115
            $o .= $submissionplugin->plugin->view($submissionplugin->submission);
1116
            $o .= $this->output->box_end();
1117
        }
1118
 
1119
        return $o;
1120
    }
1121
 
1122
    /**
1123
     * Render the grading table.
1124
     *
1125
     * @param \assign_grading_table $table
1126
     * @return string
1127
     */
1128
    public function render_assign_grading_table(\assign_grading_table $table) {
1129
        $o = '';
1130
        $o .= $this->output->box_start('boxaligncenter gradingtable position-relative');
1131
 
1132
        $this->page->requires->js_init_call('M.mod_assign.init_grading_table', array());
1133
        $this->page->requires->string_for_js('nousersselected', 'assign');
1134
        $this->page->requires->string_for_js('batchoperationconfirmgrantextension', 'assign');
1135
        $this->page->requires->string_for_js('batchoperationconfirmlock', 'assign');
1136
        $this->page->requires->string_for_js('batchoperationconfirmremovesubmission', 'assign');
1137
        $this->page->requires->string_for_js('batchoperationconfirmreverttodraft', 'assign');
1138
        $this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign');
1139
        $this->page->requires->string_for_js('batchoperationconfirmaddattempt', 'assign');
1140
        $this->page->requires->string_for_js('batchoperationconfirmdownloadselected', 'assign');
1141
        $this->page->requires->string_for_js('batchoperationconfirmsetmarkingworkflowstate', 'assign');
1142
        $this->page->requires->string_for_js('batchoperationconfirmsetmarkingallocation', 'assign');
1143
        $this->page->requires->string_for_js('editaction', 'assign');
1144
        foreach ($table->plugingradingbatchoperations as $plugin => $operations) {
1145
            foreach ($operations as $operation => $description) {
1146
                $this->page->requires->string_for_js('batchoperationconfirm' . $operation,
1147
                                                     'assignfeedback_' . $plugin);
1148
            }
1149
        }
1150
        $o .= $this->flexible_table($table, $table->get_rows_per_page(), true);
1151
        $o .= $this->output->box_end();
1152
 
1153
        return $o;
1154
    }
1155
 
1156
    /**
1157
     * Render a feedback plugin feedback
1158
     *
1159
     * @param \assign_feedback_plugin_feedback $feedbackplugin
1160
     * @return string
1161
     */
1162
    public function render_assign_feedback_plugin_feedback(\assign_feedback_plugin_feedback $feedbackplugin) {
1163
        $o = '';
1164
 
1165
        if ($feedbackplugin->view == \assign_feedback_plugin_feedback::SUMMARY) {
1166
            $showviewlink = false;
1167
            $summary = $feedbackplugin->plugin->view_summary($feedbackplugin->grade, $showviewlink);
1168
 
1169
            $classsuffix = $feedbackplugin->plugin->get_subtype() .
1170
                           '_' .
1171
                           $feedbackplugin->plugin->get_type() .
1172
                           '_' .
1173
                           $feedbackplugin->grade->id;
1174
            $o .= $this->output->box_start('boxaligncenter plugincontentsummary summary_' . $classsuffix);
1175
 
1176
            $link = '';
1177
            if ($showviewlink) {
1178
                $previewstr = get_string('viewfeedback', 'assign');
1179
                $icon = $this->output->pix_icon('t/preview', $previewstr);
1180
 
1181
                $expandstr = get_string('viewfull', 'assign');
1182
                $expandicon = $this->output->pix_icon('t/switch_plus', $expandstr);
1183
                $options = array(
1184
                    'class' => 'expandsummaryicon expand_' . $classsuffix,
1185
                    'aria-label' => $expandstr,
1186
                    'role' => 'button',
1187
                    'aria-expanded' => 'false'
1188
                );
1189
                $o .= \html_writer::link('', $expandicon, $options);
1190
 
1191
                $jsparams = array($feedbackplugin->plugin->get_subtype(),
1192
                                  $feedbackplugin->plugin->get_type(),
1193
                                  $feedbackplugin->grade->id);
1194
                $this->page->requires->js_init_call('M.mod_assign.init_plugin_summary', $jsparams);
1195
 
1196
                $urlparams = array('id' => $feedbackplugin->coursemoduleid,
1197
                                   'gid'=>$feedbackplugin->grade->id,
1198
                                   'plugin'=>$feedbackplugin->plugin->get_type(),
1199
                                   'action'=>'viewplugin' . $feedbackplugin->plugin->get_subtype(),
1200
                                   'returnaction'=>$feedbackplugin->returnaction,
1201
                                   'returnparams'=>http_build_query($feedbackplugin->returnparams));
1202
                $url = new \moodle_url('/mod/assign/view.php', $urlparams);
1203
                $link .= '<noscript>';
1204
                $link .= $this->output->action_link($url, $icon);
1205
                $link .= '</noscript>';
1206
 
1207
                $link .= $this->output->spacer(array('width'=>15));
1208
            }
1209
 
1210
            $o .= $link . $summary;
1211
            $o .= $this->output->box_end();
1212
            if ($showviewlink) {
1213
                $o .= $this->output->box_start('boxaligncenter hidefull full_' . $classsuffix);
1214
                $collapsestr = get_string('viewsummary', 'assign');
1215
                $options = array(
1216
                    'class' => 'expandsummaryicon contract_' . $classsuffix,
1217
                    'aria-label' => $collapsestr,
1218
                    'role' => 'button',
1219
                    'aria-expanded' => 'true'
1220
                );
1221
                $collapseicon = $this->output->pix_icon('t/switch_minus', $collapsestr);
1222
                $o .= \html_writer::link('', $collapseicon, $options);
1223
 
1224
                $o .= $feedbackplugin->plugin->view($feedbackplugin->grade);
1225
                $o .= $this->output->box_end();
1226
            }
1227
        } else if ($feedbackplugin->view == \assign_feedback_plugin_feedback::FULL) {
1228
            $o .= $this->output->box_start('boxaligncenter feedbackfull');
1229
            $o .= $feedbackplugin->plugin->view($feedbackplugin->grade);
1230
            $o .= $this->output->box_end();
1231
        }
1232
 
1233
        return $o;
1234
    }
1235
 
1236
    /**
1237
     * Render a course index summary
1238
     *
1239
     * @param \assign_course_index_summary $indexsummary
1240
     * @return string
1241
     */
1242
    public function render_assign_course_index_summary(\assign_course_index_summary $indexsummary) {
1243
        $o = '';
1244
 
1245
        $strplural = get_string('modulenameplural', 'assign');
1246
        $strsectionname  = $indexsummary->courseformatname;
1247
        $strduedate = get_string('duedate', 'assign');
1248
        $strsubmission = get_string('submission', 'assign');
1249
        $strgrade = get_string('gradenoun');
1250
 
1251
        $table = new \html_table();
1252
        if ($indexsummary->usesections) {
1253
            $table->head  = array ($strsectionname, $strplural, $strduedate, $strsubmission, $strgrade);
1254
            $table->align = array ('left', 'left', 'center', 'right', 'right');
1255
        } else {
1256
            $table->head  = array ($strplural, $strduedate, $strsubmission, $strgrade);
1257
            $table->align = array ('left', 'left', 'center', 'right');
1258
        }
1259
        $table->data = array();
1260
 
1261
        $currentsection = '';
1262
        foreach ($indexsummary->assignments as $info) {
1263
            $params = array('id' => $info['cmid']);
1264
            $link = \html_writer::link(new \moodle_url('/mod/assign/view.php', $params),
1265
                                      $info['cmname']);
1266
            $due = $info['timedue'] ? userdate($info['timedue']) : '-';
1267
 
1268
            if ($info['cangrade']) {
1269
                $params['action'] = 'grading';
1270
                $gradeinfo = \html_writer::link(new \moodle_url('/mod/assign/view.php', $params),
1271
                    get_string('numberofsubmissionsneedgradinglabel', 'assign', $info['gradeinfo']));
1272
            } else {
1273
                $gradeinfo = $info['gradeinfo'];
1274
            }
1275
 
1276
            $printsection = '';
1277
            if ($indexsummary->usesections) {
1278
                if ($info['sectionname'] !== $currentsection) {
1279
                    if ($info['sectionname']) {
1280
                        $printsection = $info['sectionname'];
1281
                    }
1282
                    if ($currentsection !== '') {
1283
                        $table->data[] = 'hr';
1284
                    }
1285
                    $currentsection = $info['sectionname'];
1286
                }
1287
            }
1288
 
1289
            if ($indexsummary->usesections) {
1290
                $row = [$printsection, $link, $due, $info['submissioninfo'], $gradeinfo];
1291
            } else {
1292
                $row = [$link, $due, $info['submissioninfo'], $gradeinfo];
1293
            }
1294
            $table->data[] = $row;
1295
        }
1296
 
1297
        $o .= \html_writer::table($table);
1298
 
1299
        return $o;
1300
    }
1301
 
1302
    /**
1303
     * Get the time remaining for a submission.
1304
     *
1305
     * @param \mod_assign\output\assign_submission_status $status
1306
     * @return array The first element is the time remaining as a human readable
1307
     *               string and the second is a CSS class.
1308
     */
1309
    protected function get_time_remaining(\mod_assign\output\assign_submission_status $status): array {
1310
        $time = time();
1311
        $submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
1312
        $submissionstarted = $submission && property_exists($submission, 'timestarted') && $submission->timestarted;
1313
        $timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0 && $submissionstarted;
1314
        // Define $duedate as latest between due date and extension - which is a possibility...
1315
        $extensionduedate = intval($status->extensionduedate);
1316
        $duedate = !empty($extensionduedate) ? max($status->duedate, $extensionduedate) : $status->duedate;
1317
        $duedatereached = $duedate > 0 && $duedate - $time <= 0;
1318
        $timelimitenabledbeforeduedate = $timelimitenabled && !$duedatereached;
1319
 
1320
        // There is a submission, display the relevant early/late message.
1321
        if ($submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
1322
            $latecalculation = $submission->timemodified - ($timelimitenabledbeforeduedate ? $submission->timestarted : 0);
1323
            $latethreshold = $timelimitenabledbeforeduedate ? $status->timelimit : $duedate;
1324
            $earlystring = $timelimitenabledbeforeduedate ? 'submittedundertime' : 'submittedearly';
1325
            $latestring = $timelimitenabledbeforeduedate ? 'submittedovertime' : 'submittedlate';
1326
            $ontime = $latecalculation <= $latethreshold;
1327
            return [
1328
                get_string(
1329
                    $ontime ? $earlystring : $latestring,
1330
                    'assign',
1331
                    format_time($latecalculation - $latethreshold)
1332
                ),
1333
                $ontime ? 'earlysubmission' : 'latesubmission'
1334
            ];
1335
        }
1336
 
1337
        // There is no submission, due date has passed, show assignment is overdue.
1338
        if ($duedatereached) {
1339
            return [
1340
                get_string(
1341
                    $status->submissionsenabled ? 'overdue' : 'duedatereached',
1342
                    'assign',
1343
                    format_time($time - $duedate)
1344
                ),
1345
                'overdue'
1346
            ];
1347
        }
1348
 
1349
        // An attempt has started and there is a time limit, display the time limit.
1350
        if ($timelimitenabled && !empty($submission->timestarted)) {
1351
            return [
1352
                (new \assign($status->context, null, null))->get_timelimit_panel($submission),
1353
                'timeremaining'
1354
            ];
1355
        }
1356
 
1357
        // Assignment is not overdue, and no submission has been made. Just display the due date.
1358
        return [get_string('paramtimeremaining', 'assign', format_time($duedate - $time)), 'timeremaining'];
1359
    }
1360
 
1361
    /**
1362
     * Internal function - creates htmls structure suitable for YUI tree.
1363
     *
1364
     * @param \assign_files $tree
1365
     * @param array $dir
1366
     * @return string
1367
     */
1368
    protected function htmllize_tree(\assign_files $tree, $dir) {
1369
        global $CFG;
1370
        $yuiconfig = array();
1371
        $yuiconfig['type'] = 'html';
1372
 
1373
        if (empty($dir['subdirs']) and empty($dir['files'])) {
1374
            return '';
1375
        }
1376
 
1377
        $result = '<ul>';
1378
        foreach ($dir['subdirs'] as $subdir) {
1379
            $image = $this->output->pix_icon(file_folder_icon(),
1380
                                             $subdir['dirname'],
1381
                                             'moodle',
1382
                                             array('class'=>'icon'));
1383
            $result .= '<li yuiConfig=\'' . json_encode($yuiconfig) . '\'>' .
1384
                       '<div>' . $image . ' ' . s($subdir['dirname']) . '</div> ' .
1385
                       $this->htmllize_tree($tree, $subdir) .
1386
                       '</li>';
1387
        }
1388
 
1389
        foreach ($dir['files'] as $file) {
1390
            $filename = $file->get_filename();
1391
            if ($CFG->enableplagiarism) {
1392
                require_once($CFG->libdir.'/plagiarismlib.php');
1393
                $plagiarismlinks = plagiarism_get_links(array('userid'=>$file->get_userid(),
1394
                                                             'file'=>$file,
1395
                                                             'cmid'=>$tree->cm->id,
1396
                                                             'course'=>$tree->course));
1397
            } else {
1398
                $plagiarismlinks = '';
1399
            }
1400
            $image = $this->output->pix_icon(file_file_icon($file),
1401
                                             $filename,
1402
                                             'moodle',
1403
                                             array('class'=>'icon'));
1404
            $result .= '<li yuiConfig=\'' . json_encode($yuiconfig) . '\'>' .
1405
                '<div>' .
1406
                    '<div class="fileuploadsubmission">' . $image . ' ' .
1407
                    html_writer::link($tree->get_file_url($file), $file->get_filename(), [
1408
                        'target' => '_blank',
1409
                    ]) . ' ' .
1410
                    $plagiarismlinks . ' ' .
1411
                    $this->get_portfolio_button($tree, $file) . ' ' .
1412
                    '</div>' .
1413
                    '<div class="fileuploadsubmissiontime">' . $tree->get_modified_time($file) . '</div>' .
1414
                '</div>' .
1415
            '</li>';
1416
        }
1417
 
1418
        $result .= '</ul>';
1419
 
1420
        return $result;
1421
    }
1422
 
1423
    /**
1424
     * Get the portfolio button content for the specified file.
1425
     *
1426
     * @param assign_files $tree
1427
     * @param stored_file $file
1428
     * @return string
1429
     */
1430
    protected function get_portfolio_button(assign_files $tree, stored_file $file): string {
1431
        global $CFG;
1432
        if (empty($CFG->enableportfolios)) {
1433
            return '';
1434
        }
1435
 
1436
        if (!has_capability('mod/assign:exportownsubmission', $tree->context)) {
1437
            return '';
1438
        }
1439
 
1440
        require_once($CFG->libdir . '/portfoliolib.php');
1441
 
1442
        $button = new portfolio_add_button();
1443
        $portfolioparams = [
1444
            'cmid' => $tree->cm->id,
1445
            'fileid' => $file->get_id(),
1446
        ];
1447
        $button->set_callback_options('assign_portfolio_caller', $portfolioparams, 'mod_assign');
1448
        $button->set_format_by_file($file);
1449
 
1450
        return (string) $button->to_html(PORTFOLIO_ADD_ICON_LINK);
1451
    }
1452
 
1453
    /**
1454
     * Helper method dealing with the fact we can not just fetch the output of flexible_table
1455
     *
1456
     * @param \flexible_table $table The table to render
1457
     * @param int $rowsperpage How many assignments to render in a page
1458
     * @param bool $displaylinks - Whether to render links in the table
1459
     *                             (e.g. downloads would not enable this)
1460
     * @return string HTML
1461
     */
1462
    protected function flexible_table(\flexible_table $table, $rowsperpage, $displaylinks) {
1463
 
1464
        $o = '';
1465
        ob_start();
1466
        $table->out($rowsperpage, $displaylinks);
1467
        $o = ob_get_contents();
1468
        ob_end_clean();
1469
 
1470
        return $o;
1471
    }
1472
 
1473
    /**
1474
     * Helper method dealing with the fact we can not just fetch the output of moodleforms
1475
     *
1476
     * @param \moodleform $mform
1477
     * @return string HTML
1478
     */
1479
    protected function moodleform(\moodleform $mform) {
1480
 
1481
        $o = '';
1482
        ob_start();
1483
        $mform->display();
1484
        $o = ob_get_contents();
1485
        ob_end_clean();
1486
 
1487
        return $o;
1488
    }
1489
 
1490
    /**
1491
     * Defer to template.
1492
     *
1493
     * @param grading_app $app - All the data to render the grading app.
1494
     */
1495
    public function render_grading_app(grading_app $app) {
1496
        $context = $app->export_for_template($this);
1497
        return $this->render_from_template('mod_assign/grading_app', $context);
1498
    }
1499
 
1500
    /**
1501
     * Renders the submission action menu.
1502
     *
1503
     * @param \mod_assign\output\actionmenu $actionmenu The actionmenu
1504
     * @return string Rendered action menu.
1505
     */
1506
    public function submission_actionmenu(\mod_assign\output\actionmenu $actionmenu): string {
1507
        $context = $actionmenu->export_for_template($this);
1508
        return $this->render_from_template('mod_assign/submission_actionmenu', $context);
1509
    }
1510
 
1511
    /**
1512
     * Renders the user submission action menu.
1513
     *
1514
     * @param \mod_assign\output\user_submission_actionmenu $actionmenu The actionmenu
1515
     * @return string The rendered action menu.
1516
     */
1517
    public function render_user_submission_actionmenu(\mod_assign\output\user_submission_actionmenu $actionmenu): string {
1518
        $context = $actionmenu->export_for_template($this);
1519
        return $this->render_from_template('mod_assign/user_submission_actionmenu', $context);
1520
    }
1521
 
1522
    /**
1523
     * Renders the override action menu.
1524
     *
1525
     * @param \mod_assign\output\override_actionmenu $actionmenu The actionmenu
1526
     * @return string The rendered override action menu.
1527
     */
1528
    public function render_override_actionmenu(\mod_assign\output\override_actionmenu $actionmenu): string {
1529
        $context = $actionmenu->export_for_template($this);
1530
        return $this->render_from_template('mod_assign/override_actionmenu', $context);
1531
    }
1532
 
1533
    /**
1534
     * Renders the grading action menu.
1535
     *
1536
     * @param \mod_assign\output\grading_actionmenu $actionmenu The actionmenu
1537
     * @return string The rendered grading action menu.
1538
     */
1539
    public function render_grading_actionmenu(\mod_assign\output\grading_actionmenu $actionmenu): string {
1540
        $context = $actionmenu->export_for_template($this);
1541
        return $this->render_from_template('mod_assign/grading_actionmenu', $context);
1542
    }
1543
 
1544
    /**
1545
     * Formats activity intro text.
1546
     *
1547
     * @param object $assign Instance of assign.
1548
     * @param int $cmid Course module ID.
1549
     * @return string
1550
     */
1551
    public function format_activity_text($assign, $cmid) {
1552
        global $CFG;
1553
        require_once("$CFG->libdir/filelib.php");
1554
        $context = \context_module::instance($cmid);
1555
        $options = array('noclean' => true, 'para' => false, 'filter' => true, 'context' => $context, 'overflowdiv' => true);
1556
        $activity = file_rewrite_pluginfile_urls(
1557
            $assign->activity, 'pluginfile.php', $context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
1558
        return trim(format_text($activity, $assign->activityformat, $options, null));
1559
    }
1560
}