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