647ffbc0a2317f6d881c1bd9ccca3dccbcfcbac0
[civicrm-core.git] / CRM / Core / BAO / RecurringEntity.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
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 */
35
36 require_once 'packages/When/When.php';
37
38 class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
39
40 public $schedule = array();
41 public $scheduleId = NULL;
42 public $scheduleFormValues = array();
43
44 public $dateColumns = array();
45 public $overwriteColumns = array();
46 public $intervalDateColumns = array();
47 public $excludeDates = array();
48
49 public $linkedEntities = array();
50
51 public $isRecurringEntityRecord = TRUE;
52
53 protected $recursion = NULL;
54
55 public static $_entitiesToBeDeleted = array();
56
57 static $_recurringEntityHelper =
58 array(
59 'civicrm_event' => array(
60 'helper_class' => 'CRM_Event_DAO_Event',
61 'delete_func' => 'delete',
62 'pre_delete_func' => 'CRM_Event_Form_ManageEvent_Repeat::checkRegistrationForEvents'
63 ),
64 'civicrm_activity' => array(
65 'helper_class' => 'CRM_Activity_DAO_Activity',
66 'delete_func' => 'delete',
67 'pre_delete_func' => ''
68 )
69 );
70
71 static $_dateColumns =
72 array(
73 'civicrm_event' => array(
74 'dateColumns' => array('start_date'),
75 'excludeDateRangeColumns' => array('start_date', 'end_date'),
76 'intervalDateColumns' => array('end_date')
77 ),
78 'civicrm_activity' => array(
79 'dateColumns' => array('activity_date_time'),
80 )
81 );
82
83 static $_tableDAOMapper =
84 array(
85 'civicrm_event' => 'CRM_Event_DAO_Event',
86 'civicrm_price_set_entity' => 'CRM_Price_DAO_PriceSetEntity',
87 'civicrm_uf_join' => 'CRM_Core_DAO_UFJoin',
88 'civicrm_tell_friend' => 'CRM_Friend_DAO_Friend',
89 'civicrm_pcp_block' => 'CRM_PCP_DAO_PCPBlock',
90 'civicrm_activity' => 'CRM_Activity_DAO_Activity',
91 'civicrm_activity_contact' => 'CRM_Activity_DAO_ActivityContact',
92 );
93
94 static $_updateSkipFields =
95 array(
96 'civicrm_event' => array('start_date', 'end_date'),
97 'civicrm_tell_friend' => array('entity_id'),
98 'civicrm_pcp_block' => array('entity_id'),
99 'civicrm_activity' => array('activity_date_time'),
100 );
101
102 static $_linkedEntitiesInfo =
103 array(
104 'civicrm_tell_friend' => array(
105 'entity_id_col' => 'entity_id',
106 'entity_table_col' => 'entity_table'
107 ),
108 'civicrm_price_set_entity' => array(
109 'entity_id_col' => 'entity_id',
110 'entity_table_col' => 'entity_table',
111 'is_multirecord' => TRUE,
112 ),
113 'civicrm_uf_join' => array(
114 'entity_id_col' => 'entity_id',
115 'entity_table_col' => 'entity_table',
116 'is_multirecord' => TRUE,
117 ),
118 'civicrm_pcp_block' => array(
119 'entity_id_col' => 'entity_id',
120 'entity_table_col' => 'entity_table'
121 ),
122 );
123
124 /**
125 * Function to save records in civicrm_recujrring_entity table
126 *
127 * @param array $params reference array contains the values submitted by the form
128 *
129 * @access public
130 * @static
131 *
132 * @return object
133 */
134 static function add(&$params) {
135 if (CRM_Utils_Array::value('id', $params)) {
136 CRM_Utils_Hook::pre('edit', 'RecurringEntity', $params['id'], $params);
137 }
138 else {
139 CRM_Utils_Hook::pre('create', 'RecurringEntity', NULL, $params);
140 }
141
142 $daoRecurringEntity = new CRM_Core_DAO_RecurringEntity();
143 $daoRecurringEntity->copyValues($params);
144 $daoRecurringEntity->find(TRUE);
145 $result = $daoRecurringEntity->save();
146
147 if (CRM_Utils_Array::value('id', $params)) {
148 CRM_Utils_Hook::post('edit', 'RecurringEntity', $daoRecurringEntity->id, $daoRecurringEntity);
149 }
150 else {
151 CRM_Utils_Hook::post('create', 'RecurringEntity', $daoRecurringEntity->id, $daoRecurringEntity);
152 }
153 return $result;
154 }
155
156 /**
157 * Wrapper for the function add() to add entry in recurring entity
158 *
159 * @param int $parentId Parent entity id
160 * @param int $entityId Child entity id
161 * @param String $entityTable Name of the entity table
162 *
163 * @access public
164 * @static
165 *
166 * @return object
167 */
168 static function quickAdd($parentId, $entityId, $entityTable) {
169 $params =
170 array(
171 'parent_id' => $parentId,
172 'entity_id' => $entityId,
173 'entity_table' => $entityTable
174 );
175 return self::add($params);
176 }
177
178 /**
179 * This function updates the mode column in the civicrm_recurring_entity table
180 *
181 * @param int $mode Mode of the entity to cascade changes across parent/child relations eg 1 - only this entity, 2 - this and the following entities, 3 - All the entity
182 *
183 * @access public
184 *
185 * @return void
186 */
187 function mode($mode) {
188 if ($this->entity_id && $this->entity_table) {
189 if ($this->find(TRUE)) {
190 $this->mode = $mode;
191 }
192 else {
193 $this->parent_id = $this->entity_id;
194 $this->mode = $mode;
195 }
196 $this->save();
197 }
198 }
199
200 /**
201 * This function generates all new entities based on object vars
202 *
203 * @return array
204 */
205 function generate() {
206 $this->generateRecursiveDates();
207
208 return $this->generateEntities();
209 }
210
211 /**
212 * This function builds a "When" object based on schedule/reminder params
213 *
214 * @return object When object
215 */
216 function generateRecursion() {
217 // return if already generated
218 if (is_a($this->recursion, 'When')) {
219 return $this->recursion;
220 }
221
222 if ($this->scheduleId) {
223 // get params by ID
224 $this->schedule = $this->getScheduleParams($this->scheduleId);
225 }
226 else if (!empty($this->scheduleFormValues)) {
227 $this->schedule = $this->mapFormValuesToDB($this->scheduleFormValues);
228 }
229
230 if (!empty($this->schedule)) {
231 $this->recursion = $this->getRecursionFromSchedule($this->schedule);
232 }
233 return $this->recursion;
234 }
235
236 /**
237 * Generate new DAOs and along with entries in civicrm_recurring_entity table
238 *
239 * @return array
240 */
241 function generateEntities() {
242 $newEntities = array();
243 $findCriteria = array();
244 if (!empty($this->recursionDates)) {
245 if ($this->entity_id) {
246 $findCriteria = array('id' => $this->entity_id);
247
248 // save an entry with initiating entity-id & entity-table
249 if ($this->entity_table && !$this->find(TRUE)) {
250 $this->parent_id = $this->entity_id;
251 $this->save();
252 }
253 }
254 if (empty($findCriteria)) {
255 CRM_Core_Error::fatal("Find criteria missing to generate form. Make sure entity_id and table is set.");
256 }
257
258 $count = 0;
259 foreach ($this->recursionDates as $key => $dateCols) {
260 $newCriteria = $dateCols;
261 foreach ($this->overwriteColumns as $col => $val) {
262 $newCriteria[$col] = $val;
263 }
264 // create main entities
265 $obj = CRM_Core_BAO_RecurringEntity::copyCreateEntity($this->entity_table,
266 $findCriteria,
267 $newCriteria,
268 $this->isRecurringEntityRecord
269 );
270
271 if (is_a($obj, 'CRM_Core_DAO') && $obj->id) {
272 $newCriteria = array();
273 $newEntities[$this->entity_table][$count] = $obj->id;
274
275 foreach ($this->linkedEntities as $linkedInfo) {
276 foreach ($linkedInfo['linkedColumns'] as $col) {
277 $newCriteria[$col] = $obj->id;
278 }
279 // create linked entities
280 $linkedObj = CRM_Core_BAO_RecurringEntity::copyCreateEntity($linkedInfo['table'],
281 $linkedInfo['findCriteria'],
282 $newCriteria,
283 CRM_Utils_Array::value('isRecurringEntityRecord', $linkedInfo, TRUE)
284 );
285
286 if (is_a($linkedObj, 'CRM_Core_DAO') && $linkedObj->id) {
287 $newEntities[$linkedInfo['table']][$count] = $linkedObj->id;
288 }
289 }
290 }
291 $count++;
292 }
293 }
294
295 return $newEntities;
296 }
297
298 /**
299 * This function iterates through when object criterias and
300 * generates recursive dates based on that
301 *
302 * @return array array of dates
303 */
304 function generateRecursiveDates() {
305 $this->generateRecursion();
306
307 $recursionDates = array();
308 if (is_a($this->recursion, 'When')) {
309 $initialCount = CRM_Utils_Array::value('start_action_offset', $this->schedule);
310
311 $exRangeStart = $exRangeEnd = NULL;
312 if (!empty($this->excludeDateRangeColumns)) {
313 $exRangeStart = $this->excludeDateRangeColumns[0];
314 $exRangeEnd = $this->excludeDateRangeColumns[1];
315 }
316
317 $count = 1;
318 while ($result = $this->recursion->next()) {
319 $baseDate = CRM_Utils_Date::processDate($result->format('Y-m-d H:i:s'));
320
321 foreach ($this->dateColumns as $col) {
322 $recursionDates[$count][$col] = $baseDate;
323 }
324 foreach ($this->intervalDateColumns as $col => $interval) {
325 $newDate = new DateTime($baseDate);
326 $newDate->add($interval);
327 $recursionDates[$count][$col] = CRM_Utils_Date::processDate($newDate->format('Y-m-d H:i:s'));
328 }
329 if ($exRangeStart) {
330 $exRangeStartDate = CRM_Utils_Date::processDate($recursionDates[$count][$exRangeStart], NULL, FALSE, 'Ymd');
331 $exRangeEndDate = CRM_Utils_Date::processDate($recursionDates[$count][$exRangeEnd], NULL, FALSE, 'Ymd');
332 }
333
334 $skip = FALSE;
335 foreach ($this->excludeDates as $exDate) {
336 $exDate = CRM_Utils_Date::processDate($exDate, NULL, FALSE, 'Ymd');
337 if (!$exRangeStart) {
338 if ($exDate == $result->format('Ymd')) {
339 $skip = TRUE;
340 break;
341 }
342 }
343 else {
344 if (($exDate == $exRangeStartDate) ||
345 ($exRangeEndDate && ($exDate > $exRangeStartDate) && ($exDate <= $exRangeEndDate))
346 ) {
347 $skip = TRUE;
348 break;
349 }
350 }
351 }
352
353 if ($skip) {
354 unset($recursionDates[$count]);
355 if ($initialCount && ($initialCount > 0)) {
356 // lets increase the counter, so we get correct number of occurrences
357 $initialCount++;
358 $this->recursion->count($initialCount);
359 }
360 continue;
361 }
362 $count++;
363 }
364 }
365 $this->recursionDates = $recursionDates;
366
367 return $recursionDates;
368 }
369
370 /**
371 * This function gets all the children for a particular parent entity
372 *
373 * @param int $parentId Parent entity id
374 * @param string $entityTable Name of the entity table
375 * @param boolean $includeParent If true parent id is included in result set and vice versa
376 * @param int $mode 1. retrieve only one entity. 2. retrieve all future entities in the repeating set. 3. all entities in the repeating set.
377 * @param int $initiatorId the instance where this function is invoked from
378 *
379 * @access public
380 * @static
381 *
382 * @return array an array of child ids
383 */
384 static public function getEntitiesForParent($parentId, $entityTable, $includeParent = TRUE, $mode = 3, $initiatorId = NULL) {
385 $entities = array();
386
387 if (!$initiatorId) {
388 $initiatorId = $parentId;
389 }
390
391 $queryParams = array(
392 1 => array($parentId, 'Integer'),
393 2 => array($entityTable, 'String'),
394 3 => array($initiatorId, 'Integer'),
395 );
396
397 if (!$mode) {
398 $mode = CRM_Core_DAO::singleValueQuery("SELECT mode FROM civicrm_recurring_entity WHERE entity_id = %3 AND entity_table = %2", $queryParams);
399 }
400
401 $query = "SELECT *
402 FROM civicrm_recurring_entity
403 WHERE parent_id = %1 AND entity_table = %2";
404 if (!$includeParent) {
405 $query .= " AND entity_id != " . ($initiatorId ? "%3" : "%1");
406 }
407
408 if ($mode == '1') { // MODE = SINGLE
409 $query .= " AND entity_id = %3";
410 }
411 else if ($mode == '2') { // MODE = FUTURE
412 $recurringEntityID = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_recurring_entity WHERE entity_id = %3 AND entity_table = %2", $queryParams);
413 if ($recurringEntityID) {
414 $query .= $includeParent ? " AND id >= %4" : " AND id > %4";
415 $query .= " ORDER BY id ASC"; // FIXME: change to order by dates
416 $queryParams[4] = array($recurringEntityID, 'Integer');
417 }
418 else {
419 // something wrong, return empty
420 return array();
421 }
422 }
423
424 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
425 while ($dao->fetch()) {
426 $entities["{$dao->entity_table}_{$dao->entity_id}"]['table'] = $dao->entity_table;
427 $entities["{$dao->entity_table}_{$dao->entity_id}"]['id'] = $dao->entity_id;
428 }
429 return $entities;
430 }
431
432 /**
433 * This function when passed an entity id checks if it has parent and
434 * returns all other entities that are connected to same parent.
435 *
436 * @param int $entityId entity id
437 * @param string $entityTable Entity table name
438 * @param boolean $includeParent Include parent in result set
439 * @param int $mode 1. retrieve only one entity. 2. retrieve all future entities in the repeating set. 3. all entities in the repeating set.
440 *
441 * @access public
442 * @static
443 *
444 * @return array array of connected ids
445 */
446 static public function getEntitiesFor($entityId, $entityTable, $includeParent = TRUE, $mode = 3) {
447 $parentId = self::getParentFor($entityId, $entityTable);
448 if ($parentId) {
449 return self::getEntitiesForParent($parentId, $entityTable, $includeParent, $mode, $entityId);
450 }
451 return array();
452 }
453
454 /**
455 * This function gets the parent for the entity id passed to it
456 *
457 * @param int $entityId entity ID
458 * @param string $entityTable Entity table name
459 * @param boolean $includeParent Include parent in result set
460 *
461 * @access public
462 * @static
463 *
464 * @return int unsigned $parentId Parent ID
465 */
466 static public function getParentFor($entityId, $entityTable, $includeParent = TRUE) {
467 $query = "
468 SELECT parent_id
469 FROM civicrm_recurring_entity
470 WHERE entity_id = %1 AND entity_table = %2";
471 if (!$includeParent) {
472 $query .= " AND parent_id != %1";
473 }
474 $parentId =
475 CRM_Core_DAO::singleValueQuery($query,
476 array(
477 1 => array($entityId, 'Integer'),
478 2 => array($entityTable, 'String'),
479 )
480 );
481 return $parentId;
482 }
483
484 /**
485 * This function copies the information from parent entity and creates other entities with same information
486 *
487 * @param string $entityTable Entity table name
488 * @param array $fromCriteria array of all the fields & values on which basis to copy
489 * @param array $newParams array of all the fields & values to be copied besides the other fields
490 * @param boolean $createRecurringEntity if to create a record in recurring_entity table
491 *
492 * @access public
493 * @static
494 *
495 * @return object
496 */
497 static public function copyCreateEntity($entityTable, $fromCriteria, $newParams, $createRecurringEntity = TRUE) {
498 $daoName = self::$_tableDAOMapper[$entityTable];
499 if (!$daoName) {
500 CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
501 }
502 $newObject = CRM_Core_DAO::copyGeneric($daoName, $fromCriteria, $newParams);
503
504 if (is_a($newObject, 'CRM_Core_DAO') && $newObject->id && $createRecurringEntity) {
505 $object = new $daoName( );
506 foreach ($fromCriteria as $key => $value) {
507 $object->$key = $value;
508 }
509 $object->find(TRUE);
510
511 CRM_Core_BAO_RecurringEntity::quickAdd($object->id, $newObject->id, $entityTable);
512 }
513 return $newObject;
514 }
515
516 /**
517 * This function acts as a listener to dao->update whenever there is an update,
518 * and propagates any changes to all related entities present in recurring entity table
519 *
520 * @param object $event An object of /Civi/Core/DAO/Event/PostUpdate containing dao object that was just updated
521 *
522 * @access public
523 * @static
524 *
525 * @return void
526 */
527 static public function triggerUpdate($event) {
528 // if DB version is earlier than 4.6 skip any processing
529 static $currentVer = NULL;
530 if (!$currentVer) {
531 $currentVer = CRM_Core_BAO_Domain::version();
532 }
533 if (version_compare($currentVer, '4.6.alpha1') < 0) {
534 return;
535 }
536
537 static $processedEntities = array();
538 $obj =& $event->object;
539 if (empty($obj->id) || empty($obj->__table)) {
540 return FALSE;
541 }
542 $key = "{$obj->__table}_{$obj->id}";
543
544 if (array_key_exists($key, $processedEntities)) {
545 // already processed
546 return NULL;
547 }
548
549 // get related entities
550 $repeatingEntities = self::getEntitiesFor($obj->id, $obj->__table, FALSE, NULL);
551 if (empty($repeatingEntities)) {
552 // return if its not a recurring entity parent
553 return NULL;
554 }
555 // mark being processed
556 $processedEntities[$key] = 1;
557
558 // to make sure we not copying to source itself
559 unset($repeatingEntities[$key]);
560
561 foreach($repeatingEntities as $key => $val) {
562 $entityID = $val['id'];
563 $entityTable = $val['table'];
564
565 $processedEntities[$key] = 1;
566
567 if (array_key_exists($entityTable, self::$_tableDAOMapper)) {
568 $daoName = self::$_tableDAOMapper[$entityTable];
569
570 $skipData = array();
571 if (array_key_exists($entityTable, self::$_updateSkipFields)) {
572 $skipFields = self::$_updateSkipFields[$entityTable];
573 foreach ($skipFields as $sfield) {
574 $skipData[$sfield] = NULL;
575 }
576 }
577
578 $updateDAO = CRM_Core_DAO::cascadeUpdate($daoName, $obj->id, $entityID, $skipData);
579 CRM_Core_DAO::freeResult();
580 }
581 else {
582 CRM_Core_Error::fatal("DAO Mapper missing for $entityTable.");
583 }
584 }
585 // done with processing. lets unset static var.
586 unset($processedEntities);
587 }
588
589 /**
590 * This function acts as a listener to dao->save,
591 * and creates entries for linked entities in recurring entity table
592 *
593 * @param object $event An object of /Civi/Core/DAO/Event/PostUpdate containing dao object that was just inserted
594 *
595 * @access public
596 * @static
597 *
598 * @return void
599 */
600 static public function triggerInsert($event) {
601 $obj =& $event->object;
602 if (!array_key_exists($obj->__table, self::$_linkedEntitiesInfo)) {
603 return FALSE;
604 }
605
606 // if DB version is earlier than 4.6 skip any processing
607 static $currentVer = NULL;
608 if (!$currentVer) {
609 $currentVer = CRM_Core_BAO_Domain::version();
610 }
611 if (version_compare($currentVer, '4.6.alpha1') < 0) {
612 return;
613 }
614
615 static $processedEntities = array();
616 if (empty($obj->id) || empty($obj->__table)) {
617 return FALSE;
618 }
619 $key = "{$obj->__table}_{$obj->id}";
620
621 if (array_key_exists($key, $processedEntities)) {
622 // already being processed. Exit recursive calls.
623 return NULL;
624 }
625
626 // mark being processed
627 $processedEntities[$key] = 1;
628
629 // get related entities for table being saved
630 $hasaRecurringRecord = self::getParentFor($obj->id, $obj->__table);
631
632 if (empty($hasaRecurringRecord)) {
633 // check if its a linked entity
634 if (array_key_exists($obj->__table, self::$_linkedEntitiesInfo) &&
635 !CRM_Utils_Array::value('is_multirecord', self::$_linkedEntitiesInfo[$obj->__table])) {
636 $linkedDAO = new self::$_tableDAOMapper[$obj->__table]();
637 $linkedDAO->id = $obj->id;
638 if ($linkedDAO->find(TRUE)) {
639 $idCol = self::$_linkedEntitiesInfo[$obj->__table]['entity_id_col'];
640 $tableCol = self::$_linkedEntitiesInfo[$obj->__table]['entity_table_col'];
641
642 $pEntityID = $linkedDAO->$idCol;
643 $pEntityTable = $linkedDAO->$tableCol;
644
645 // find all parent recurring entity set
646 $pRepeatingEntities = self::getEntitiesFor($pEntityID, $pEntityTable);
647
648 if (!empty($pRepeatingEntities)) {
649 // for each parent entity in the set, find out a similar linked entity,
650 // if doesn't exist create one, and also create entries in recurring_entity table
651
652 foreach($pRepeatingEntities as $key => $val) {
653 if (array_key_exists($key, $processedEntities)) {
654 // this graph is already being processed
655 return NULL;
656 }
657 $processedEntities[$key] = 1;
658 }
659
660 // start with first entry with just itself
661 CRM_Core_BAO_RecurringEntity::quickAdd($obj->id, $obj->id, $obj->__table);
662
663 foreach($pRepeatingEntities as $key => $val) {
664 $rlinkedDAO = new self::$_tableDAOMapper[$obj->__table]();
665 $rlinkedDAO->$idCol = $val['id'];
666 $rlinkedDAO->$tableCol = $val['table'];
667 if ($rlinkedDAO->find(TRUE)) {
668 CRM_Core_BAO_RecurringEntity::quickAdd($obj->id, $rlinkedDAO->id, $obj->__table);
669 }
670 else {
671 // linked entity doesn't exist. lets create them
672 $newCriteria = array(
673 $idCol => $val['id'],
674 $tableCol => $val['table']
675 );
676 $linkedObj = CRM_Core_BAO_RecurringEntity::copyCreateEntity($obj->__table,
677 array('id' => $obj->id),
678 $newCriteria,
679 TRUE
680 );
681 if ($linkedObj->id) {
682 CRM_Core_BAO_RecurringEntity::quickAdd($obj->id, $linkedObj->id, $obj->__table);
683 }
684 }
685 }
686 }
687 }
688 }
689 }
690
691 // done with processing. lets unset static var.
692 unset($processedEntities);
693 }
694
695 /**
696 * This function acts as a listener to dao->delete, and deletes an entry from recurring_entity table
697 *
698 * @param object $event An object of /Civi/Core/DAO/Event/PostUpdate containing dao object that was just deleted
699 *
700 * @access public
701 * @static
702 *
703 * @return void
704 */
705 static public function triggerDelete($event) {
706 $obj =& $event->object;
707
708 // if DB version is earlier than 4.6 skip any processing
709 static $currentVer = NULL;
710 if (!$currentVer) {
711 $currentVer = CRM_Core_BAO_Domain::version();
712 }
713 if (version_compare($currentVer, '4.6.alpha1') < 0) {
714 return;
715 }
716
717 static $processedEntities = array();
718 if (empty($obj->id) || empty($obj->__table) || !$event->result) {
719 return FALSE;
720 }
721 $key = "{$obj->__table}_{$obj->id}";
722
723 if (array_key_exists($key, $processedEntities)) {
724 // already processed
725 return NULL;
726 }
727
728 // mark being processed
729 $processedEntities[$key] = 1;
730
731 $parentID = self::getParentFor($obj->id, $obj->__table);
732 if ($parentID) {
733 CRM_Core_BAO_RecurringEntity::delEntity($obj->id, $obj->__table, TRUE);
734 }
735 }
736
737 /**
738 * This function deletes main entity and related linked entities from recurring-entity table
739 *
740 * @param int $entityId Entity id
741 * @param string $entityTable Name of the entity table
742 *
743 * @access public
744 * @static
745 *
746 * @return boolean|object Returns either boolean value or CRM_Core_DAO_RecurringEntity object
747 */
748 static public function delEntity($entityId, $entityTable, $isDelLinkedEntities = FALSE) {
749 if (empty($entityId) || empty($entityTable)) {
750 return FALSE;
751 }
752 $dao = new CRM_Core_DAO_RecurringEntity();
753 $dao->entity_id = $entityId;
754 $dao->entity_table = $entityTable;
755 if ($dao->find(TRUE)) {
756 // make sure its not a linked entity thats being deleted
757 if ($isDelLinkedEntities && !array_key_exists($entityTable, self::$_linkedEntitiesInfo)) {
758 // delete all linked entities from recurring entity table
759 foreach (self::$_linkedEntitiesInfo as $linkedTable => $linfo) {
760 $daoName = self::$_tableDAOMapper[$linkedTable];
761 if (!$daoName) {
762 CRM_Core_Error::fatal("DAO Mapper missing for $linkedTable.");
763 }
764
765 $linkedDao = new $daoName();
766 $linkedDao->$linfo['entity_id_col'] = $entityId;
767 $linkedDao->$linfo['entity_table_col'] = $entityTable;
768 $linkedDao->find();
769 while ($linkedDao->fetch()) {
770 CRM_Core_BAO_RecurringEntity::delEntity($linkedDao->id, $linkedTable, FALSE);
771 }
772 }
773 }
774 // delete main entity
775 return $dao->delete();
776 }
777 return FALSE;
778 }
779
780 /**
781 * This function maps values posted from form to civicrm_action_schedule columns
782 *
783 * @param array $formParams and array of form values posted
784 *
785 * @return array
786 */
787 function mapFormValuesToDB($formParams = array()) {
788 $dbParams = array();
789 if (CRM_Utils_Array::value('used_for', $formParams)) {
790 $dbParams['used_for'] = $formParams['used_for'];
791 }
792
793 if (CRM_Utils_Array::value('entity_id', $formParams)) {
794 $dbParams['entity_value'] = $formParams['entity_id'];
795 }
796
797 if (CRM_Utils_Array::value('repetition_start_date', $formParams)) {
798 $repetitionStartDate = $formParams['repetition_start_date'];
799 if (CRM_Utils_Array::value('repetition_start_date_time', $formParams)) {
800 $repetitionStartDate = $repetitionStartDate . " " . $formParams['repetition_start_date_time'];
801 }
802 $repetition_start_date = new DateTime($repetitionStartDate);
803 $repetition_start_date->modify('+1 day');
804 $dbParams['start_action_date'] = CRM_Utils_Date::processDate($repetition_start_date->format('Y-m-d H:i:s'));
805 }
806
807 if (CRM_Utils_Array::value('repetition_frequency_unit', $formParams)) {
808 $dbParams['repetition_frequency_unit'] = $formParams['repetition_frequency_unit'];
809 }
810
811 if (CRM_Utils_Array::value('repetition_frequency_interval', $formParams)) {
812 $dbParams['repetition_frequency_interval'] = $formParams['repetition_frequency_interval'];
813 }
814
815 //For Repeats on:(weekly case)
816 if ($formParams['repetition_frequency_unit'] == 'week') {
817 if (CRM_Utils_Array::value('start_action_condition', $formParams)) {
818 $repeats_on = CRM_Utils_Array::value('start_action_condition', $formParams);
819 $dbParams['start_action_condition'] = implode(",", array_keys($repeats_on));
820 }
821 }
822
823 //For Repeats By:(monthly case)
824 if ($formParams['repetition_frequency_unit'] == 'month') {
825 if ($formParams['repeats_by'] == 1) {
826 if (CRM_Utils_Array::value('limit_to', $formParams)) {
827 $dbParams['limit_to'] = $formParams['limit_to'];
828 }
829 }
830 if ($formParams['repeats_by'] == 2) {
831 if (CRM_Utils_Array::value('entity_status_1', $formParams) && CRM_Utils_Array::value('entity_status_2', $formParams)) {
832 $dbParams['entity_status'] = $formParams['entity_status_1']." ".$formParams['entity_status_2'];
833 }
834 }
835 }
836
837 //For "Ends" - After:
838 if ($formParams['ends'] == 1) {
839 if (CRM_Utils_Array::value('start_action_offset', $formParams)) {
840 $dbParams['start_action_offset'] = $formParams['start_action_offset'];
841 }
842 }
843
844 //For "Ends" - On:
845 if ($formParams['ends'] == 2) {
846 if (CRM_Utils_Array::value('repeat_absolute_date', $formParams)) {
847 $dbParams['absolute_date'] = CRM_Utils_Date::processDate($formParams['repeat_absolute_date']);
848 }
849 }
850 return $dbParams;
851 }
852
853 /**
854 * This function gets all the columns of civicrm_action_schedule table based on id(primary key)
855 *
856 * @param int $scheduleReminderId primary key of civicrm_action_schedule table
857 *
858 * @access public
859 * @static
860 *
861 * @return object
862 */
863 static public function getScheduleReminderDetailsById($scheduleReminderId) {
864 $query = "SELECT *
865 FROM civicrm_action_schedule WHERE 1";
866 if ($scheduleReminderId) {
867 $query .= "
868 AND id = %1";
869 }
870 $dao = CRM_Core_DAO::executeQuery($query,
871 array(
872 1 => array($scheduleReminderId, 'Integer')
873 )
874 );
875 $dao->fetch();
876 return $dao;
877 }
878
879 /**
880 * This function is a wrapper of getScheduleReminderDetailsById function
881 *
882 * @param int $scheduleReminderId primary key of civicrm_action_schedule table
883 *
884 * @return array
885 */
886 function getScheduleParams($scheduleReminderId) {
887 $scheduleReminderDetails = array();
888 if ($scheduleReminderId) {
889 //Get all the details from schedule reminder table
890 $scheduleReminderDetails = self::getScheduleReminderDetailsById($scheduleReminderId);
891 $scheduleReminderDetails = (array) $scheduleReminderDetails;
892 }
893 return $scheduleReminderDetails;
894 }
895
896 /**
897 * This function takes criterias saved in civicrm_action_schedule table
898 * and creates recursion rule
899 *
900 * @param array $scheduleReminderDetails array of repeat criterias saved in civicrm_action_schedule table
901 *
902 * @return object When object
903 */
904 function getRecursionFromSchedule($scheduleReminderDetails = array()) {
905 $r = new When();
906 //If there is some data for this id
907 if ($scheduleReminderDetails['repetition_frequency_unit']) {
908 if ($scheduleReminderDetails['start_action_date']) {
909 $currDate = date('Y-m-d H:i:s', strtotime($scheduleReminderDetails['start_action_date']));
910 }
911 else {
912 $currDate = date("Y-m-d H:i:s");
913 }
914 $start = new DateTime($currDate);
915 if ($scheduleReminderDetails['repetition_frequency_unit']) {
916 $repetition_frequency_unit = $scheduleReminderDetails['repetition_frequency_unit'];
917 if ($repetition_frequency_unit == "day") {
918 $repetition_frequency_unit = "dai";
919 }
920 $repetition_frequency_unit = $repetition_frequency_unit.'ly';
921 $r->recur($start, $repetition_frequency_unit);
922 }
923
924 if ($scheduleReminderDetails['repetition_frequency_interval']) {
925 $r->interval($scheduleReminderDetails['repetition_frequency_interval']);
926 }
927 else {
928 $r->errors[] = 'Repeats every: is a required field';
929 }
930
931 //week
932 if ($scheduleReminderDetails['repetition_frequency_unit'] == 'week') {
933 if ($scheduleReminderDetails['start_action_condition']) {
934 $startActionCondition = $scheduleReminderDetails['start_action_condition'];
935 $explodeStartActionCondition = explode(',', $startActionCondition);
936 $buildRuleArray = array();
937 foreach($explodeStartActionCondition as $key => $val) {
938 $buildRuleArray[] = strtoupper(substr($val, 0, 2));
939 }
940 $r->wkst('MO')->byday($buildRuleArray);
941 }
942 }
943
944 //month
945 if ($scheduleReminderDetails['repetition_frequency_unit'] == 'month') {
946 if ($scheduleReminderDetails['entity_status']) {
947 $startActionDate = explode(" ", $scheduleReminderDetails['entity_status']);
948 switch ($startActionDate[0]) {
949 case 'first':
950 $startActionDate1 = 1;
951 break;
952 case 'second':
953 $startActionDate1 = 2;
954 break;
955 case 'third':
956 $startActionDate1 = 3;
957 break;
958 case 'fourth':
959 $startActionDate1 = 4;
960 break;
961 case 'last':
962 $startActionDate1 = -1;
963 break;
964 }
965 $concatStartActionDateBits = $startActionDate1.strtoupper(substr($startActionDate[1], 0, 2));
966 $r->byday(array($concatStartActionDateBits));
967 }
968 else if ($scheduleReminderDetails['limit_to']) {
969 $r->bymonthday(array($scheduleReminderDetails['limit_to']));
970 }
971 }
972
973 //Ends
974 if ($scheduleReminderDetails['start_action_offset']) {
975 if ($scheduleReminderDetails['start_action_offset'] > 30) {
976 $r->errors[] = 'Occurrences should be less than or equal to 30';
977 }
978 $r->count($scheduleReminderDetails['start_action_offset']);
979 }
980
981 if (CRM_Utils_Array::value('absolute_date', $scheduleReminderDetails)) {
982 $absoluteDate = CRM_Utils_Date::setDateDefaults($scheduleReminderDetails['absolute_date']);
983 $endDate = new DateTime($absoluteDate[0].' '.$absoluteDate[1]);
984 $r->until($endDate);
985 }
986
987 if (!$scheduleReminderDetails['start_action_offset'] && !$scheduleReminderDetails['absolute_date']) {
988 $r->errors[] = 'Ends: is a required field';
989 }
990 }
991 else {
992 $r->errors[] = 'Repeats: is a required field';
993 }
994 return $r;
995 }
996
997
998 /**
999 * This function gets time difference between the two datetime object
1000 *
1001 * @param DateTime $startDate Start Date
1002 * @param DateTime $endDate End Date
1003 *
1004 * @access public
1005 * @static
1006 *
1007 * @return object DateTime object which contain time difference
1008 */
1009 static public function getInterval($startDate, $endDate) {
1010 if ($startDate && $endDate) {
1011 $startDate = new DateTime($startDate);
1012 $endDate = new DateTime($endDate);
1013 return $startDate->diff($endDate);
1014 }
1015 }
1016
1017 /**
1018 * This function gets all columns from civicrm_action_schedule on the basis of event id
1019 *
1020 * @param int $entityId Entity ID
1021 * @param string $used_for Specifies for which entity type it's used for
1022 *
1023 * @access public
1024 * @static
1025 *
1026 * @return object
1027 */
1028 public static function getReminderDetailsByEntityId($entityId, $used_for) {
1029 if ($entityId) {
1030 $query = "
1031 SELECT *
1032 FROM civicrm_action_schedule
1033 WHERE entity_value = %1";
1034 if ($used_for) {
1035 $query .= " AND used_for = %2";
1036 }
1037 $params = array(
1038 1 => array($entityId, 'Integer'),
1039 2 => array($used_for, 'String')
1040 );
1041 $dao = CRM_Core_DAO::executeQuery($query, $params);
1042 $dao->fetch();
1043 }
1044 return $dao;
1045 }
1046
1047 /**
1048 * Update mode column in civicrm_recurring_entity table for event related tabs
1049 *
1050 * @params int $entityId event id
1051 * @params string $linkedEntityTable Linked entity table name for this event
1052 * @return array
1053 */
1054 public static function updateModeLinkedEntity($entityId, $linkedEntityTable, $mainEntityTable) {
1055 $result = array();
1056 if ( $entityId && $linkedEntityTable && $mainEntityTable ) {
1057 if (CRM_Utils_Array::value($linkedEntityTable, self::$_tableDAOMapper)) {
1058 $dao = self::$_tableDAOMapper[$linkedEntityTable];
1059 }
1060 else {
1061 CRM_Core_Session::setStatus('Could not update mode for linked entities');
1062 return;
1063 }
1064 $entityTable = $linkedEntityTable;
1065 $params = array(
1066 'entity_id' => $entityId,
1067 'entity_table' => $mainEntityTable
1068 );
1069 $defaults = array();
1070 CRM_Core_DAO::commonRetrieve($dao, $params, $defaults);
1071 if (CRM_Utils_Array::value('id', $defaults)) {
1072 $result['entityId'] = $defaults['id'];
1073 $result['entityTable'] = $entityTable;
1074 }
1075 }
1076 return $result;
1077 }
1078 }