3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2018
33 * Add static functions to include some common functionality used across location sub object BAO classes.
35 class CRM_Core_BAO_Block
{
38 * Fields that are required for a valid block.
40 static $requiredBlockFields = array(
41 'email' => array('email'),
42 'phone' => array('phone'),
43 'im' => array('name'),
44 'openid' => array('openid'),
48 * Given the list of params in the params array, fetch the object
49 * and store the values in the values array
51 * @param string $blockName
52 * Name of the above object.
53 * @param array $params
54 * Input parameters to find object.
57 * Array of $block objects.
59 public static function &getValues($blockName, $params) {
63 $BAOString = 'CRM_Core_BAO_' . $blockName;
64 $block = new $BAOString();
67 if (!isset($params['entity_table'])) {
68 $block->contact_id
= $params['contact_id'];
69 if (!$block->contact_id
) {
70 CRM_Core_Error
::fatal();
72 $blocks = self
::retrieveBlock($block, $blockName);
75 $blockIds = self
::getBlockIds($blockName, NULL, $params);
77 if (empty($blockIds)) {
82 foreach ($blockIds as $blockId) {
83 $block = new $BAOString();
84 $block->id
= $blockId['id'];
85 $getBlocks = self
::retrieveBlock($block, $blockName);
86 $blocks[$count++
] = array_pop($getBlocks);
94 * Given the list of params in the params array, fetch the object
95 * and store the values in the values array
97 * @param Object $block
98 * Typically a Phone|Email|IM|OpenID object.
99 * @param string $blockName
100 * Name of the above object.
103 * Array of $block objects.
105 public static function retrieveBlock(&$block, $blockName) {
106 // we first get the primary location due to the order by clause
107 $block->orderBy('is_primary desc, id');
112 while ($block->fetch()) {
113 CRM_Core_DAO
::storeValues($block, $blocks[$count]);
114 //unset is_primary after first block. Due to some bug in earlier version
115 //there might be more than one primary blocks, hence unset is_primary other than first
117 unset($blocks[$count]['is_primary']);
126 * Check if the current block object has any valid data.
128 * @param array $blockFields
129 * Array of fields that are of interest for this object.
130 * @param array $params
131 * Associated array of submitted fields.
134 * true if the block has data, otherwise false
136 public static function dataExists($blockFields, &$params) {
137 foreach ($blockFields as $field) {
138 if (CRM_Utils_System
::isNull(CRM_Utils_Array
::value($field, $params))) {
146 * Check if the current block exits.
148 * @param string $blockName
150 * @param array $params
151 * Associated array of submitted fields.
154 * true if the block exits, otherwise false
156 public static function blockExists($blockName, &$params) {
157 // return if no data present
158 if (empty($params[$blockName]) ||
!is_array($params[$blockName])) {
166 * Get all block ids for a contact.
168 * @param string $blockName
170 * @param int $contactId
173 * @param null $entityElements
174 * @param bool $updateBlankLocInfo
177 * formatted array of block ids
180 public static function getBlockIds($blockName, $contactId = NULL, $entityElements = NULL, $updateBlankLocInfo = FALSE) {
181 $allBlocks = array();
183 $name = ucfirst($blockName);
184 if ($blockName == 'im') {
187 elseif ($blockName == 'openid') {
191 $baoString = 'CRM_Core_BAO_' . $name;
193 //@todo a cleverer way to do this would be to use the same fn name on each
194 // BAO rather than constructing the fn
195 // it would also be easier to grep for
196 // e.g $bao = new $baoString;
197 // $bao->getAllBlocks()
198 $baoFunction = 'all' . $name . 's';
199 $allBlocks = $baoString::$baoFunction($contactId, $updateBlankLocInfo);
201 elseif (!empty($entityElements) && $blockName != 'openid') {
202 $baoFunction = 'allEntity' . $name . 's';
203 $allBlocks = $baoString::$baoFunction($entityElements);
210 * Takes an associative array and creates a block.
212 * @param string $blockName
214 * @param array $params
215 * (reference ) an assoc array of name/value pairs.
216 * @param null $entity
217 * @param int $contactId
220 * CRM_Core_BAO_Block object on success, null otherwise
222 public static function create($blockName, &$params, $entity = NULL, $contactId = NULL) {
223 if (!self
::blockExists($blockName, $params)) {
227 $name = ucfirst($blockName);
228 $isPrimary = $isBilling = TRUE;
229 $entityElements = $blocks = array();
230 $resetPrimaryId = NULL;
234 $entityElements = array(
235 'entity_table' => $params['entity_table'],
236 'entity_id' => $params['entity_id'],
240 $contactId = $params['contact_id'];
243 $updateBlankLocInfo = CRM_Utils_Array
::value('updateBlankLocInfo', $params, FALSE);
244 $isIdSet = CRM_Utils_Array
::value('isIdSet', $params[$blockName], FALSE);
246 //get existing block ids.
247 $blockIds = self
::getBlockIds($blockName, $contactId, $entityElements);
248 foreach ($params[$blockName] as $count => $value) {
249 $blockId = CRM_Utils_Array
::value('id', $value);
251 if (is_array($blockIds) && array_key_exists($blockId, $blockIds)) {
252 unset($blockIds[$blockId]);
258 //lets allow to update primary w/ more cleanly.
259 if (!$resetPrimaryId && !empty($value['is_primary'])) {
261 if (is_array($blockIds)) {
262 foreach ($blockIds as $blockId => $blockValue) {
263 if (!empty($blockValue['is_primary'])) {
264 $resetPrimaryId = $blockId;
269 if ($resetPrimaryId) {
270 $baoString = 'CRM_Core_BAO_' . $blockName;
271 $block = new $baoString();
273 $block->selectAdd("id, is_primary");
274 $block->id
= $resetPrimaryId;
275 if ($block->find(TRUE)) {
276 $block->is_primary
= FALSE;
284 foreach ($params[$blockName] as $count => $value) {
285 if (!is_array($value)) {
288 // if in some cases (eg. email used in Online Conribution Page, Profiles, etc.) id is not set
289 // lets try to add using the previous method to avoid any false creation of existing data.
290 foreach ($blockIds as $blockId => $blockValue) {
291 if (empty($value['id']) && $blockValue['locationTypeId'] == CRM_Utils_Array
::value('location_type_id', $value) && !$isIdSet) {
293 if ($blockName == 'phone') {
294 $phoneTypeBlockValue = CRM_Utils_Array
::value('phoneTypeId', $blockValue);
295 if ($phoneTypeBlockValue == CRM_Utils_Array
::value('phone_type_id', $value)) {
299 elseif ($blockName == 'im') {
300 $providerBlockValue = CRM_Utils_Array
::value('providerId', $blockValue);
301 if (!empty($value['provider_id']) && $providerBlockValue == $value['provider_id']) {
309 $value['id'] = $blockValue['id'];
310 if (!$primaryId && !empty($blockValue['is_primary'])) {
311 $value['is_primary'] = $blockValue['is_primary'];
317 $dataExists = self
::dataExists(self
::$requiredBlockFields[$blockName], $value);
318 // Note there could be cases when block info already exist ($value[id] is set) for a contact/entity
319 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
320 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
321 if (!empty($value['id']) && !$dataExists && $updateBlankLocInfo) {
322 //delete the existing record
323 self
::blockDelete($blockName, array('id' => $value['id']));
326 elseif (!$dataExists) {
329 $contactFields = array(
330 'contact_id' => $contactId,
331 'location_type_id' => CRM_Utils_Array
::value('location_type_id', $value),
334 $contactFields['is_primary'] = 0;
335 if ($isPrimary && !empty($value['is_primary'])) {
336 $contactFields['is_primary'] = $value['is_primary'];
340 $contactFields['is_billing'] = 0;
341 if ($isBilling && !empty($value['is_billing'])) {
342 $contactFields['is_billing'] = $value['is_billing'];
346 $blockFields = array_merge($value, $contactFields);
347 $baoString = 'CRM_Core_BAO_' . $name;
348 $blocks[] = $baoString::add($blockFields);
357 * @param string $blockName
362 public static function blockDelete($blockName, $params) {
363 $name = ucfirst($blockName);
364 if ($blockName == 'im') {
367 elseif ($blockName == 'openid') {
371 $baoString = 'CRM_Core_DAO_' . $name;
372 $block = new $baoString();
374 $block->copyValues($params);
376 // CRM-11006 add call to pre and post hook for delete action
377 CRM_Utils_Hook
::pre('delete', $name, $block->id
, CRM_Core_DAO
::$_nullArray);
379 CRM_Utils_Hook
::post('delete', $name, $block->id
, $block);
383 * Handling for is_primary.
384 * $params is_primary could be
385 * # 1 - find other entries with is_primary = 1 & reset them to 0
386 * # 0 - make sure at least one entry is set to 1
387 * - if no other entry is 1 change to 1
388 * - if one other entry exists change that to 1
389 * - if more than one other entry exists change first one to 1
390 * @fixme - perhaps should choose by location_type
391 * # empty - same as 0 as once we have checked first step
392 * we know if it should be 1 or 0
394 * if $params['id'] is set $params['contact_id'] may need to be retrieved
396 * @param array $params
399 * @throws API_Exception
401 public static function handlePrimary(&$params, $class) {
402 $table = CRM_Core_DAO_AllCoreTables
::getTableForClass($class);
404 throw new API_Exception("Failed to locate table for class [$class]");
407 // contact_id in params might be empty or the string 'null' so cast to integer
408 $contactId = (int) CRM_Utils_Array
::value('contact_id', $params);
409 // If id is set & we haven't been passed a contact_id, retrieve it
410 if (!empty($params['id']) && !isset($params['contact_id'])) {
411 $entity = new $class();
412 $entity->id
= $params['id'];
414 $contactId = $entity->contact_id
;
416 // If entity is not associated with contact, concept of is_primary not relevant
421 // if params is_primary then set all others to not be primary & exit out
423 if (!empty($params['is_primary'])) {
424 $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1";
425 $sqlParams = array(1 => array($contactId, 'Integer'));
426 // we don't want to create unnecessary entries in the log_ tables so exclude the one we are working on
427 if (!empty($params['id'])) {
428 $sql .= " AND id <> %2";
429 $sqlParams[2] = array($params['id'], 'Integer');
431 CRM_Core_DAO
::executeQuery($sql, $sqlParams);
435 //Check what other emails exist for the contact
436 $existingEntities = new $class();
437 $existingEntities->contact_id
= $contactId;
438 $existingEntities->orderBy('is_primary DESC');
439 if (!$existingEntities->find(TRUE) ||
(!empty($params['id']) && $existingEntities->id
== $params['id'])) {
440 // ie. if no others is set to be primary then this has to be primary set to 1 so change
441 $params['is_primary'] = 1;
446 * If the only existing email is the one we are editing then we must set
450 if ($existingEntities->N
== 1 && $existingEntities->id
== CRM_Utils_Array
::value('id', $params)) {
451 $params['is_primary'] = 1;
455 if ($existingEntities->is_primary
== 1) {
458 // so at this point we are only dealing with ones explicity setting is_primary to 0
459 // since we have reverse sorted by email we can either set the first one to
460 // primary or return if is already is
461 $existingEntities->is_primary
= 1;
462 $existingEntities->save();
467 * Sort location array so primary element is first.
469 * @param array $locations
471 public static function sortPrimaryFirst(&$locations) {
472 uasort($locations, 'self::primaryComparison');
476 * compare 2 locations to see which should go first based on is_primary
477 * (sort function for sortPrimaryFirst)
478 * @param array $location1
479 * @param array $location2
482 public static function primaryComparison($location1, $location2) {
483 $l1 = CRM_Utils_Array
::value('is_primary', $location1);
484 $l2 = CRM_Utils_Array
::value('is_primary', $location2);
488 return ($l1 < $l2) ?
-1 : 1;