Merge pull request #115 from dlobo/CRM-12088
[civicrm-core.git] / CRM / Core / BAO / Block.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
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 * 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 *
54 * @param Object $block typically a Phone|Email|IM|OpenID object
55 * @param string $blockName name of the above object
56 * @param array $params input parameters to find object
57 * @param array $values output values of the object
58 *
59 * @return array of $block objects.
60 * @access public
61 * @static
62 */
63 static function &getValues($blockName, $params) {
64 if (empty($params)) {
65 return NULL;
66 }
67 eval('$block = new CRM_Core_BAO_' . $blockName . '( );');
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) {
86 eval('$block = new CRM_Core_BAO_' . $blockName . '( );');
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 *
100 * @param Object $block typically a Phone|Email|IM|OpenID object
101 * @param string $blockName name of the above object
102 * @param array $values output values of the object
103 *
104 * @return array of $block objects.
105 * @access public
106 * @static
107 */
108 static function retrieveBlock(&$block, $blockName) {
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 /**
129 * check if the current block object has any valid data
130 *
131 * @param array $blockFields array of fields that are of interest for this object
132 * @param array $params associated array of submitted fields
133 *
134 * @return boolean true if the block has data, otherwise false
135 * @access public
136 * @static
137 */
138 static function dataExists($blockFields, &$params) {
139 foreach ($blockFields as $field) {
140 if (CRM_Utils_System::isNull(CRM_Utils_Array::value($field, $params))) {
141 return FALSE;
142 }
143 }
144 return TRUE;
145 }
146
147 /**
148 * check if the current block exits
149 *
150 * @param string $blockName bloack name
151 * @param array $params associated array of submitted fields
152 *
153 * @return boolean true if the block exits, otherwise false
154 * @access public
155 * @static
156 */
157 static function blockExists($blockName, &$params) {
158 // return if no data present
159 if (!CRM_Utils_Array::value($blockName, $params) || !is_array($params[$blockName])) {
160 return FALSE;
161 }
162
163 return TRUE;
164 }
165
166 /**
167 * Function to get all block ids for a contact
168 *
169 * @param string $blockName block name
170 * @param int $contactId contact id
171 *
172 * @return array $contactBlockIds formatted array of block ids
173 *
174 * @access public
175 * @static
176 */
177 static function getBlockIds($blockName, $contactId = NULL, $entityElements = NULL, $updateBlankLocInfo = FALSE) {
178 $allBlocks = array();
179 $name = ucfirst($blockName);
180 if ($blockName == 'im') {
181 $name = 'IM';
182 }
183 elseif ($blockName == 'openid') {
184 $name = 'OpenID';
185 }
186
187 if ($contactId) {
188 eval('$allBlocks = CRM_Core_BAO_' . $name . '::all' . $name . 's( $contactId, $updateBlankLocInfo );');
189 }
190 elseif (!empty($entityElements) && $blockName != 'openid') {
191 eval('$allBlocks = CRM_Core_BAO_' . $name . '::allEntity' . $name . 's( $entityElements );');
192 }
193
194 return $allBlocks;
195 }
196
197 /**
198 * takes an associative array and creates a block
199 *
200 * @param string $blockName block name
201 * @param array $params (reference ) an assoc array of name/value pairs
202 * @param array $requiredFields fields that's are required in a block
203 *
204 * @return object CRM_Core_BAO_Block object on success, null otherwise
205 * @access public
206 * @static
207 */
208 static function create($blockName, &$params, $entity = NULL, $contactId = NULL) {
209 if (!self::blockExists($blockName, $params)) {
210 return NULL;
211 }
212
213 $name = ucfirst($blockName);
214 $contactId = NULL;
215 $isPrimary = $isBilling = TRUE;
216 $entityElements = $blocks = array();
217
218 if ($entity) {
219 $entityElements = array(
220 'entity_table' => $params['entity_table'],
221 'entity_id' => $params['entity_id'],
222 );
223 }
224 else {
225 $contactId = $params['contact_id'];
226 }
227
228 $updateBlankLocInfo = CRM_Utils_Array::value('updateBlankLocInfo', $params, FALSE);
229
230 //get existsing block ids.
231 $blockIds = self::getBlockIds($blockName, $contactId, $entityElements, $updateBlankLocInfo);
232
233 if (!$updateBlankLocInfo) {
234 $resetPrimaryId = NULL;
235 $primaryId = FALSE;
236 foreach ($params[$blockName] as $count => $value) {
237 $blockId = CRM_Utils_Array::value('id', $value);
238 if ($blockId) {
239 if (is_array($blockIds)
240 && array_key_exists($blockId, $blockIds)
241 ) {
242 unset($blockIds[$blockId]);
243 }
244 else {
245 unset($value['id']);
246 }
247 }
248 //lets allow to update primary w/ more cleanly.
249 if (!$resetPrimaryId &&
250 CRM_Utils_Array::value('is_primary', $value)
251 ) {
252 $primaryId = TRUE;
253 if (is_array($blockIds)) {
254 foreach ($blockIds as $blockId => $blockValue) {
255 if (CRM_Utils_Array::value('is_primary', $blockValue)) {
256 $resetPrimaryId = $blockId;
257 break;
258 }
259 }
260 }
261 if ($resetPrimaryId) {
262 eval('$block = new CRM_Core_BAO_' . $blockName . '( );');
263 $block->selectAdd();
264 $block->selectAdd("id, is_primary");
265 $block->id = $resetPrimaryId;
266 if ($block->find(TRUE)) {
267 $block->is_primary = FALSE;
268 $block->save();
269 }
270 $block->free();
271 }
272 }
273 }
274 }
275
276 foreach ($params[$blockName] as $count => $value) {
277 if (!is_array($value)) {
278 continue;
279 }
280 $contactFields = array(
281 'contact_id' => $contactId,
282 'location_type_id' => CRM_Utils_Array::value('location_type_id', $value),
283 );
284
285 //check for update
286 if (!CRM_Utils_Array::value('id', $value) &&
287 is_array($blockIds) && !empty($blockIds)
288 ) {
289 foreach ($blockIds as $blockId => $blockValue) {
290 if ($updateBlankLocInfo) {
291 if (CRM_Utils_Array::value($count, $blockIds)) {
292 $value['id'] = $blockIds[$count]['id'];
293 unset($blockIds[$count]);
294 }
295 }
296 else {
297 if ($blockValue['locationTypeId'] == CRM_Utils_Array::value('location_type_id', $value)) {
298 $valueId = FALSE;
299
300 if ($blockName == 'phone') {
301 $phoneTypeBlockValue = CRM_Utils_Array::value('phoneTypeId', $blockValue);
302 if ($phoneTypeBlockValue == $value['phone_type_id']) {
303 $valueId = TRUE;
304 }
305 }
306 elseif ($blockName == 'im') {
307 $providerBlockValue = CRM_Utils_Array::value('providerId', $blockValue);
308 if ($providerBlockValue == $value['provider_id']) {
309 $valueId = TRUE;
310 }
311 }
312 else {
313 $valueId = TRUE;
314 }
315
316 if ($valueId) {
317 //assigned id as first come first serve basis
318 $value['id'] = $blockValue['id'];
319 if (!$primaryId && CRM_Utils_Array::value('is_primary', $blockValue)) {
320 $value['is_primary'] = $blockValue['is_primary'];
321 }
322 unset($blockIds[$blockId]);
323 break;
324 }
325 }
326 }
327 }
328 }
329
330 $dataExits = self::dataExists(self::$requiredBlockFields[$blockName], $value);
331
332 // Note there could be cases when block info already exist ($value[id] is set) for a contact/entity
333 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
334 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
335 if (CRM_Utils_Array::value('id', $value) && !$dataExits && $updateBlankLocInfo) {
336 //delete the existing record
337 self::blockDelete($blockName, array('id' => $value['id']));
338 continue;
339 }
340 elseif (!$dataExits) {
341 continue;
342 }
343
344 if ($isPrimary && CRM_Utils_Array::value('is_primary', $value)) {
345 $contactFields['is_primary'] = $value['is_primary'];
346 $isPrimary = FALSE;
347 }
348 else {
349 $contactFields['is_primary'] = 0;
350 }
351
352 if ($isBilling && CRM_Utils_Array::value('is_billing', $value)) {
353 $contactFields['is_billing'] = $value['is_billing'];
354 $isBilling = FALSE;
355 }
356 else {
357 $contactFields['is_billing'] = 0;
358 }
359
360 $blockFields = array_merge($value, $contactFields);
361 eval('$blocks[] = CRM_Core_BAO_' . $name . '::add( $blockFields );');
362 }
363
364 // we need to delete blocks that were deleted during update
365 if ($updateBlankLocInfo && !empty($blockIds)) {
366 foreach ($blockIds as $deleteBlock) {
367 if (!CRM_Utils_Array::value('id', $deleteBlock)) {
368 continue;
369 }
370 self::blockDelete($blockName, array('id' => $deleteBlock['id']));
371 }
372 }
373
374 return $blocks;
375 }
376
377 /**
378 * Function to delete block
379 *
380 * @param string $blockName block name
381 * @param int $params associates array
382 *
383 * @return void
384 * @static
385 */
386 static function blockDelete($blockName, $params) {
387 $name = ucfirst($blockName);
388 if ($blockName == 'im') {
389 $name = 'IM';
390 }
391 elseif ($blockName == 'openid') {
392 $name = 'OpenID';
393 }
394
395 require_once "CRM/Core/DAO/{$name}.php";
396 eval('$block = new CRM_Core_DAO_' . $name . '( );');
397
398 $block->copyValues($params);
399 /*
400 * CRM-11006 add call to pre and post hook for delete action
401 */
402 CRM_Utils_Hook::pre('delete', $name, $block->id, CRM_Core_DAO::$_nullArray);
403 $block->delete();
404 CRM_Utils_Hook::post('delete', $name, $block->id, $block);
405 }
406
407 /**
408 * Handling for is_primary.
409 * $params is_primary could be
410 * # 1 - find other entries with is_primary = 1 & reset them to 0
411 * # 0 - make sure at least one entry is set to 1
412 * - if no other entry is 1 change to 1
413 * - if one other entry exists change that to 1
414 * - if more than one other entry exists change first one to 1
415 * @fixme - perhaps should choose by location_type
416 * # empty - same as 0 as once we have checked first step
417 * we know if it should be 1 or 0
418 *
419 * if $params['id'] is set $params['contact_id'] may need to be retrieved
420 *
421 * @param array $params
422 * @static
423 */
424 public static function handlePrimary(&$params, $class) {
425 switch ($class) {
426 case 'CRM_Core_BAO_Phone':
427 $table = 'civicrm_phone';
428 break;
429
430 case 'CRM_Core_BAO_Email':
431 $table = 'civicrm_email';
432 break;
433
434 case 'CRM_Core_BAO_Address':
435 $table = 'civicrm_address';
436 break;
437 }
438 // if id is set & we don't have contact_id we need to retrieve it
439 $contactId = null;
440 if (!empty($params['id']) && empty($params['contact_id'])) {
441 $entity = new $class();
442 $entity->id = $params['id'];
443 $entity->find(TRUE);
444 $contactId = $entity->contact_id;
445 }
446 elseif (CRM_Utils_Array::value('contact_id', $params)) {
447 $contactId = $params['contact_id'];
448 }
449 if ( !$contactId ) {
450 // entity not associated with contact so concept of is_primary not relevant
451 return;
452 }
453
454 // if params is_primary then set all others to not be primary & exit out
455 if (CRM_Utils_Array::value('is_primary', $params)) {
456 $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1";
457 $sqlParams = array(1 => array($contactId, 'Integer'));
458 // we don't want to create unecessary entries in the log_ tables so exclude the one we are working on
459 if(!empty($params['id'])){
460 $sql .= " AND id <> %2";
461 $sqlParams[2] = array($params['id'], 'Integer');
462 }
463 CRM_Core_DAO::executeQuery($sql, $sqlParams);
464 return;
465 }
466
467 //Check what other emails exist for the contact
468 $existingEntities = new $class();
469 $existingEntities->contact_id = $contactId;
470 $existingEntities->orderBy('is_primary DESC');
471 if (!$existingEntities->find(TRUE) || (!empty($params['id']) && $existingEntities->id == $params['id'])) {
472 // ie. if no others is set to be primary then this has to be primary set to 1 so change
473 $params['is_primary'] = 1;
474 return;
475 }
476 else {
477 /*
478 * If the only existing email is the one we are editing then we must set
479 * is_primary to 1
480 * CRM-10451
481 */
482 if ( $existingEntities->N == 1 && $existingEntities->id == CRM_Utils_Array::value( 'id', $params ) ) {
483 $params['is_primary'] = 1;
484 return;
485 }
486
487 if ($existingEntities->is_primary == 1) {
488 return;
489 }
490 // so at this point we are only dealing with ones explicity setting is_primary to 0
491 // since we have reverse sorted by email we can either set the first one to
492 // primary or return if is already is
493 $existingEntities->is_primary = 1;
494 $existingEntities->save();
495 }
496 }
497
498 /**
499 * Sort location array so primary element is first
500 * @param Array $location
501 */
502 static function sortPrimaryFirst(&$locations){
503 uasort($locations, 'self::primaryComparison');
504 }
505
506/**
507 * compare 2 locations to see which should go first based on is_primary
508 * (sort function for sortPrimaryFirst)
509 * @param array $location1
510 * @param array_type $location2
511 * @return number
512 */
513 static function primaryComparison($location1, $location2){
514 $l1 = CRM_Utils_Array::value('is_primary', $location1);
515 $l2 = CRM_Utils_Array::value('is_primary', $location2);
516 if ($l1 == $l2) {
517 return 0;
518 }
519 return ($l1 < $l2) ? -1 : 1;
520 }
521}
522