CRM-20420 - PDFLetterCommon - Record attachments (in mode "combined-attached")
authorTim Otten <totten@civicrm.org>
Thu, 13 Apr 2017 05:07:37 +0000 (22:07 -0700)
committerTim Otten <totten@civicrm.org>
Thu, 13 Apr 2017 05:16:29 +0000 (22:16 -0700)
If the setting `recordGeneratedLetters` is `combined-attached`, then we
should capture the content of the generated file and save it to an
attachment.

Note: There are several different ways generating files (eg HTML=>PDF,
HTML=>docx/ODT, or docx/ODT template file).  Ideally, these would support
common set of options for directing output (to a file, string, or console).
In fact, some do, and all the underlying libraries do...  but the
middle-logic only handles the console consistently.  We use output buffering
(ob_start()) to capture the content and save it to a file.

CRM/Contact/Form/Task/PDFLetterCommon.php

index 404f2ef0d6d36bb31a423ab64901d8d46896de36..bb423eb25d11c2e609aa48e713baedf2b89c99c1 100644 (file)
@@ -359,11 +359,11 @@ class CRM_Contact_Form_Task_PDFLetterCommon {
     $buttonName = $form->controller->getButtonName();
     $skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE;
     $skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE;
-    $html = $document = array();
+    $html = $activityIds = array();
 
     // CRM-16725 Skip creation of activities if user is previewing their PDF letter(s)
     if ($buttonName == '_qf_PDF_upload') {
-      self::createActivities($form, $html_message, $form->_contactIds);
+      $activityIds = self::createActivities($form, $html_message, $form->_contactIds);
     }
 
     if (!empty($formValues['document_file_path'])) {
@@ -410,17 +410,53 @@ class CRM_Contact_Form_Task_PDFLetterCommon {
       $html[] = $tokenHtml;
     }
 
+    $saveToFile = count($activityIds) === 1 && Civi::settings()->get('recordGeneratedLetters') === 'combined-attached';
+    if ($saveToFile) {
+      // We need to capture data from various output-generators whose only
+      // consistent output format is writing to the console (for download).
+      $tmpFile = tempnam(sys_get_temp_dir(), 'PDFLetterCommon-');
+      $tmpFh = fopen($tmpFile, 'w');
+      ob_start(function ($buf) use ($tmpFh) {
+        // Pass to file, then continue to stdout.
+        fwrite($tmpFh, $buf);
+        return FALSE;
+      });
+    }
+
     $type = $formValues['document_type'];
+    $mimeType = self::getMimeType($type);
 
     if ($type == 'pdf') {
-      CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues);
+      $fileName = "CiviLetter.$type";
+      CRM_Utils_PDF_Utils::html2pdf($html, $fileName, FALSE, $formValues);
     }
     elseif (!empty($formValues['document_file_path'])) {
       $fileName = pathinfo($formValues['document_file_path'], PATHINFO_FILENAME) . '.' . $type;
       CRM_Utils_PDF_Document::printDocuments($html, $fileName, $type, $zip);
     }
     else {
-      CRM_Utils_PDF_Document::html2doc($html, "CiviLetter.$type", $formValues);
+      $fileName = "CiviLetter.$type";
+      CRM_Utils_PDF_Document::html2doc($html, $fileName, $formValues);
+    }
+
+    if ($saveToFile) {
+      ob_end_flush();
+      fclose($tmpFh);
+      $content = file_get_contents($tmpFile, NULL, NULL, NULL, 5);
+      if (empty($content)) {
+        throw new \CRM_Core_Exception("Failed to capture document content (type=$type)!");
+      }
+      foreach ($activityIds as $activityId) {
+        civicrm_api3('Attachment', 'create', array(
+          'entity_table' => 'civicrm_activity',
+          'entity_id' => $activityId,
+          'name' => $fileName,
+          'mime_type' => $mimeType,
+          'options' => array(
+            'move-file' => $tmpFile,
+          ),
+        ));
+      }
     }
 
     $form->postProcessHook();
@@ -562,4 +598,26 @@ class CRM_Contact_Form_Task_PDFLetterCommon {
     $message = implode($newLineOperators['p']['oper'], $htmlMsg);
   }
 
+  /**
+   * Convert from a vague-type/file-extension to mime-type.
+   *
+   * @param string $type
+   * @return string
+   * @throws \CRM_Core_Exception
+   */
+  private static function getMimeType($type) {
+    $mimeTypes = array(
+      'pdf' => 'applicatoin/pdf',
+      'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'odt' => 'application/vnd.oasis.opendocument.text',
+      'html' => 'text/html',
+    );
+    if (isset($mimeTypes[$type])) {
+      return $mimeTypes[$type];
+    }
+    else {
+      throw new \CRM_Core_Exception("Cannot determine mime type");
+    }
+  }
+
 }