INFRA-132 - CRM/Core - phpcbf
[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 +--------------------------------------------------------------------+
26*/
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 /**
41 * Fields that are required for a valid block
42 */
43 static $requiredBlockFields = array(
44 'email' => array('email'),
45 'phone' => array('phone'),
46 'im' => array('name'),
47 'openid' => array('openid')
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 *
6a488035 59 * @return array of $block objects.
6a488035
TO
60 * @static
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;
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) {
e52506b0 86 $block = new $BAOString( );
6a488035
TO
87 $block->id = $blockId['id'];
88 $getBlocks = self::retrieveBlock($block, $blockName);
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 *
6a488035 105 * @return array of $block objects.
6a488035
TO
106 * @static
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 /**
100fef9d 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
TO
135 *
136 * @return boolean true if the block has data, otherwise false
6a488035
TO
137 * @static
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 /**
100fef9d 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
TO
155 *
156 * @return boolean true if the block exits, otherwise false
6a488035
TO
157 * @static
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 /**
100fef9d 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
TO
178 *
179 * @return array $contactBlockIds formatted array of block ids
180 *
6a488035
TO
181 * @static
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';
202 $allBlocks = $baoString::$baoFunction( $contactId, $updateBlankLocInfo );
6a488035
TO
203 }
204 elseif (!empty($entityElements) && $blockName != 'openid') {
e4f5a5f9 205 $baoFunction = 'allEntity' . $name . 's';
206 $allBlocks = $baoString::$baoFunction( $entityElements );
6a488035
TO
207 }
208
209 return $allBlocks;
210 }
211
212 /**
100fef9d 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 *
6a488035 222 * @return object CRM_Core_BAO_Block object on success, null otherwise
6a488035
TO
223 * @static
224 */
00be9182 225 public static function create($blockName, &$params, $entity = NULL, $contactId = NULL) {
6a488035
TO
226 if (!self::blockExists($blockName, $params)) {
227 return NULL;
228 }
229
230 $name = ucfirst($blockName);
231 $contactId = NULL;
232 $isPrimary = $isBilling = TRUE;
233 $entityElements = $blocks = array();
234
235 if ($entity) {
236 $entityElements = array(
237 'entity_table' => $params['entity_table'],
238 'entity_id' => $params['entity_id'],
239 );
240 }
241 else {
242 $contactId = $params['contact_id'];
243 }
244
245 $updateBlankLocInfo = CRM_Utils_Array::value('updateBlankLocInfo', $params, FALSE);
246
247 //get existsing block ids.
248 $blockIds = self::getBlockIds($blockName, $contactId, $entityElements, $updateBlankLocInfo);
249
250 if (!$updateBlankLocInfo) {
251 $resetPrimaryId = NULL;
252 $primaryId = FALSE;
253 foreach ($params[$blockName] as $count => $value) {
254 $blockId = CRM_Utils_Array::value('id', $value);
255 if ($blockId) {
256 if (is_array($blockIds)
257 && array_key_exists($blockId, $blockIds)
258 ) {
259 unset($blockIds[$blockId]);
260 }
261 else {
262 unset($value['id']);
263 }
264 }
265 //lets allow to update primary w/ more cleanly.
8cc574cf 266 if (!$resetPrimaryId && !empty($value['is_primary'])) {
6a488035
TO
267 $primaryId = TRUE;
268 if (is_array($blockIds)) {
269 foreach ($blockIds as $blockId => $blockValue) {
a7488080 270 if (!empty($blockValue['is_primary'])) {
6a488035
TO
271 $resetPrimaryId = $blockId;
272 break;
273 }
274 }
275 }
276 if ($resetPrimaryId) {
e4f5a5f9 277 $baoString = 'CRM_Core_BAO_' . $blockName;
278 $block = new $baoString( );
6a488035
TO
279 $block->selectAdd();
280 $block->selectAdd("id, is_primary");
281 $block->id = $resetPrimaryId;
282 if ($block->find(TRUE)) {
283 $block->is_primary = FALSE;
284 $block->save();
285 }
286 $block->free();
287 }
288 }
289 }
290 }
291
292 foreach ($params[$blockName] as $count => $value) {
293 if (!is_array($value)) {
294 continue;
295 }
296 $contactFields = array(
297 'contact_id' => $contactId,
298 'location_type_id' => CRM_Utils_Array::value('location_type_id', $value),
299 );
300
301 //check for update
a7488080 302 if (empty($value['id']) &&
6a488035
TO
303 is_array($blockIds) && !empty($blockIds)
304 ) {
305 foreach ($blockIds as $blockId => $blockValue) {
306 if ($updateBlankLocInfo) {
a7488080 307 if (!empty($blockIds[$count])) {
6a488035
TO
308 $value['id'] = $blockIds[$count]['id'];
309 unset($blockIds[$count]);
310 }
311 }
312 else {
313 if ($blockValue['locationTypeId'] == CRM_Utils_Array::value('location_type_id', $value)) {
314 $valueId = FALSE;
315
316 if ($blockName == 'phone') {
317 $phoneTypeBlockValue = CRM_Utils_Array::value('phoneTypeId', $blockValue);
d5f1ee75 318 if ($phoneTypeBlockValue == CRM_Utils_Array::value('phone_type_id', $value)) {
6a488035
TO
319 $valueId = TRUE;
320 }
321 }
322 elseif ($blockName == 'im') {
323 $providerBlockValue = CRM_Utils_Array::value('providerId', $blockValue);
324 if ($providerBlockValue == $value['provider_id']) {
325 $valueId = TRUE;
326 }
327 }
328 else {
329 $valueId = TRUE;
330 }
331
332 if ($valueId) {
333 //assigned id as first come first serve basis
334 $value['id'] = $blockValue['id'];
8cc574cf 335 if (!$primaryId && !empty($blockValue['is_primary'])) {
6a488035
TO
336 $value['is_primary'] = $blockValue['is_primary'];
337 }
338 unset($blockIds[$blockId]);
339 break;
340 }
341 }
342 }
343 }
344 }
345
346 $dataExits = self::dataExists(self::$requiredBlockFields[$blockName], $value);
347
348 // Note there could be cases when block info already exist ($value[id] is set) for a contact/entity
349 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
350 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
a7488080 351 if (!empty($value['id']) && !$dataExits && $updateBlankLocInfo) {
6a488035
TO
352 //delete the existing record
353 self::blockDelete($blockName, array('id' => $value['id']));
354 continue;
355 }
356 elseif (!$dataExits) {
357 continue;
358 }
359
8cc574cf 360 if ($isPrimary && !empty($value['is_primary'])) {
6a488035
TO
361 $contactFields['is_primary'] = $value['is_primary'];
362 $isPrimary = FALSE;
363 }
364 else {
365 $contactFields['is_primary'] = 0;
366 }
367
8cc574cf 368 if ($isBilling && !empty($value['is_billing'])) {
6a488035
TO
369 $contactFields['is_billing'] = $value['is_billing'];
370 $isBilling = FALSE;
371 }
372 else {
373 $contactFields['is_billing'] = 0;
374 }
375
376 $blockFields = array_merge($value, $contactFields);
293daac2 377 $baoString = 'CRM_Core_BAO_' . $name;
378 $blocks[] = $baoString::add( $blockFields );
6a488035
TO
379 }
380
381 // we need to delete blocks that were deleted during update
382 if ($updateBlankLocInfo && !empty($blockIds)) {
383 foreach ($blockIds as $deleteBlock) {
a7488080 384 if (empty($deleteBlock['id'])) {
6a488035
TO
385 continue;
386 }
387 self::blockDelete($blockName, array('id' => $deleteBlock['id']));
388 }
389 }
390
391 return $blocks;
392 }
393
394 /**
100fef9d 395 * Delete block
6a488035 396 *
6a0b768e
TO
397 * @param string $blockName
398 * Block name.
399 * @param int $params
400 * Associates array.
6a488035
TO
401 *
402 * @return void
403 * @static
404 */
00be9182 405 public static function blockDelete($blockName, $params) {
6a488035
TO
406 $name = ucfirst($blockName);
407 if ($blockName == 'im') {
408 $name = 'IM';
409 }
410 elseif ($blockName == 'openid') {
411 $name = 'OpenID';
412 }
413
e4f5a5f9 414 $baoString = 'CRM_Core_DAO_' . $name;
415 $block = new $baoString( );
6a488035
TO
416
417 $block->copyValues($params);
418 /*
419 * CRM-11006 add call to pre and post hook for delete action
420 */
421 CRM_Utils_Hook::pre('delete', $name, $block->id, CRM_Core_DAO::$_nullArray);
422 $block->delete();
423 CRM_Utils_Hook::post('delete', $name, $block->id, $block);
424 }
425
426 /**
427 * Handling for is_primary.
428 * $params is_primary could be
429 * # 1 - find other entries with is_primary = 1 & reset them to 0
430 * # 0 - make sure at least one entry is set to 1
431 * - if no other entry is 1 change to 1
432 * - if one other entry exists change that to 1
433 * - if more than one other entry exists change first one to 1
77b97be7 434 * @fixme - perhaps should choose by location_type
6a488035
TO
435 * # empty - same as 0 as once we have checked first step
436 * we know if it should be 1 or 0
437 *
438 * if $params['id'] is set $params['contact_id'] may need to be retrieved
439 *
77b97be7
EM
440 * @param array $params
441 * @param $class
442 *
443 * @throws API_Exception
6a488035
TO
444 * @static
445 */
446 public static function handlePrimary(&$params, $class) {
37a77d9c
TO
447 $table = CRM_Core_DAO_AllCoreTables::getTableForClass($class);
448 if (!$table) {
449 throw new API_Exception("Failed to locate table for class [$class]");
450 }
d195b60c 451
50fa40e8
CW
452 // contact_id in params might be empty or the string 'null' so cast to integer
453 $contactId = (int) CRM_Utils_Array::value('contact_id', $params);
454 // If id is set & we haven't been passed a contact_id, retrieve it
455 if (!empty($params['id']) && !isset($params['contact_id'])) {
6a488035
TO
456 $entity = new $class();
457 $entity->id = $params['id'];
458 $entity->find(TRUE);
459 $contactId = $entity->contact_id;
460 }
50fa40e8
CW
461 // If entity is not associated with contact, concept of is_primary not relevant
462 if (!$contactId) {
6a488035
TO
463 return;
464 }
465
466 // if params is_primary then set all others to not be primary & exit out
a7488080 467 if (!empty($params['is_primary'])) {
6a488035
TO
468 $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1";
469 $sqlParams = array(1 => array($contactId, 'Integer'));
470 // we don't want to create unecessary entries in the log_ tables so exclude the one we are working on
471 if(!empty($params['id'])){
472 $sql .= " AND id <> %2";
473 $sqlParams[2] = array($params['id'], 'Integer');
474 }
475 CRM_Core_DAO::executeQuery($sql, $sqlParams);
476 return;
477 }
478
479 //Check what other emails exist for the contact
480 $existingEntities = new $class();
481 $existingEntities->contact_id = $contactId;
482 $existingEntities->orderBy('is_primary DESC');
483 if (!$existingEntities->find(TRUE) || (!empty($params['id']) && $existingEntities->id == $params['id'])) {
484 // ie. if no others is set to be primary then this has to be primary set to 1 so change
485 $params['is_primary'] = 1;
486 return;
487 }
488 else {
489 /*
490 * If the only existing email is the one we are editing then we must set
491 * is_primary to 1
492 * CRM-10451
493 */
494 if ( $existingEntities->N == 1 && $existingEntities->id == CRM_Utils_Array::value( 'id', $params ) ) {
495 $params['is_primary'] = 1;
496 return;
497 }
498
499 if ($existingEntities->is_primary == 1) {
500 return;
501 }
502 // so at this point we are only dealing with ones explicity setting is_primary to 0
503 // since we have reverse sorted by email we can either set the first one to
504 // primary or return if is already is
505 $existingEntities->is_primary = 1;
506 $existingEntities->save();
507 }
508 }
509
510 /**
511 * Sort location array so primary element is first
2a6da8d7 512 *
c490a46a 513 * @param array $locations
2a6da8d7 514 *
6a488035 515 */
00be9182 516 public static function sortPrimaryFirst(&$locations){
6a488035
TO
517 uasort($locations, 'self::primaryComparison');
518 }
519
2aa397bc
TO
520 /**
521 * compare 2 locations to see which should go first based on is_primary
522 * (sort function for sortPrimaryFirst)
523 * @param array $location1
524 * @param array $location2
525 * @return number
526 */
00be9182 527 public static function primaryComparison($location1, $location2){
6a488035
TO
528 $l1 = CRM_Utils_Array::value('is_primary', $location1);
529 $l2 = CRM_Utils_Array::value('is_primary', $location2);
530 if ($l1 == $l2) {
531 return 0;
532 }
533 return ($l1 < $l2) ? -1 : 1;
534 }
535}