You are here

function fillpdf_merge_pdf in FillPDF 7

Same name and namespace in other branches
  1. 6 fillpdf.module \fillpdf_merge_pdf()
  2. 7.2 fillpdf.module \fillpdf_merge_pdf()

Constructs a page and sends it to the browser or saves it.

Constructs a page from scratch (pdf content-type) and sends it to the browser or saves it, depending on if a custom path is configured or not.

@todo Refactor to take fewer arguments once tests in place. MAYBE in FillPDF 3 - might not want to break backwards-compatibility.

Parameters

int $fid: The integer ID of the PDF.

int[] $nids: Array of integer IDs of the content (nodes) from which to draw data.

array[] $webform_array: Array of arrays, each containing 'nid' and 'sid' keys. The 'nid' key refers to the ID of the Webform node, and the 'sid' refers to the Webform submission ID. If no nid is supplied, the default ID from the FillPDF Form (if set) is used to try to load a node.

bool $sample: If "true" (exact string), each field will be filled with its field name.

bool $force_download: Boolean. If TRUE, always send a PDF to the browser, even if a destination_path is set for the PDF.

bool $skip_access_check: Boolean. If TRUE, do not do any access checks. Allow the user to download any PDF with data from any node. Only use when access checks are being done some other way.

bool $flatten: Boolean. If TRUE, flatten the PDF so that fields cannot be edited. Otherwise leave fields editable.

bool $handle: Boolean. If TRUE, handle the PDF, which usually consists of sending it to the users's browser or saving it as a file.

int[] $uc_order_ids: Array of integer IDs of Ubercart orders from which to.

int[] $uc_order_product_ids: Array of integer IDs of Ubercart ordered products from which to draw data.

array $entity_ids: Array of IDs of entities from which to draw data. IDs may be integers or strings depending on the entity type.

Return value

object|null When $handle is FALSE, this function returns the variable it would have used to invoke hook_fillpdf_merge_pre_handle(). When $handle is TRUE, it returns nothing.

See also

fillpdf_pdf_link()

4 calls to fillpdf_merge_pdf()
FillPdfTestCase::testFileAccess in tests/FillPdfTestCase.test
Make sure that file access works properly.
fillpdf_parse_uri in ./fillpdf.module
Get the input data and print the PDF.
fillpdf_rules_action_merge_node in ./fillpdf.rules.inc
Populates a loaded FillPDF configuration's PDF with node data.
fillpdf_rules_action_merge_webform in ./fillpdf.rules.inc
Populates a loaded FillPDF configuration's PDF with data.

File

./fillpdf.module, line 544

Code

