Completed
Push — master ( f6a37f...25a2ff )
by
unknown
9s
created

BasePageController::generatePDF()   D

Complexity

Conditions 12
Paths 145

Size

Total Lines 83
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 40
nc 145
nop 0
dl 0
loc 83
rs 4.6933
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 Page;
0 ignored issues
show
Bug introduced by
The type Page was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use SilverStripe\Assets\Filesystem;
10
use SilverStripe\CMS\Controllers\ContentController;
11
use SilverStripe\CMS\Search\SearchForm;
12
use SilverStripe\Control\Director;
13
use SilverStripe\Control\HTTPRequest;
14
use SilverStripe\Core\Config\Config;
15
use SilverStripe\Core\Environment;
16
use SilverStripe\Core\Injector\Injector;
17
use SilverStripe\Forms\FieldList;
18
use SilverStripe\Forms\FormAction;
19
use SilverStripe\Forms\TextField;
20
use SilverStripe\ORM\FieldType\DBDatetime;
21
use SilverStripe\ORM\FieldType\DBHTMLText;
22
use SilverStripe\Versioned\Versioned;
23
24
class BasePageController extends ContentController
25
{
26
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
27
        'downloadpdf',
28
        'SearchForm',
29
        'results',
30
    ];
31
32
    /**
33
     * How many search results should be shown per-page?
34
     * @var int
35
     */
36
    public static $results_per_page = 10;
37
38
    public static $search_index_class = CwpSolrIndex::class;
39
40
    /**
41
     * If spelling suggestions for searches are given, enable
42
     * suggested searches to be followed immediately
43
     *
44
     * @config
45
     * @var bool
46
     */
47
    private static $search_follow_suggestions = true;
48
49
    /**
50
     * Which classes should be queried when searching?
51
     * @var array
52
     */
53
    public static $classes_to_search = [
54
        [
55
            'class' => 'Page',
56
            'includeSubclasses' => true,
57
        ]
58
    ];
59
60
    /**
61
     * Serve the page rendered as PDF.
62
     */
63
    public function downloadpdf()
64
    {
65
        if (!Config::inst()->get(BasePage::class, 'pdf_export')) {
66
            return false;
67
        }
68
69
        // We only allow producing live pdf. There is no way to secure the draft files.
70
        Versioned::set_stage(Versioned::LIVE);
71
72
        $path = $this->dataRecord->getPdfFilename();
73
        if (!file_exists($path)) {
74
            $this->generatePDF();
75
        }
76
77
        return HTTPRequest::send_file(file_get_contents($path), basename($path), 'application/pdf');
78
    }
79
80
    /*
81
    * This will return either pdf_base_url from YML, CWP_SECURE_DOMAIN
82
    * from _ss_environment, or blank. In that order of importance.
83
    */
84
    public function getPDFBaseURL()
85
    {
86
        //if base url YML is defined in YML, use that
87
        if (Config::inst()->get(BasePage::class, 'pdf_base_url')) {
88
            $pdfBaseUrl = Config::inst()->get(BasePage::class, 'pdf_base_url').'/';
89
            //otherwise, if we are CWP use the secure domain
90
        } elseif (Environment::getEnv('CWP_SECURE_DOMAIN')) {
91
            $pdfBaseUrl = Environment::getEnv('CWP_SECURE_DOMAIN') . '/';
92
            //or if neither, leave blank
93
        } else {
94
            $pdfBaseUrl = '';
95
        }
96
        return $pdfBaseUrl;
97
    }
98
99
    /*
100
    * Don't use the proxy if the pdf domain is the CWP secure domain
101
    * Or if we aren't on a CWP server
102
    */
103
    public function getPDFProxy($pdfBaseUrl)
104
    {
105
        if (!Environment::getEnv('CWP_SECURE_DOMAIN')
106
            || $pdfBaseUrl == Environment::getEnv('CWP_SECURE_DOMAIN') . '/'
107
        ) {
108
            $proxy = '';
109
        } else {
110
            $proxy = ' --proxy ' . Environment::getEnv('SS_OUTBOUND_PROXY')
111
                . ':' . Environment::getEnv('SS_OUTBOUND_PROXY_PORT');
112
        }
113
        return $proxy;
114
    }
115
116
    /**
117
     * Render the page as PDF using wkhtmltopdf.
118
     */
