From 2bd6b6cfb7bdcf5350dc77d2fe0677a1f9652859 Mon Sep 17 00:00:00 2001 From: "deb.monish" Date: Sun, 17 Jul 2016 03:34:23 +0530 Subject: [PATCH] modified docx pagebreak, optimisation and added unit test --- CRM/Contact/Form/Task/PDFLetterCommon.php | 11 +- CRM/Utils/PDF/Document.php | 13 ++- .../Contact/Form/Task/PrintDocumentTest.php | 94 ++++++++++++++++++ .../Form/Task/sample_documents/Template.docx | Bin 0 -> 4768 bytes .../Form/Task/sample_documents/Template.odt | Bin 0 -> 8506 bytes 5 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 tests/phpunit/CRM/Contact/Form/Task/PrintDocumentTest.php create mode 100644 tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.docx create mode 100644 tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.odt diff --git a/CRM/Contact/Form/Task/PDFLetterCommon.php b/CRM/Contact/Form/Task/PDFLetterCommon.php index e4849c93af..9f45d26ae7 100644 --- a/CRM/Contact/Form/Task/PDFLetterCommon.php +++ b/CRM/Contact/Form/Task/PDFLetterCommon.php @@ -248,15 +248,13 @@ class CRM_Contact_Form_Task_PDFLetterCommon { * Part of the post process which prepare and extract information from the template. * * - * @param CRM_Core_Form $form + * @param array $formValues * * @return array * [$categories, $html_message, $messageToken, $returnProperties] */ - static protected function processMessageTemplate(&$form) { - $formValues = $form->controller->exportValues($form->getName()); - - $html_message = $formValues['html_message']; + public static function processMessageTemplate($formValues) { + $html_message = CRM_Utils_Array::value('html_message', $formValues); // process message template if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) { @@ -335,7 +333,8 @@ class CRM_Contact_Form_Task_PDFLetterCommon { * @param CRM_Core_Form $form */ public static function postProcess(&$form) { - list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($form); + $formValues = $form->controller->exportValues($form->getName()); + list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($formValues); $buttonName = $form->controller->getButtonName(); $skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE; $skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE; diff --git a/CRM/Utils/PDF/Document.php b/CRM/Utils/PDF/Document.php index b18fdb55c2..d49547b154 100644 --- a/CRM/Utils/PDF/Document.php +++ b/CRM/Utils/PDF/Document.php @@ -39,8 +39,7 @@ class CRM_Utils_PDF_Document { 'docx' => array( 'dataFile' => 'word/document.xml', 'startTag' => '', - // TODO need to provide proper ooxml tag for pagebreak - 'pageBreak' => '', + 'pageBreak' => '', 'endTag' => '', ), 'odt' => array( @@ -176,8 +175,12 @@ class CRM_Utils_PDF_Document { * Document type e.g. odt/docx * @param clsTbsZip $zip * Zip archive + * @param bool $returnFinalContent + * Return the content of file document as a string used in unit test + * + * @return string */ - public static function printDocuments($filePath, $contents, $docType, $zip) { + public static function printDocuments($filePath, $contents, $docType, $zip, $returnFinalContent = FALSE) { $dataMap = SELF::$ooxmlMap[$docType]; $finalContent = $zip->FileRead($dataMap['dataFile']); @@ -199,6 +202,10 @@ class CRM_Utils_PDF_Document { $finalContent = str_replace($dataMap['endTag'], $content, $finalContent); } + if ($returnFinalContent) { + return $finalContent; + } + // Replace the loaded document file content located at $filePath with $finaContent $zip->FileReplace($dataMap['dataFile'], $finalContent, TBSZIP_STRING); diff --git a/tests/phpunit/CRM/Contact/Form/Task/PrintDocumentTest.php b/tests/phpunit/CRM/Contact/Form/Task/PrintDocumentTest.php new file mode 100644 index 0000000000..ecbf7d5a2d --- /dev/null +++ b/tests/phpunit/CRM/Contact/Form/Task/PrintDocumentTest.php @@ -0,0 +1,94 @@ +_contactIds = array( + $this->individualCreate(array('first_name' => 'Antonia', 'last_name' => 'D`souza')), + $this->individualCreate(array('first_name' => 'Anthony', 'last_name' => 'Collins')), + ); + $this->_docTypes = CRM_Core_SelectValues::documentApplicationType(); + } + + /** + * Test the documents got token replaced rightfully. + */ + public function testPrintDocument() { + foreach (array('docx', 'odt') as $docType) { + $formValues = array( + 'document_file' => array( + 'name' => __DIR__ . "/sample_documents/Template.$docType", + 'type' => $this->_docTypes[$docType], + ), + ); + $this->_testDocumentContent($formValues, $docType); + } + } + + /** + * Assert the content of document + * + * @param array $formValues + * @param array $type + */ + public function _testDocumentContent($formValues, $type) { + $html = array(); + $form = new CRM_Contact_Form_Task_PDFLetterCommon(); + list($formValues, $categories, $html_message, $messageToken, $returnProperties) = $form->processMessageTemplate($formValues); + list($html_message, $zip) = CRM_Utils_PDF_Document::unzipDoc($formValues['document_file_path'], $formValues['document_type']); + + foreach ($this->_contactIds as $item => $contactId) { + $params = array('contact_id' => $contactId); + list($contact) = CRM_Utils_Token::getTokenDetails($params, + $returnProperties, + FALSE, + FALSE, + NULL, + $messageToken, + 'CRM_Contact_Form_Task_PDFLetterCommon' + ); + $html[] = CRM_Utils_Token::replaceContactTokens($html_message, $contact[$contactId], TRUE, $messageToken); + } + + $returnContent = CRM_Utils_PDF_Document::printDocuments($formValues['document_file_path'], $html, $type, $zip, TRUE); + $returnContent = strip_tags($returnContent); + + $this->assertTrue(strpos($returnContent, 'Hello Antonia D`souza') !== 0); + $this->assertTrue(strpos($returnContent, 'Hello Anthony Collins') !== 0); + } + +} diff --git a/tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.docx b/tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.docx new file mode 100644 index 0000000000000000000000000000000000000000..ee02098d0bbe4a98d18b7b519a4e2a14fb61f1b8 GIT binary patch literal 4768 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 literal 0 HcmV?d00001 diff --git a/tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.odt b/tests/phpunit/CRM/Contact/Form/Task/sample_documents/Template.odt new file mode 100644 index 0000000000000000000000000000000000000000..cced3a7e4f638e521a08138293f280eee9594c86 GIT binary patch literal 8506 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 literal 0 HcmV?d00001 -- 2.25.1