function fillpdf_merge_pdf($fid, $nids = NULL, $webform_array = NULL, $sample = NULL, $force_download = FALSE, $skip_access_check = FALSE, $flatten = TRUE, $handle = TRUE, $uc_order_ids = NULL, $uc_order_product_ids = NULL, $entity_ids = NULL) {

  // Case 1: No $fid.
  if (is_null($fid)) {
    drupal_set_message(t('FillPDF Form ID required to print a PDF.'), 'warning');
    drupal_goto();
  }
  $fillpdf_info = fillpdf_load($fid);

  // Case 1.5: $fid is not valid.
  if ($fillpdf_info === FALSE) {
    drupal_set_message(t('Non-existent FillPDF Form ID.'), 'error');
    drupal_not_found();
    drupal_exit();
  }
  global $user;
  $context = fillpdf_load_entities($fillpdf_info, $nids, $webform_array, $uc_order_ids, $uc_order_product_ids, $user, $entity_ids);
  $nodes = $context['nodes'];
  $webforms = $context['webforms'];
  $uc_orders = $context['uc_orders'];
  $uc_order_products = $context['uc_order_products'];
  $entities = $context['entities'];
  if ($skip_access_check !== TRUE) {
    if (!fillpdf_merge_pdf_access($nodes, $webforms, $uc_orders, $uc_order_products, $entities)) {
      drupal_access_denied();
      drupal_exit();
    }
  }
  $fields = $token_objects = $image_data = array();
  $query = db_query("SELECT * FROM {fillpdf_fields} WHERE fid = :fid", array(
    ':fid' => $fid,
  ));
  foreach ($query as $obj) {
    $obj->replacements = _fillpdf_replacements_to_array($obj->replacements);
    $token = '';

    // Keep track of whether we're dealing with an image or not.
    $transform_string = FALSE;
    if ($sample) {

      // Fill template with the PDF field names to produce a sample PDF.
      $fields[$obj->pdf_key] = $obj->pdf_key;
    }
    else {

      // Multiple nids, #516840 we want the last nid in $_GET to override
      // previous ones (aka, of fillpdf?nids[]=1&nids[]=2, 2 wins).
      $nodes = array_reverse($nodes);
      $webforms = array_reverse($webforms);
      $uc_orders = array_reverse($uc_orders);
      $uc_order_products = array_reverse($uc_order_products);
      $entities = array_reverse($entities);

      // Node token replacements.
      if (!empty($nodes)) {
        foreach ($nodes as $node) {
          $token_objects['node'] = $node;
          _fillpdf_merge_pdf_token_replace($obj->value, $token_objects, $token);

          // If the token points to an image, treat it as an image-stamping
          // request.
          $entity_type = 'node';
          $entity = $node;
          _fillpdf_process_image_tokens($entity_type, $entity, $obj, $fields, $image_data, $transform_string);

          // (Legacy approach.) If they're populating a node with an Image
          // field.
          if (strstr($obj->value, '[stamp:')) {

            // HACK: Use a pseudo-token to stamp images.
            // Find the two sides of the square bracket contents.
            // 7 is the length of [stamp:. We don't want the brackets
            // themselves.
            $left_side = strpos($obj->value, '[stamp:') + 7;
            $right_side = strpos($obj->value, ']');
            $field_name = substr($obj->value, $left_side, $right_side - $left_side);
            if (isset($node->{$field_name}[$node->language])) {
              $image_path = $node->{$field_name}[$node->language][0]['uri'];
              $transform_string = FALSE;
              $fields[$obj->pdf_key] = '{image}' . drupal_realpath($image_path);
              $image_path_info = pathinfo(drupal_realpath($image_path));

              // Store the image data to transmit to the remote service if
              // necessary.
              $file_data = file_get_contents(drupal_realpath($image_path));
              if ($file_data) {
                $image_data[$obj->pdf_key] = array(
                  'data' => base64_encode($file_data),
                  'filenamehash' => md5($image_path_info['filename']) . '.' . $image_path_info['extension'],
                );
              }
            }
          }
        }
      }
      if (!empty($entities)) {
        foreach ($entities as $entity_type => $entities_of_type) {
          foreach ($entities_of_type as $entity) {

            // We have to pass the correct data key. It needs to match the
            // type the token expects. For now, we assume that the 'token type'
            // matches what core and contributed entity type tokens expect.
            // This is more accurate than using the entity type in cases such
            // as taxonomy terms. They have an entity type of 'taxonomy_term',
            // but they expect 'term' data.
            if (!module_exists('entity_token')) {

              // We can't provide good functionality without the Entity Tokens
              // module.
              break 2;
            }
            $entity_info = entity_get_info($entity_type);
            $token_type = !empty($entity_info['token type']) ? $entity_info['token type'] : $entity_type;
            $entity_token_objects = array(
              $token_type => $entity,
            );

            // @todo array_merge() in loops is bad for performance. Would be good to rewrite this in a more performant way later if possible, especially once we start doing cleanups for Drupal 7 + PHP 7.
            $token_objects = array_merge($token_objects, $entity_token_objects);
            _fillpdf_merge_pdf_token_replace($obj->value, $token_objects, $token);
            $transform_string = TRUE;
            _fillpdf_process_image_tokens($entity_type, $entity, $obj, $fields, $image_data, $transform_string);
          }
        }
      }

      // Webform token replacements.
      if (!empty($webforms)) {
        foreach ($webforms as $webform) {
          $token_objects = array_merge($token_objects, array(
            'webform-submission' => $webform['submission'],
            'submission' => $webform['submission'],
            'node' => $webform['webform'],
          ));
          _fillpdf_merge_pdf_token_replace($obj->value, $token_objects, $token);
          $transform_string = TRUE;

          // Include image data if they used a compatible Webform component
          // token.
          $webform_component_data = array_filter($webform['webform']->webform['components'], function ($value) {
            if (!empty($value['type']) && $value['type'] !== 'file') {
              return FALSE;
            }
            return TRUE;
          });
          foreach ($webform_component_data as $cid => $component) {
            if (empty($webform['submission']->data[$cid]) || !count($webform['submission']->data[$cid])) {
              continue;
            }
            $submission_component_value = $webform['submission']->data[$cid];
            if ($obj->value !== "[submission:values:{$component['form_key']}]") {
              continue;
            }
            $webform_file = file_load($submission_component_value[0]);

            // If the file doesn't exist or is not an image file, bail.
            // Incompatible formats will break the PDF.
            if (!$webform_file || count(file_validate_is_image($webform_file))) {
              break;
            }
            _fillpdf_prepare_image_data($webform_file->uri, $obj, $fields, $image_data, $transform_string);
          }
        }
      }

      // Ubercart Order token replacements.
      if (!empty($uc_orders)) {
        foreach ($uc_orders as $uc_order) {
          $token_objects['uc_order'] = $uc_order;
          _fillpdf_merge_pdf_token_replace($obj->value, $token_objects, $token);
        }
        $transform_string = TRUE;
      }

      // Ubercart Order Product token replacements.
      if (!empty($uc_order_products)) {
        foreach ($uc_order_products as $uc_order_product) {
          $token_objects = array_merge($token_objects, array(
            'uc_order_product' => $uc_order_product,
            'uc_order' => uc_order_load($uc_order_product->order_id),
            'node' => node_load($uc_order_product->nid),
          ));
          _fillpdf_merge_pdf_token_replace($obj->value, $token_objects, $token);
        }
        $transform_string = TRUE;
      }
      if ($transform_string) {

        // Replace <br /> occurrences with newlines.
        $str = preg_replace('|<br />|', '
', $token);
        $str = _fillpdf_transform_field_value($str, $fillpdf_info->replacements, $obj->replacements);
        $fields[$obj->pdf_key] = $str;
      }
    }

    // Apply prefix and suffix, if applicable.
    if (isset($fields[$obj->pdf_key]) && $fields[$obj->pdf_key]) {
      if (isset($obj->prefix)) {
        $fields[$obj->pdf_key] = $obj->prefix . $fields[$obj->pdf_key];
      }
      if (isset($obj->suffix)) {
        $fields[$obj->pdf_key] .= $obj->suffix;
      }
    }
  }

  // Provide hook_fillpdf_merge_fields_alter() to let other modules
  // alter fields before pdf generation.
  // @todo Remove first hook in next major version. There for backwards-compatibility.
  drupal_alter('fillpdf_merge_fields_alter', $fields, $context, $fillpdf_info);
  drupal_alter('fillpdf_merge_fields', $fields, $context, $fillpdf_info);
  $method = variable_get('fillpdf_service');
  if (empty($method)) {
    drupal_set_message(t('FillPDF is not configured.'), 'error');
    drupal_goto();
  }
  $data = fillpdf_execute_merge($method, $fields, $fillpdf_info, 'url', $flatten, $image_data);
  if (!$data) {
    return;
  }
  if (!empty($webform['webform'])) {
    $node = $webform['webform'];
  }
  if (!empty($node)) {

    // Log this, could be useful.
    watchdog('fillpdf', 'User generated form "%form" for node "%node".', array(
      '%form' => $fillpdf_info->title,
      '%node' => $node->title,
    ));
  }

  // Assemble some metadata that will be useful for the handling phase.
  $fillpdf_object = _fillpdf_build_options_object($force_download, $flatten, $fillpdf_info, $data, $nodes, $webforms, $uc_orders, $uc_order_products, $token_objects, $sample, $entities);
  if ($handle === TRUE) {

    // Allow modules to step in here and change the way the PDF is handled.
    module_invoke_all('fillpdf_merge_pre_handle', $fillpdf_object);

    // Perform the default action on the PDF - in other words, the one it was
    // configured to do in the administrative area.
    fillpdf_merge_perform_pdf_action($fillpdf_object, 'default', $force_download);
  }
  else {
    return $fillpdf_object;
  }
}