Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.36% covered (warning)
86.36%
38 / 44
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
pdf
86.36% covered (warning)
86.36%
38 / 44
0.00% covered (danger)
0.00%
0 / 2
10.25
0.00% covered (danger)
0.00%
0 / 1
 build
88.37% covered (warning)
88.37%
38 / 43
0.00% covered (danger)
0.00%
0 / 1
9.13
 send
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
16
17namespace local_transcript\local;
18
19/**
20 * Builds the PDF of an issued transcript, from the stored copy, never from live grades.
21 *
22 * @package    local_transcript
23 * @copyright  2026 DevLMS <contact@devlms.com>
24 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26class pdf {
27    /**
28     * The PDF as a string.
29     *
30     * @param \stdClass $record A local_transcript row.
31     * @return string
32     */
33    public static function build(\stdClass $record): string {
34        global $CFG;
35        require_once($CFG->libdir . '/pdflib.php');
36        $data = json_decode($record->payload, true);
37        $fmt = get_string('strftimedate', 'langconfig');
38        $url = registry::verify_url($record->code)->out(false);
39
40        $doc = new \pdf('P', 'mm', 'A4');
41        $doc->SetTitle(get_string('transcriptfor', 'local_transcript', $data['student']['fullname']));
42        $doc->SetCreator('local_transcript');
43        $doc->setPrintHeader(false);
44        $doc->setPrintFooter(false);
45        $doc->SetMargins(18, 18, 18);
46        $doc->AddPage();
47
48        $doc->SetFont('helvetica', 'B', 16);
49        $doc->Cell(0, 9, $data['institution'], 0, 1);
50        $doc->SetFont('helvetica', '', 11);
51        $doc->Cell(0, 6, get_string('transcriptfor', 'local_transcript', $data['student']['fullname']), 0, 1);
52        if ($data['student']['idnumber'] !== '') {
53            $doc->Cell(0, 6, get_string('studentid', 'local_transcript') . ': ' . $data['student']['idnumber'], 0, 1);
54        }
55        $doc->Cell(0, 6, get_string('issuedon', 'local_transcript') . ': ' . userdate($record->timecreated, $fmt), 0, 1);
56        $doc->Ln(4);
57
58        $grades = (bool) array_filter(array_column($data['courses'], 'grade'), fn($g) => $g !== '');
59        $rows = '';
60        foreach ($data['courses'] as $c) {
61            $rows .= '<tr><td width="' . ($grades ? '60%' : '75%') . '"><b>' . s($c['shortname']) . '</b> '
62                . s($c['fullname']) . '</td>'
63                . ($grades ? '<td width="15%">' . s($c['grade']) . '</td>' : '')
64                . '<td width="25%">' . ($c['completed'] ? userdate($c['completed'], $fmt) : '') . '</td></tr>';
65        }
66        if ($rows === '') {
67            $rows = '<tr><td>' . s(get_string('nocourses', 'local_transcript')) . '</td></tr>';
68        }
69        $head = '<tr style="background-color:#eeeeee;"><th width="' . ($grades ? '60%' : '75%') . '"><b>'
70            . s(get_string('course', 'local_transcript')) . '</b></th>'
71            . ($grades ? '<th width="15%"><b>' . s(get_string('grade', 'local_transcript')) . '</b></th>' : '')
72            . '<th width="25%"><b>' . s(get_string('completed', 'local_transcript')) . '</b></th></tr>';
73        $doc->SetFont('helvetica', '', 10);
74        $doc->writeHTML('<table cellpadding="4" border="0.2">' . $head . $rows . '</table>', true, false, true);
75
76        $doc->Ln(8);
77        $y = $doc->GetY();
78        $doc->write2DBarcode($url, 'QRCODE,M', 18, $y, 32, 32);
79        $doc->SetXY(56, $y + 4);
80        $doc->SetFont('helvetica', 'B', 12);
81        $doc->Cell(0, 7, get_string('code', 'local_transcript') . ': ' . $record->code, 0, 2);
82        $doc->SetFont('helvetica', '', 9);
83        $doc->MultiCell(0, 5, get_string('scan', 'local_transcript') . ' ' . $url, 0, 'L', false, 1, 56);
84        return $doc->Output('', 'S');
85    }
86
87    /**
88     * Send the PDF to the browser and stop.
89     *
90     * @param \stdClass $record
91     */
92    public static function send(\stdClass $record): void {
93        send_file(self::build($record), 'transcript-' . $record->code . '.pdf', 0, 0, true, true, 'application/pdf');
94    }
95}