Merge pull request #18360 from colemanw/fixMultiInProfile
[civicrm-core.git] / CRM / Contact / Form / Task / LabelCommon.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * This class provides the common functionality for sending email to one or a group of contact ids.
20 */
21 class CRM_Contact_Form_Task_LabelCommon {
22
23 /**
24 * Create labels (pdf).
25 *
26 * @param array $contactRows
27 * Associated array of contact data.
28 * @param string $format
29 * Format in which labels needs to be printed.
30 * @param string $fileName
31 * The name of the file to save the label in.
32 */
33 public static function createLabel($contactRows, $format, $fileName = 'MailingLabels_CiviCRM.pdf') {
34 if (CIVICRM_UF === 'UnitTests') {
35 throw new CRM_Core_Exception_PrematureExitException('civiExit called', ['rows' => $contactRows, 'format' => $format, 'file_name' => $fileName]);
36 }
37 $pdf = new CRM_Utils_PDF_Label($format, 'mm');
38 $pdf->Open();
39 $pdf->AddPage();
40
41 //build contact string that needs to be printed
42 $val = NULL;
43 foreach ((array) $contactRows as $row => $value) {
44 foreach ($value as $k => $v) {
45 $val .= "$v\n";
46 }
47
48 $pdf->AddPdfLabel($val);
49 $val = '';
50 }
51 $pdf->Output($fileName, 'D');
52 }
53
54 /**
55 * Get the rows for the labels.
56 *
57 * @param $contactIDs
58 * @param int $locationTypeID
59 * @param bool $respectDoNotMail
60 * @param $mergeSameAddress
61 * @param bool $mergeSameHousehold
62 * UNUSED.
63 *
64 * @return array
65 * Array of rows for labels
66 */
67 public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, $mergeSameAddress, $mergeSameHousehold) {
68 $locName = NULL;
69 $rows = [];
70 //get the address format sequence from the config file
71 $addressReturnProperties = CRM_Contact_Form_Task_LabelCommon::getAddressReturnProperties();
72
73 //build the return properties
74 $returnProperties = ['display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1];
75 $mailingFormat = Civi::settings()->get('mailing_format');
76
77 $mailingFormatProperties = [];
78 if ($mailingFormat) {
79 $mailingFormatProperties = CRM_Utils_Token::getReturnProperties($mailingFormat);
80 $returnProperties = array_merge($returnProperties, $mailingFormatProperties);
81 }
82
83 $customFormatProperties = [];
84 if (stristr($mailingFormat, 'custom_')) {
85 foreach ($mailingFormatProperties as $token => $true) {
86 if (substr($token, 0, 7) == 'custom_') {
87 if (empty($customFormatProperties[$token])) {
88 $customFormatProperties[$token] = $mailingFormatProperties[$token];
89 }
90 }
91 }
92 }
93 $returnProperties = array_merge($returnProperties, $customFormatProperties);
94
95 if ($mergeSameAddress) {
96 // we need first name/last name for summarising to avoid spillage
97 $returnProperties['first_name'] = 1;
98 $returnProperties['last_name'] = 1;
99 }
100
101 //get the contacts information
102 $params = $custom = [];
103 foreach ($contactIDs as $key => $contactID) {
104 $params[] = [
105 CRM_Core_Form::CB_PREFIX . $contactID,
106 '=',
107 1,
108 0,
109 0,
110 ];
111 }
112
113 // fix for CRM-2651
114 if (!empty($respectDoNotMail['do_not_mail'])) {
115 $params[] = ['do_not_mail', '=', 0, 0, 0];
116 }
117 // fix for CRM-2613
118 $params[] = ['is_deceased', '=', 0, 0, 0];
119
120 if ($locationTypeID) {
121 $locType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
122 $locName = $locType[$locationTypeID];
123 $location = ['location' => ["{$locName}" => $addressReturnProperties]];
124 $returnProperties = array_merge($returnProperties, $location);
125 $params[] = ['location_type', '=', [$locationTypeID => 1], 0, 0];
126 }
127 else {
128 $returnProperties = array_merge($returnProperties, $addressReturnProperties);
129 }
130
131 foreach ($returnProperties as $name) {
132 $cfID = CRM_Core_BAO_CustomField::getKeyID($name);
133 if ($cfID) {
134 $custom[] = $cfID;
135 }
136 }
137
138 //get the total number of contacts to fetch from database.
139 $numberofContacts = count($contactIDs);
140 //this does the same as calling civicrm_api3('contact, get, array('id' => array('IN' => $this->_contactIds)
141 // except it also handles multiple locations
142 [$details] = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, NULL, NULL, 0, $numberofContacts);
143
144 // $details is an array of [ contactID => contactDetails ]
145 $tokenFields = CRM_Contact_Form_Task_LabelCommon::getTokenData($details);
146
147 foreach ($contactIDs as $value) {
148 foreach ($custom as $cfID) {
149 if (isset($details[$value]["custom_{$cfID}"])) {
150 $details[$value]["custom_{$cfID}"] = CRM_Core_BAO_CustomField::displayValue($details[$value]["custom_{$cfID}"], $cfID);
151 }
152 }
153 $contact = $details[$value] ?? NULL;
154
155 if (is_a($contact, 'CRM_Core_Error')) {
156 return NULL;
157 }
158
159 // we need to remove all the "_id"
160 unset($contact['contact_id']);
161
162 if ($locName && !empty($contact[$locName])) {
163 // If location type is not primary, $contact contains
164 // one more array as "$contact[$locName] = array( values... )"
165
166 if (!CRM_Contact_Form_Task_Label::tokenIsFound($contact, $mailingFormatProperties, $tokenFields)) {
167 continue;
168 }
169
170 unset($contact[$locName]);
171
172 if (!empty($contact['county_id'])) {
173 unset($contact['county_id']);
174 }
175
176 foreach ($contact as $field => $fieldValue) {
177 $rows[$value][$field] = $fieldValue;
178 }
179
180 $valuesothers = [];
181 $paramsothers = ['contact_id' => $value];
182 $valuesothers = CRM_Core_BAO_Location::getValues($paramsothers, $valuesothers);
183 if ($locationTypeID) {
184 foreach ($valuesothers as $vals) {
185 if (CRM_Utils_Array::value('location_type_id', $vals) ==
186 $locationTypeID
187 ) {
188 foreach ($vals as $k => $v) {
189 if (in_array($k, [
190 'email',
191 'phone',
192 'im',
193 'openid',
194 ])) {
195 if ($k == 'im') {
196 $rows[$value][$k] = $v['1']['name'];
197 }
198 else {
199 $rows[$value][$k] = $v['1'][$k];
200 }
201 $rows[$value][$k . '_id'] = $v['1']['id'];
202 }
203 }
204 }
205 }
206 }
207 }
208 else {
209 if (!CRM_Contact_Form_Task_Label::tokenIsFound($contact, $mailingFormatProperties, $tokenFields)) {
210 continue;
211 }
212
213 if (!empty($contact['addressee_display'])) {
214 $contact['addressee_display'] = trim($contact['addressee_display']);
215 }
216 if (!empty($contact['addressee'])) {
217 $contact['addressee'] = $contact['addressee_display'];
218 }
219
220 // now create the rows for generating mailing labels
221 foreach ($contact as $field => $fieldValue) {
222 $rows[$value][$field] = $fieldValue;
223 }
224 }
225 }
226 // sigh couldn't extract out tokenfields yet
227 return [$rows, $tokenFields];
228 }
229
230 /**
231 * Get array of return properties for address fields required for mailing label.
232 *
233 * @return array
234 * return properties for address e.g
235 * [street_address => 1, supplemental_address_1 => 1, supplemental_address_2 => 1]
236 */
237 public static function getAddressReturnProperties() {
238 $mailingFormat = Civi::settings()->get('mailing_format');
239
240 $addressFields = CRM_Utils_Address::sequence($mailingFormat);
241 $addressReturnProperties = array_fill_keys($addressFields, 1);
242
243 if (array_key_exists('postal_code', $addressReturnProperties)) {
244 $addressReturnProperties['postal_code_suffix'] = 1;
245 }
246 return $addressReturnProperties;
247 }
248
249 /**
250 * Get token list from mailing format & contacts
251 * @param array $contacts
252 * @return array
253 */
254 public static function getTokenData(&$contacts) {
255 $mailingFormat = Civi::settings()->get('mailing_format');
256 $tokens = $tokenFields = [];
257 $messageToken = CRM_Utils_Token::getTokens($mailingFormat);
258
259 // also get all token values
260 CRM_Utils_Hook::tokenValues($contacts,
261 array_keys($contacts),
262 NULL,
263 $messageToken,
264 'CRM_Contact_Form_Task_LabelCommon'
265 );
266
267 CRM_Utils_Hook::tokens($tokens);
268
269 foreach ($tokens as $category => $catTokens) {
270 foreach ($catTokens as $token => $tokenName) {
271 $tokenFields[] = $token;
272 }
273 }
274 return $tokenFields;
275
276 }
277
278 /**
279 * @param array $rows
280 *
281 * @return array
282 */
283 public function mergeSameHousehold(&$rows) {
284 // group selected contacts by type
285 $individuals = [];
286 $households = [];
287 foreach ($rows as $contact_id => $row) {
288 if ($row['contact_type'] == 'Household') {
289 $households[$contact_id] = $row;
290 }
291 elseif ($row['contact_type'] == 'Individual') {
292 $individuals[$contact_id] = $row;
293 }
294 }
295
296 // exclude individuals belonging to selected households
297 foreach ($households as $household_id => $row) {
298 $dao = new CRM_Contact_DAO_Relationship();
299 $dao->contact_id_b = $household_id;
300 $dao->find();
301 while ($dao->fetch()) {
302 $individual_id = $dao->contact_id_a;
303 if (array_key_exists($individual_id, $individuals)) {
304 unset($individuals[$individual_id]);
305 }
306 }
307 }
308
309 // merge back individuals and households
310 $rows = array_merge($individuals, $households);
311 return $rows;
312 }
313
314 }