Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
bc77d7c0 TO |
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 | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
16 | */ |
17 | ||
18 | /** | |
07f8d162 | 19 | * This class provides the functionality to create PDF letter for a group of contacts or a single contact. |
6a488035 TO |
20 | */ |
21 | class CRM_Contribute_Form_Task_PDFLetter extends CRM_Contribute_Form_Task { | |
22 | ||
23 | /** | |
fe482240 | 24 | * All the existing templates in the system. |
6a488035 TO |
25 | * |
26 | * @var array | |
27 | */ | |
28 | public $_templates = NULL; | |
29 | ||
30 | public $_single = NULL; | |
31 | ||
32 | public $_cid = NULL; | |
33 | ||
34 | /** | |
fe482240 | 35 | * Build all the data structures needed to build the form. |
ed106721 | 36 | */ |
00be9182 | 37 | public function preProcess() { |
6a488035 TO |
38 | $this->skipOnHold = $this->skipDeceased = FALSE; |
39 | CRM_Contact_Form_Task_PDFLetterCommon::preProcess($this); | |
6a488035 | 40 | // store case id if present |
fe61faf3 CW |
41 | $this->_caseId = CRM_Utils_Request::retrieve('caseid', 'CommaSeparatedIntegers', $this, FALSE); |
42 | if (!empty($this->_caseId) && strpos($this->_caseId, ',')) { | |
43 | $this->_caseIds = explode(',', $this->_caseId); | |
44 | unset($this->_caseId); | |
45 | } | |
6a488035 TO |
46 | |
47 | // retrieve contact ID if this is 'single' mode | |
fe61faf3 | 48 | $cid = CRM_Utils_Request::retrieve('cid', 'CommaSeparatedIntegers', $this, FALSE); |
6a488035 TO |
49 | |
50 | $this->_activityId = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE); | |
51 | ||
52 | if ($cid) { | |
53 | CRM_Contact_Form_Task_PDFLetterCommon::preProcessSingle($this, $cid); | |
54 | $this->_single = TRUE; | |
6a488035 TO |
55 | } |
56 | else { | |
57 | parent::preProcess(); | |
58 | } | |
59 | $this->assign('single', $this->_single); | |
60 | } | |
61 | ||
186c9c17 EM |
62 | /** |
63 | * This virtual function is used to set the default values of | |
64 | * various form elements | |
65 | * | |
66 | * access public | |
67 | * | |
a6c01b45 CW |
68 | * @return array |
69 | * reference to the array of default values | |
186c9c17 | 70 | */ |
1330f57a | 71 | |
186c9c17 EM |
72 | /** |
73 | * @return array | |
74 | */ | |
00be9182 | 75 | public function setDefaultValues() { |
be2fb01f | 76 | $defaults = []; |
6a488035 | 77 | if (isset($this->_activityId)) { |
be2fb01f | 78 | $params = ['id' => $this->_activityId]; |
6a488035 | 79 | CRM_Activity_BAO_Activity::retrieve($params, $defaults); |
9c1bc317 | 80 | $defaults['html_message'] = $defaults['details'] ?? NULL; |
6a488035 | 81 | } |
b5fd2bef CW |
82 | else { |
83 | $defaults['thankyou_update'] = 1; | |
84 | } | |
6a488035 TO |
85 | $defaults = $defaults + CRM_Contact_Form_Task_PDFLetterCommon::setDefaultValues(); |
86 | return $defaults; | |
87 | } | |
88 | ||
89 | /** | |
fe482240 | 90 | * Build the form object. |
6a488035 TO |
91 | */ |
92 | public function buildQuickForm() { | |
93 | //enable form element | |
94 | $this->assign('suppressForm', FALSE); | |
95 | ||
99ad38df MW |
96 | // Build common form elements |
97 | CRM_Contribute_Form_Task_PDFLetterCommon::buildQuickForm($this); | |
6a488035 TO |
98 | |
99 | // specific need for contributions | |
383c047b | 100 | $this->add('static', 'more_options_header', NULL, ts('Thank-you Letter Options')); |
6a488035 TO |
101 | $this->add('checkbox', 'receipt_update', ts('Update receipt dates for these contributions'), FALSE); |
102 | $this->add('checkbox', 'thankyou_update', ts('Update thank-you dates for these contributions'), FALSE); | |
6a488035 TO |
103 | |
104 | // Group options for tokens are not yet implemented. dgg | |
be2fb01f | 105 | $options = [ |
383c047b DG |
106 | '' => ts('- no grouping -'), |
107 | 'contact_id' => ts('Contact'), | |
108 | 'contribution_recur_id' => ts('Contact and Recurring'), | |
109 | 'financial_type_id' => ts('Contact and Financial Type'), | |
110 | 'campaign_id' => ts('Contact and Campaign'), | |
536f0e02 | 111 | 'payment_instrument_id' => ts('Contact and Payment Method'), |
be2fb01f CW |
112 | ]; |
113 | $this->addElement('select', 'group_by', ts('Group contributions by'), $options, [], "<br/>", FALSE); | |
383c047b | 114 | // this was going to be free-text but I opted for radio options in case there was a script injection risk |
be2fb01f | 115 | $separatorOptions = ['comma' => 'Comma', 'td' => 'Horizontal Table Cell', 'tr' => 'Vertical Table Cell', 'br' => 'Line Break']; |
698253db | 116 | |
383c047b | 117 | $this->addElement('select', 'group_by_separator', ts('Separator (grouped contributions)'), $separatorOptions); |
be2fb01f | 118 | $emailOptions = [ |
383c047b DG |
119 | '' => ts('Generate PDFs for printing (only)'), |
120 | 'email' => ts('Send emails where possible. Generate printable PDFs for contacts who cannot receive email.'), | |
121 | 'both' => ts('Send emails where possible. Generate printable PDFs for all contacts.'), | |
be2fb01f | 122 | ]; |
353ffa53 | 123 | if (CRM_Core_Config::singleton()->doNotAttachPDFReceipt) { |
97eaa51b EM |
124 | $emailOptions['pdfemail'] = ts('Send emails with an attached PDF where possible. Generate printable PDFs for contacts who cannot receive email.'); |
125 | $emailOptions['pdfemail_both'] = ts('Send emails with an attached PDF where possible. Generate printable PDFs for all contacts.'); | |
126 | } | |
be2fb01f | 127 | $this->addElement('select', 'email_options', ts('Print and email options'), $emailOptions, [], "<br/>", FALSE); |
ed106721 | 128 | |
be2fb01f | 129 | $this->addButtons([ |
1330f57a SL |
130 | [ |
131 | 'type' => 'upload', | |
132 | 'name' => ts('Make Thank-you Letters'), | |
133 | 'isDefault' => TRUE, | |
134 | ], | |
135 | [ | |
136 | 'type' => 'cancel', | |
137 | 'name' => ts('Done'), | |
138 | ], | |
139 | ]); | |
6a488035 TO |
140 | |
141 | } | |
142 | ||
143 | /** | |
fe482240 | 144 | * Process the form after the input has been submitted and validated. |
6a488035 TO |
145 | */ |
146 | public function postProcess() { | |
6a488035 TO |
147 | CRM_Contribute_Form_Task_PDFLetterCommon::postProcess($this); |
148 | } | |
96025800 | 149 | |
5ec6b0ad TM |
150 | /** |
151 | * List available tokens for this form. | |
152 | * | |
153 | * @return array | |
154 | */ | |
155 | public function listTokens() { | |
156 | $tokens = CRM_Core_SelectValues::contactTokens(); | |
8ee05f69 | 157 | $tokens = array_merge(CRM_Core_SelectValues::contributionTokens(), $tokens); |
cb9b8644 | 158 | $tokens = array_merge(CRM_Core_SelectValues::domainTokens(), $tokens); |
5ec6b0ad TM |
159 | return $tokens; |
160 | } | |
161 | ||
01a5461d | 162 | /** |
163 | * Generate the contribution array from the form, we fill in the contact details and determine any aggregation | |
164 | * around contact_id of contribution_recur_id | |
165 | * | |
166 | * @param string $groupBy | |
167 | * @param array $contributionIDs | |
168 | * @param array $returnProperties | |
169 | * @param bool $skipOnHold | |
170 | * @param bool $skipDeceased | |
171 | * @param array $messageToken | |
172 | * @param string $task | |
173 | * @param string $separator | |
174 | * @param bool $isIncludeSoftCredits | |
175 | * | |
176 | * @return array | |
177 | */ | |
178 | public static function buildContributionArray($groupBy, $contributionIDs, $returnProperties, $skipOnHold, $skipDeceased, $messageToken, $task, $separator, $isIncludeSoftCredits) { | |
179 | $contributions = $contacts = []; | |
180 | foreach ($contributionIDs as $item => $contributionId) { | |
181 | $contribution = CRM_Contribute_BAO_Contribution::getContributionTokenValues($contributionId, $messageToken)['values'][$contributionId]; | |
182 | $contribution['campaign'] = $contribution['contribution_campaign_title'] ?? NULL; | |
183 | $contributions[$contributionId] = $contribution; | |
184 | ||
185 | if ($isIncludeSoftCredits) { | |
186 | //@todo find out why this happens & add comments | |
187 | [$contactID] = explode('-', $item); | |
188 | $contactID = (int) $contactID; | |
189 | } | |
190 | else { | |
191 | $contactID = $contribution['contact_id']; | |
192 | } | |
193 | if (!isset($contacts[$contactID])) { | |
194 | $contacts[$contactID] = []; | |
195 | $contacts[$contactID]['contact_aggregate'] = 0; | |
196 | $contacts[$contactID]['combined'] = $contacts[$contactID]['contribution_ids'] = []; | |
197 | } | |
198 | ||
199 | $contacts[$contactID]['contact_aggregate'] += $contribution['total_amount']; | |
200 | $groupByID = empty($contribution[$groupBy]) ? 0 : $contribution[$groupBy]; | |
201 | ||
202 | $contacts[$contactID]['contribution_ids'][$groupBy][$groupByID][$contributionId] = TRUE; | |
203 | if (!isset($contacts[$contactID]['combined'][$groupBy]) || !isset($contacts[$contactID]['combined'][$groupBy][$groupByID])) { | |
204 | $contacts[$contactID]['combined'][$groupBy][$groupByID] = $contribution; | |
205 | $contacts[$contactID]['aggregates'][$groupBy][$groupByID] = $contribution['total_amount']; | |
206 | } | |
207 | else { | |
208 | $contacts[$contactID]['combined'][$groupBy][$groupByID] = self::combineContributions($contacts[$contactID]['combined'][$groupBy][$groupByID], $contribution, $separator); | |
209 | $contacts[$contactID]['aggregates'][$groupBy][$groupByID] += $contribution['total_amount']; | |
210 | } | |
211 | } | |
212 | // Assign the available contributions before calling tokens so hooks parsing smarty can access it. | |
213 | // Note that in core code you can only use smarty here if enable if for the whole site, incl | |
214 | // CiviMail, with a big performance impact. | |
215 | // Hooks allow more nuanced smarty usage here. | |
216 | CRM_Core_Smarty::singleton()->assign('contributions', $contributions); | |
217 | foreach ($contacts as $contactID => $contact) { | |
218 | [$tokenResolvedContacts] = CRM_Utils_Token::getTokenDetails(['contact_id' => $contactID], | |
219 | $returnProperties, | |
220 | $skipOnHold, | |
221 | $skipDeceased, | |
222 | NULL, | |
223 | $messageToken, | |
224 | $task | |
225 | ); | |
226 | $contacts[$contactID] = array_merge($tokenResolvedContacts[$contactID], $contact); | |
227 | } | |
228 | return [$contributions, $contacts]; | |
229 | } | |
230 | ||
231 | /** | |
232 | * We combine the contributions by adding the contribution to each field with the separator in | |
233 | * between the existing value and the new one. We put the separator there even if empty so it is clear what the | |
234 | * value for previous contributions was | |
235 | * | |
236 | * @param array $existing | |
237 | * @param array $contribution | |
238 | * @param string $separator | |
239 | * | |
240 | * @return array | |
241 | */ | |
242 | public static function combineContributions($existing, $contribution, $separator) { | |
243 | foreach ($contribution as $field => $value) { | |
244 | $existing[$field] = isset($existing[$field]) ? $existing[$field] . $separator : ''; | |
245 | $existing[$field] .= $value; | |
246 | } | |
247 | return $existing; | |
248 | } | |
249 | ||
250 | /** | |
251 | * We are going to retrieve the combined contribution and if smarty mail is enabled we | |
252 | * will also assign an array of contributions for this contact to the smarty template | |
253 | * | |
254 | * @param array $contact | |
255 | * @param array $contributions | |
256 | * @param $groupBy | |
257 | * @param int $groupByID | |
258 | */ | |
259 | public static function assignCombinedContributionValues($contact, $contributions, $groupBy, $groupByID) { | |
260 | CRM_Core_Smarty::singleton()->assign('contact_aggregate', $contact['contact_aggregate']); | |
261 | CRM_Core_Smarty::singleton() | |
262 | ->assign('contributions', $contributions); | |
263 | CRM_Core_Smarty::singleton()->assign('contribution_aggregate', $contact['aggregates'][$groupBy][$groupByID]); | |
264 | } | |
265 | ||
266 | /** | |
267 | * @param $contact | |
268 | * @param $formValues | |
269 | * @param $contribution | |
270 | * @param $groupBy | |
271 | * @param $contributions | |
272 | * @param $realSeparator | |
273 | * @param $tableSeparators | |
274 | * @param $messageToken | |
275 | * @param $html_message | |
276 | * @param $separator | |
277 | * @param $categories | |
278 | * @param bool $grouped | |
279 | * @param int $groupByID | |
280 | * | |
281 | * @return string | |
282 | * @throws \CRM_Core_Exception | |
283 | */ | |
284 | public static function generateHtml(&$contact, $contribution, $groupBy, $contributions, $realSeparator, $tableSeparators, $messageToken, $html_message, $separator, $grouped, $groupByID) { | |
285 | static $validated = FALSE; | |
286 | $html = NULL; | |
287 | ||
288 | $groupedContributions = array_intersect_key($contributions, $contact['contribution_ids'][$groupBy][$groupByID]); | |
289 | CRM_Contribute_Form_Task_PDFLetter::assignCombinedContributionValues($contact, $groupedContributions, $groupBy, $groupByID); | |
290 | ||
291 | if (empty($groupBy) || empty($contact['is_sent'][$groupBy][$groupByID])) { | |
292 | if (!$validated && in_array($realSeparator, $tableSeparators) && !CRM_Contribute_Form_Task_PDFLetterCommon::isValidHTMLWithTableSeparator($messageToken, $html_message)) { | |
293 | $realSeparator = ', '; | |
294 | CRM_Core_Session::setStatus(ts('You have selected the table cell separator, but one or more token fields are not placed inside a table cell. This would result in invalid HTML, so comma separators have been used instead.')); | |
295 | } | |
296 | $validated = TRUE; | |
297 | $html = str_replace($separator, $realSeparator, CRM_Contribute_Form_Task_PDFLetterCommon::resolveTokens($html_message, $contact, $contribution, $messageToken, $grouped, $separator, $groupedContributions)); | |
298 | } | |
299 | ||
300 | return $html; | |
301 | } | |
302 | ||
6a488035 | 303 | } |