commerce_invoice_pdf.module in Commerce Invoice 7.2
The Commerce Invoice PDF module.
File
modules/pdf/commerce_invoice_pdf.moduleView source
<?php
/**
* @file
* The Commerce Invoice PDF module.
*/
use Drupal\commerce_invoice\Entity\Invoice;
/**
* Implements hook_menu().
*/
function commerce_invoice_pdf_menu() {
$items = [];
$items['invoices/%entity_object/pdf'] = [
'title' => 'PDF',
'load arguments' => [
'commerce_invoice',
],
'page callback' => 'commerce_invoice_pdf_deliver',
'page arguments' => [
1,
],
'access callback' => 'entity_access',
'access arguments' => [
'view',
'commerce_invoice',
1,
],
'type' => MENU_CALLBACK,
];
$items['invoices/%entity_object/pdf/html'] = [
'title' => 'PDF - HTML source',
'load arguments' => [
'commerce_invoice',
],
'page callback' => 'commerce_invoice_pdf_html',
'page arguments' => [
1,
],
'access arguments' => [
'administer commerce_invoice entities',
],
'type' => MENU_CALLBACK,
];
return $items;
}
/**
* Implements hook_entity_info_alter().
*/
function commerce_invoice_pdf_entity_info_alter(&$entity_info) {
$entity_info['commerce_invoice']['view modes']['pdf'] = array(
'label' => t('PDF'),
'custom settings' => FALSE,
);
}
/**
* Implements hook_entity_view().
*/
function commerce_invoice_pdf_entity_view($entity, $type, $view_mode, $langcode) {
// Add "Download PDF" link to the bottom of invoice page.
if ($type == 'commerce_invoice' && $view_mode == 'full') {
$entity->content['pdf_link'] = [
'#theme' => 'link',
'#weight' => 50,
'#text' => t('Download PDF'),
'#path' => 'invoices/' . $entity->invoice_id . '/pdf',
'#options' => [
'query' => [
'download' => 1,
],
'html' => FALSE,
'attributes' => [],
],
];
}
}
/**
* Debug page callback to view the HTML used for generating the PDF.
*/
function commerce_invoice_pdf_html(Invoice $invoice) {
drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
echo theme('commerce_invoice_pdf_page', [
'invoice' => $invoice,
'browser' => TRUE,
]);
drupal_exit();
}
/**
* Page callback to generate and deliver a PDF file for an invoice.
*/
function commerce_invoice_pdf_deliver(Invoice $invoice) {
$file = commerce_invoice_pdf_create($invoice);
if (!$file || !is_file($file->uri)) {
// If this error happens, it's probably to do with file permissions.
drupal_set_message(t('The invoice PDF file could not be found.'), 'error');
return MENU_NOT_FOUND;
}
// Transfer the file.
$headers = array(
'Content-Type' => $file->filemime,
'Content-Length' => $file->filesize,
);
// The 'download' GET parameter will attempt to force a download, rather than
// viewing the file directly.
if (!empty($_GET['download'])) {
$headers = array(
'Content-Type' => 'force-download',
'Content-Disposition' => 'attachment; filename="' . $file->filename . '"',
'Content-Transfer-Encoding' => 'binary',
'Accept-Ranges' => 'bytes',
);
}
// The following invocation of hook_file_download() uses the same logic as the
// core function file_download().
// @see file_download()
foreach (module_implements('file_download') as $module) {
$function = $module . '_file_download';
$result = $function($file->uri);
if ($result == -1) {
$headers = array();
break;
}
if (isset($result) && is_array($result)) {
$headers = array_merge($headers, $result);
}
}
if (count($headers)) {
file_transfer($file->uri, $headers);
}
return MENU_ACCESS_DENIED;
}
/**
* Generate a PDF for an invoice.
*
* @param \Drupal\commerce_invoice\Entity\Invoice $invoice
*
* @return string
* The PDF contents, as a string.
*/
function commerce_invoice_pdf_generate(Invoice $invoice) {
$dompdf = commerce_invoice_pdf_load_dompdf();
if (!$dompdf) {
throw new RuntimeException('The DOMPDF library was not found.');
}
$html = theme('commerce_invoice_pdf_page', [
'invoice' => $invoice,
]);
$dompdf
->set_paper('a4', 'portrait');
$dompdf
->load_html($html);
$dompdf
->render();
return $dompdf
->output();
}
/**
* Create a PDF for an invoice.
*
* @param Invoice $invoice
* @param bool $create
* Whether to create a new file if it doesn't already exist (default: TRUE).
* You can check whether the file exists already by setting this to FALSE.
* @param bool $recreate
* Whether to recreate (replace) the file if it already exists (default:
* FALSE).
*
*
* @return object|FALSE
* An object describing the managed file for the PDF (including the 'uri'
* property), or FALSE on failure.
*
*/
function commerce_invoice_pdf_create(Invoice $invoice, $create = TRUE, $recreate = FALSE) {
// Find existing pdf if it exists.
$fid = db_select('file_usage', 'f')
->fields('f', array(
'fid',
))
->condition('module', 'commerce_invoice_pdf')
->condition('type', 'commerce_invoice')
->condition('id', $invoice->invoice_id)
->execute()
->fetchField();
// If file exists and does not need to be recreated, just return it!
if (!$recreate && !empty($fid)) {
$file = file_load($fid);
if ($file && file_exists($file->uri)) {
return $file;
}
}
// Return false if file should not be created.
if (!$create) {
return FALSE;
}
// Invoices must not be public! Make sure private file system is configured.
if (!drupal_realpath('private://')) {
watchdog('commerce_invoice', 'The private filesystem directory is not configured.', [], WATCHDOG_ERROR);
return FALSE;
}
$dir = 'private://commerce_invoice/' . date('Y-m', $invoice->created);
$filename = preg_replace('/[^a-z0-9-]+/i', '_', 'invoice_' . $invoice
->getInvoiceNumber()) . '.pdf';
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
watchdog('commerce_invoice', 'The directory does not exist or is not writable: @dir', [
'@dir' => $dir,
], WATCHDOG_ERROR);
return FALSE;
}
$data = commerce_invoice_pdf_generate($invoice);
// file_save_data() cannot be used as it insists on global $user to be owner
// of the file. In this use case, owner of the invoice must be owner of the
// pdf.
if ($uri = file_unmanaged_save_data($data, $dir . '/' . $filename, FILE_EXISTS_REPLACE)) {
// Create a file object.
$file = new stdClass();
$file->fid = empty($fid) ? NULL : $fid;
$file->uri = $uri;
$file->filename = drupal_basename($uri);
$file->filemime = file_get_mimetype($file->uri);
$file->uid = $invoice->uid;
$file->status = FILE_STATUS_PERMANENT;
$file = file_save($file);
// Increment usage only if the file was not recreated.
if (empty($fid)) {
file_usage_add($file, 'commerce_invoice_pdf', 'commerce_invoice', $invoice->invoice_id);
}
rules_invoke_all('commerce_invoice_pdf_created', $invoice, $file);
return $file;
}
watchdog('commerce_invoice', 'Failed to create PDF file for invoice @id, path @path', [
'@id' => $invoice->invoice_id,
'@path' => $dir . '/' . $filename,
], WATCHDOG_ERROR);
return FALSE;
}
/**
* Helper function that instantiates new DOMPDF class.
*
* Uses config functionality from Commerce Billy.
*
* @see commerce_billy_pdf_prepare_dompdf()
*
* @return DOMPDF|\Dompdf\Dompdf|FALSE
*/
function commerce_invoice_pdf_load_dompdf() {
$path = libraries_get_path('dompdf');
if (!empty($path)) {
$dir = drupal_realpath('public://');
//@todo: Ditch these constants once DOMPDF 0.6.* support gets removed.
if (!defined('DOMPDF_FONT_DIR')) {
define('DOMPDF_FONT_DIR', $dir . '/fonts/');
}
if (!defined('DOMPDF_TEMP_DIR')) {
define('DOMPDF_TEMP_DIR', file_directory_temp());
}
// Support for Dompdf 0.7.0 and newer, using PHP 5.3 namespaces.
if (file_exists(DRUPAL_ROOT . '/' . $path . '/autoload.inc.php')) {
require_once DRUPAL_ROOT . '/' . $path . '/autoload.inc.php';
$dompdf = new \Dompdf\Dompdf([
'tempDir' => DOMPDF_TEMP_DIR,
'fontDir' => DOMPDF_FONT_DIR,
'isHtml5ParserEnabled' => TRUE,
]);
$dompdf
->setPaper('a4', 'portrait');
}
else {
require_once DRUPAL_ROOT . '/' . $path . '/dompdf_config.inc.php';
spl_autoload_register('DOMPDF_autoload');
$dompdf = new DOMPDF();
$dompdf
->set_paper('a4', 'portrait');
}
return $dompdf;
}
return FALSE;
}
/**
* Implements hook_views_api().
*/
function commerce_invoice_pdf_views_api() {
return [
'api' => 3,
];
}
/**
* Implements hook_theme().
*/
function commerce_invoice_pdf_theme() {
$items['commerce_invoice_pdf_page'] = array(
'variables' => [
'invoice' => NULL,
'inline_css' => '',
'browser' => FALSE,
],
'template' => 'commerce-invoice-pdf-page',
'path' => drupal_get_path('module', 'commerce_invoice_pdf') . '/templates',
);
return $items;
}
/**
* Preprocess function for the invoice PDF page template.
*/
function template_preprocess_commerce_invoice_pdf_page(&$variables) {
/** @var Invoice $invoice */
$invoice = $variables['invoice'];
$variables['content'] = $invoice
->buildContent('pdf', NULL);
$variables['title'] = commerce_invoice_title($invoice);
$variables['invoice_date'] = format_date($invoice->invoice_date, 'custom', 'Y-m-d');
$variables['invoice_due'] = format_date($invoice->invoice_due, 'custom', 'Y-m-d');
$variables['invoice_number'] = check_plain($invoice
->getInvoiceNumber());
if (!empty($invoice->order_id)) {
$variables['order_number'] = check_plain($invoice
->wrapper()->order->order_number
->value());
}
$variables['logo'] = '';
$logo = variable_get('commerce_invoice_pdf_logo', 'misc/druplicon.png');
if ($logo) {
$logo = !empty($variables['browser']) ? file_create_url($logo) : drupal_realpath($logo);
$variables['logo'] = theme('image', [
'path' => $logo,
]);
}
$price = $invoice
->wrapper()->commerce_invoice_total->amount
->value();
$currency_code = $invoice
->wrapper()->commerce_invoice_total->currency_code
->value();
$variables['total'] = commerce_currency_format($price, $currency_code);
$css_files = variable_get('commerce_invoice_pdf_css_files', array(
drupal_get_path('module', 'commerce_invoice_pdf') . '/css/pdf.css',
));
$variables['inline_css'] = "";
foreach ($css_files as $file) {
$variables['inline_css'] .= file_get_contents($file);
}
$settings = variable_get('commerce_invoice_pdf_text_settings', array());
foreach ([
'header',
'footer',
'text',
] as $part) {
$variables['invoice_' . $part] = isset($settings['invoice_' . $part]) ? $settings['invoice_' . $part] : '';
}
$variables['theme_hook_suggestions'][] = 'commerce_invoice_pdf_page';
$variables['theme_hook_suggestions'][] = 'commerce_invoice_pdf_page__' . $invoice->number_pattern;
}
/**
* Implements hook_action_info().
*/
function commerce_invoice_pdf_action_info() {
$items['commerce_invoice_pdf_download_invoices'] = array(
'type' => 'commerce_invoice',
'label' => t('Zip and download PDF invoices'),
'configurable' => FALSE,
'vbo_configurable' => TRUE,
'pass rows' => FALSE,
);
$items['commerce_invoice_pdf_recreate_pdfs'] = array(
'type' => 'commerce_invoice',
'label' => t('Recreate PDF invoices'),
'configurable' => FALSE,
'vbo_configurable' => FALSE,
'pass rows' => FALSE,
);
return $items;
}
/**
* Configuration form shown to the user before the action gets executed.
*/
function commerce_invoice_pdf_download_invoices_form($context) {
$form['filename'] = array(
'#type' => 'textfield',
'#title' => t('Filename'),
'#default_value' => 'invoices-archive--' . format_date(time(), 'custom', 'Y-m'),
'#field_suffix' => '.zip',
'#description' => t('The name of the archive file.'),
);
return $form;
}
/**
* Assembles a unique URI for the archive.
*/
function commerce_invoice_pdf_download_invoices_submit($form, $form_state) {
$dir = 'private://commerce_invoice/archives';
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
throw new Exception(t('The directory does not exist or is not writable: @dir', [
'@dir' => $dir,
]));
}
$destination = $dir . basename($form_state['values']['filename']) . '.zip';
// If the chosen filename already exists, file_destination() will append
// an integer to it in order to make it unique.
$destination = file_destination($destination, FILE_EXISTS_RENAME);
return array(
'destination' => $destination,
);
}
/**
* Action callback to generate a PDF.
*/
function commerce_invoice_pdf_download_invoices($invoice, $context) {
global $user;
$destination = $context['destination'];
_commerce_invoice_pdf_ensure_default_theme();
// Get the invoice file.
if ($file = commerce_invoice_pdf_create($invoice)) {
$zip = new ZipArchive();
// If the archive already exists, open it. If not, create it.
if (file_exists($destination)) {
$opened = $zip
->open(drupal_realpath($destination));
}
else {
$opened = $zip
->open(drupal_realpath($destination), ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE);
}
if ($opened) {
$zip
->addFile(drupal_realpath($file->uri), $file->filename);
$zip
->close();
}
}
// The operation is complete, create a temporary file entity and provide a
// download link to the user.
if ($context['progress']['current'] == $context['progress']['total']) {
$archive_file = new stdClass();
$archive_file->uri = $destination;
$archive_file->filename = basename($destination);
$archive_file->filemime = file_get_mimetype($destination);
$archive_file->uid = $user->uid;
$archive_file->status = FALSE;
clearstatcache();
file_save($archive_file);
$url = file_create_url($archive_file->uri);
$url = l($url, $url, array(
'absolute' => TRUE,
));
_views_bulk_operations_log(t('An archive has been created and can be downloaded from: !url', array(
'!url' => $url,
)));
}
}
/**
* Action callback to generate a PDF.
*/
function commerce_invoice_pdf_recreate_pdfs($invoice) {
_commerce_invoice_pdf_ensure_default_theme();
commerce_invoice_pdf_create($invoice, TRUE, TRUE);
}
/**
* Switch to the default theme, in order to render the invoice properly if the
* admin theme was other than the default one.
*/
function _commerce_invoice_pdf_ensure_default_theme() {
global $theme;
$theme = NULL;
$custom_theme =& drupal_static('menu_get_custom_theme');
$custom_theme = variable_get('theme_default', 'bartik');
drupal_theme_initialize();
}
/**
* Implements hook_advanced_queue_info().
*/
function commerce_invoice_pdf_advanced_queue_info() {
$items['commerce_invoice_pdf_create_queue'] = array(
'label' => t('Create PDF invoices queue'),
'worker callback' => 'commerce_invoice_pdf_create_worker',
'retry after' => 30,
'max attempts' => 5,
// PDF creation is relatively slow process with a low priority
'weight' => 100,
);
return $items;
}
function commerce_invoice_pdf_create_worker($item, $end_time = FALSE) {
$data = $item->data;
/** @var Invoice $invoice */
if ($invoice = entity_load_single('commerce_invoice', $data['invoice_id'])) {
_commerce_invoice_pdf_ensure_default_theme();
// Make sure that any entity_access() down the line returns TRUE.
global $user;
if ($user->uid !== $invoice->uid) {
$user = user_load($invoice->uid);
}
if (commerce_invoice_pdf_create($invoice, TRUE, $data['recreate'])) {
return array(
'status' => ADVANCEDQUEUE_STATUS_SUCCESS,
'result' => 'Processed ' . $item->item_id,
);
}
}
return array(
'status' => ADVANCEDQUEUE_STATUS_FAILURE,
'result' => 'Error processing queue item ' . $item->item_id,
);
}