Completed
Pull Request — master (#13)
by Robbie
02:33
created

BasePageController::generatePDF()   F

Complexity

Conditions 13
Paths 289

Size

Total Lines 86
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 42
nc 289
nop 0
dl 0
loc 86
rs 3.7737
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace CWP\CWP\PageTypes;
4
5
use CWP\Core\Model\CwpSearchIndex;
6
use CWP\Core\Model\CwpSolrIndex;
7
use CWP\CWP\Search\CwpSearchEngine;
8
use SilverStripe\Assets\Filesystem;
9
use SilverStripe\CMS\Controllers\ContentController;
10
use SilverStripe\CMS\Search\SearchForm;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Control\HTTPRequest;
13
use SilverStripe\Core\Config\Config;
14
use SilverStripe\Core\Environment;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Forms\FieldList;
17
use SilverStripe\Forms\FormAction;
18
use SilverStripe\Forms\TextField;
19
use SilverStripe\ORM\FieldType\DBDatetime;
20
use SilverStripe\ORM\FieldType\DBHTMLText;
21
use SilverStripe\Versioned\Versioned;
22
23
class BasePageController extends ContentController
24
{
25
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
26
        'downloadpdf',
27
        'SearchForm',
28
        'results',
29
    ];
30
31
    /**
32
     * How many search results should be shown per-page?
33
     * @var int
34
     */
35
    public static $results_per_page = 10;
36
37
    public static $search_index_class = CwpSolrIndex::class;
38
39
    /**
40
     * If spelling suggestions for searches are given, enable
41
     * suggested searches to be followed immediately
42
     *
43
     * @config
44
     * @var bool
45
     */
46
    private static $search_follow_suggestions = true;
47
48
    /**
49
     * Which classes should be queried when searching?
50
     * @var array
51
     */
52
    public static $classes_to_search = [
53
        [
54
            'class' => 'Page',
55
            'includeSubclasses' => true,
56
        ]
57
    ];
58
59
    /**
60
     * Serve the page rendered as PDF.
61
     */
62
    public function downloadpdf()
63
    {
64
        if (!Config::inst()->get(BasePage::class, 'pdf_export')) {
65
            return false;
66
        }
67
68
        // We only allow producing live pdf. There is no way to secure the draft files.
69
        Versioned::set_stage(Versioned::LIVE);
70
71
        $path = $this->dataRecord->getPdfFilename();
72
        if (!file_exists($path)) {
73
            $this->generatePDF();
74
        }
75
76
        return HTTPRequest::send_file(file_get_contents($path), basename($path), 'application/pdf');
77
    }
78
79
    /*
80
    * This will return either pdf_base_url from YML, CWP_SECURE_DOMAIN
81
    * from _ss_environment, or blank. In that order of importance.
82
    */
83
    public function getPDFBaseURL()
84
    {
85
        //if base url YML is defined in YML, use that
86
        if (Config::inst()->get(BasePage::class, 'pdf_base_url')) {
87
            $pdfBaseUrl = Config::inst()->get(BasePage::class, 'pdf_base_url').'/';
88
            //otherwise, if we are CWP use the secure domain
89
        } elseif (Environment::getEnv('CWP_SECURE_DOMAIN')) {
90
            $pdfBaseUrl = Environment::getEnv('CWP_SECURE_DOMAIN') . '/';
91
            //or if neither, leave blank
92
        } else {
93
            $pdfBaseUrl = '';
94
        }
95
        return $pdfBaseUrl;
96
    }
97
98
    /*
99
    * Don't use the proxy if the pdf domain is the CWP secure domain
100
    * Or if we aren't on a CWP server
101
    */
102
    public function getPDFProxy($pdfBaseUrl)
103
    {
104
        if (!defined('CWP_SECURE_DOMAIN') || $pdfBaseUrl == CWP_SECURE_DOMAIN.'/') {
0 ignored issues
show
Bug introduced by
The constant CWP\CWP\PageTypes\CWP_SECURE_DOMAIN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
105
            $proxy = '';
106
        } else {
107
            $proxy = ' --proxy ' . SS_OUTBOUND_PROXY . ':' . SS_OUTBOUND_PROXY_PORT;
0 ignored issues
show
Bug introduced by
The constant CWP\CWP\PageTypes\SS_OUTBOUND_PROXY_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant CWP\CWP\PageTypes\SS_OUTBOUND_PROXY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
108
        }
109
        return $proxy;
110
    }
111
112
    /**
113
     * Render the page as PDF using wkhtmltopdf.
114
     */
115
    public function generatePDF()
116
    {
117
        if (!Config::inst()->get(BasePage::class, 'pdf_export')) {
118
            return false;
119
        }
120
121
        $binaryPath = Config::inst()->get(BasePage::class, 'wkhtmltopdf_binary');
122
        if (!$binaryPath || !is_executable($binaryPath)) {
123
            if (defined('WKHTMLTOPDF_BINARY') && is_executable(WKHTMLTOPDF_BINARY)) {
0 ignored issues
show
Bug introduced by
The constant CWP\CWP\PageTypes\WKHTMLTOPDF_BINARY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
124
                $binaryPath = WKHTMLTOPDF_BINARY;
125
            }
126
        }
127
128
        if (!$binaryPath) {
129
            user_error('Neither WKHTMLTOPDF_BINARY nor BasePage.wkhtmltopdf_binary are defined', E_USER_ERROR);
130
        }
131
132
        if (Versioned::get_reading_mode() == 'Stage.Stage') {
133
            user_error('Generating PDFs on draft is not supported', E_USER_ERROR);
134
        }
135
136
        set_time_limit(60);
137
138
        // prepare the paths
139
        $pdfFile = $this->dataRecord->getPdfFilename();
140
        $bodyFile = str_replace('.pdf', '_pdf.html', $pdfFile);
141
        $footerFile = str_replace('.pdf', '_pdffooter.html', $pdfFile);
142
143
        // make sure the work directory exists
144
        if (!file_exists(dirname($pdfFile))) {
145
            Filesystem::makeFolder(dirname($pdfFile));
146
        }
147
148
        //decide the domain to use in generation
149
        $pdfBaseUrl = $this->getPDFBaseURL();
150
151
        // Force http protocol on CWP - fetching from localhost without using the proxy, SSL terminates on gateway.
152
        if (defined('CWP_ENVIRONMENT')) {
153
            Config::inst()->nest();
154
            Config::inst()->update(Director::class, 'alternate_protocol', 'http');
0 ignored issues
show
Bug introduced by
The method update() does not exist on SilverStripe\Config\Coll...nfigCollectionInterface. It seems like you code against a sub-type of SilverStripe\Config\Coll...nfigCollectionInterface such as SilverStripe\Config\Coll...\MemoryConfigCollection. ( Ignorable by Annotation )

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

154
            Config::inst()->/** @scrutinizer ignore-call */ update(Director::class, 'alternate_protocol', 'http');
Loading history...
155
            //only set alternate protocol if CWP_SECURE_DOMAIN is defined OR pdf_base_url is
156
            if ($pdfBaseUrl) {
157
                Config::inst()->update(Director::class, 'alternate_base_url', 'http://'.$pdfBaseUrl);
158
            }
159
        }
160
161
        $bodyViewer = $this->getViewer('pdf');
162
163
        // write the output of this page to HTML, ready for conversion to PDF
164
        file_put_contents($bodyFile, $bodyViewer->process($this));
165
166
        // get the viewer for the current template with _pdffooter
167
        $footerViewer = $this->getViewer('pdffooter');
168
169
        // write the output of the footer template to HTML, ready for conversion to PDF
170
        file_put_contents($footerFile, $footerViewer->process($this));
171
172
        if (defined('CWP_ENVIRONMENT')) {
173
            Config::inst()->unnest();
0 ignored issues
show
Bug introduced by
The method unnest() does not exist on SilverStripe\Config\Coll...nfigCollectionInterface. ( Ignorable by Annotation )

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

173
            Config::inst()->/** @scrutinizer ignore-call */ unnest();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
174
        }
175
176
        //decide what the proxy should look like
177
        $proxy = $this->getPDFProxy($pdfBaseUrl);
178
179
        // finally, generate the PDF
180
        $command = $binaryPath . $proxy . ' --outline -B 40pt -L 20pt -R 20pt -T 20pt --encoding utf-8 '
181
            . '--orientation Portrait --disable-javascript --quiet --print-media-type ';
182
        $retVal = 0;
183
        $output = array();
184
        exec(
185
            $command . " --footer-html \"$footerFile\" \"$bodyFile\" \"$pdfFile\" &> /dev/stdout",
186
            $output,
187
            $retVal
188
        );
189
190
        // remove temporary file
191
        unlink($bodyFile);
192
        unlink($footerFile);
193
194
        // output any errors
195
        if ($retVal != 0) {
196
            user_error('wkhtmltopdf failed: ' . implode("\n", $output), E_USER_ERROR);
197
        }
198
199
        // serve the generated file
200
        return HTTPRequest::send_file(file_get_contents($pdfFile), basename($pdfFile), 'application/pdf');
201
    }
202
203
    /**
204
     * Site search form
205
     */
206
    public function SearchForm()
207
    {
208
        $searchText = $this->getRequest()->getVar('Search');
209
210
        $fields = FieldList::create(
211
            TextField::create('Search', false, $searchText)
0 ignored issues
show
Bug introduced by
'Search' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

211
            TextField::create(/** @scrutinizer ignore-type */ 'Search', false, $searchText)
Loading history...
Bug introduced by
false of type false is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

211
            TextField::create('Search', /** @scrutinizer ignore-type */ false, $searchText)
Loading history...
212
        );
213
        $actions = FieldList::create(
214
            FormAction::create('results', _t('SearchForm.GO', 'Go'))
215
        );
216
217
        $form = SearchForm::create($this, SearchForm::class, $fields, $actions);
0 ignored issues
show
Bug introduced by
$this of type CWP\CWP\PageTypes\BasePageController is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

217
        $form = SearchForm::create(/** @scrutinizer ignore-type */ $this, SearchForm::class, $fields, $actions);
Loading history...
218
        $form->setFormAction('search/SearchForm');
219
220
        return $form;
221
    }
222
223
    /**
224
     * Get search form with _header suffix
225
     *
226
     * @return SearchForm
227
     */
228
    public function HeaderSearchForm()
229
    {
230
        return $this->SearchForm()->setTemplate('SearchForm_header');
231
    }
232
233
    /**
234
     * Process and render search results.
235
     *
236
     * @param array $data The raw request data submitted by user
237
     * @param SearchForm $form The form instance that was submitted
238
     * @param HTTPRequest $request Request generated for this action
239
     * @return DBHTMLText
240
     */
241
    public function results($data, $form, $request)
242
    {
243
        // Check parameters for terms, pagination, and if we should follow suggestions
244
        $keywords = empty($data['Search']) ? '' : $data['Search'];
245
        $start = isset($data['start']) ? $data['start'] : 0;
246
        $suggestions = isset($data['suggestions'])
247
            ? $data['suggestions']
248
            : $this->config()->search_follow_suggestions;
249
250
        $results = CwpSearchEngine::create()
251
            ->search(
252
                $keywords,
253
                $this->getClassesToSearch(),
254
                $this->getSearchIndex(),
255
                $this->getSearchPageSize(),
256
                $start,
257
                $suggestions
258
            );
259
260
        // Customise content with these results
261
        $properties = array(
262
            'MetaTitle' => _t('CWP_Search.MetaTitle', 'Search {keywords}', array('keywords' => $keywords)),
263
            'NoSearchResults' => _t('CWP_Search.NoResult', 'Sorry, your search query did not return any results.'),
264
            'EmptySearch' => _t('CWP_Search.EmptySearch', 'Search field empty, please enter your search query.'),
265
            'PdfLink' => '',
266
            'Title' => _t('SearchForm.SearchResults', 'Search Results'),
267
        );
268
        $this->extend('updateSearchResults', $results, $properties);
269
270
        // Customise page
271
        $response = $this->customise($properties);
272
        if ($results) {
273
            $response = $response
274
                ->customise($results)
275
                ->customise(array( 'Results' => $results->getResults() ));
276
        }
277
278
        // Render
279
        $templates = $this->getResultsTemplate($request);
280
        return $response->renderWith($templates);
281
    }
282
283
    /**
284
     * Select the template to render search results with
285
     *
286
     * @param HTTPRequest $request
287
     * @return array
288
     */
289
    protected function getResultsTemplate($request)
290
    {
291
        $templates = array('Page_results', 'Page');
292
        if ($request->getVar('format') == 'rss') {
293
            array_unshift($templates, 'Page_results_rss');
294
        }
295
        if ($request->getVar('format') == 'atom') {
296
            array_unshift($templates, 'Page_results_atom');
297
        }
298
        return $templates;
299
    }
300
301
    /**
302
     * Provide scripts as needed by the *default* theme.
303
     * Override this function if you are using a custom theme based on the *default*.
304
     *
305
     * @deprecated 1.6..2.0 Use "starter" theme instead
306
     */
307
    public function getBaseScripts()
308
    {
309
        $scripts = array();
310
        $this->extend('updateBaseScripts', $scripts);
311
        return $scripts;
312
    }
313
314
    /**
315
     * Provide stylesheets, as needed by the *default* theme assumed by this recipe.
316
     * Override this function if you are using a custom theme based on the *default*.
317
     *
318
     * @deprecated 1.6..2.0 Use "starter" theme instead
319
     */
320
    public function getBaseStyles()
321
    {
322
        $styles = array();
323
        $this->extend('updateBaseStyles', $styles);
324
        return $styles;
325
    }
326
327
    /**
328
     * Provide current year.
329
     */
330
    public function CurrentDatetime()
331
    {
332
        return DBDatetime::now();
333
    }
334
335
    public function getRSSLink()
336
    {
337
    }
338
339
    /**
340
     * Get the search index registered for this application
341
     *
342
     * @return CwpSearchIndex
343
     */
344
    protected function getSearchIndex()
345
    {
346
        // Will be a service name in 2.0 and returned via injector
347
        /** @var CwpSearchIndex $index */
348
        $index = null;
349
        if (self::$search_index_class) {
350
            $index = Injector::inst()->get(self::$search_index_class);
351
        }
352
        return $index;
353
    }
354
355
    /**
356
     * Gets the list of configured classes to search
357
     *
358
     * @return array
359
     */
360
    protected function getClassesToSearch()
361
    {
362
        // Will be private static config in 2.0
363
        return self::$classes_to_search;
364
    }
365
366
    /**
367
     * Get page size for search
368
     *
369
     * @return int
370
     */
371
    protected function getSearchPageSize()
372
    {
373
        // Will be private static config in 2.0
374
        return self::$results_per_page;
375
    }
376
}
377