119
    public function generatePDF()
120
    {
121
        if (!Config::inst()->get(BasePage::class, 'pdf_export')) {
122
            return false;
123
        }
124
125
        $binaryPath = Config::inst()->get(BasePage::class, 'wkhtmltopdf_binary');
126
        if (!$binaryPath || !is_executable($binaryPath)) {
127
            if (Environment::getEnv('WKHTMLTOPDF_BINARY')
128
                && is_executable(Environment::getEnv('WKHTMLTOPDF_BINARY'))
129
            ) {
130
                $binaryPath = Environment::getEnv('WKHTMLTOPDF_BINARY');
131
            }
132
        }
133
134
        if (!$binaryPath) {
135
            user_error('Neither WKHTMLTOPDF_BINARY nor BasePage.wkhtmltopdf_binary are defined', E_USER_ERROR);
136
        }
137
138
        if (Versioned::get_reading_mode() == 'Stage.Stage') {
139
            user_error('Generating PDFs on draft is not supported', E_USER_ERROR);
140
        }
141
142
        set_time_limit(60);
143
144
        // prepare the paths
145
        $pdfFile = $this->dataRecord->getPdfFilename();
146
        $bodyFile = str_replace('.pdf', '_pdf.html', $pdfFile);
147
        $footerFile = str_replace('.pdf', '_pdffooter.html', $pdfFile);
148
149
        // make sure the work directory exists
150
        if (!file_exists(dirname($pdfFile))) {
151
            Filesystem::makeFolder(dirname($pdfFile));
152
        }
153
154
        //decide the domain to use in generation
155
        $pdfBaseUrl = $this->getPDFBaseURL();
156
157
        // Force http protocol on CWP - fetching from localhost without using the proxy, SSL terminates on gateway.
158
        if (Environment::getEnv('CWP_ENVIRONMENT')) {
159
            Config::modify()->set(Director::class, 'alternate_protocol', 'http');
160
            //only set alternate protocol if CWP_SECURE_DOMAIN is defined OR pdf_base_url is
161
            if ($pdfBaseUrl) {
162
                Config::modify()->set(Director::class, 'alternate_base_url', 'http://' . $pdfBaseUrl);
163
            }
164
        }
165
166
        $bodyViewer = $this->getViewer('pdf');
167
168
        // write the output of this page to HTML, ready for conversion to PDF
169
        file_put_contents($bodyFile, $bodyViewer->process($this));
170
171
        // get the viewer for the current template with _pdffooter
172
        $footerViewer = $this->getViewer('pdffooter');
173
174
        // write the output of the footer template to HTML, ready for conversion to PDF
175
        file_put_contents($footerFile, $footerViewer->process($this));
176
177
        //decide what the proxy should look like
178
        $proxy = $this->getPDFProxy($pdfBaseUrl);
179
180
        // finally, generate the PDF
181
        $command = $binaryPath . $proxy . ' --outline -B 40pt -L 20pt -R 20pt -T 20pt --encoding utf-8 '
182
            . '--orientation Portrait --disable-javascript --quiet --print-media-type ';
183
        $retVal = 0;
184
        $output = array();
185
        exec(
186
            $command . " --footer-html \"$footerFile\" \"$bodyFile\" \"$pdfFile\" &> /dev/stdout",
187
            $output,
188
            $retVal
189
        );
190
191
        // remove temporary file
192
        unlink($bodyFile);
193
        unlink($footerFile);
194
195
        // output any errors
196
        if ($retVal != 0) {
197
            user_error('wkhtmltopdf failed: ' . implode("\n", $output), E_USER_ERROR);
198
        }
199
200
        // serve the generated file
201
        return HTTPRequest::send_file(file_get_contents($pdfFile), basename($pdfFile), 'application/pdf');
202
    }
203
204
    /**
205
     * Site search form
206
     */
207
    public function SearchForm()
208
    {
209
        $searchText = $this->getRequest()->getVar('Search');
210
211
        $fields = FieldList::create(
212
            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

212
            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

212
            TextField::create('Search', /** @scrutinizer ignore-type */ false, $searchText)
Loading history...
213
        );
214
        $actions = FieldList::create(
215
            FormAction::create('results', _t('SilverStripe\\CMS\\Search\\SearchForm.GO', 'Go'))
216
        );
217
218
        $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

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