3 // This file is part of Moodle - http://moodle.org/
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
19 * All workshop module renderers are defined here
21 * @package mod-workshop
22 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
29 * Workshop module renderer class
31 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 class mod_workshop_renderer extends plugin_renderer_base {
37 * Returns html code for a status message
39 * This should be replaced by a core system of displaying messages, as for example Mahara has.
41 * @param string $message to display
44 public function status_message(stdclass $message) {
45 if (empty($message->text)) {
48 $sty = empty($message->sty) ? 'info' : $message->sty;
50 $o = html_writer::tag('span', $message->text);
51 $closer = html_writer::tag('a', array('href' => $this->page->url->out()),
52 get_string('messageclose', 'workshop'));
53 $o .= $this->output->container($closer, 'status-message-closer');
54 if (isset($message->extra)) {
55 $o .= $message->extra;
57 return $this->output->container($o, array('status-message', $sty));
61 * Wraps html code returned by the allocator init() method
63 * Supplied argument can be either integer status code or an array of string messages. Messages
64 * in a array can have optional prefix or prefixes, using '::' as delimiter. Prefixes determine
65 * the type of the message and may influence its visualisation.
67 * @param mixed $result int|array returned by init()
68 * @return string html to be echoed
70 public function allocation_init_result($result='') {
71 $msg = new stdclass();
72 if ($result === workshop::ALLOCATION_ERROR) {
73 $msg = (object)array('text' => get_string('allocationerror', 'workshop'), 'sty' => 'error');
75 $msg = (object)array('text' => get_string('allocationdone', 'workshop'), 'sty' => 'ok');
77 $o = $this->status_message($msg);
78 if (is_array($result)) {
79 $o .= html_writer::start_tag('ul', array('class' => 'allocation-init-results'));
80 foreach ($result as $message) {
81 $parts = explode('::', $message);
82 $text = array_pop($parts);
83 $class = implode(' ', $parts);
84 if (in_array('debug', $parts) && !debugging('', DEBUG_DEVELOPER)) {
85 // do not display allocation debugging messages
88 $o .= html_writer::tag('li', $text, array('class' => $class)) . "\n";
90 $o .= html_writer::end_tag('ul');
91 $o .= $this->output->continue_button($this->page->url->out());
97 * Display a short summary of the submission
99 * The passed submission object must define at least: id, title, timecreated, timemodified,
100 * authorid, authorfirstname, authorlastname, authorpicture and authorimagealt
102 * @param stdclass $submission The submission record
103 * @param bool $showauthorname Should the author name be displayed
104 * @return string html to be echoed
106 public function submission_summary(stdclass $submission, $showauthorname=false) {
109 $o = ''; // output HTML code
110 $classes = 'submission-summary';
111 if (!$showauthorname) {
112 $classes .= ' anonymous';
114 $o .= $this->output->container_start($classes); // main wrapper
115 $url = new moodle_url('/mod/workshop/submission.php',
116 array('cmid' => $this->page->context->instanceid, 'id' => $submission->id));
117 $o .= html_writer::link($url, format_string($submission->title), array('class'=>'title'));
118 if ($showauthorname) {
119 $author = new stdclass();
120 $author->id = $submission->authorid;
121 $author->firstname = $submission->authorfirstname;
122 $author->lastname = $submission->authorlastname;
123 $author->picture = $submission->authorpicture;
124 $author->imagealt = $submission->authorimagealt;
125 $userpic = $this->output->user_picture($author, array('courseid' => $this->page->course->id, 'size' => 35));
126 $userurl = new moodle_url('/user/view.php',
127 array('id' => $author->id, 'course' => $this->page->course->id));
129 $a->name = fullname($author);
130 $a->url = $userurl->out();
131 $byfullname = get_string('byfullname', 'workshop', $a);
133 $oo = $this->output->container($userpic, 'picture');
134 $oo .= $this->output->container($byfullname, 'fullname');
135 $o .= $this->output->container($oo, 'author');
137 $created = get_string('userdatecreated', 'workshop', userdate($submission->timecreated));
138 $o .= $this->output->container($created, 'userdate created');
139 if ($submission->timemodified > $submission->timecreated) {
140 $modified = get_string('userdatemodified', 'workshop', userdate($submission->timemodified));
141 $o .= $this->output->container($modified, 'userdate modified');
143 $o .= $this->output->container_end(); // end of the main wrapper
149 * Displays the submission fulltext
151 * By default, this looks similar to a forum post.
153 * @param stdclass $submission The submission data
154 * @param bool $showauthorname Should the author name be displayed
155 * @return string html to be echoed
157 public function submission_full(stdclass $submission, $showauthorname=false) {
160 $o = ''; // output HTML code
161 $classes = 'submission-full';
162 if (!$showauthorname) {
163 $classes .= ' anonymous';
165 $o .= $this->output->container_start($classes);
166 $o .= $this->output->container_start('header');
167 $o .= $this->output->heading(format_string($submission->title), 3, 'title');
168 if ($showauthorname) {
169 $author = new stdclass();
170 $author->id = $submission->authorid;
171 $author->firstname = $submission->authorfirstname;
172 $author->lastname = $submission->authorlastname;
173 $author->picture = $submission->authorpicture;
174 $author->imagealt = $submission->authorimagealt;
175 $userpic = $this->output->user_picture($author, array('courseid' => $this->page->course->id, 'size' => 64));
176 $userurl = new moodle_url('/user/view.php',
177 array('id' => $author->id, 'course' => $this->page->course->id));
179 $a->name = fullname($author);
180 $a->url = $userurl->out();
181 $byfullname = get_string('byfullname', 'workshop', $a);
182 $oo = $this->output->container($userpic, 'picture');
183 $oo .= $this->output->container($byfullname, 'fullname');
185 $o .= $this->output->container($oo, 'author');
187 $created = get_string('userdatecreated', 'workshop', userdate($submission->timecreated));
188 $o .= $this->output->container($created, 'userdate created');
189 if ($submission->timemodified > $submission->timecreated) {
190 $modified = get_string('userdatemodified', 'workshop', userdate($submission->timemodified));
191 $o .= $this->output->container($modified, 'userdate modified');
193 $o .= $this->output->container_end(); // end of header
195 $content = format_text($submission->content, $submission->contentformat);
196 $content = file_rewrite_pluginfile_urls($content, 'pluginfile.php', $this->page->context->id,
197 'workshop_submission_content', $submission->id);
198 $o .= $this->output->container($content, 'content');
200 $o .= $this->submission_attachments($submission);
202 $o .= $this->output->container_end(); // end of submission-full
208 * Renders a list of files attached to the submission
210 * If format==html, then format a html string. If format==text, then format a text-only string.
211 * Otherwise, returns html for non-images and html to display the image inline.
213 * @param stdclass $submission Submission record
214 * @param string format The format of the returned string
215 * @return string HTML code to be echoed
217 public function submission_attachments(stdclass $submission, $format=null) {
219 require_once($CFG->libdir.'/filelib.php');
221 $fs = get_file_storage();
222 $ctx = $this->page->context;
223 $files = $fs->get_area_files($ctx->id, 'workshop_submission_attachment', $submission->id);
225 $outputimgs = ""; // images to be displayed inline
226 $outputfiles = ""; // list of attachment files
228 foreach ($files as $file) {
229 if ($file->is_directory()) {
233 $filepath = $file->get_filepath();
234 $filename = $file->get_filename();
235 $fileurl = file_encode_url($CFG->wwwroot . '/pluginfile.php',
236 '/' . $ctx->id . '/workshop_submission_attachment/' . $submission->id . $filepath . $filename, true);
237 $type = $file->get_mimetype();
238 $type = mimeinfo_from_type("type", $type);
239 $image = html_writer::empty_tag('img', array('src'=>$this->output->pix_url(file_mimetype_icon($type)), 'alt'=>$type, 'class'=>'icon'));
241 $linkhtml = html_writer::link($fileurl, $image) . html_writer::link($fileurl, $filename);
242 $linktxt = "$filename [$fileurl]";
244 if ($format == "html") {
245 // this is the same as the code in the last else-branch
246 $outputfiles .= html_writer::tag('li', $linkhtml, array('class' => $type));
248 } else if ($format == "text") {
249 $outputfiles .= $linktxt . "\n";
252 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {
253 $preview = html_writer::empty_tag('img', array('src'=>$fileurl, 'alt'=>'', 'class'=>'preview'));
254 $preview = html_writer::tag('a', $preview, array('href'=>$fileurl));
255 $outputimgs .= $this->output->container($preview);
257 // this is the same as the code in html if-branch
258 $outputfiles .= html_writer::tag('li', $linkhtml, array('class' => $type));
264 $outputimgs = $this->output->container($outputimgs, 'images');
266 if ($format !== "text") {
267 $outputfiles = html_writer::tag('ul', $outputfiles, array('class' => 'files'));
269 return $this->output->container($outputimgs . $outputfiles, 'attachments');
273 * Display a short summary of the example submission
275 * The passed submission object must define at least: id and title
277 * @param stdclass $data prepared by workshop::prepare_example_summary()
278 * @return string html to be echoed
280 public function example_summary(stdclass $summary) {
283 $o = ''; // output HTML code
286 $o .= $this->output->box_start('generalbox example-summary ' . $summary->status);
289 $o .= $this->output->container_start('example-title');
290 $url = new moodle_url('/mod/workshop/exsubmission.php',
291 array('cmid' => $this->page->context->instanceid, 'id' => $summary->example->id));
292 $o .= html_writer::link($url, format_string($summary->example->title), array('class'=>'title'));
294 // dirty hack to guess if the current user is example manager or not
295 if ($summary->example->weight == 1) {
296 $url = new moodle_url('/mod/workshop/exsubmission.php',
297 array('cmid' => $this->page->context->instanceid, 'id' => $summary->example->id, 'edit' => 'on'));
298 $o .= $this->output->action_icon($url, new pix_icon('i/edit', get_string('edit')));
300 $o .= $this->output->container_end();
303 if ($summary->status == 'notgraded') {
304 $o .= $this->output->container(get_string('nogradeyet', 'workshop'), 'example-info nograde');
306 $o .= $this->output->container(get_string('examplegrade', 'workshop' , $summary->gradeinfo), 'example-info grade');
310 $o .= $this->output->container($this->output->render($summary->btnform), 'example-actions');
312 // end of wrapping box
313 $o .= $this->output->box_end();
319 * Displays the example submission fulltext
321 * By default, this looks similar to a forum post.
323 * @param stdclass $example The example submission data
324 * @return string html to be echoed
326 public function example_full(stdclass $example) {
329 $o = ''; // output HTML code
330 $classes = 'submission-full example';
331 $o .= $this->output->container_start($classes);
332 $o .= $this->output->container_start('header');
333 $o .= $this->output->heading(format_string($example->title), 3, 'title');
334 $created = get_string('userdatecreated', 'workshop', userdate($example->timecreated));
335 $o .= $this->output->container($created, 'userdate created');
336 if ($example->timemodified > $example->timecreated) {
337 $modified = get_string('userdatemodified', 'workshop', userdate($example->timemodified));
338 $o .= $this->output->container($modified, 'userdate modified');
340 $o .= $this->output->container_end(); // end of header
342 $content = format_text($example->content, $example->contentformat);
343 $content = file_rewrite_pluginfile_urls($content, 'pluginfile.php', $this->page->context->id,
344 'workshop_submission_content', $example->id);
345 $o .= $this->output->container($content, 'content');
347 $o .= $this->submission_attachments($example);
349 $o .= $this->output->container_end(); // end of example-full
355 * Renders the user plannner tool
357 * @param array $plan as returned by {@link workshop::prepare_user_plan()}
358 * @return string html code to be displayed
360 public function user_plan(array $plan) {
362 throw new coding_exception('you must provide the prepared user plan to be rendered');
364 $table = new html_table();
365 $table->set_classes('userplan');
366 $table->head = array();
367 $table->colclasses = array();
368 $row = new html_table_row();
369 $row->set_classes('phasetasks');
370 foreach ($plan as $phasecode => $phase) {
371 $title = html_writer::tag('span', $phase->title);
373 foreach ($phase->actions as $action) {
374 switch ($action->type) {
376 $actions .= $this->output->action_icon($action->url, new pix_icon('i/marker', get_string('switchphase', 'workshop')));
380 if (!empty($actions)) {
381 $actions = $this->output->container($actions, 'actions');
383 $table->head[] = $this->output->container($title . $actions);
384 $classes = 'phase' . $phasecode;
385 if ($phase->active) {
386 $classes .= ' active';
388 $classes .= ' nonactive';
390 $table->colclasses[] = $classes;
391 $cell = new html_table_cell();
392 $cell->text = $this->user_plan_tasks($phase->tasks);
393 $row->cells[] = $cell;
395 $table->data = array($row);
397 return $this->output->table($table);
401 * Renders the tasks for the single phase in the user plan
403 * @param stdclass $tasks
404 * @return string html code
406 protected function user_plan_tasks(array $tasks) {
408 foreach ($tasks as $taskcode => $task) {
411 if ($task->completed === true) {
412 $classes .= ' completed';
413 } elseif ($task->completed === false) {
415 } elseif ($task->completed === 'info') {
418 if (is_null($task->link)) {
419 $title = $task->title;
421 $title = html_writer::link($task->link, $task->title);
423 $title = $this->output->container($title, 'title');
424 $details = $this->output->container($task->details, 'details');
425 $out .= html_writer::tag('li', $title . $details, array('class' => $classes));
428 $out = html_writer::tag('ul', $out, array('class' => 'tasks'));
434 * Renders the workshop grading report
436 * Grades must be already rounded to the set number of decimals or must be null (in which later case,
437 * the [[nullgrade]] string shall be displayed).
439 * @param stdclass $data prepared by {@link workshop::prepare_grading_report()}
440 * @param stdclass $options display options object with properties ->showauthornames ->showreviewernames ->sortby ->sorthow
441 * ->showsubmissiongrade ->showgradinggrade
442 * @return string html code
444 public function grading_report(stdclass $data, stdclass $options) {
445 $grades = $data->grades;
446 $userinfo = $data->userinfo;
448 if (empty($grades)) {
452 $table = new html_table();
453 $table->set_classes('grading-report');
455 $sortbyfirstname = $this->sortable_heading(get_string('firstname'), 'firstname', $options->sortby, $options->sorthow);
456 $sortbylastname = $this->sortable_heading(get_string('lastname'), 'lastname', $options->sortby, $options->sorthow);
457 if (self::fullname_format() == 'lf') {
458 $sortbyname = $sortbylastname . ' / ' . $sortbyfirstname;
460 $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
463 $table->head = array();
464 $table->head[] = $sortbyname;
465 $table->head[] = $this->sortable_heading(get_string('submission', 'workshop'), 'submissiontitle',
466 $options->sortby, $options->sorthow);
467 $table->head[] = $this->sortable_heading(get_string('receivedgrades', 'workshop'));
468 if ($options->showsubmissiongrade) {
469 $table->head[] = $this->sortable_heading(get_string('submissiongradeof', 'workshop', $data->maxgrade),
470 'submissiongrade', $options->sortby, $options->sorthow);
472 $table->head[] = $this->sortable_heading(get_string('givengrades', 'workshop'));
473 if ($options->showgradinggrade) {
474 $table->head[] = $this->sortable_heading(get_string('gradinggradeof', 'workshop', $data->maxgradinggrade),
475 'gradinggrade', $options->sortby, $options->sorthow);
478 $table->rowclasses = array();
479 $table->colclasses = array();
480 $table->data = array();
482 foreach ($grades as $participant) {
483 $numofreceived = count($participant->reviewedby);
484 $numofgiven = count($participant->reviewerof);
486 // compute the number of <tr> table rows needed to display this participant
487 if ($numofreceived > 0 and $numofgiven > 0) {
488 $numoftrs = workshop::lcm($numofreceived, $numofgiven);
489 $spanreceived = $numoftrs / $numofreceived;
490 $spangiven = $numoftrs / $numofgiven;
491 } elseif ($numofreceived == 0 and $numofgiven > 0) {
492 $numoftrs = $numofgiven;
493 $spanreceived = $numoftrs;
494 $spangiven = $numoftrs / $numofgiven;
495 } elseif ($numofreceived > 0 and $numofgiven == 0) {
496 $numoftrs = $numofreceived;
497 $spanreceived = $numoftrs / $numofreceived;
498 $spangiven = $numoftrs;
505 for ($tr = 0; $tr < $numoftrs; $tr++) {
506 $row = new html_table_row();
507 // column #1 - participant - spans over all rows
509 $cell = new html_table_cell();
510 $cell->text = $this->grading_report_participant($participant, $userinfo);
511 $cell->rowspan = $numoftrs;
512 $cell->add_class('participant');
513 $row->cells[] = $cell;
515 // column #2 - submission - spans over all rows
517 $cell = new html_table_cell();
518 $cell->text = $this->grading_report_submission($participant);
519 $cell->rowspan = $numoftrs;
520 $cell->add_class('submission');
521 $row->cells[] = $cell;
523 // column #3 - received grades
524 if ($tr % $spanreceived == 0) {
525 $idx = intval($tr / $spanreceived);
526 $assessment = self::array_nth($participant->reviewedby, $idx);
527 $cell = new html_table_cell();
528 $cell->text = $this->grading_report_assessment($assessment, $options->showreviewernames, $userinfo,
529 get_string('gradereceivedfrom', 'workshop'));
530 $cell->rowspan = $spanreceived;
531 $cell->add_class('receivedgrade');
532 if (is_null($assessment) or is_null($assessment->grade)) {
533 $cell->add_class('null');
535 $cell->add_class('notnull');
537 $row->cells[] = $cell;
539 // column #4 - total grade for submission
540 if ($options->showsubmissiongrade and $tr == 0) {
541 $cell = new html_table_cell();
542 $cell->text = $this->grading_report_grade($participant->submissiongrade, $participant->submissiongradeover);
543 $cell->rowspan = $numoftrs;
544 $cell->add_class('submissiongrade');
545 $row->cells[] = $cell;
547 // column #5 - given grades
548 if ($tr % $spangiven == 0) {
549 $idx = intval($tr / $spangiven);
550 $assessment = self::array_nth($participant->reviewerof, $idx);
551 $cell = new html_table_cell();
552 $cell->text = $this->grading_report_assessment($assessment, $options->showauthornames, $userinfo,
553 get_string('gradegivento', 'workshop'));
554 $cell->rowspan = $spangiven;
555 $cell->add_class('givengrade');
556 if (is_null($assessment) or is_null($assessment->grade)) {
557 $cell->add_class('null');
559 $cell->add_class('notnull');
561 $row->cells[] = $cell;
563 // column #6 - total grade for assessment
564 if ($options->showgradinggrade and $tr == 0) {
565 $cell = new html_table_cell();
566 $cell->text = $this->grading_report_grade($participant->gradinggrade);
567 $cell->rowspan = $numoftrs;
568 $cell->add_class('gradinggrade');
569 $row->cells[] = $cell;
572 $table->data[] = $row;
576 return $this->output->table($table);
580 * Renders a text with icons to sort by the given column
582 * This is intended for table headings.
584 * @param string $text The heading text
585 * @param string $sortid The column id used for sorting
586 * @param string $sortby Currently sorted by (column id)
587 * @param string $sorthow Currently sorted how (ASC|DESC)
591 protected function sortable_heading($text, $sortid=null, $sortby=null, $sorthow=null) {
594 $out = html_writer::tag('span', $text, array('class'=>'text'));
596 if (!is_null($sortid)) {
597 if ($sortby !== $sortid or $sorthow !== 'ASC') {
598 $url = new moodle_url($PAGE->url);
599 $url->params(array('sortby' => $sortid, 'sorthow' => 'ASC'));
600 $out .= $this->output->action_icon($url, new pix_icon('t/up', get_string('sortasc', 'workshop')), null, array('class' => 'sort asc'));
602 if ($sortby !== $sortid or $sorthow !== 'DESC') {
603 $url = new moodle_url($PAGE->url);
604 $url->params(array('sortby' => $sortid, 'sorthow' => 'DESC'));
605 $out .= $this->output->action_icon($url, new pix_icon('t/down', get_string('sortdesc', 'workshop')), null, array('class' => 'sort desc'));
612 * @param stdclass $participant
613 * @param array $userinfo
616 protected function grading_report_participant(stdclass $participant, array $userinfo) {
617 $userid = $participant->userid;
618 $out = $this->output->user_picture($userinfo[$userid], array('courseid' => $this->page->course->id, 'size' => 35));
619 $out .= html_writer::tag('span', fullname($userinfo[$userid]));
625 * @param stdclass $participant
628 protected function grading_report_submission(stdclass $participant) {
631 if (is_null($participant->submissionid)) {
632 $out = $this->output->container(get_string('nosubmissionfound', 'workshop'), 'info');
634 $url = new moodle_url('/mod/workshop/submission.php',
635 array('cmid' => $this->page->context->instanceid, 'id' => $participant->submissionid));
636 $out = html_writer::link($url, format_string($participant->submissiontitle), array('class'=>'title'));
643 * @todo Highlight the nulls
644 * @param stdclass|null $assessment
645 * @param bool $shownames
646 * @param string $separator between the grade and the reviewer/author
649 protected function grading_report_assessment($assessment, $shownames, array $userinfo, $separator) {
652 if (is_null($assessment)) {
653 return get_string('nullgrade', 'workshop');
656 $a->grade = is_null($assessment->grade) ? get_string('nullgrade', 'workshop') : $assessment->grade;
657 $a->gradinggrade = is_null($assessment->gradinggrade) ? get_string('nullgrade', 'workshop') : $assessment->gradinggrade;
658 $a->weight = $assessment->weight;
659 // grrr the following logic should really be handled by a future language pack feature
660 if (is_null($assessment->gradinggradeover)) {
661 if ($a->weight == 1) {
662 $grade = get_string('formatpeergrade', 'workshop', $a);
664 $grade = get_string('formatpeergradeweighted', 'workshop', $a);
667 $a->gradinggradeover = $assessment->gradinggradeover;
668 if ($a->weight == 1) {
669 $grade = get_string('formatpeergradeover', 'workshop', $a);
671 $grade = get_string('formatpeergradeoverweighted', 'workshop', $a);
674 $url = new moodle_url('/mod/workshop/assessment.php',
675 array('asid' => $assessment->assessmentid));
676 $grade = html_writer::link($url, $grade, array('class'=>'grade'));
679 $userid = $assessment->userid;
680 $name = $this->output->user_picture($userinfo[$userid], array('courseid' => $this->page->course->id, 'size' => 16));
681 $name .= html_writer::tag('span', fullname($userinfo[$userid]), array('class' => 'fullname'));
682 $name = $separator . html_writer::tag('span', $name, array('class' => 'user'));
687 return $this->output->container($grade . $name, 'assessmentdetails');
691 * Formats the aggreagated grades
693 protected function grading_report_grade($grade, $over=null) {
695 $a->grade = is_null($grade) ? get_string('nullgrade', 'workshop') : $grade;
696 if (is_null($over)) {
697 $text = get_string('formataggregatedgrade', 'workshop', $a);
699 $a->over = is_null($over) ? get_string('nullgrade', 'workshop') : $over;
700 $text = get_string('formataggregatedgradeover', 'workshop', $a);
705 ////////////////////////////////////////////////////////////////////////////
707 ////////////////////////////////////////////////////////////////////////////
710 * Helper function returning the n-th item of the array
713 * @param int $n from 0 to m, where m is th number of items in the array
714 * @return mixed the $n-th element of $a
716 protected static function array_nth(array $a, $n) {
717 $keys = array_keys($a);
718 if ($n < 0 or $n > count($keys) - 1) {
726 * Tries to guess the fullname format set at the site
728 * @return string fl|lf
730 protected static function fullname_format() {
731 $fake = new stdclass(); // fake user
732 $fake->lastname = 'LLLL';
733 $fake->firstname = 'FFFF';
734 $fullname = get_string('fullnamedisplay', '', $fake);
735 if (strpos($fullname, 'LLLL') < strpos($fullname, 'FFFF')) {