3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
13 * Add static functions to include some common functionality used across location sub object BAO classes.
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 class CRM_Core_BAO_Block
{
21 * Fields that are required for a valid block.
24 public static $requiredBlockFields = [
28 'openid' => ['openid'],
32 * Given the list of params in the params array, fetch the object
33 * and store the values in the values array
35 * @param string $blockName
36 * Name of the above object.
37 * @param array $params
38 * Input parameters to find object.
41 * Array of $block objects.
42 * @throws CRM_Core_Exception
44 public static function &getValues($blockName, $params) {
48 $BAOString = 'CRM_Core_BAO_' . $blockName;
49 $block = new $BAOString();
52 if (!isset($params['entity_table'])) {
53 $block->contact_id
= $params['contact_id'];
54 if (!$block->contact_id
) {
55 throw new CRM_Core_Exception('Invalid Contact ID parameter passed');
57 $blocks = self
::retrieveBlock($block, $blockName);
60 $blockIds = self
::getBlockIds($blockName, NULL, $params);
62 if (empty($blockIds)) {
67 foreach ($blockIds as $blockId) {
68 $block = new $BAOString();
69 $block->id
= $blockId['id'];
70 $getBlocks = self
::retrieveBlock($block, $blockName);
71 $blocks[$count++
] = array_pop($getBlocks);
79 * Given the list of params in the params array, fetch the object
80 * and store the values in the values array
82 * @param Object $block
83 * Typically a Phone|Email|IM|OpenID object.
84 * @param string $blockName
85 * Name of the above object.
88 * Array of $block objects.
90 public static function retrieveBlock(&$block, $blockName) {
91 // we first get the primary location due to the order by clause
92 $block->orderBy('is_primary desc, id');
97 while ($block->fetch()) {
98 CRM_Core_DAO
::storeValues($block, $blocks[$count]);
99 //unset is_primary after first block. Due to some bug in earlier version
100 //there might be more than one primary blocks, hence unset is_primary other than first
102 unset($blocks[$count]['is_primary']);
111 * Check if the current block object has any valid data.
113 * @param array $blockFields
114 * Array of fields that are of interest for this object.
115 * @param array $params
116 * Associated array of submitted fields.
119 * true if the block has data, otherwise false
121 public static function dataExists($blockFields, &$params) {
122 foreach ($blockFields as $field) {
123 if (CRM_Utils_System
::isNull(CRM_Utils_Array
::value($field, $params))) {
131 * Check if the current block exits.
133 * @param string $blockName
135 * @param array $params
136 * Array of submitted fields.
139 * true if the block is in the params and is an array
141 public static function blockExists($blockName, $params) {
142 return !empty($params[$blockName]) && is_array($params[$blockName]);
146 * Get all block ids for a contact.
148 * @param string $blockName
150 * @param int $contactId
153 * @param null $entityElements
154 * @param bool $updateBlankLocInfo
157 * formatted array of block ids
160 public static function getBlockIds($blockName, $contactId = NULL, $entityElements = NULL, $updateBlankLocInfo = FALSE) {
163 $name = ucfirst($blockName);
164 if ($blockName == 'im') {
167 elseif ($blockName == 'openid') {
171 $baoString = 'CRM_Core_BAO_' . $name;
173 //@todo a cleverer way to do this would be to use the same fn name on each
174 // BAO rather than constructing the fn
175 // it would also be easier to grep for
176 // e.g $bao = new $baoString;
177 // $bao->getAllBlocks()
178 $baoFunction = 'all' . $name . 's';
179 $allBlocks = $baoString::$baoFunction($contactId, $updateBlankLocInfo);
181 elseif (!empty($entityElements) && $blockName != 'openid') {
182 $baoFunction = 'allEntity' . $name . 's';
183 $allBlocks = $baoString::$baoFunction($entityElements);
190 * Takes an associative array and creates a block.
192 * @param string $blockName
194 * @param array $params
195 * Array of name/value pairs.
198 * Array of created location entities or NULL if none to create.
200 public static function create($blockName, $params) {
201 if (!self
::blockExists($blockName, $params)) {
205 $name = ucfirst($blockName);
206 $isPrimary = $isBilling = TRUE;
207 $entityElements = $blocks = [];
208 $resetPrimaryId = NULL;
211 $contactId = $params['contact_id'];
213 $updateBlankLocInfo = CRM_Utils_Array
::value('updateBlankLocInfo', $params, FALSE);
214 $isIdSet = CRM_Utils_Array
::value('isIdSet', $params[$blockName], FALSE);
216 //get existing block ids.
217 $blockIds = self
::getBlockIds($blockName, $contactId, $entityElements);
218 foreach ($params[$blockName] as $count => $value) {
219 $blockId = $value['id'] ??
NULL;
221 if (is_array($blockIds) && array_key_exists($blockId, $blockIds)) {
222 unset($blockIds[$blockId]);
229 $baoString = 'CRM_Core_BAO_' . $name;
230 foreach ($params[$blockName] as $count => $value) {
231 if (!is_array($value)) {
234 // if in some cases (eg. email used in Online Conribution Page, Profiles, etc.) id is not set
235 // lets try to add using the previous method to avoid any false creation of existing data.
236 foreach ($blockIds as $blockId => $blockValue) {
237 if (empty($value['id']) && $blockValue['locationTypeId'] == CRM_Utils_Array
::value('location_type_id', $value) && !$isIdSet) {
239 if ($blockName == 'phone') {
240 $phoneTypeBlockValue = $blockValue['phoneTypeId'] ??
NULL;
241 if ($phoneTypeBlockValue == CRM_Utils_Array
::value('phone_type_id', $value)) {
245 elseif ($blockName == 'im') {
246 $providerBlockValue = $blockValue['providerId'] ??
NULL;
247 if (!empty($value['provider_id']) && $providerBlockValue == $value['provider_id']) {
255 $value['id'] = $blockValue['id'];
256 if (!$primaryId && !empty($blockValue['is_primary'])) {
257 $value['is_primary'] = $blockValue['is_primary'];
263 $dataExists = self
::dataExists(self
::$requiredBlockFields[$blockName], $value);
264 // Note there could be cases when block info already exist ($value[id] is set) for a contact/entity
265 // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
266 // $updateBlankLocInfo will help take appropriate decision. CRM-5969
267 if (!empty($value['id']) && !$dataExists && $updateBlankLocInfo) {
268 //delete the existing record
269 $baoString::del($value['id']);
272 elseif (!$dataExists) {
276 'contact_id' => $contactId,
277 'location_type_id' => $value['location_type_id'] ??
NULL,
280 $contactFields['is_billing'] = 0;
281 if ($isBilling && !empty($value['is_billing'])) {
282 $contactFields['is_billing'] = $value['is_billing'];
286 $blockFields = array_merge($value, $contactFields);
287 $blocks[] = $baoString::create($blockFields);
295 * @deprecated - just call the BAO / api directly.
297 * @param string $blockName
302 public static function blockDelete($blockName, $params) {
303 $name = ucfirst($blockName);
304 if ($blockName == 'im') {
307 elseif ($blockName == 'openid') {
311 $baoString = 'CRM_Core_BAO_' . $name;
312 $baoString::del($params['id']);
316 * Handling for is_primary.
317 * $params is_primary could be
318 * # 1 - find other entries with is_primary = 1 & reset them to 0
319 * # 0 - make sure at least one entry is set to 1
320 * - if no other entry is 1 change to 1
321 * - if one other entry exists change that to 1
322 * - if more than one other entry exists change first one to 1
323 * @fixme - perhaps should choose by location_type
324 * # empty - same as 0 as once we have checked first step
325 * we know if it should be 1 or 0
327 * if $params['id'] is set $params['contact_id'] may need to be retrieved
329 * @param array $params
332 * @throws API_Exception
334 public static function handlePrimary(&$params, $class) {
335 if (isset($params['id']) && CRM_Utils_System
::isNull($params['is_primary'] ??
NULL)) {
336 // if id is set & is_primary isn't we can assume no change)
339 $table = CRM_Core_DAO_AllCoreTables
::getTableForClass($class);
341 throw new API_Exception("Failed to locate table for class [$class]");
344 // contact_id in params might be empty or the string 'null' so cast to integer
345 $contactId = (int) ($params['contact_id'] ??
0);
346 // If id is set & we haven't been passed a contact_id, retrieve it
347 if (!empty($params['id']) && !isset($params['contact_id'])) {
348 $entity = new $class();
349 $entity->id
= $params['id'];
351 $contactId = $entity->contact_id
;
353 // If entity is not associated with contact, concept of is_primary not relevant
358 // if params is_primary then set all others to not be primary & exit out
360 if (!empty($params['is_primary'])) {
361 $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1";
362 $sqlParams = [1 => [$contactId, 'Integer']];
363 // we don't want to create unnecessary entries in the log_ tables so exclude the one we are working on
364 if (!empty($params['id'])) {
365 $sql .= " AND id <> %2";
366 $sqlParams[2] = [$params['id'], 'Integer'];
368 CRM_Core_DAO
::executeQuery($sql, $sqlParams);
372 //Check what other emails exist for the contact
373 $existingEntities = new $class();
374 $existingEntities->contact_id
= $contactId;
375 $existingEntities->orderBy('is_primary DESC');
376 if (!$existingEntities->find(TRUE) ||
(!empty($params['id']) && $existingEntities->id
== $params['id'])) {
377 // ie. if no others is set to be primary then this has to be primary set to 1 so change
378 $params['is_primary'] = 1;
383 * If the only existing email is the one we are editing then we must set
385 * @see https://issues.civicrm.org/jira/browse/CRM-10451
387 if ($existingEntities->N
== 1 && $existingEntities->id
== CRM_Utils_Array
::value('id', $params)) {
388 $params['is_primary'] = 1;
392 if ($existingEntities->is_primary
== 1) {
395 // so at this point we are only dealing with ones explicity setting is_primary to 0
396 // since we have reverse sorted by email we can either set the first one to
397 // primary or return if is already is
398 $existingEntities->is_primary
= 1;
399 $existingEntities->save();
404 * Sort location array so primary element is first.
406 * @param array $locations
408 public static function sortPrimaryFirst(&$locations) {
409 uasort($locations, 'self::primaryComparison');
413 * compare 2 locations to see which should go first based on is_primary
414 * (sort function for sortPrimaryFirst)
415 * @param array $location1
416 * @param array $location2
419 public static function primaryComparison($location1, $location2) {
420 $l1 = $location1['is_primary'] ??
NULL;
421 $l2 = $location2['is_primary'] ??
NULL;
425 return ($l1 < $l2) ?
-1 : 1;