CRM-10551: Coding standards
[civicrm-core.git] / CRM / Core / BAO / Block.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 * add static functions to include some common functionality
35 * used across location sub object BAO classes
36 *
37 */
38class CRM_Core_BAO_Block {
39
40 /**
fe482240 41 * Fields that are required for a valid block.
6a488035
TO
42 */
43 static $requiredBlockFields = array(
44 'email' => array('email'),
45 'phone' => array('phone'),
46 'im' => array('name'),
21dfd5f5 47 'openid' => array('openid'),
6a488035
TO
48 );
49
50 /**
51 * Given the list of params in the params array, fetch the object
52 * and store the values in the values array
53 *
6a0b768e
TO
54 * @param string $blockName
55 * Name of the above object.
56 * @param array $params
57 * Input parameters to find object.
77b97be7 58 *
a6c01b45 59 * @return array
16b10e64 60 * Array of $block objects.
6a488035 61 */
00be9182 62 public static function &getValues($blockName, $params) {
6a488035
TO
63 if (empty($params)) {
64 return NULL;
65 }
e52506b0 66 $BAOString = 'CRM_Core_BAO_' . $blockName;
481a74f4 67 $block = new $BAOString();
6a488035
TO
68
69 $blocks = array();
70 if (!isset($params['entity_table'])) {
71 $block->contact_id = $params['contact_id'];
72 if (!$block->contact_id) {
73 CRM_Core_Error::fatal();
74 }
75 $blocks = self::retrieveBlock($block, $blockName);
76 }
77 else {
78 $blockIds = self::getBlockIds($blockName, NULL, $params);
79
80 if (empty($blockIds)) {
81 return $blocks;
82 }
83
84 $count = 1;
85 foreach ($blockIds as $blockId) {
481a74f4 86 $block = new $BAOString();
353ffa53
TO
87 $block->id = $blockId['id'];
88 $getBlocks = self::retrieveBlock($block, $blockName);
6a488035
TO
89 $blocks[$count++] = array_pop($getBlocks);
90 }
91 }
92
93 return $blocks;
94 }
95
96 /**
97 * Given the list of params in the params array, fetch the object
98 * and store the values in the values array
99 *
6a0b768e
TO
100 * @param Object $block
101 * Typically a Phone|Email|IM|OpenID object.
102 * @param string $blockName
103 * Name of the above object.
77b97be7 104 *
a6c01b45 105 * @return array
16b10e64 106 * Array of $block objects.
6a488035 107 */
00be9182 108 public static function retrieveBlock(&$block, $blockName) {
6a488035
TO
109 // we first get the primary location due to the order by clause
110 $block->orderBy('is_primary desc, id');
111 $block->find();
112
113 $count = 1;
114 $blocks = array();
115 while ($block->fetch()) {
116 CRM_Core_DAO::storeValues($block, $blocks[$count]);
117 //unset is_primary after first block. Due to some bug in earlier version
118 //there might be more than one primary blocks, hence unset is_primary other than first
119 if ($count > 1) {
120 unset($blocks[$count]['is_primary']);
121 }
122 $count++;
123 }
124
125 return $blocks;
126 }
127
128 /**
fe482240 129 * Check if the current block object has any valid data.
6a488035 130 *
6a0b768e
TO
131 * @param array $blockFields
132 * Array of fields that are of interest for this object.
133 * @param array $params
134 * Associated array of submitted fields.
6a488035 135 *
317fceb4 136 * @return bool
a6c01b45 137 * true if the block has data, otherwise false
6a488035 138 */
00be9182 139 public static function dataExists($blockFields, &$params) {
6a488035
TO
140 foreach ($blockFields as $field) {
141 if (CRM_Utils_System::isNull(CRM_Utils_Array::value($field, $params))) {
142 return FALSE;
143 }
144 }
145 return TRUE;
146 }
147
148 /**
fe482240 149 * Check if the current block exits.
6a488035 150 *
6a0b768e
TO
151 * @param string $blockName
152 * Bloack name.
153 * @param array $params
154 * Associated array of submitted fields.
6a488035 155 *
317fceb4 156 * @return bool
a6c01b45 157 * true if the block exits, otherwise false
6a488035 158 */
00be9182 159 public static function blockExists($blockName, &$params) {
6a488035 160 // return if no data present
a7488080 161 if (empty($params[$blockName]) || !is_array($params[$blockName])) {
6a488035
TO
162 return FALSE;
163 }
164
165 return TRUE;
166 }
167
168 /**
fe482240 169 * Get all block ids for a contact.
6a488035 170 *
6a0b768e
TO
171 * @param string $blockName
172 * Block name.
173 * @param int $contactId
174 * Contact id.
fd31fa4c
EM
175 *
176 * @param null $entityElements
177 * @param bool $updateBlankLocInfo
6a488035 178 *
a6c01b45
CW
179 * @return array
180 * formatted array of block ids
6a488035 181 *
6a488035 182 */
00be9182 183 public static function getBlockIds($blockName, $contactId = NULL, $entityElements = NULL, $updateBlankLocInfo = FALSE) {
6a488035 184 $allBlocks = array();
e4f5a5f9 185
6a488035
TO
186 $name = ucfirst($blockName);
187 if ($blockName == 'im') {
188 $name = 'IM';
189 }
190 elseif ($blockName == 'openid') {
191 $name = 'OpenID';
192 }
193
e4f5a5f9 194 $baoString = 'CRM_Core_BAO_' . $name;
6a488035 195 if ($contactId) {
e4f5a5f9 196 //@todo a cleverer way to do this would be to use the same fn name on each
197 // BAO rather than constructing the fn
198 // it would also be easier to grep for
199 // e.g $bao = new $baoString;
200 // $bao->getAllBlocks()
201 $baoFunction = 'all' . $name . 's';
481a74f4 202 $allBlocks = $baoString::$baoFunction($contactId, $updateBlankLocInfo);
6a488035
TO
203 }
204 elseif (!empty($entityElements) && $blockName != 'openid') {
e4f5a5f9 205 $baoFunction = 'allEntity' . $name . 's';
481a74f4 206 $allBlocks = $baoString::$baoFunction($entityElements);
6a488035
TO
207 }
208
209 return $allBlocks;
210 }
211
212 /**
fe482240 213 * Takes an associative array and creates a block.
6a488035 214 *
6a0b768e
TO
215 * @param string $blockName
216 * Block name.
217 * @param array $params
218 * (reference ) an assoc array of name/value pairs.
da6b46f4 219 * @param null $entity
100fef9d 220 * @param int $contactId
da6b46f4 221 *
a6c01b45
CW
222 * @return object
223 * CRM_Core_BAO_Block object on success, null otherwise
6a488035 224 */
00be9182 225 public static function create($blockName, &$params, $entity = NULL, $contactId = NULL) {
7f660ef3 226
6a488035
TO
227 if (!self::blockExists($blockName, $params)) {
228 return NULL;
229 }
7f660ef3 230
d0a860ed 231 // Set up required information / defaults
6a488035 232 $entityElements = $blocks = array();
8ded2b4d
J
233 $resetPrimary = $primarySet = $billingSet = FALSE;
234 $contactId = NULL;
7f660ef3 235
8ded2b4d 236 $baoString = 'CRM_Core_BAO_' . ucfirst($blockName);
d0a860ed 237 $updateBlankLocInfo = CRM_Utils_Array::value('updateBlankLocInfo', $params, FALSE);
7f660ef3 238
6a488035
TO
239 if ($entity) {
240 $entityElements = array(
241 'entity_table' => $params['entity_table'],
242 'entity_id' => $params['entity_id'],
243 );
244 }
245 else {
8ded2b4d 246 $contactId = $params['contact_id'];
6a488035 247 }
7f660ef3 248
d0a860ed 249 // Get current and submitted values
8ded2b4d
J
250 $existingValues = self::getBlockIds($blockName, $contactId, $entityElements, $updateBlankLocInfo);
251 $submittedValues = $params[$blockName];
7f660ef3 252
d0a860ed 253 // For each submitted value
8ded2b4d 254 foreach ($submittedValues as $count => $submittedValue) {
7f660ef3 255
d0a860ed 256 // Set the contact ID
8ded2b4d 257 $submittedValue['contact_id'] = $contactId;
7f660ef3 258
d0a860ed
J
259 // If this is a primary value, and we haven't unset a primary value yet, and there are values on the contact
260 // Then unset any primary value currently on the Contact
8ded2b4d
J
261 if (!empty($submittedValue['is_primary']) && !$resetPrimary && is_array($existingValues)) {
262 foreach ($existingValues as $existingValueId => $existingValue) {
263 if (!empty($existingValue['is_primary'])) {
7f660ef3 264
d0a860ed 265 // @todo Can we refactor this?
8ded2b4d 266 $block = new $baoString();
6a488035
TO
267 $block->selectAdd();
268 $block->selectAdd("id, is_primary");
8ded2b4d 269 $block->id = $existingValue['id'];
6a488035
TO
270 if ($block->find(TRUE)) {
271 $block->is_primary = FALSE;
272 $block->save();
273 }
274 $block->free();
7f660ef3 275
d0a860ed 276 // Stop looping since we found a match
8ded2b4d 277 $resetPrimary = TRUE;
d0a860ed 278 break;
6a488035
TO
279 }
280 }
281 }
7f660ef3 282
d0a860ed 283 // If there is already an ID passed in
8ded2b4d 284 if (!empty($submittedValue['id'])) {
d0a860ed
J
285 // If the ID already exists on the contact
286 // Then we don't want to match on it later, so unset it
8ded2b4d
J
287 if (array_key_exists($submittedValue['id'], $existingValues)) {
288 unset($existingValues[$existingValueId]);
d0a860ed 289 }
8ded2b4d 290 // Otherwise it is a new value, so ignore the passed in ID
d0a860ed 291 else {
8ded2b4d 292 unset($submittedValue['id']);
d0a860ed 293 }
6a488035 294 }
7f660ef3 295
d0a860ed
J
296 // Otherwise, if there was no ID passed in
297 // Loop through the current values, and find the first match on location type
298 else {
8ded2b4d
J
299 foreach ($existingValues as $existingValueId => $existingValue) {
300 if ($existingValue['locationTypeId'] == $submittedValue['location_type_id']) {
7f660ef3 301
d0a860ed 302 // Also require a match on 'type id' for phone and IM blocks
8ded2b4d 303 $matchFound = FALSE;
7f660ef3 304
d0a860ed 305 if ($blockName == 'phone') {
8ded2b4d
J
306 if (CRM_Utils_Array::value('phoneTypeId', $existingValue) == CRM_Utils_Array::value('phone_type_id', $submittedValue)) {
307 $matchFound = TRUE;
6a488035 308 }
d0a860ed
J
309 }
310 elseif ($blockName == 'im') {
8ded2b4d
J
311 if (CRM_Utils_Array::value('providerId', $existingValue) == CRM_Utils_Array::value('provider_id', $submittedValue)) {
312 $matchFound = TRUE;
6a488035 313 }
d0a860ed
J
314 }
315 else {
8ded2b4d 316 $matchFound = TRUE;
d0a860ed 317 }
7f660ef3 318
d0a860ed 319 // If we found a match
8ded2b4d 320 if ($matchFound) {
d0a860ed 321 // Match up the ID
8ded2b4d 322 $submittedValue['id'] = $existingValue['id'];
d0a860ed
J
323 // If the submitted value is not primary, but the matched value is
324 // Then set the submitted value to be primary
8ded2b4d
J
325 if (empty($submittedValue['is_primary']) && !empty($existingValue['is_primary'])) {
326 $submittedValue['is_primary'] = 1;
6a488035 327 }
d0a860ed 328 // Remove the original value from the array so we don't match on it again
8ded2b4d 329 unset($existingValues[$existingValueId]);
d0a860ed 330 break;
6a488035
TO
331 }
332 }
333 }
334 }
7f660ef3 335
8ded2b4d
J
336 // Check if data exists in the submitted value
337 $dataExists = self::dataExists(self::$requiredBlockFields[$blockName], $submittedValue);
7f660ef3 338
d0a860ed 339 // If there is data
8ded2b4d 340 if ($dataExists) {
7f660ef3 341
8ded2b4d
J
342 // "There can be only one" primary / billing block
343 if (!$primarySet && !empty($submittedValue['is_primary'])) {
344 $submittedValue['is_primary'] = 1;
345 $primarySet = TRUE;
d0a860ed
J
346 }
347 else {
348 $contactFields['is_primary'] = 0;
349 }
7f660ef3 350
8ded2b4d
J
351 if (!$billingSet && !empty($submittedValue['is_billing'])) {
352 $submittedValue['is_billing'] = 1;
353 $billingSet = TRUE;
d0a860ed
J
354 }
355 else {
356 $contactFields['is_billing'] = 0;
357 }
7f660ef3 358
d0a860ed 359 // Add the value to the list of blocks
8ded2b4d 360 $blocks[] = $baoString::add($submittedValue);
6a488035 361 }
7f660ef3 362
d0a860ed
J
363 // Otherwise, if there is no data, and there is an ID, and we are deleting 'blanked' values
364 // Then delete it
8ded2b4d
J
365 elseif (!empty($submittedValue['id']) && $updateBlankLocInfo) {
366 self::blockDelete($blockName, array('id' => $submittedValue['id']));
6a488035 367 }
7f660ef3 368
d0a860ed 369 // Otherwise we ignore it
6a488035 370 else {
6a488035 371 }
7f660ef3 372
6a488035 373 }
7f660ef3 374
6a488035
TO
375 return $blocks;
376 }
377
378 /**
fe482240 379 * Delete block.
6a488035 380 *
6a0b768e
TO
381 * @param string $blockName
382 * Block name.
383 * @param int $params
384 * Associates array.
6a488035
TO
385 *
386 * @return void
6a488035 387 */
00be9182 388 public static function blockDelete($blockName, $params) {
6a488035
TO
389 $name = ucfirst($blockName);
390 if ($blockName == 'im') {
391 $name = 'IM';
392 }
393 elseif ($blockName == 'openid') {
394 $name = 'OpenID';
395 }
396
e4f5a5f9 397 $baoString = 'CRM_Core_DAO_' . $name;
481a74f4 398 $block = new $baoString();
6a488035
TO
399
400 $block->copyValues($params);
d424ffde
CW
401
402 // CRM-11006 add call to pre and post hook for delete action
6a488035
TO
403 CRM_Utils_Hook::pre('delete', $name, $block->id, CRM_Core_DAO::$_nullArray);
404 $block->delete();
405 CRM_Utils_Hook::post('delete', $name, $block->id, $block);
406 }
407
408 /**
409 * Handling for is_primary.
410 * $params is_primary could be
411 * # 1 - find other entries with is_primary = 1 & reset them to 0
412 * # 0 - make sure at least one entry is set to 1
413 * - if no other entry is 1 change to 1
414 * - if one other entry exists change that to 1
415 * - if more than one other entry exists change first one to 1
77b97be7 416 * @fixme - perhaps should choose by location_type
6a488035
TO
417 * # empty - same as 0 as once we have checked first step
418 * we know if it should be 1 or 0
419 *
420 * if $params['id'] is set $params['contact_id'] may need to be retrieved
421 *
77b97be7
EM
422 * @param array $params
423 * @param $class
424 *
425 * @throws API_Exception
6a488035
TO
426 */
427 public static function handlePrimary(&$params, $class) {
37a77d9c
TO
428 $table = CRM_Core_DAO_AllCoreTables::getTableForClass($class);
429 if (!$table) {
430 throw new API_Exception("Failed to locate table for class [$class]");
431 }
d195b60c 432
50fa40e8
CW
433 // contact_id in params might be empty or the string 'null' so cast to integer
434 $contactId = (int) CRM_Utils_Array::value('contact_id', $params);
435 // If id is set & we haven't been passed a contact_id, retrieve it
436 if (!empty($params['id']) && !isset($params['contact_id'])) {
6a488035
TO
437 $entity = new $class();
438 $entity->id = $params['id'];
439 $entity->find(TRUE);
440 $contactId = $entity->contact_id;
441 }
50fa40e8
CW
442 // If entity is not associated with contact, concept of is_primary not relevant
443 if (!$contactId) {
6a488035
TO
444 return;
445 }
446
447 // if params is_primary then set all others to not be primary & exit out
a7488080 448 if (!empty($params['is_primary'])) {
6a488035
TO
449 $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1";
450 $sqlParams = array(1 => array($contactId, 'Integer'));
451 // we don't want to create unecessary entries in the log_ tables so exclude the one we are working on
9b873358 452 if (!empty($params['id'])) {
6a488035
TO
453 $sql .= " AND id <> %2";
454 $sqlParams[2] = array($params['id'], 'Integer');
455 }
456 CRM_Core_DAO::executeQuery($sql, $sqlParams);
457 return;
458 }
459
460 //Check what other emails exist for the contact
461 $existingEntities = new $class();
462 $existingEntities->contact_id = $contactId;
463 $existingEntities->orderBy('is_primary DESC');
464 if (!$existingEntities->find(TRUE) || (!empty($params['id']) && $existingEntities->id == $params['id'])) {
465 // ie. if no others is set to be primary then this has to be primary set to 1 so change
466 $params['is_primary'] = 1;
467 return;
468 }
469 else {
470 /*
471 * If the only existing email is the one we are editing then we must set
472 * is_primary to 1
473 * CRM-10451
474 */
481a74f4 475 if ($existingEntities->N == 1 && $existingEntities->id == CRM_Utils_Array::value('id', $params)) {
6a488035
TO
476 $params['is_primary'] = 1;
477 return;
478 }
479
480 if ($existingEntities->is_primary == 1) {
481 return;
482 }
483 // so at this point we are only dealing with ones explicity setting is_primary to 0
484 // since we have reverse sorted by email we can either set the first one to
485 // primary or return if is already is
486 $existingEntities->is_primary = 1;
487 $existingEntities->save();
488 }
489 }
490
491 /**
fe482240 492 * Sort location array so primary element is first.
2a6da8d7 493 *
c490a46a 494 * @param array $locations
6a488035 495 */
9b873358 496 public static function sortPrimaryFirst(&$locations) {
6a488035
TO
497 uasort($locations, 'self::primaryComparison');
498 }
499
2aa397bc
TO
500 /**
501 * compare 2 locations to see which should go first based on is_primary
502 * (sort function for sortPrimaryFirst)
503 * @param array $location1
504 * @param array $location2
317fceb4 505 * @return int
2aa397bc 506 */
9b873358 507 public static function primaryComparison($location1, $location2) {
6a488035
TO
508 $l1 = CRM_Utils_Array::value('is_primary', $location1);
509 $l2 = CRM_Utils_Array::value('is_primary', $location2);
510 if ($l1 == $l2) {
511 return 0;
512 }
513 return ($l1 < $l2) ? -1 : 1;
514 }
96025800 515
6a488035 516}