PdfExportControllerExtension::downloadpdf()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 15
rs 10
1
<?php
2
3
namespace CWP\PDFExport\Extensions;
4
5
use SilverStripe\Assets\Filesystem;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Core\Environment;
11
use SilverStripe\Core\Extension;
12
use SilverStripe\Versioned\Versioned;
13
use SilverStripe\Dev\Deprecation;
14
15
class PdfExportControllerExtension extends Extension
16
{
17
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
18
        'downloadpdf',
19
    ];
20
21
    /**
22
     * Serve the page rendered as PDF.
23
     *
24
     * @return HTTPResponse|false
25
     */
26
    public function downloadpdf()
27
    {
28
        if (!$this->owner->data()->config()->get('pdf_export')) {
29
            return false;
30
        }
31
32
        // We only allow producing live pdf. There is no way to secure the draft files.
33
        Versioned::set_stage(Versioned::LIVE);
34
35
        $path = $this->owner->data()->getPdfFilename();
36
        if (!file_exists($path)) {
37
            $this->owner->generatePDF();
38
        }
39
40
        return HTTPRequest::send_file(file_get_contents($path), basename($path), 'application/pdf');
41
    }
42
43
    /**
44
     * This will return either pdf_base_url from YML, CWP_SECURE_DOMAIN from _ss_environment, or blank. In that
45
     * order of importance.
46
     *
47
     * @return string
48
     */
49
    public function getPDFBaseURL()
50
    {
51
        // if base url YML is defined in YML, use that
52
        if ($this->owner->data()->config()->get('pdf_base_url')) {
53
            $pdfBaseUrl = $this->owner->data()->config()->get('pdf_base_url').'/';
54
            // otherwise, if we are CWP use the secure domain
55
        } elseif (Environment::getEnv('CWP_SECURE_DOMAIN')) {
56
            $pdfBaseUrl = Environment::getEnv('CWP_SECURE_DOMAIN') . '/';
57
            // or if neither, leave blank
58
        } else {
59
            $pdfBaseUrl = '';
60
        }
61
        return $pdfBaseUrl;
62
    }
63
64
    /**
65
     * Don't use the proxy if the pdf domain is the CWP secure domain or if we aren't on a CWP server
66
     *
67
     * @return string
68
     */
69
    public function getPDFProxy($pdfBaseUrl)
70
    {
71
        if (!Environment::getEnv('CWP_SECURE_DOMAIN')
72
            || $pdfBaseUrl == Environment::getEnv('CWP_SECURE_DOMAIN') . '/'
73
        ) {
74
            $proxy = '';
75
        } else {
76
            $proxy = ' --proxy ' . Environment::getEnv('SS_OUTBOUND_PROXY')
77
                . ':' . Environment::getEnv('SS_OUTBOUND_PROXY_PORT');
78
        }
79
        return $proxy;
80
    }
81
82
    /**
83
     * Render the page as PDF using wkhtmltopdf.
84
     *
85
     * @return HTTPResponse|false
86
     */
87
    public function generatePDF()
88
    {
89
        if (!$this->owner->data()->config()->get('pdf_export')) {
90
            return false;
91
        }
92
93
        $binaryPath = $this->owner->data()->config()->get('wkhtmltopdf_binary');
94
        if ($binaryPath) {
95
            Deprecation::notice('3.0', 'wkhtmltopdf_binary config is deprecated. '.
96
                'Use WKHTMLTOPDF_BINARY env var instead.');
97
        }
98
        if (!$binaryPath || !is_executable($binaryPath)) {
99
            if (Environment::getEnv('WKHTMLTOPDF_BINARY')
100
                && is_executable(Environment::getEnv('WKHTMLTOPDF_BINARY'))
101
            ) {
102
                $binaryPath = Environment::getEnv('WKHTMLTOPDF_BINARY');
103
            }
104
        }
105
106
        if (!$binaryPath) {
107
            user_error(
108
                'Neither WKHTMLTOPDF_BINARY nor ' . get_class($this->owner->data()) . '.wkhtmltopdf_binary are defined',
109
                E_USER_ERROR
110
            );
111
        }
112
113
        if (Versioned::get_reading_mode() == 'Stage.Stage') {
114
            user_error('Generating PDFs on draft is not supported', E_USER_ERROR);
115
        }
116
117
        set_time_limit(60);
118
119
        // prepare the paths
120
        $pdfFile = $this->owner->data()->getPdfFilename();
121
        $bodyFile = str_replace('.pdf', '_pdf.html', $pdfFile);
122
        $footerFile = str_replace('.pdf', '_pdffooter.html', $pdfFile);
123
124
        // make sure the work directory exists
125
        if (!file_exists(dirname($pdfFile))) {
126
            Filesystem::makeFolder(dirname($pdfFile));
127
        }
128
129
        //decide the domain to use in generation
130
        $pdfBaseUrl = $this->owner->getPDFBaseURL();
131
132
        // Force http protocol on CWP - fetching from localhost without using the proxy, SSL terminates on gateway.
133
        if (Environment::getEnv('CWP_ENVIRONMENT')) {
134
            Config::modify()->set(Director::class, 'alternate_protocol', 'http');
135
            //only set alternate protocol if CWP_SECURE_DOMAIN is defined OR pdf_base_url is
136
            if ($pdfBaseUrl) {
137
                Config::modify()->set(Director::class, 'alternate_base_url', 'http://' . $pdfBaseUrl);
138
            }
139
        }
140
141
        $bodyViewer = $this->owner->getViewer('pdf');
142
143
        // write the output of this page to HTML, ready for conversion to PDF
144
        file_put_contents($bodyFile, $bodyViewer->process($this->owner));
145
146
        // get the viewer for the current template with _pdffooter
147
        $footerViewer = $this->owner->getViewer('pdffooter');
148
149
        // write the output of the footer template to HTML, ready for conversion to PDF
150
        file_put_contents($footerFile, $footerViewer->process($this->owner));
151
152
        //decide what the proxy should look like
153
        $proxy = $this->owner->getPDFProxy($pdfBaseUrl);
154
155
        // finally, generate the PDF
156
        $command = $binaryPath . $proxy . ' --outline -B 40pt -L 20pt -R 20pt -T 20pt --encoding utf-8 '
157
            . '--orientation Portrait --disable-javascript --quiet --print-media-type ';
158
        $retVal = 0;
159
        $output = [];
160
        exec(
161
            $command . " --footer-html \"$footerFile\" \"$bodyFile\" \"$pdfFile\" &> /dev/stdout",
162
            $output,
163
            $retVal
164
        );
165
166
        // remove temporary file
167
        unlink($bodyFile);
168
        unlink($footerFile);
169
170
        // output any errors
171
        if ($retVal != 0) {
172
            user_error('wkhtmltopdf failed: ' . implode("\n", $output), E_USER_ERROR);
173
        }
174
175
        // serve the generated file
176
        return HTTPRequest::send_file(file_get_contents($pdfFile), basename($pdfFile), 'application/pdf');
177
    }
178
}
179