class WkhtmltopdfController in wkhtmltopdf 2.0.x
Same name and namespace in other branches
- 8 src/Controller/WkhtmltopdfController.php \Drupal\wkhtmltopdf\Controller\WkhtmltopdfController
A Controller to generate PDFs and return them as a binary reponse.
Hierarchy
- class \Drupal\Core\Controller\ControllerBase implements ContainerInjectionInterface uses LoggerChannelTrait, MessengerTrait, RedirectDestinationTrait, StringTranslationTrait
- class \Drupal\wkhtmltopdf\Controller\WkhtmltopdfController
Expanded class hierarchy of WkhtmltopdfController
File
- src/
Controller/ WkhtmltopdfController.php, line 18
Namespace
Drupal\wkhtmltopdf\ControllerView source
class WkhtmltopdfController extends ControllerBase {
/**
* @var Drupal\Core\Config\ImmutableConfig
*
* The wkhtmltopdf module config
*/
public $settings;
/**
* @var Symfony\Component\HttpFoundation\Request
*
* The current request object
*/
public $request;
/**
* @var Drupal\Core\File\FileSystemInterface
*/
public $filesystem;
/**
* @var Drupal\Core\Logger\LoggerChannelInterface
*
* The logger for the wkhtmltopdf module
*/
public $logger;
/**
* {@inheritdoc}
*/
public function __construct(ConfigFactory $config, Request $request, FileSystemInterface $filesystem, LoggerChannelInterface $logger) {
$this->settings = $config
->get('wkhtmltopdf.settings');
$this->request = $request;
$this->filesystem = $filesystem;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container
->get('config.factory'), $container
->get('request_stack')
->getCurrentRequest(), $container
->get('file_system'), $container
->get('logger.factory')
->get('wkhtmltopdf'));
}
/**
* Generate pdf file.
*
* @return Symfony\Component\HttpFoundation\Response
* The generated PDF with an appropriate Content-Type header
*/
public function generatePdf() {
$url = $this->request->query
->get('url');
$drupal_file_path = $this
->createFile($url);
$filename = $this
->generateFilename($url);
$file_response = $this
->getFileResponse($drupal_file_path, $filename);
return $file_response;
}
/**
* Creates a file response object to return to the client
*
* @param string $drupal_file_path
* The drupal filepath in public://
*
* @param string $filename
* The filename for http content disposition header
*
* @return Symfony\Component\HttpFoundation\BinaryFileResponse
* The file response to return to the client
*/
protected function getFileResponse($drupal_file_path, $filename) {
$force_download = $this->settings
->get('wkhtmltopdf_download');
$headers = [
'Content-Type' => 'application/pdf',
];
if ($force_download) {
$headers['Content-Disposition'] = 'attachment;filename="' . $filename . '"';
}
$fileResponse = new BinaryFileResponse($drupal_file_path, 200, $headers, true);
// We don't want these generated files to hang around forever
$fileResponse
->deleteFileAfterSend(TRUE);
return $fileResponse;
}
/**
* Actually sends the shell command, and returns after the pdf file has been created
*
* @param string $url
* The absolute url that we're generating a pdf of
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* If the wkhtmltopdf subdirectory can't be created in the public filesystem, just give up
*
* @return string
* The drupal filepath in public://
*/
protected function createFile($url) {
$wkhtmltopdf_directory = 'public://wkhtmltopdf';
if (!$this->filesystem
->prepareDirectory($wkhtmltopdf_directory, FileSystemInterface::CREATE_DIRECTORY)) {
// folder could not be created
$this->logger
->error('wkhtmltopdf subdirectory could not be created in the public filesystem');
throw new HttpException(500, 'An error occurred generating the pdf.');
}
// create a likely to be unique filename for generating the pdf
$filename = uniqid('wkhtmltopdf_', TRUE) . '.pdf';
$drupal_file_path = $wkhtmltopdf_directory . '/' . $filename;
$command = $this
->buildShellCommand($url, $drupal_file_path);
$lock = \Drupal::lock();
if ($lock
->acquire(__FILE__) !== FALSE) {
shell_exec($command);
$lock
->release(__FILE__);
}
else {
while ($lock
->acquire(__FILE__) === FALSE) {
$lock
->wait(__FILE__, 3);
}
if ($lock
->acquire(__FILE__) !== FALSE) {
shell_exec($command);
$lock
->release(__FILE__);
}
}
return $drupal_file_path;
}
/**
* Builds the shell command including parameters
*
* @param string $url
* The absolute url that we're generating a pdf of
*
* @param string $drupal_file_path
* The drupal file path where the pdf will be generated
*
* @return string
* The full shell command including parameters
*/
protected function buildShellCommand($url, $drupal_file_path) {
$binary = $this->settings
->get('wkhtmltopdf_bin');
if (empty($binary)) {
$this->logger
->error('wkhtmltopdf binary path has not been set in the configuration');
throw new HttpException(500, 'An error occurred generating the pdf.');
}
$physical_file_path = $this->filesystem
->realpath($drupal_file_path);
$arguments = $this
->getCommandLineArguments();
$command = $binary;
foreach ($arguments as $argument => $argument_value) {
if (empty($argument_value)) {
$command .= ' --' . $argument;
}
else {
$command .= ' --' . $argument . ' ' . escapeshellarg($argument_value);
}
}
$command .= ' ' . $url . ' ' . $physical_file_path;
return $command;
}
/**
* Gets the command line arguments for the wkhtmltopdf shell command based on the settings
*
* @return
* An associative array where argument name is the array key, and the argument value is the array value.
* Arguments without values use an empty string for value
*/
protected function getCommandLineArguments() {
$arguments = [];
if ($this->settings
->get('wkhtmltopdf_zoom')) {
$arguments['zoom'] = $this->settings
->get('wkhtmltopdf_zoom');
}
if ($this->settings
->get('wkhtmltopdf_printmedia')) {
$arguments['print-media-type'] = '';
}
$this
->validateArguments($arguments);
return $arguments;
}
/**
* Validates the command line arguments are valid
*
* @param array $arguments
* An associative array where argument name is the array key, and the argument value is the array value.
*/
private function validateArguments($arguments) {
foreach ($arguments as $argument_name => $argument_value) {
$this
->validateArgument($argument_name, $argument_value);
}
}
private function validateArgument($argument_name, $argument_value) {
$validArguments = [
'allow',
'background',
'bypass-proxy-for',
'cache-dir',
'checkbox-checked-svg',
'checkbox-svg',
'collate',
'cookie',
'cookie-jar',
'copies',
'custom-header',
'custom-header-propagation',
'debug-javascript',
'default-header',
'disable-dotted-lines',
'disable-external-links',
'disable-forms',
'disable-internal-links',
'disable-javascript',
'disable-local-file-access',
'disable-plugins',
'disable-smart-shrinking',
'disable-toc-back-links',
'disable-toc-links',
'dpi',
'dump-default-toc-xsl',
'dump-outline',
'enable-external-links',
'enable-forms',
'enable-internal-links',
'enable-javascript',
'enable-local-file-access',
'enable-plugins',
'enable-smart-shrinking',
'enable-toc-back-links',
'encoding',
'exclude-from-outline',
'extended-help',
'footer-center',
'footer-font-name',
'footer-font-size',
'footer-html',
'footer-left',
'footer-line',
'footer-right',
'footer-spacing',
'grayscale',
'header-center',
'header-font-name',
'header-font-size',
'header-html',
'header-left',
'header-line',
'header-right',
'header-spacing',
'help',
'htmldoc',
'image-dpi',
'image-quality',
'images',
'include-in-outline',
'javascript-delay',
'keep-relative-links',
'license',
'load-error-handling',
'load-media-error-handling',
'log-level',
'lowquality',
'manpage',
'margin-bottom',
'margin-left',
'margin-right',
'margin-top',
'minimum-font-size',
'no-background',
'no-collate',
'no-custom-header-propagation',
'no-debug-javascript',
'no-footer-line',
'no-header-line',
'no-images',
'no-outline',
'no-pdf-compression',
'no-print-media-type',
'no-stop-slow-scripts',
'orientation',
'outline',
'outline-depth',
'page-height',
'page-offset',
'page-size',
'page-width',
'password',
'post',
'post-file',
'print-media-type',
'proxy',
'proxy-hostname-lookup',
'quiet',
'radiobutton-checked-svg',
'radiobutton-svg',
'read-args-from-stdin',
'readme',
'replace',
'resolve-relative-links',
'run-script',
'ssl-crt-path',
'ssl-key-password',
'ssl-key-path',
'stop-slow-scripts',
'title',
'toc-header-text',
'toc-level-indentation',
'toc-text-size-shrink',
'top',
'username',
'user-style-sheet',
'use-xserver',
'version',
'viewport-size',
'window-status',
'xsl-style-sheet',
'zoom',
];
if (!in_array($argument_name, $validArguments, TRUE)) {
$this->logger
->error('An invalid argument was passed to the wkhtmltopdf binary: %argument_name', [
'%argument_name' => $argument_name,
]);
throw new HttpException(500, 'An error occurred generating the pdf.');
}
}
/**
* Generates a filename based on the url
*
* @param string $url
* The absolute url that we're generating a pdf of
*
* @return string
* a generated filename that's no longer than 255 chars containing only alphanumeric characters
*/
protected function generateFilename($url) {
$filename = trim(preg_replace('/[^a-zA-Z0-9]+/', '_', $url), '_') . '.pdf';
// filenames shouldn't be longer than 255 chars
if (strlen($filename) > 255) {
// take from the back to preserve extension
// URLs tend to have their most important info at the end
$filename = substr($filename, -255, 255);
}
return $filename;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ControllerBase:: |
protected | property | The configuration factory. | |
ControllerBase:: |
protected | property | The current user service. | 1 |
ControllerBase:: |
protected | property | The entity form builder. | |
ControllerBase:: |
protected | property | The entity type manager. | |
ControllerBase:: |
protected | property | The form builder. | 2 |
ControllerBase:: |
protected | property | The key-value storage. | 1 |
ControllerBase:: |
protected | property | The language manager. | 1 |
ControllerBase:: |
protected | property | The module handler. | 2 |
ControllerBase:: |
protected | property | The state service. | |
ControllerBase:: |
protected | function | Returns the requested cache bin. | |
ControllerBase:: |
protected | function | Retrieves a configuration object. | |
ControllerBase:: |
private | function | Returns the service container. | |
ControllerBase:: |
protected | function | Returns the current user. | 1 |
ControllerBase:: |
protected | function | Retrieves the entity form builder. | |
ControllerBase:: |
protected | function | Retrieves the entity type manager. | |
ControllerBase:: |
protected | function | Returns the form builder service. | 2 |
ControllerBase:: |
protected | function | Returns a key/value storage collection. | 1 |
ControllerBase:: |
protected | function | Returns the language manager service. | 1 |
ControllerBase:: |
protected | function | Returns the module handler. | 2 |
ControllerBase:: |
protected | function | Returns a redirect response object for the specified route. | |
ControllerBase:: |
protected | function | Returns the state storage service. | |
LoggerChannelTrait:: |
protected | property | The logger channel factory service. | |
LoggerChannelTrait:: |
protected | function | Gets the logger for a specific channel. | |
LoggerChannelTrait:: |
public | function | Injects the logger channel factory. | |
MessengerTrait:: |
protected | property | The messenger. | 27 |
MessengerTrait:: |
public | function | Gets the messenger. | 27 |
MessengerTrait:: |
public | function | Sets the messenger. | |
RedirectDestinationTrait:: |
protected | property | The redirect destination service. | 1 |
RedirectDestinationTrait:: |
protected | function | Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url. | |
RedirectDestinationTrait:: |
protected | function | Returns the redirect destination service. | |
RedirectDestinationTrait:: |
public | function | Sets the redirect destination service. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
WkhtmltopdfController:: |
public | property | ||
WkhtmltopdfController:: |
public | property | The logger for the wkhtmltopdf module | |
WkhtmltopdfController:: |
public | property | The current request object | |
WkhtmltopdfController:: |
public | property | The wkhtmltopdf module config | |
WkhtmltopdfController:: |
protected | function | Builds the shell command including parameters | |
WkhtmltopdfController:: |
public static | function |
Instantiates a new instance of this class. Overrides ControllerBase:: |
|
WkhtmltopdfController:: |
protected | function | Actually sends the shell command, and returns after the pdf file has been created | |
WkhtmltopdfController:: |
protected | function | Generates a filename based on the url | |
WkhtmltopdfController:: |
public | function | Generate pdf file. | |
WkhtmltopdfController:: |
protected | function | Gets the command line arguments for the wkhtmltopdf shell command based on the settings | |
WkhtmltopdfController:: |
protected | function | Creates a file response object to return to the client | |
WkhtmltopdfController:: |
private | function | ||
WkhtmltopdfController:: |
private | function | Validates the command line arguments are valid | |
WkhtmltopdfController:: |
public | function |