3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class provides the common functionality for creating PDF letter for one or a group of contact ids.
21 class CRM_Contact_Form_Task_PDFLetterCommon
extends CRM_Core_Form_Task_PDFLetterCommon
{
23 protected static $tokenCategories;
27 * Array(string $machineName => string $label).
29 public static function getLoggingOptions() {
31 'none' => ts('Do not record'),
32 'multiple' => ts('Multiple activities (one per contact)'),
33 'combined' => ts('One combined activity'),
34 'combined-attached' => ts('One combined activity plus one file attachment'),
35 // 'multiple-attached' <== not worth the work
40 * Build all the data structures needed to build the form.
42 * @param CRM_Core_Form $form
44 public static function preProcess(&$form) {
45 CRM_Contact_Form_Task_EmailCommon
::preProcessFromAddress($form);
48 $dao = new CRM_Core_BAO_MessageTemplate();
51 while ($dao->fetch()) {
52 $messageText[$dao->id
] = $dao->msg_text
;
53 $messageSubject[$dao->id
] = $dao->msg_subject
;
56 $form->assign('message', $messageText);
57 $form->assign('messageSubject', $messageSubject);
58 parent
::preProcess($form);
62 * @param CRM_Core_Form $form
65 public static function preProcessSingle(&$form, $cid) {
66 $form->_contactIds
= explode(',', $cid);
67 // put contact display name in title for single contact mode
68 if (count($form->_contactIds
) === 1) {
69 CRM_Utils_System
::setTitle(ts('Print/Merge Document for %1', [1 => CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'display_name')]));
74 * Part of the post process which prepare and extract information from the template.
77 * @param array $formValues
80 * [$categories, $html_message, $messageToken, $returnProperties]
82 public static function processMessageTemplate($formValues) {
83 $html_message = self
::processTemplate($formValues);
85 $categories = self
::getTokenCategories();
87 //time being hack to strip ' '
88 //from particular letter line, CRM-6798
89 self
::formatMessage($html_message);
91 $messageToken = CRM_Utils_Token
::getTokens($html_message);
93 $returnProperties = [];
94 if (isset($messageToken['contact'])) {
95 foreach ($messageToken['contact'] as $key => $value) {
96 $returnProperties[$value] = 1;
100 return [$formValues, $categories, $html_message, $messageToken, $returnProperties];
104 * Process the form after the input has been submitted and validated.
106 * @param CRM_Core_Form $form
108 * @throws \CRM_Core_Exception
110 public static function postProcess(&$form) {
111 $formValues = $form->controller
->exportValues($form->getName());
112 list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self
::processMessageTemplate($formValues);
113 $skipOnHold = isset($form->skipOnHold
) ?
$form->skipOnHold
: FALSE;
114 $skipDeceased = isset($form->skipDeceased
) ?
$form->skipDeceased
: TRUE;
115 $html = $activityIds = [];
117 // CRM-16725 Skip creation of activities if user is previewing their PDF letter(s)
118 if (self
::isLiveMode($form)) {
119 $activityIds = self
::createActivities($form, $html_message, $form->_contactIds
, $formValues['subject'], CRM_Utils_Array
::value('campaign_id', $formValues));
122 if (!empty($formValues['document_file_path'])) {
123 list($html_message, $zip) = CRM_Utils_PDF_Document
::unzipDoc($formValues['document_file_path'], $formValues['document_type']);
126 foreach ($form->_contactIds
as $item => $contactId) {
128 $params = ['contact_id' => $contactId];
130 list($contact) = CRM_Utils_Token
::getTokenDetails($params,
136 'CRM_Contact_Form_Task_PDFLetterCommon'
139 if (civicrm_error($contact)) {
140 $notSent[] = $contactId;
144 $tokenHtml = CRM_Utils_Token
::replaceContactTokens($html_message, $contact[$contactId], TRUE, $messageToken);
145 if (!empty($form->_caseId
)) {
146 $caseId = $form->_caseId
;
148 if (empty($caseId) && !empty($form->_caseIds
[$item])) {
149 $caseId = $form->_caseIds
[$item];
152 $tokenHtml = CRM_Utils_Token
::replaceCaseTokens($caseId, $tokenHtml, $messageToken);
154 $tokenHtml = CRM_Utils_Token
::replaceHookTokens($tokenHtml, $contact[$contactId], $categories, TRUE);
156 if (defined('CIVICRM_MAIL_SMARTY') && CIVICRM_MAIL_SMARTY
) {
157 $smarty = CRM_Core_Smarty
::singleton();
158 // also add the contact tokens to the template
159 $smarty->assign_by_ref('contact', $contact);
160 $tokenHtml = $smarty->fetch("string:$tokenHtml");
163 $html[] = $tokenHtml;
167 if (self
::isLiveMode($form) && Civi
::settings()->get('recordGeneratedLetters') === 'combined-attached') {
168 if (count($activityIds) !== 1) {
169 throw new CRM_Core_Exception("When recordGeneratedLetters=combined-attached, there should only be one activity.");
171 $tee = CRM_Utils_ConsoleTee
::create()->start();
174 $type = $formValues['document_type'];
175 $mimeType = self
::getMimeType($type);
176 // ^^ Useful side-effect: consistently throws error for unrecognized types.
178 if ($type == 'pdf') {
179 $fileName = "CiviLetter.$type";
180 CRM_Utils_PDF_Utils
::html2pdf($html, $fileName, FALSE, $formValues);
182 elseif (!empty($formValues['document_file_path'])) {
183 $fileName = pathinfo($formValues['document_file_path'], PATHINFO_FILENAME
) . '.' . $type;
184 CRM_Utils_PDF_Document
::printDocuments($html, $fileName, $type, $zip);
187 $fileName = "CiviLetter.$type";
188 CRM_Utils_PDF_Document
::html2doc($html, $fileName, $formValues);
193 $content = file_get_contents($tee->getFileName(), NULL, NULL, NULL, 5);
194 if (empty($content)) {
195 throw new \
CRM_Core_Exception("Failed to capture document content (type=$type)!");
197 foreach ($activityIds as $activityId) {
198 civicrm_api3('Attachment', 'create', [
199 'entity_table' => 'civicrm_activity',
200 'entity_id' => $activityId,
202 'mime_type' => $mimeType,
204 'move-file' => $tee->getFileName(),
210 $form->postProcessHook();
212 CRM_Utils_System
::civiExit();
216 * @param CRM_Core_Form $form
217 * @param string $html_message
218 * @param array $contactIds
219 * @param string $subject
220 * @param int $campaign_id
221 * @param array $perContactHtml
224 * List of activity IDs.
225 * There may be 1 or more, depending on the system-settings
228 * @throws CRM_Core_Exception
230 public static function createActivities($form, $html_message, $contactIds, $subject, $campaign_id, $perContactHtml = []) {
233 'subject' => $subject,
234 'campaign_id' => $campaign_id,
235 'source_contact_id' => CRM_Core_Session
::singleton()->getLoggedInContactID(),
236 'activity_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Print PDF Letter'),
237 'activity_date_time' => date('YmdHis'),
238 'details' => $html_message,
240 if (!empty($form->_activityId
)) {
241 $activityParams +
= ['id' => $form->_activityId
];
245 switch (Civi
::settings()->get('recordGeneratedLetters')) {
250 // One activity per contact.
251 foreach ($contactIds as $i => $contactId) {
253 'target_contact_id' => $contactId,
255 if (!empty($form->_caseId
)) {
256 $fullParams['case_id'] = $form->_caseId
;
258 elseif (!empty($form->_caseIds
[$i])) {
259 $fullParams['case_id'] = $form->_caseIds
[$i];
262 if (isset($perContactHtml[$contactId])) {
263 $fullParams['details'] = implode('<hr>', $perContactHtml[$contactId]);
265 $activity = civicrm_api3('Activity', 'create', $fullParams);
266 $activityIds[$contactId] = $activity['id'];
272 case 'combined-attached':
273 // One activity with all contacts.
275 'target_contact_id' => $contactIds,
277 if (!empty($form->_caseId
)) {
278 $fullParams['case_id'] = $form->_caseId
;
280 elseif (!empty($form->_caseIds
)) {
281 $fullParams['case_id'] = $form->_caseIds
;
283 $activity = civicrm_api3('Activity', 'create', $fullParams);
284 $activityIds[] = $activity['id'];
288 throw new CRM_Core_Exception("Unrecognized option in recordGeneratedLetters: " . Civi
::settings()->get('recordGeneratedLetters'));
295 * Convert from a vague-type/file-extension to mime-type.
297 * @param string $type
299 * @throws \CRM_Core_Exception
301 private static function getMimeType($type) {
303 'pdf' => 'application/pdf',
304 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
305 'odt' => 'application/vnd.oasis.opendocument.text',
306 'html' => 'text/html',
308 if (isset($mimeTypes[$type])) {
309 return $mimeTypes[$type];
312 throw new \
CRM_Core_Exception("Cannot determine mime type");
317 * Get the categories required for rendering tokens.
321 protected static function getTokenCategories() {
322 if (!isset(Civi
::$statics[__CLASS__
]['token_categories'])) {
324 CRM_Utils_Hook
::tokens($tokens);
325 Civi
::$statics[__CLASS__
]['token_categories'] = array_keys($tokens);
327 return Civi
::$statics[__CLASS__
]['token_categories'];
331 * Is the form in live mode (as opposed to being run as a preview).
333 * Returns true if the user has clicked the Download Document button on a
334 * Print/Merge Document (PDF Letter) search task form, or false if the Preview
335 * button was clicked.
337 * @param CRM_Core_Form $form
340 * TRUE if the Download Document button was clicked (also defaults to TRUE
341 * if the form controller does not exist), else FALSE
343 protected static function isLiveMode($form) {
344 // CRM-21255 - Hrm, CiviCase 4+5 seem to report buttons differently...
345 $buttonName = $form->controller
->getButtonName();
346 $c = $form->controller
->container();
347 $isLiveMode = ($buttonName == '_qf_PDF_upload') ||
isset($c['values']['PDF']['buttons']['_qf_PDF_upload']);