Merge pull request #4686 from lcdservices/CRM-15698-b
[civicrm-core.git] / CRM / Contact / Form / Task / LabelCommon.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 /**
37 * This class provides the common functionality for sending email to
38 * one or a group of contact ids. This class is reused by all the search
39 * components in CiviCRM (since they all have send email as a task)
40 */
41 class CRM_Contact_Form_Task_LabelCommon {
42 /**
43 * Check for presence of tokens to be swapped out
44 *
45 * @param array $contact
46 * @param array $mailingFormatProperties
47 * @param array $tokenFields
48 *
49 * @return bool
50 */
51 function tokenIsFound($contact, $mailingFormatProperties, $tokenFields) {
52 foreach (array_merge($mailingFormatProperties, array_fill_keys($tokenFields, 1)) as $key => $dontCare) {
53 //we should not consider addressee for data exists, CRM-6025
54 if ($key != 'addressee' && !empty($contact[$key])) {
55 return TRUE;
56 }
57 }
58 return FALSE;
59 }
60 /**
61 * Create labels (pdf)
62 *
63 * @param array $contactRows assciated array of contact data
64 * @param string $format format in which labels needs to be printed
65 * @param string $fileName The name of the file to save the label in
66 *
67 * @return null
68 * @access public
69 */
70 static function createLabel(&$contactRows, &$format, $fileName = 'MailingLabels_CiviCRM.pdf') {
71 $pdf = new CRM_Utils_PDF_Label($format, 'mm');
72 $pdf->Open();
73 $pdf->AddPage();
74
75 //build contact string that needs to be printed
76 $val = NULL;
77 foreach ($contactRows as $row => $value) {
78 foreach ($value as $k => $v) {
79 $val .= "$v\n";
80 }
81
82 $pdf->AddPdfLabel($val);
83 $val = '';
84 }
85 $pdf->Output($fileName, 'D');
86 }
87
88
89 /**
90 * Get the rows for the labels
91 *
92 * @param $contactIDs
93 * @param integer $locationTypeID
94 * @param boolean $respectDoNotMail
95 * @param $mergeSameAddress
96 * @param bool $mergeSameHousehold UNUSED
97 *
98 * @return array of rows for labels
99 * @access public
100 */
101
102 static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, $mergeSameAddress, $mergeSameHousehold) {
103 $locName = NULL;
104 //get the address format sequence from the config file
105 $addressReturnProperties = CRM_Contact_Form_Task_LabelCommon::getAddressReturnProperties();
106
107 //build the returnproperties
108 $returnProperties = array('display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1);
109 $mailingFormat = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
110 'mailing_format'
111 );
112
113 $mailingFormatProperties = array();
114 if ($mailingFormat) {
115 $mailingFormatProperties = CRM_Contact_Form_Task_LabelCommon::regexReturnProperties($mailingFormat);
116 $returnProperties = array_merge($returnProperties, $mailingFormatProperties);
117 }
118
119 $customFormatProperties = array();
120 if (stristr($mailingFormat, 'custom_')) {
121 foreach ($mailingFormatProperties as $token => $true) {
122 if (substr($token, 0, 7) == 'custom_') {
123 if (empty($customFormatProperties[$token])) {
124 $customFormatProperties[$token] = $mailingFormatProperties[$token];
125 }
126 }
127 }
128 }
129 $returnProperties = array_merge($returnProperties, $customFormatProperties);
130
131 if ($mergeSameAddress) {
132 // we need first name/last name for summarising to avoid spillage
133 $returnProperties['first_name'] = 1;
134 $returnProperties['last_name'] = 1;
135 }
136
137 //get the contacts information
138 $params = $custom = array();
139 foreach ($contactIDs as $key => $contactID) {
140 $params[] = array(
141 CRM_Core_Form::CB_PREFIX . $contactID,
142 '=', 1, 0, 0,
143 );
144 }
145
146 // fix for CRM-2651
147 if (!empty($respectDoNotMail['do_not_mail'])) {
148 $params[] = array('do_not_mail', '=', 0, 0, 0);
149 }
150 // fix for CRM-2613
151 $params[] = array('is_deceased', '=', 0, 0, 0);
152
153 if ($locationTypeID) {
154 $locType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
155 $locName = $locType[$locationTypeID];
156 $location = array('location' => array("{$locName}" => $addressReturnProperties));
157 $returnProperties = array_merge($returnProperties, $location);
158 $params[] = array('location_type', '=', array($locationTypeID => 1), 0, 0);
159 }
160 else {
161 $returnProperties = array_merge($returnProperties, $addressReturnProperties);
162 }
163
164 foreach ($returnProperties as $name) {
165 $cfID = CRM_Core_BAO_CustomField::getKeyID($name);
166 if ($cfID) {
167 $custom[] = $cfID;
168 }
169 }
170
171 //get the total number of contacts to fetch from database.
172 $numberofContacts = count($contactIDs);
173 //this does the same as calling civicrm_api3('contact, get, array('id' => array('IN' => $this->_contactIds)
174 // except it also handles multiple locations
175 $query = new CRM_Contact_BAO_Query($params, $returnProperties);
176 $details = $query->apiQuery($params, $returnProperties, NULL, NULL, 0, $numberofContacts);
177
178 $messageToken = CRM_Utils_Token::getTokens($mailingFormat);
179 $details = $details[0];
180 $tokenFields = CRM_Contact_Form_Task_LabelCommon::getTokenData($details);
181
182 foreach ($contactIDs as $value) {
183 foreach ($custom as $cfID) {
184 if (isset($details[$value]["custom_{$cfID}"])) {
185 $details[$value]["custom_{$cfID}"] = CRM_Core_BAO_CustomField::getDisplayValue($details[$value]["custom_{$cfID}"], $cfID, $details[1]);
186 }
187 }
188 $contact = CRM_Utils_Array::value($value, $details);
189
190 if (is_a($contact, 'CRM_Core_Error')) {
191 return NULL;
192 }
193
194 // we need to remove all the "_id"
195 unset($contact['contact_id']);
196
197 if ($locName && !empty($contact[$locName])) {
198 // If location type is not primary, $contact contains
199 // one more array as "$contact[$locName] = array( values... )"
200
201 if(!CRM_Contact_Form_Task_LabelCommon::tokenIsFound($contact, $mailingFormatProperties, $tokenFields)) {
202 continue;
203 }
204
205 unset($contact[$locName]);
206
207 if (!empty($contact['county_id'])) {
208 unset($contact['county_id']);
209 }
210
211 foreach ($contact as $field => $fieldValue) {
212 $rows[$value][$field] = $fieldValue;
213 }
214
215 $valuesothers = array();
216 $paramsothers = array('contact_id' => $value);
217 $valuesothers = CRM_Core_BAO_Location::getValues($paramsothers, $valuesothers);
218 if ($locationTypeID) {
219 foreach ($valuesothers as $vals) {
220 if ( CRM_Utils_Array::value('location_type_id', $vals) ==
221 $locationTypeID) {
222 foreach ($vals as $k => $v) {
223 if (in_array($k, array(
224 'email', 'phone', 'im', 'openid'))) {
225 if ($k == 'im') {
226 $rows[$value][$k] = $v['1']['name'];
227 }
228 else {
229 $rows[$value][$k] = $v['1'][$k];
230 }
231 $rows[$value][$k . '_id'] = $v['1']['id'];
232 }
233 }
234 }
235 }
236 }
237 }
238 else {
239 if(!CRM_Contact_Form_Task_LabelCommon::tokenIsFound($contact, $mailingFormatProperties, $tokenFields)) {
240 continue;
241 }
242
243 if (!empty($contact['addressee_display'])) {
244 $contact['addressee_display'] = trim($contact['addressee_display']);
245 }
246 if (!empty($contact['addressee'])) {
247 $contact['addressee'] = $contact['addressee_display'];
248 }
249
250 // now create the rows for generating mailing labels
251 foreach ($contact as $field => $fieldValue) {
252 $rows[$value][$field] = $fieldValue;
253 }
254 }
255 }
256 // sigh couldn't extract out tokenfields yet
257 return array($rows, $tokenFields);
258 }
259
260 /**
261 * Extract the return properties from the mailing format
262 * @todo I'm placing bets this is a duplicate of code elsewhere - find & merge
263 * @param unknown_type $format
264 * @return multitype:number
265 */
266 function regexReturnProperties(&$format) {
267 $returnProperties = array();
268 $matches = array();
269 preg_match_all('/(?<!\{|\\\\)\{(\w+\.\w+)\}(?!\})/',
270 $format,
271 $matches,
272 PREG_PATTERN_ORDER
273 );
274 if ($matches[1]) {
275 foreach ($matches[1] as $token) {
276 list($type, $name) = preg_split('/\./', $token, 2);
277 if ($name) {
278 $returnProperties["{$name}"] = 1;
279 }
280 }
281 }
282
283 return $returnProperties;
284 }
285
286 /**
287 * Get array of return properties for address fields required for mailing label
288 * @return array return properites for address e.g
289 * array (
290 * - [street_address] => 1,
291 * - [supplemental_address_1] => 1,
292 * - [supplemental_address_2] => 1
293 * )
294 */
295 function getAddressReturnProperties() {
296 $mailingFormat = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
297 'mailing_format'
298 );
299
300 $addressFields = CRM_Utils_Address::sequence($mailingFormat);
301 $addressReturnProperties = array_fill_keys($addressFields, 1);
302
303 if (array_key_exists('postal_code', $addressReturnProperties)) {
304 $addressReturnProperties['postal_code_suffix'] = 1;
305 }
306 return $addressReturnProperties;
307 }
308
309 /**
310 * Get token list from mailing format & contacts
311 * @param unknown_type $contacts
312 * @return unknown
313 */
314 function getTokenData(&$contacts) {
315 $mailingFormat = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
316 'mailing_format'
317 );
318 $tokens = $tokenFields = array();
319 $messageToken = CRM_Utils_Token::getTokens($mailingFormat);
320
321 // also get all token values
322 CRM_Utils_Hook::tokenValues($contacts,
323 array_keys($contacts),
324 NULL,
325 $messageToken,
326 'CRM_Contact_Form_Task_LabelCommon'
327 );
328
329 CRM_Utils_Hook::tokens($tokens);
330
331 foreach ($tokens as $category => $catTokens) {
332 foreach ($catTokens as $token => $tokenName) {
333 $tokenFields[] = $token;
334 }
335 }
336 return $tokenFields;
337
338 }
339 /**
340 * Merge contacts with the Same address to get one shared label
341 * @param unknown_type $rows
342 */
343 function mergeSameAddress(&$rows) {
344 $uniqueAddress = array();
345 foreach (array_keys($rows) as $rowID) {
346 // load complete address as array key
347 $address =
348 trim($rows[$rowID]['street_address']) . trim($rows[$rowID]['city']) . trim($rows[$rowID]['state_province']) . trim($rows[$rowID]['postal_code']) . trim($rows[$rowID]['country']);
349 if (isset($rows[$rowID]['last_name'])) {
350 $name = $rows[$rowID]['last_name'];
351 }
352 else {
353 $name = $rows[$rowID]['display_name'];
354 }
355 $formatted = array(
356 'first_name' => $rows[$rowID]['first_name'],
357 'individual_prefix' => $rows[$rowID]['individual_prefix']
358 );
359 $format = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'display_name_format');
360 $firstNameWithPrefix = CRM_Utils_Address::format($formatted, $format, FALSE, FALSE, TRUE);
361 $firstNameWithPrefix = trim($firstNameWithPrefix);
362
363 // fill uniqueAddress array with last/first name tree
364 if (isset($uniqueAddress[$address])) {
365 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['first_name'] = $rows[$rowID]['first_name'];
366 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['addressee_display'] = $rows[$rowID]['addressee_display'];
367 // drop unnecessary rows
368 unset($rows[$rowID]);
369 // this is the first listing at this address
370 }
371 else {
372 $uniqueAddress[$address]['ID'] = $rowID;
373 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['first_name'] = $rows[$rowID]['first_name'];
374 $uniqueAddress[$address]['names'][$name][$firstNameWithPrefix]['addressee_display'] = $rows[$rowID]['addressee_display'];
375 }
376 }
377 foreach ($uniqueAddress as $address => $data) {
378 // copy data back to $rows
379 $count = 0;
380 // one last name list per row
381 foreach ($data['names'] as $last_name => $first_names) {
382 // too many to list
383 if ($count > 2) {
384 break;
385 }
386 if(count($first_names) == 1) {
387 $family = $first_names[current(array_keys($first_names))]['addressee_display'];
388 }
389 else {
390 // collapse the tree to summarize
391 $family = trim(implode(" & ", $first_names) . " " . $last_name);
392 }
393 if ($count) {
394 $processedNames .= "\n" . $family;
395 }
396 else {
397 // build display_name string
398 $processedNames = $family;
399 }
400 $count++;
401 }
402 $rows[$data['ID']]['addressee'] = $rows[$data['ID']]['addressee_display'] = $rows[$data['ID']]['display_name'] = $processedNames;
403 }
404 }
405
406 /**
407 * @param $rows
408 *
409 * @return array
410 */
411 function mergeSameHousehold(&$rows) {
412 # group selected contacts by type
413 $individuals = array();
414 $households = array();
415 foreach ($rows as $contact_id => $row) {
416 if ($row['contact_type'] == 'Household') {
417 $households[$contact_id] = $row;
418 }
419 elseif ($row['contact_type'] == 'Individual') {
420 $individuals[$contact_id] = $row;
421 }
422 }
423
424 # exclude individuals belonging to selected households
425 foreach ($households as $household_id => $row) {
426 $dao = new CRM_Contact_DAO_Relationship();
427 $dao->contact_id_b = $household_id;
428 $dao->find();
429 while ($dao->fetch()) {
430 $individual_id = $dao->contact_id_a;
431 if (array_key_exists($individual_id, $individuals)) {
432 unset($individuals[$individual_id]);
433 }
434 }
435 }
436
437 # merge back individuals and households
438 $rows = array_merge($individuals, $households);
439 return $rows;
440 }
441 }