Total Complexity | 40 |
Total Lines | 331 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like BasePage_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use BasePage_Controller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
271 | class BasePage_Controller extends ContentController { |
||
272 | |||
273 | private static $allowed_actions = array( |
||
274 | 'downloadpdf', |
||
275 | 'SearchForm', |
||
276 | 'results' |
||
277 | ); |
||
278 | |||
279 | /** |
||
280 | * How many search results should be shown per-page? |
||
281 | * @var int |
||
282 | */ |
||
283 | public static $results_per_page = 10; |
||
284 | |||
285 | public static $search_index_class = 'SolrSearchIndex'; |
||
286 | |||
287 | /** |
||
288 | * If spelling suggestions for searches are given, enable |
||
289 | * suggested searches to be followed immediately |
||
290 | * |
||
291 | * @config |
||
292 | * @var bool |
||
293 | */ |
||
294 | private static $search_follow_suggestions = true; |
||
295 | |||
296 | /** |
||
297 | * Which classes should be queried when searching? |
||
298 | * @var array |
||
299 | */ |
||
300 | public static $classes_to_search = array( |
||
301 | array( |
||
302 | 'class' => 'Page', |
||
303 | 'includeSubclasses' => true |
||
304 | ) |
||
305 | ); |
||
306 | |||
307 | /** |
||
308 | * Serve the page rendered as PDF. |
||
309 | */ |
||
310 | public function downloadpdf() { |
||
311 | if(!Config::inst()->get('BasePage', 'pdf_export')) return false; |
||
312 | |||
313 | // We only allow producing live pdf. There is no way to secure the draft files. |
||
314 | Versioned::reading_stage('Live'); |
||
315 | |||
316 | $path = $this->dataRecord->getPdfFilename(); |
||
317 | if(!file_exists($path)) { |
||
318 | $this->generatePDF(); |
||
319 | } |
||
320 | |||
321 | return SS_HTTPRequest::send_file(file_get_contents($path), basename($path), 'application/pdf'); |
||
322 | } |
||
323 | |||
324 | /* |
||
325 | * This will return either pdf_base_url from YML, CWP_SECURE_DOMAIN |
||
326 | * from _ss_environment, or blank. In that order of importance. |
||
327 | */ |
||
328 | public function getPDFBaseURL() { |
||
340 | } |
||
341 | |||
342 | /* |
||
343 | * Don't use the proxy if the pdf domain is the CWP secure domain |
||
344 | * Or if we aren't on a CWP server |
||
345 | */ |
||
346 | public function getPDFProxy($pdf_base_url) { |
||
347 | if (!defined('CWP_SECURE_DOMAIN') || $pdf_base_url == CWP_SECURE_DOMAIN.'/') { |
||
348 | $proxy = ''; |
||
349 | } else { |
||
350 | $proxy = ' --proxy ' . SS_OUTBOUND_PROXY . ':' . SS_OUTBOUND_PROXY_PORT; |
||
351 | } |
||
352 | return $proxy; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Render the page as PDF using wkhtmltopdf. |
||
357 | */ |
||
358 | public function generatePDF() { |
||
359 | if(!Config::inst()->get('BasePage', 'pdf_export')) return false; |
||
360 | |||
361 | $binaryPath = Config::inst()->get('BasePage', 'wkhtmltopdf_binary'); |
||
362 | if(!$binaryPath || !is_executable($binaryPath)) { |
||
363 | if(defined('WKHTMLTOPDF_BINARY') && is_executable(WKHTMLTOPDF_BINARY)) { |
||
364 | $binaryPath = WKHTMLTOPDF_BINARY; |
||
365 | } |
||
366 | } |
||
367 | |||
368 | if(!$binaryPath) { |
||
369 | user_error('Neither WKHTMLTOPDF_BINARY nor BasePage.wkhtmltopdf_binary are defined', E_USER_ERROR); |
||
370 | } |
||
371 | |||
372 | if(Versioned::get_reading_mode() == 'Stage.Stage') { |
||
373 | user_error('Generating PDFs on draft is not supported', E_USER_ERROR); |
||
374 | } |
||
375 | |||
376 | set_time_limit(60); |
||
377 | |||
378 | // prepare the paths |
||
379 | $pdfFile = $this->dataRecord->getPdfFilename(); |
||
380 | $bodyFile = str_replace('.pdf', '_pdf.html', $pdfFile); |
||
381 | $footerFile = str_replace('.pdf', '_pdffooter.html', $pdfFile); |
||
382 | |||
383 | // make sure the work directory exists |
||
384 | if(!file_exists(dirname($pdfFile))) Filesystem::makeFolder(dirname($pdfFile)); |
||
385 | |||
386 | //decide the domain to use in generation |
||
387 | $pdf_base_url = $this->getPDFBaseURL(); |
||
388 | |||
389 | // Force http protocol on CWP - fetching from localhost without using the proxy, SSL terminates on gateway. |
||
390 | if (defined('CWP_ENVIRONMENT')) { |
||
391 | Config::inst()->nest(); |
||
392 | Config::inst()->update('Director', 'alternate_protocol', 'http'); |
||
393 | //only set alternate protocol if CWP_SECURE_DOMAIN is defined OR pdf_base_url is |
||
394 | if($pdf_base_url){ |
||
395 | Config::inst()->update('Director', 'alternate_base_url', 'http://'.$pdf_base_url); |
||
396 | } |
||
397 | } |
||
398 | |||
399 | $bodyViewer = $this->getViewer('pdf'); |
||
400 | |||
401 | // write the output of this page to HTML, ready for conversion to PDF |
||
402 | file_put_contents($bodyFile, $bodyViewer->process($this)); |
||
403 | |||
404 | // get the viewer for the current template with _pdffooter |
||
405 | $footerViewer = $this->getViewer('pdffooter'); |
||
406 | |||
407 | // write the output of the footer template to HTML, ready for conversion to PDF |
||
408 | file_put_contents($footerFile, $footerViewer->process($this)); |
||
409 | |||
410 | if (defined('CWP_ENVIRONMENT')) { |
||
411 | Config::inst()->unnest(); |
||
412 | } |
||
413 | |||
414 | //decide what the proxy should look like |
||
415 | $proxy = $this->getPDFProxy($pdf_base_url); |
||
416 | |||
417 | // finally, generate the PDF |
||
418 | $command = $binaryPath . $proxy . ' --outline -B 40pt -L 20pt -R 20pt -T 20pt --encoding utf-8 --orientation Portrait --disable-javascript --quiet --print-media-type '; |
||
419 | $retVal = 0; |
||
420 | $output = array(); |
||
421 | exec($command . " --footer-html \"$footerFile\" \"$bodyFile\" \"$pdfFile\" &> /dev/stdout", $output, $return_val); |
||
422 | |||
423 | // remove temporary file |
||
424 | unlink($bodyFile); |
||
425 | unlink($footerFile); |
||
426 | |||
427 | // output any errors |
||
428 | if($return_val != 0) { |
||
429 | user_error('wkhtmltopdf failed: ' . implode("\n", $output), E_USER_ERROR); |
||
430 | } |
||
431 | |||
432 | // serve the generated file |
||
433 | return SS_HTTPRequest::send_file(file_get_contents($pdfFile), basename($pdfFile), 'application/pdf'); |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Site search form |
||
438 | */ |
||
439 | public function SearchForm() |
||
440 | { |
||
441 | $searchText = $this->getRequest()->getVar('Search'); |
||
442 | |||
443 | $fields = new FieldList( |
||
444 | TextField::create('Search', false, $searchText) |
||
445 | ); |
||
446 | $actions = new FieldList( |
||
447 | new FormAction('results', _t('SearchForm.GO', 'Go')) |
||
448 | ); |
||
449 | |||
450 | $form = SearchForm::create($this, 'SearchForm', $fields, $actions); |
||
451 | $form->setFormAction('search/SearchForm'); |
||
452 | |||
453 | return $form; |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Get search form with _header suffix |
||
458 | * |
||
459 | * @return SearchForm |
||
460 | */ |
||
461 | public function HeaderSearchForm() |
||
462 | { |
||
463 | return $this->SearchForm()->setTemplate('SearchForm_header'); |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Process and render search results. |
||
468 | * |
||
469 | * @param array $data The raw request data submitted by user |
||
470 | * @param SearchForm $form The form instance that was submitted |
||
471 | * @param SS_HTTPRequest $request Request generated for this action |
||
472 | * @return HTMLText |
||
473 | */ |
||
474 | public function results($data, $form, $request) { |
||
475 | // Check parameters for terms, pagination, and if we should follow suggestions |
||
476 | $keywords = empty($data['Search']) ? '' : $data['Search']; |
||
477 | $start = isset($data['start']) ? $data['start'] : 0; |
||
478 | $suggestions = isset($data['suggestions']) |
||
479 | ? $data['suggestions'] |
||
480 | : $this->config()->search_follow_suggestions; |
||
481 | |||
482 | $results = CwpSearchEngine::create() |
||
483 | ->search( |
||
484 | $keywords, |
||
485 | $this->getClassesToSearch(), |
||
486 | $this->getSearchIndex(), |
||
487 | $this->getSearchPageSize(), |
||
488 | $start, |
||
489 | $suggestions |
||
490 | ); |
||
491 | |||
492 | // Customise content with these results |
||
493 | $properties = array( |
||
494 | 'MetaTitle' => _t('CWP_Search.MetaTitle', 'Search {keywords}', array('keywords' => $keywords)), |
||
495 | 'NoSearchResults' => _t('CWP_Search.NoResult', 'Sorry, your search query did not return any results.'), |
||
496 | 'EmptySearch' => _t('CWP_Search.EmptySearch', 'Search field empty, please enter your search query.'), |
||
497 | 'PdfLink' => '', |
||
498 | 'Title' => _t('SearchForm.SearchResults', 'Search Results'), |
||
499 | ); |
||
500 | $this->extend('updateSearchResults', $results, $properties); |
||
501 | |||
502 | // Customise page |
||
503 | $response = $this->customise($properties); |
||
504 | if($results) { |
||
505 | $response = $response |
||
506 | ->customise($results) |
||
507 | ->customise(array( 'Results' => $results->getResults() )); |
||
508 | } |
||
509 | |||
510 | // Render |
||
511 | $templates = $this->getResultsTemplate($request); |
||
512 | return $response->renderWith($templates); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * Select the template to render search results with |
||
517 | * |
||
518 | * @param SS_HTTPRequest $request |
||
519 | * @return array |
||
520 | */ |
||
521 | protected function getResultsTemplate($request) { |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * Provide scripts as needed by the *default* theme. |
||
534 | * Override this function if you are using a custom theme based on the *default*. |
||
535 | * |
||
536 | * @deprecated 1.6..2.0 Use "starter" theme instead |
||
537 | */ |
||
538 | public function getBaseScripts() { |
||
539 | $scripts = array(); |
||
540 | $this->extend('updateBaseScripts', $scripts); |
||
541 | return $scripts; |
||
542 | } |
||
543 | |||
544 | /** |
||
545 | * Provide stylesheets, as needed by the *default* theme assumed by this recipe. |
||
546 | * Override this function if you are using a custom theme based on the *default*. |
||
547 | * |
||
548 | * @deprecated 1.6..2.0 Use "starter" theme instead |
||
549 | */ |
||
550 | public function getBaseStyles() { |
||
551 | $styles = array(); |
||
552 | $this->extend('updateBaseStyles', $styles); |
||
553 | return $styles; |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * Provide current year. |
||
558 | */ |
||
559 | public function CurrentDatetime() { |
||
560 | return SS_Datetime::now(); |
||
561 | } |
||
562 | |||
563 | public function getRSSLink() { |
||
564 | } |
||
565 | |||
566 | /** |
||
567 | * Get the search index registered for this application |
||
568 | * |
||
569 | * @return CwpSearchIndex |
||
570 | */ |
||
571 | protected function getSearchIndex() |
||
572 | { |
||
573 | // Will be a service name in 2.0 and returned via injector |
||
574 | /** @var CwpSearchIndex $index */ |
||
575 | $index = null; |
||
576 | if (self::$search_index_class) { |
||
577 | $index = Object::singleton(self::$search_index_class); |
||
578 | } |
||
579 | return $index; |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * Gets the list of configured classes to search |
||
584 | * |
||
585 | * @return array |
||
586 | */ |
||
587 | protected function getClassesToSearch() |
||
588 | { |
||
589 | // Will be private static config in 2.0 |
||
590 | return self::$classes_to_search; |
||
591 | } |
||
592 | |||
593 | /** |
||
594 | * Get page size for search |
||
595 | * |
||
596 | * @return int |
||
597 | */ |
||
598 | protected function getSearchPageSize() |
||
602 | } |
||
603 | } |
||
604 |