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