Passed
Push — 1.11.x ( 88cb71...7bec48 )
by Julito
09:57
created

PDF::exportFromHtmlToDocumentsArea()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 26
nc 1
nop 4
dl 0
loc 39
rs 9.504
c 0
b 0
f 0
1
<?php
2
/* See license terms in /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
6
/**
7
 * Class PDF.
8
 */
9
class PDF
10
{
11
    /** @var mPDF */
12
    public $pdf;
13
    public $custom_header = [];
14
    public $custom_footer = [];
15
    public $params = [];
16
    public $template;
17
18
    /**
19
     * Creates the mPDF object.
20
     *
21
     * @param string   $pageFormat  format A4 A4-L see
22
     *                              http://mpdf1.com/manual/index.php?tid=184&searchstring=format
23
     * @param string   $orientation orientation "P" = Portrait "L" = Landscape
24
     * @param array    $params
25
     * @param Template $template
26
     */
27
    public function __construct(
28
        $pageFormat = 'A4',
29
        $orientation = 'P',
30
        $params = [],
31
        $template = null
32
    ) {
33
        $this->template = $template;
34
        /* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=mPDF */
35
        if (!in_array($orientation, ['P', 'L'])) {
36
            $orientation = 'P';
37
        }
38
        //left, right, top, bottom, margin_header, margin footer
39
40
        $params['left'] = isset($params['left']) ? $params['left'] : 15;
41
        $params['right'] = isset($params['right']) ? $params['right'] : 15;
42
        $params['top'] = isset($params['top']) ? $params['top'] : 30;
43
        $params['bottom'] = isset($params['bottom']) ? $params['bottom'] : 30;
44
        $params['margin_footer'] = isset($params['margin_footer']) ? $params['margin_footer'] : 8;
45
46
        $this->params['filename'] = isset($params['filename']) ? $params['filename'] : api_get_local_time();
47
        $this->params['pdf_title'] = isset($params['pdf_title']) ? $params['pdf_title'] : '';
48
        $this->params['course_info'] = isset($params['course_info']) ? $params['course_info'] : api_get_course_info();
49
        $this->params['session_info'] = isset($params['session_info']) ? $params['session_info'] : api_get_session_info(api_get_session_id());
50
        $this->params['course_code'] = isset($params['course_code']) ? $params['course_code'] : api_get_course_id();
51
        $this->params['add_signatures'] = isset($params['add_signatures']) ? $params['add_signatures'] : [];
52
        $this->params['show_real_course_teachers'] = isset($params['show_real_course_teachers']) ? $params['show_real_course_teachers'] : false;
53
        $this->params['student_info'] = isset($params['student_info']) ? $params['student_info'] : false;
54
        $this->params['show_grade_generated_date'] = isset($params['show_grade_generated_date']) ? $params['show_grade_generated_date'] : false;
55
        $this->params['show_teacher_as_myself'] = isset($params['show_teacher_as_myself']) ? $params['show_teacher_as_myself'] : true;
56
        $localTime = api_get_local_time();
57
        $this->params['pdf_date'] = isset($params['pdf_date']) ? $params['pdf_date'] : api_format_date($localTime, DATE_TIME_FORMAT_LONG);
58
        $this->params['pdf_date_only'] = isset($params['pdf_date']) ? $params['pdf_date'] : api_format_date($localTime, DATE_FORMAT_LONG);
59
60
        @$this->pdf = new mPDF(
61
            'UTF-8',
62
            $pageFormat,
63
            '',
64
            '',
65
            $params['left'],
66
            $params['right'],
67
            $params['top'],
68
            $params['bottom'],
69
            8,
70
            8,
71
            $orientation
72
        );
73
74
        $this->pdf->margin_footer = $params['margin_footer'];
75
76
        // Default value is 96 set in the mpdf library file config.php
77
        $value = api_get_configuration_value('pdf_img_dpi');
78
        if (!empty($value)) {
79
            $this->pdf->img_dpi = (int) $value;
80
        }
81
    }
82
83
    /**
84
     * Export the given HTML to PDF, using a global template.
85
     *
86
     * @uses \export/table_pdf.tpl
87
     *
88
     * @param string     $content
89
     * @param bool|false $saveToFile
90
     * @param bool|false $returnHtml
91
     * @param bool       $addDefaultCss (bootstrap/default/base.css)
92
     * @param array
93
     *
94
     * @return string
95
     */
96
    public function html_to_pdf_with_template(
97
        $content,
98
        $saveToFile = false,
99
        $returnHtml = false,
100
        $addDefaultCss = false,
101
        $extraRows = []
102
    ) {
103
        if (empty($this->template)) {
104
            $tpl = new Template('', false, false, false, false, true, false);
105
        } else {
106
            $tpl = $this->template;
107
        }
108
109
        // Assignments
110
        $tpl->assign('pdf_content', $content);
111
112
        // Showing only the current teacher/admin instead the all teacher list name see BT#4080
113
        $teacher_list = null;
114
        if (isset($this->params['show_real_course_teachers']) &&
115
            $this->params['show_real_course_teachers']
116
        ) {
117
            if (isset($this->params['session_info']) &&
118
                !empty($this->params['session_info'])
119
            ) {
120
                $teacher_list = SessionManager::getCoachesByCourseSessionToString(
121
                    $this->params['session_info']['id'],
122
                    $this->params['course_info']['real_id']
123
                );
124
            } else {
125
                $teacher_list = CourseManager::getTeacherListFromCourseCodeToString(
126
                    $this->params['course_code']
127
                );
128
            }
129
        } else {
130
            $user_info = api_get_user_info();
131
            if ($this->params['show_teacher_as_myself']) {
132
                $teacher_list = $user_info['complete_name'];
133
            }
134
        }
135
136
        $tpl->assign('pdf_course', $this->params['course_code']);
137
        $tpl->assign('pdf_course_info', $this->params['course_info']);
138
        $tpl->assign('pdf_session_info', $this->params['session_info']);
139
        $tpl->assign('pdf_date', $this->params['pdf_date']);
140
        $tpl->assign('pdf_date_only', $this->params['pdf_date_only']);
141
        $tpl->assign('pdf_teachers', $teacher_list);
142
        $tpl->assign('pdf_title', $this->params['pdf_title']);
143
        $tpl->assign('pdf_student_info', $this->params['student_info']);
144
        $tpl->assign('show_grade_generated_date', $this->params['show_grade_generated_date']);
145
        $tpl->assign('add_signatures', $this->params['add_signatures']);
146
        $tpl->assign('extra_rows', $extraRows);
147
148
        // Getting template
149
        $tableTemplate = $tpl->get_template('export/table_pdf.tpl');
150
        $html = $tpl->fetch($tableTemplate);
151
        $html = api_utf8_encode($html);
152
153
        if ($returnHtml) {
154
            return $html;
155
        }
156
157
        $css_file = api_get_path(SYS_CSS_PATH).'themes/'.$tpl->theme.'/print.css';
158
        if (!file_exists($css_file)) {
159
            $css_file = api_get_path(SYS_CSS_PATH).'/print.css';
160
        }
161
        $css = file_get_contents($css_file);
162
163
        self::content_to_pdf(
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::content_to_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
        self::/** @scrutinizer ignore-call */ 
164
              content_to_pdf(
Loading history...
164
            $html,
165
            $css,
166
            $this->params['filename'],
167
            $this->params['course_code'],
168
            'D',
169
            $saveToFile,
170
            null,
171
            $returnHtml,
172
            $addDefaultCss
173
        );
174
    }
175
176
    /**
177
     * Converts HTML files to PDF.
178
     *
179
     * @param mixed  $html_file_array could be an html file path or an array
180
     *                                with paths example:
181
     *                                /var/www/myfile.html or array('/myfile.html','myotherfile.html') or
182
     *                                even an indexed array with both 'title' and 'path' indexes
183
     *                                for each element like
184
     *                                array(
185
     *                                0 => array('title'=>'Hello','path'=>'file.html'),
186
     *                                1 => array('title'=>'Bye','path'=>'file2.html')
187
     *                                );
188
     * @param string $pdf_name        pdf name
189
     * @param string $course_code     (if you are using html that are located
190
     *                                in the document tool you must provide this)
191
     * @param bool   $print_title     add title
192
     * @param bool   $complete_style  show header and footer if true
193
     * @param bool   $addStyle
194
     * @param string $mainTitle
195
     *
196
     * @return false|null
197
     */
198
    public function html_to_pdf(
199
        $html_file_array,
200
        $pdf_name = '',
201
        $course_code = null,
202
        $print_title = false,
203
        $complete_style = true,
204
        $addStyle = true,
205
        $mainTitle = ''
206
    ) {
207
        if (empty($html_file_array)) {
208
            return false;
209
        }
210
211
        if (is_array($html_file_array)) {
212
            if (count($html_file_array) == 0) {
213
                return false;
214
            }
215
        } else {
216
            if (!file_exists($html_file_array)) {
217
                return false;
218
            }
219
            // Converting the string into an array
220
            $html_file_array = [$html_file_array];
221
        }
222
223
        if (!empty($course_code)) {
224
            $course_data = api_get_course_info($course_code);
225
        } else {
226
            $course_data = api_get_course_info();
227
        }
228
229
        // Clean styles and javascript document
230
        $clean_search = [
231
            '@<script[^>]*?>.*?</script>@si',
232
            '@<style[^>]*?>.*?</style>@si',
233
        ];
234
235
        // Formatting the pdf
236
        self::format_pdf($course_data, $complete_style);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::format_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
        self::/** @scrutinizer ignore-call */ 
237
              format_pdf($course_data, $complete_style);
Loading history...
237
238
        $counter = 1;
239
        foreach ($html_file_array as $file) {
240
            // Add a page break per file
241
            $page_break = '<pagebreak>';
242
            if ($counter == count($html_file_array)) {
243
                $page_break = '';
244
            }
245
246
            // if the array provided contained subarrays with 'title' entry,
247
            // then print the title in the PDF
248
            if (is_array($file) && isset($file['title'])) {
249
                $html_title = $file['title'];
250
                $file = $file['path'];
251
            } else {
252
                //we suppose we've only been sent a file path
253
                $html_title = basename($file);
254
            }
255
256
            $counter++;
257
258
            if (empty($file) && !empty($html_title)) {
259
                // this is a chapter, print title & skip the rest
260
                if ($counter === 2 && !empty($mainTitle)) {
261
                    $this->pdf->WriteHTML(
262
                        '<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
263
                    );
264
                }
265
                if ($print_title) {
266
                    $this->pdf->WriteHTML(
267
                        '<html><body><h3>'.$html_title.'</h3></body></html>'.$page_break
268
                    );
269
                }
270
                continue;
271
            } else {
272
                if ($counter === 2 && !empty($mainTitle)) {
273
                    $this->pdf->WriteHTML(
274
                        '<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
275
                    );
276
                }
277
            }
278
279
            if (!file_exists($file)) {
280
                continue;
281
            }
282
283
            if ($addStyle) {
284
                $basicStyles = [
285
                    api_get_path(SYS_PATH).'web/assets/bootstrap/dist/css/bootstrap.min.css',
286
                    api_get_path(SYS_PATH).'web/css/base.css',
287
                    api_get_path(SYS_PATH).'web/css/themes/'.api_get_visual_theme().'/default.css',
288
                    api_get_path(SYS_PATH).'web/css/themes/'.api_get_visual_theme().'/print.css',
289
                ];
290
                foreach ($basicStyles as $style) {
291
                    if (file_exists($style)) {
292
                        $cssContent = file_get_contents($style);
293
                        try {
294
                            @$this->pdf->WriteHTML($cssContent, 1);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for WriteHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

294
                            /** @scrutinizer ignore-unhandled */ @$this->pdf->WriteHTML($cssContent, 1);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
295
                        } catch (MpdfException $e) {
296
                            error_log($e);
297
                        }
298
                    }
299
                }
300
            }
301
302
            // it's not a chapter but the file exists, print its title
303
            if ($print_title) {
304
                @$this->pdf->WriteHTML(
305
                    '<html><body><h3>'.$html_title.'</h3></body></html>'
306
                );
307
            }
308
309
            $file_info = pathinfo($file);
310
            $extension = $file_info['extension'];
311
312
            if (in_array($extension, ['html', 'htm'])) {
313
                $dirName = $file_info['dirname'];
314
                $filename = $file_info['basename'];
315
                $filename = str_replace('_', ' ', $filename);
316
317
                if ($extension === 'html') {
318
                    $filename = basename($filename, '.html');
319
                } elseif ($extension === 'htm') {
320
                    $filename = basename($filename, '.htm');
321
                }
322
323
                $document_html = @file_get_contents($file);
324
                $document_html = preg_replace($clean_search, '', $document_html);
325
326
                //absolute path for frames.css //TODO: necessary?
327
                $absolute_css_path = api_get_path(WEB_CODE_PATH).'css/'.api_get_setting('stylesheets').'/frames.css';
328
                $document_html = str_replace('href="./css/frames.css"', $absolute_css_path, $document_html);
329
330
                if (!empty($course_data['path'])) {
331
                    $document_html = str_replace('../', '', $document_html);
332
333
                    // Fix app/upload links convert web to system paths
334
                    $document_html = str_replace(
335
                        api_get_path(WEB_UPLOAD_PATH),
336
                        api_get_path(SYS_UPLOAD_PATH),
337
                        $document_html
338
                    );
339
                }
340
341
                $document_html = self::fixImagesPaths($document_html, $course_data, $dirName);
342
343
                // The library mPDF expects UTF-8 encoded input data.
344
                api_set_encoding_html($document_html, 'UTF-8');
345
                // TODO: Maybe it is better idea the title to be passed through
346
                $title = api_get_title_html($document_html, 'UTF-8', 'UTF-8');
347
                // $_GET[] too, as it is done with file name.
348
                // At the moment the title is retrieved from the html document itself.
349
                if (empty($title)) {
350
                    $title = $filename; // Here file name is expected to contain ASCII symbols only.
351
                }
352
                if (!empty($document_html)) {
353
                    @$this->pdf->WriteHTML($document_html.$page_break);
354
                }
355
            } elseif (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
356
                // Images
357
                $image = Display::img($file);
358
                @$this->pdf->WriteHTML('<html><body>'.$image.'</body></html>'.$page_break);
359
            }
360
        }
361
        if (empty($pdf_name)) {
362
            $output_file = 'pdf_'.date('Y-m-d-his').'.pdf';
363
        } else {
364
            $pdf_name = api_replace_dangerous_char($pdf_name);
365
            $output_file = $pdf_name.'.pdf';
366
        }
367
        // F to save the pdf in a file
368
        @$this->pdf->Output($output_file, 'D');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for Output(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

368
        /** @scrutinizer ignore-unhandled */ @$this->pdf->Output($output_file, 'D');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
369
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
370
    }
371
372
    /**
373
     * Converts an html string to PDF.
374
     *
375
     * @param string $document_html  valid html
376
     * @param string $css            CSS content of a CSS file
377
     * @param string $pdf_name       pdf name
378
     * @param string $course_code    course code
379
     *                               (if you are using html that are located in the document tool you must provide this)
380
     * @param string $outputMode     the MPDF output mode can be:
381
     * @param bool   $saveInFile
382
     * @param string $fileToSave
383
     * @param bool   $returnHtml
384
     * @param bool   $addDefaultCss
385
     * @param bool   $completeHeader
386
     *
387
     * 'I' (print on standard output),
388
     * 'D' (download file) (this is the default value),
389
     * 'F' (save to local file) or
390
     * 'S' (return as a string)
391
     *
392
     * @throws MpdfException
393
     *
394
     * @return string Web path
395
     */
396
    public function content_to_pdf(
397
        $document_html,
398
        $css = '',
399
        $pdf_name = '',
400
        $course_code = null,
401
        $outputMode = 'D',
402
        $saveInFile = false,
403
        $fileToSave = null,
404
        $returnHtml = false,
405
        $addDefaultCss = false,
406
        $completeHeader = true
407
    ) {
408
        $urlAppend = api_get_configuration_value('url_append');
409
410
        if (empty($document_html)) {
411
            return false;
412
        }
413
414
        // clean styles and javascript document
415
        $clean_search = [
416
            '@<script[^>]*?>.*?</script>@si',
417
            '@<style[^>]*?>.*?</style>@siU',
418
        ];
419
420
        // Formatting the pdf
421
        $course_data = api_get_course_info($course_code);
422
        self::format_pdf($course_data, $completeHeader);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::format_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

422
        self::/** @scrutinizer ignore-call */ 
423
              format_pdf($course_data, $completeHeader);
Loading history...
423
        $document_html = preg_replace($clean_search, '', $document_html);
424
425
        //absolute path for frames.css //TODO: necessary?
426
        $absolute_css_path = api_get_path(WEB_CSS_PATH).api_get_setting('stylesheets').'/frames.css';
427
        $document_html = str_replace('href="./css/frames.css"', 'href="'.$absolute_css_path.'"', $document_html);
428
        $document_html = str_replace('../../', '', $document_html);
429
        $document_html = str_replace('../', '', $document_html);
430
        $document_html = str_replace(
431
            (empty($urlAppend) ? '' : $urlAppend.'/').'courses/'.$course_code.'/document/',
432
            '',
433
            $document_html
434
        );
435
436
        if (!empty($course_data['path'])) {
437
            $document_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document/';
438
439
            $doc = new DOMDocument();
440
            @$doc->loadHTML($document_html);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for loadHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

440
            /** @scrutinizer ignore-unhandled */ @$doc->loadHTML($document_html);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
441
442
            //Fixing only images @todo do the same thing with other elements
443
            $elements = $doc->getElementsByTagName('img');
444
            $protocol = api_get_protocol();
445
            $replaced = [];
446
            if (!empty($elements)) {
447
                foreach ($elements as $item) {
448
                    $old_src = $item->getAttribute('src');
449
450
                    if (in_array($old_src, $replaced)) {
451
                        continue;
452
                    }
453
454
                    if (strpos($old_src, $protocol) === false) {
455
                        if (strpos($old_src, '/main/default_course_document') === false) {
456
                            if (strpos($old_src, '/main/inc/lib/') === false &&
457
                                strpos($old_src, '/app/upload/') === false
458
                            ) {
459
                                $old_src_fixed = str_replace(
460
                                    api_get_path(REL_COURSE_PATH).$course_data['path'].'/document/',
461
                                    '',
462
                                    $old_src
463
                                );
464
                                $old_src_fixed = str_replace(
465
                                    'courses/'.$course_data['path'].'/document/',
466
                                    '',
467
                                    $old_src_fixed
468
                                );
469
                                $new_path = $document_path.$old_src_fixed;
470
                                $document_html = str_replace($old_src, $new_path, $document_html);
471
                                $replaced[] = $old_src;
472
                            }
473
                        }
474
                    }
475
                }
476
            }
477
        }
478
479
        // Use sys path to correct export images
480
        $document_html = str_replace(
481
            api_get_path(WEB_CODE_PATH).'img/',
482
            api_get_path(SYS_CODE_PATH).'img/',
483
            $document_html
484
        );
485
486
        $theme = api_get_visual_theme();
487
        $document_html = str_replace(
488
            api_get_path(WEB_CSS_PATH).'themes/'.$theme,
489
            api_get_path(SYS_PUBLIC_PATH).'css/themes/'.$theme,
490
            $document_html
491
        );
492
493
        $document_html = str_replace(api_get_path(WEB_ARCHIVE_PATH), api_get_path(SYS_ARCHIVE_PATH), $document_html);
494
495
        // The library mPDF expects UTF-8 encoded input data.
496
        api_set_encoding_html($document_html, 'UTF-8');
497
        // At the moment the title is retrieved from the html document itself.
498
        if ($returnHtml) {
499
            return "<style>$css</style>".$document_html;
500
        }
501
502
        if (!empty($css)) {
503
            try {
504
                @$this->pdf->WriteHTML($css, 1);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for WriteHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

504
                /** @scrutinizer ignore-unhandled */ @$this->pdf->WriteHTML($css, 1);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
505
            } catch (MpdfException $e) {
506
                error_log($e);
507
            }
508
        }
509
510
        $cssBootstrap = file_get_contents(api_get_path(SYS_PATH).'web/assets/bootstrap/dist/css/bootstrap.min.css');
511
        if ($addDefaultCss) {
512
            $css_file = api_get_path(SYS_CSS_PATH).'themes/'.$theme.'/print.css';
513
            if (!file_exists($css_file)) {
514
                $css_file = api_get_path(SYS_CSS_PATH).'/print.css';
515
            }
516
            $cssContent = file_get_contents($css_file);
517
            try {
518
                @$this->pdf->WriteHTML($cssBootstrap, 1);
519
                @$this->pdf->WriteHTML($cssContent, 1);
520
            } catch (MpdfException $e) {
521
                error_log($e);
522
            }
523
        }
524
525
        try {
526
            @$this->pdf->WriteHTML($document_html);
527
        } catch (MpdfException $e) {
528
            error_log($e);
529
        }
530
531
        if (empty($pdf_name)) {
532
            $output_file = 'pdf_'.date('Y-m-d-his').'.pdf';
533
        } else {
534
            $pdf_name = api_replace_dangerous_char($pdf_name);
535
            $output_file = $pdf_name.'.pdf';
536
        }
537
538
        if ($outputMode === 'F') {
539
            $output_file = api_get_path(SYS_ARCHIVE_PATH).$output_file;
540
        }
541
542
        if ($saveInFile) {
543
            $fileToSave = !empty($fileToSave) ? $fileToSave : api_get_path(SYS_ARCHIVE_PATH).uniqid();
544
            @$this->pdf->Output(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for Output(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

544
            /** @scrutinizer ignore-unhandled */ @$this->pdf->Output(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
545
                $fileToSave,
546
                $outputMode
547
            ); // F to save the pdf in a file
548
        } else {
549
            @$this->pdf->Output(
550
                $output_file,
551
                $outputMode
552
            ); // F to save the pdf in a file
553
        }
554
555
        if ($outputMode != 'F') {
556
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
557
        }
558
559
        return $output_file;
560
    }
561
562
    /**
563
     * Gets the watermark from the platform or a course.
564
     *
565
     * @param   string  course code (optional)
566
     * @param   mixed   web path of the watermark image, false if there is nothing to return
567
     *
568
     * @return string
569
     */
570
    public static function get_watermark($course_code = null)
571
    {
572
        $web_path = false;
573
        $urlId = api_get_current_access_url_id();
574
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') === 'true') {
575
            $course_info = api_get_course_info($course_code);
576
            // course path
577
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
578
            if (file_exists($store_path)) {
579
                $web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
580
            }
581
        } else {
582
            // course path
583
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
584
            if (file_exists($store_path)) {
585
                $web_path = api_get_path(WEB_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
586
            }
587
        }
588
589
        return $web_path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $web_path could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
590
    }
591
592
    /**
593
     * Deletes the watermark from the Platform or Course.
594
     *
595
     * @param string $course_code course code (optional)
596
     * @param   mixed   web path of the watermark image, false if there is nothing to return
597
     *
598
     * @return bool
599
     */
600
    public function delete_watermark($course_code = null)
601
    {
602
        $urlId = api_get_current_access_url_id();
603
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
604
            $course_info = api_get_course_info($course_code);
605
            // course path
606
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
607
        } else {
608
            // course path
609
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
610
        }
611
        if (file_exists($store_path)) {
612
            unlink($store_path);
613
614
            return true;
615
        }
616
617
        return false;
618
    }
619
620
    /**
621
     * Uploads the pdf watermark in the main/default_course_document directory or in the course directory.
622
     *
623
     * @param string $filename    filename
624
     * @param string $source_file path of the file
625
     * @param string $course_code
626
     *
627
     * @return mixed web path of the file if sucess, false otherwise
628
     */
629
    public function upload_watermark($filename, $source_file, $course_code = null)
630
    {
631
        $urlId = api_get_current_access_url_id();
632
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
633
            $course_info = api_get_course_info($course_code);
634
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path']; // course path
635
            $web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/pdf_watermark.png';
636
        } else {
637
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images'; // course path
638
            $web_path = api_get_path(WEB_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
639
        }
640
        $course_image = $store_path.'/'.$urlId.'_pdf_watermark.png';
641
642
        if (file_exists($course_image)) {
643
            @unlink($course_image);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

643
            /** @scrutinizer ignore-unhandled */ @unlink($course_image);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
644
        }
645
        $my_image = new Image($source_file);
646
        $result = $my_image->send_image($course_image, -1, 'png');
647
        if ($result) {
648
            $result = $web_path;
649
        }
650
651
        return $result;
652
    }
653
654
    /**
655
     * Returns the default header.
656
     */
657
    public function get_header($course_code = null)
658
    {
659
        /*$header = api_get_setting('pdf_export_watermark_text');
660
    	if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
661
            $header = api_get_course_setting('pdf_export_watermark_text');
662
        }
663
        return $header;*/
664
    }
665
666
    /**
667
     * Sets the PDF footer.
668
     */
669
    public function set_footer()
670
    {
671
        $this->pdf->defaultfooterfontsize = 12; // in pts
672
        $this->pdf->defaultfooterfontstyle = 'B'; // blank, B, I, or BI
673
        $this->pdf->defaultfooterline = 1; // 1 to include line below header/above footer
674
675
        $view = new Template('', false, false, false, true, false, false);
676
        $template = $view->get_template('export/pdf_footer.tpl');
677
        $footerHTML = $view->fetch($template);
678
679
        $this->pdf->SetHTMLFooter($footerHTML, 'E'); //Even pages
680
        $this->pdf->SetHTMLFooter($footerHTML, 'O'); //Odd pages
681
    }
682
683
    public function setCertificateFooter()
684
    {
685
        $this->pdf->defaultfooterfontsize = 12; // in pts
686
        $this->pdf->defaultfooterfontstyle = 'B'; // blank, B, I, or BI
687
        $this->pdf->defaultfooterline = 1; // 1 to include line below header/above footer
688
689
        $view = new Template('', false, false, false, true, false, false);
690
        $template = $view->get_template('export/pdf_certificate_footer.tpl');
691
        $footerHTML = $view->fetch($template);
692
693
        $this->pdf->SetHTMLFooter($footerHTML, 'E'); //Even pages
694
        $this->pdf->SetHTMLFooter($footerHTML, 'O'); //Odd pages
695
    }
696
697
    /**
698
     * Sets the PDF header.
699
     *
700
     * @param array $courseInfo
701
     */
702
    public function set_header($courseInfo)
703
    {
704
        $this->pdf->defaultheaderfontsize = 10; // in pts
705
        $this->pdf->defaultheaderfontstyle = 'BI'; // blank, B, I, or BI
706
        $this->pdf->defaultheaderline = 1; // 1 to include line below header/above footer
707
708
        $userId = api_get_user_id();
709
        if (!empty($courseInfo['code'])) {
710
            $teacher_list = CourseManager::get_teacher_list_from_course_code($courseInfo['code']);
711
712
            $teachers = '';
713
            if (!empty($teacher_list)) {
714
                foreach ($teacher_list as $teacher) {
715
                    if ($teacher['user_id'] != $userId) {
716
                        continue;
717
                    }
718
719
                    // Do not show the teacher list see BT#4080 only the current teacher name
720
                    $teachers = api_get_person_name($teacher['firstname'], $teacher['lastname']);
721
                }
722
            }
723
724
            $organization = ChamiloApi::getPlatformLogo('', [], true, true);
725
            // Use custom logo image.
726
            $pdfLogo = api_get_setting('pdf_logo_header');
727
            if ($pdfLogo === 'true') {
728
                $visualTheme = api_get_visual_theme();
729
                $img = api_get_path(SYS_CSS_PATH).'themes/'.$visualTheme.'/images/pdf_logo_header.png';
730
                if (file_exists($img)) {
731
                    $organization = "<img src='$img'>";
732
                }
733
            }
734
735
            $view = new Template('', false, false, false, true, false, false);
736
            $view->assign('teacher_name', $teachers);
737
            $view->assign('organization', $organization);
738
            $template = $view->get_template('export/pdf_header.tpl');
739
            $headerHTML = $view->fetch($template);
740
741
            $this->pdf->SetHTMLHeader($headerHTML, 'E');
742
            $this->pdf->SetHTMLHeader($headerHTML, 'O');
743
        }
744
    }
745
746
    /**
747
     * @param string $header html content
748
     */
749
    public function set_custom_header($header)
750
    {
751
        $this->custom_header = $header;
752
    }
753
754
    /**
755
     * @param array $footer html content
756
     */
757
    public function set_custom_footer($footer)
758
    {
759
        $this->custom_footer = $footer;
760
    }
761
762
    /**
763
     * Pre-formats a PDF to the right size and, if not stated otherwise, with
764
     * header, footer and watermark (if any).
765
     *
766
     * @param array $courseInfo General course information (to fill headers)
767
     * @param bool  $complete   Whether we want headers, footers and watermark or not
768
     */
769
    public function format_pdf($courseInfo, $complete = true)
770
    {
771
        $courseCode = null;
772
        if (!empty($courseInfo)) {
773
            $courseCode = $courseInfo['code'];
774
        }
775
776
        /*$pdf->SetAuthor('Documents Chamilo');
777
        $pdf->SetTitle('title');
778
        $pdf->SetSubject('Exported from Chamilo Documents');
779
        $pdf->SetKeywords('Chamilo Documents');
780
        */
781
        // TODO: To be read from the html document.
782
        $this->pdf->directionality = api_get_text_direction();
783
        $this->pdf->useOnlyCoreFonts = true;
784
        // Use different Odd/Even headers and footers and mirror margins
785
        $this->pdf->mirrorMargins = 1;
786
787
        // Add decoration only if not stated otherwise
788
        if ($complete) {
789
            // Adding watermark
790
            if (api_get_setting('pdf_export_watermark_enable') === 'true') {
791
                $watermark_file = self::get_watermark($courseCode);
792
                if ($watermark_file) {
793
                    //http://mpdf1.com/manual/index.php?tid=269&searchstring=watermark
794
                    $this->pdf->SetWatermarkImage($watermark_file);
795
                    $this->pdf->showWatermarkImage = true;
796
                } else {
797
                    $watermark_file = self::get_watermark(null);
798
799
                    if ($watermark_file) {
800
                        $this->pdf->SetWatermarkImage($watermark_file);
801
                        $this->pdf->showWatermarkImage = true;
802
                    }
803
                }
804
805
                $watermark_text = api_get_setting('pdf_export_watermark_text');
806
                if ($courseCode && 'true' === api_get_setting('pdf_export_watermark_by_course')) {
807
                    $courseWaterMark = api_get_course_setting('pdf_export_watermark_text');
808
                    if (!empty($courseWaterMark) && -1 != $courseWaterMark) {
809
                        $watermark_text = $courseWaterMark;
810
                    }
811
                }
812
813
                if (!empty($watermark_text)) {
814
                    $this->pdf->SetWatermarkText(
815
                        strcode2utf($watermark_text),
0 ignored issues
show
Bug introduced by
The function strcode2utf was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

815
                        /** @scrutinizer ignore-call */ 
816
                        strcode2utf($watermark_text),
Loading history...
816
                        0.1
817
                    );
818
                    $this->pdf->showWatermarkText = true;
819
                }
820
            }
821
822
            if (empty($this->custom_header)) {
823
                self::set_header($courseInfo);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::set_header() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

823
                self::/** @scrutinizer ignore-call */ 
824
                      set_header($courseInfo);
Loading history...
824
            } else {
825
                $this->pdf->SetHTMLHeader($this->custom_header, 'E');
826
                $this->pdf->SetHTMLHeader($this->custom_header, 'O');
827
            }
828
829
            if (empty($this->custom_footer)) {
830
                self::set_footer();
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::set_footer() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

830
                self::/** @scrutinizer ignore-call */ 
831
                      set_footer();
Loading history...
831
            } else {
832
                $this->pdf->SetHTMLFooter($this->custom_footer);
833
            }
834
        }
835
    }
836
837
    /**
838
     * Generate a PDF file from $html in SYS_APP_PATH.
839
     *
840
     * @param string $html     PDF content
841
     * @param string $fileName File name
842
     * @param string $dest     Optional. Directory to move file
843
     *
844
     * @return string The PDF path
845
     */
846
    public function exportFromHtmlToFile($html, $fileName, $dest = null)
847
    {
848
        $this->template = $this->template ?: new Template('', false, false, false, false, false, false);
849
850
        $cssFile = api_get_path(SYS_CSS_PATH).'themes/'.$this->template->theme.'/print.css';
851
852
        if (!file_exists($cssFile)) {
853
            $cssFile = api_get_path(SYS_CSS_PATH).'print.css';
854
        }
855
856
        $pdfPath = self::content_to_pdf(
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::content_to_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

856
        /** @scrutinizer ignore-call */ 
857
        $pdfPath = self::content_to_pdf(
Loading history...
857
            $html,
858
            file_get_contents($cssFile),
859
            $fileName,
860
            $this->params['course_code'],
861
            'F'
862
        );
863
864
        if (!$dest) {
865
            return $pdfPath;
866
        }
867
868
        move($pdfPath, $dest);
869
870
        return $dest.basename($pdfPath);
871
    }
872
873
    /**
874
     * Create a PDF and save it into the documents area.
875
     *
876
     * @param string $htmlContent HTML Content
877
     * @param string $fileName    The file name
878
     * @param int    $courseId    The course ID
879
     * @param int    $sessionId   Optional. The session ID
880
     */
881
    public function exportFromHtmlToDocumentsArea(
882
        $htmlContent,
883
        $fileName,
884
        $courseId,
885
        $sessionId = 0
886
    ) {
887
        $userId = api_get_user_id();
888
        $courseInfo = api_get_course_info_by_id($courseId);
889
        $courseDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document/';
890
891
        $docPath = $this->exportFromHtmlToFile(
892
            $htmlContent,
893
            $fileName,
894
            $courseDirectory
895
        );
896
897
        $docId = add_document(
898
            $courseInfo,
899
            str_replace($courseDirectory, '/', $docPath),
900
            'file',
901
            filesize($docPath),
902
            $fileName,
903
            null,
904
            false,
905
            true,
906
            null,
907
            $sessionId,
908
            $userId
909
        );
910
911
        api_item_property_update(
912
            $courseInfo,
913
            TOOL_DOCUMENT,
914
            $docId,
915
            'DocumentAdded',
916
            $userId
917
        );
918
919
        Display::addFlash(Display::return_message(get_lang('ItemAdded')));
920
    }
921
922
    /**
923
     * @param string $theme
924
     * @param bool   $fullPage
925
     *
926
     * @throws MpdfException
927
     */
928
    public function setBackground($theme, $fullPage = false)
929
    {
930
        $themeName = empty($theme) ? api_get_visual_theme() : $theme;
931
        $themeDir = \Template::getThemeDir($themeName);
932
        $customLetterhead = $themeDir.'images/letterhead.png';
933
        $urlPathLetterhead = api_get_path(SYS_CSS_PATH).$customLetterhead;
934
935
        if (file_exists($urlPathLetterhead)) {
936
            $urlWebLetterhead = 'url('.api_get_path(WEB_CSS_PATH).$customLetterhead.')';
937
        } else {
938
            $urlWebLetterhead = 'url('.api_get_path(WEB_CSS_PATH).'themes/chamilo/images/letterhead.png)';
939
        }
940
941
        if ($fullPage) {
942
            $this->pdf->SetDisplayMode('fullpage');
943
            $this->pdf->SetDefaultBodyCSS('background', $urlWebLetterhead);
944
            $this->pdf->SetDefaultBodyCSS('background-image-resize', '6');
945
        }
946
    }
947
948
    /**
949
     * Fix images source paths to allow export to pdf.
950
     *
951
     * @param string $documentHtml
952
     * @param string $dirName
953
     *
954
     * @return string
955
     */
956
    private static function fixImagesPaths($documentHtml, array $courseInfo, $dirName = '')
957
    {
958
        $documentHtml = '<?xml encoding="utf-8" ?>'.$documentHtml;
959
        $doc = new DOMDocument();
960
        @$doc->loadHTML($documentHtml);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for loadHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

960
        /** @scrutinizer ignore-unhandled */ @$doc->loadHTML($documentHtml);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
961
962
        $elements = $doc->getElementsByTagName('img');
963
964
        if (empty($elements)) {
965
            return $doc->saveHTML();
966
        }
967
968
        $protocol = api_get_protocol();
969
        $sysCodePath = api_get_path(SYS_CODE_PATH);
970
        $sysCoursePath = api_get_path(SYS_COURSE_PATH);
971
        $sysUploadPath = api_get_path(SYS_UPLOAD_PATH);
972
973
        $documentPath = $courseInfo ? $sysCoursePath.$courseInfo['path'].'/document/' : '';
974
975
        /** @var \DOMElement $element */
976
        foreach ($elements as $element) {
977
            $src = $element->getAttribute('src');
978
            $src = trim($src);
979
980
            if (strpos($src, $protocol) !== false) {
981
                continue;
982
            }
983
984
            // It's a reference to a file in the system, do not change it
985
            if (file_exists($src)) {
986
                continue;
987
            }
988
989
            if (strpos($src, '/main/default_course_document') === 0) {
990
                $element->setAttribute(
991
                    'src',
992
                    str_replace('/main/default_course_document', $sysCodePath.'default_course_document', $src)
993
                );
994
                continue;
995
            }
996
997
            if (strpos($src, '/main/img') === 0) {
998
                $element->setAttribute(
999
                    'src',
1000
                    str_replace('/main/img/', $sysCodePath.'img/', $src)
1001
                );
1002
                continue;
1003
            }
1004
1005
            if (strpos($src, '/app/upload/') === 0) {
1006
                $element->setAttribute(
1007
                    'src',
1008
                    str_replace('/app/upload/', $sysUploadPath, $src)
1009
                );
1010
                continue;
1011
            }
1012
1013
            if (empty($courseInfo)) {
1014
                continue;
1015
            }
1016
1017
            if (api_get_path(REL_PATH) != '/') {
1018
                $oldSrcFixed = str_replace(
1019
                    api_get_path(REL_PATH).'courses/'.$courseInfo['path'].'/document/',
1020
                    '',
1021
                    $src
1022
                );
1023
1024
                // Try with the dirname if exists
1025
                if ($oldSrcFixed == $src) {
1026
                    if (file_exists($dirName.'/'.$src)) {
1027
                        $documentPath = '';
1028
                        $oldSrcFixed = $dirName.'/'.$src;
1029
                    }
1030
                }
1031
            } else {
1032
                if (strpos($src, 'courses/'.$courseInfo['path'].'/document/') !== false) {
1033
                    $oldSrcFixed = str_replace('courses/'.$courseInfo['path'].'/document/', '', $src);
1034
                } else {
1035
                    // Try with the dirname if exists
1036
                    if (file_exists($dirName.'/'.$src)) {
1037
                        $documentPath = '';
1038
                        $oldSrcFixed = $dirName.'/'.$src;
1039
                    } else {
1040
                        $documentPath = '';
1041
                        $oldSrcFixed = $src;
1042
                    }
1043
                }
1044
            }
1045
1046
            $element->setAttribute('src', $documentPath.$oldSrcFixed);
1047
        }
1048
1049
        return $doc->saveHTML();
1050
    }
1051
}
1052