Testcase for RecurringEntity:Activities
[civicrm-core.git] / CRM / Core / BAO / RecurringEntity.php
CommitLineData
62933949 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
36require_once 'packages/When/When.php';
37
38class CRM_Core_BAO_RecurringEntity extends CRM_Core_DAO_RecurringEntity {
39
40 static $_tableDAOMapper =
41 array(
42 'civicrm_event' => 'CRM_Event_DAO_Event',
43 'civicrm_price_set_entity' => 'CRM_Price_DAO_PriceSetEntity',
44 'civicrm_uf_join' => 'CRM_Core_DAO_UFJoin',
45 'civicrm_tell_friend' => 'CRM_Friend_DAO_Friend',
46 'civicrm_pcp_block' => 'CRM_PCP_DAO_PCPBlock',
47 );
48
49 static function add(&$params) {
50 if (CRM_Utils_Array::value('id', $params)) {
51 CRM_Utils_Hook::pre('edit', 'RecurringEntity', $params['id'], $params);
52 }
53 else {
54 CRM_Utils_Hook::pre('create', 'RecurringEntity', NULL, $params);
55 }
56
57 $daoRecurringEntity = new CRM_Core_DAO_RecurringEntity();
58 $daoRecurringEntity->copyValues($params);
59 $result = $daoRecurringEntity->save();
60
61 if (CRM_Utils_Array::value('id', $params)) {
62 CRM_Utils_Hook::post('edit', 'RecurringEntity', $daoRecurringEntity->id, $daoRecurringEntity);
63 }
64 else {
65 CRM_Utils_Hook::post('create', 'RecurringEntity', $daoRecurringEntity->id, $daoRecurringEntity);
66 }
67 return $result;
68 }
69
70 static function quickAdd($parentId, $entityId, $entityTable) {
71 $params =
72 array(
73 'parent_id' => $parentId,
74 'entity_id' => $entityId,
75 'entity_table' => $entityTable
76 );
77 return self::add($params);
78 }
79
36b8b5f3 80 // MODE = 3 (ALL)
81 static public function getEntitiesForParent($parentId, $entityTable, $includeParent = TRUE, $mode = 3, $initiatorId = NULL) {
62933949 82 $entities = array();
83
36b8b5f3 84 if (!$initiatorId) {
85 $initiatorId = $parentId;
86 }
87
88 $queryParams = array(
89 1 => array($parentId, 'Integer'),
90 2 => array($entityTable, 'String'),
91 3 => array($initiatorId, 'Integer'),
92 );
93
94 if (!$mode) {
60b36f60 95 $mode = CRM_Core_DAO::singleValueQuery("SELECT mode FROM civicrm_recurring_entity WHERE entity_id = %3 AND entity_table = %2", $queryParams);
36b8b5f3 96 }
97
62933949 98 $query = "SELECT *
99 FROM civicrm_recurring_entity
100 WHERE parent_id = %1 AND entity_table = %2";
101 if (!$includeParent) {
36b8b5f3 102 $query .= " AND entity_id != " . ($initiatorId ? "%3" : "%1");
103 }
104
105 if ($mode == '1') { // MODE = SINGLE
106 $query .= " AND entity_id = %3";
107 } else if ($mode == '2') { // MODE = FUTURE
108 $recurringEntityID = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_recurring_entity WHERE entity_id = %3 AND entity_table = %2", $queryParams);
109 if ($recurringEntityID) {
110 $query .= $includeParent ? " AND id >= %4" : " AND id > %4";
111 $query .= " ORDER BY id ASC"; // FIXME: change to order by dates
112 $queryParams[4] = array($recurringEntityID, 'Integer');
113 } else {
114 // something wrong, return empty
115 return array();
116 }
62933949 117 }
62933949 118
36b8b5f3 119 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
62933949 120 while ($dao->fetch()) {
121 $entities["{$dao->entity_table}_{$dao->entity_id}"]['table'] = $dao->entity_table;
122 $entities["{$dao->entity_table}_{$dao->entity_id}"]['id'] = $dao->entity_id;
123 }
124 return $entities;
125 }
126
36b8b5f3 127 static public function getEntitiesFor($entityId, $entityTable, $includeParent = TRUE, $mode = 3) {
62933949 128 $parentId = self::getParentFor($entityId, $entityTable);
129 if ($parentId) {
36b8b5f3 130 return self::getEntitiesForParent($parentId, $entityTable, $includeParent, $mode, $entityId);
62933949 131 }
132 return array();
133 }
134
135 static public function getParentFor($entityId, $entityTable, $includeParent = TRUE) {
136 $query = "
137 SELECT parent_id
138 FROM civicrm_recurring_entity
139 WHERE entity_id = %1 AND entity_table = %2";
140 if (!$includeParent) {
141 $query .= " AND parent_id != %1";
142 }
143 $parentId =
144 CRM_Core_DAO::singleValueQuery($query,
145 array(
146 1 => array($entityId, 'Integer'),
147 2 => array($entityTable, 'String'),
148 )
149 );
150 return $parentId;
151 }
152
62933949 153 static public function copyCreateEntity($entityTable, $fromCriteria, $newParams, $createRecurringEntity = TRUE) {
154 $daoName = self::$_tableDAOMapper[$entityTable];
155 $newObject = CRM_Core_DAO::copyGeneric($daoName, $fromCriteria, $newParams);
156
157 if ($newObject->id && $createRecurringEntity) {
158 $object = new $daoName( );
159 foreach ($fromCriteria as $key => $value) {
160 $object->$key = $value;
161 }
162 $object->find(TRUE);
163
164 CRM_Core_BAO_RecurringEntity::quickAdd($object->id, $newObject->id, $entityTable);
165 }
166 return $newObject;
167 }
168
169 static public function triggerUpdate($obj) {
60b36f60 170 // if DB version is earlier than 4.6 skip any processing
171 static $currentVer = NULL;
172 if (!$currentVer) {
173 $currentVer = CRM_Core_BAO_Domain::version();
174 }
175 if (version_compare($currentVer, '4.6.alpha1') < 0) {
176 return;
177 }
178
62933949 179 static $processedEntities = array();
180 if (empty($obj->id) || empty($obj->__table)) {
181 return FALSE;
182 }
183 $key = "{$obj->__table}_{$obj->id}";
184
185 if (array_key_exists($key, $processedEntities)) {
186 // already processed
187 return NULL;
188 }
189
190 // get related entities
36b8b5f3 191 $repeatingEntities = self::getEntitiesFor($obj->id, $obj->__table, FALSE, NULL);
62933949 192 if (empty($repeatingEntities)) {
193 // return if its not a recurring entity parent
194 return NULL;
195 }
36b8b5f3 196 // mark being processed
62933949 197 $processedEntities[$key] = 1;
198
36b8b5f3 199 // to make sure we not copying to source itself
200 unset($repeatingEntities[$key]);
201
202 foreach($repeatingEntities as $key => $val) {
62933949 203 $entityID = $val['id'];
204 $entityTable = $val['table'];
205
206 $processedEntities[$key] = 1;
207
208 if (array_key_exists($entityTable, self::$_tableDAOMapper)) {
209 $daoName = self::$_tableDAOMapper[$entityTable];
210
62933949 211 // FIXME: generalize me
212 $skipData = array('start_date' => NULL,
213 'end_date' => NULL,
214 );
215
36b8b5f3 216 $updateDAO = CRM_Core_DAO::cascadeUpdate($daoName, $obj->id, $entityID, $skipData);
217 CRM_Core_DAO::freeResult();
62933949 218 }
219 }
220 // done with processing. lets unset static var.
221 unset($processedEntities);
222 }
223
224 static function mapFormValuesToDB($formParams = array()){
225 $dbParams = array();
226 if(CRM_Utils_Array::value('used_for', $formParams)){
227 $dbParams['used_for'] = $formParams['used_for'];
228 }
229
230 if(CRM_Utils_Array::value('parent_event_id', $formParams)){
231 $dbParams['entity_value'] = $formParams['parent_event_id'];
b1d1479a 232 }
233
66f66040 234 if(CRM_Utils_Array::value('repetition_start_date', $formParams) &&
235 CRM_Utils_Array::value('repetition_start_date_time', $formParams)){
236 $repetition_start_date = new DateTime($formParams['repetition_start_date']." ".$formParams['repetition_start_date_time']);
b1d1479a 237 $repetition_start_date->modify('+1 day');
238 $dbParams['entity_status'] = CRM_Utils_Date::processDate($repetition_start_date->format('Y-m-d H:i:s'));
239 }
62933949 240
241 if(CRM_Utils_Array::value('repetition_frequency_unit', $formParams)){
b1d1479a 242 $dbParams['repetition_frequency_unit'] = $formParams['repetition_frequency_unit'];
243 }
62933949 244
245 if(CRM_Utils_Array::value('repetition_frequency_interval', $formParams)){
b1d1479a 246 $dbParams['repetition_frequency_interval'] = $formParams['repetition_frequency_interval'];
247 }
62933949 248
249 //For Repeats on:(weekly case)
b1d1479a 250 if($formParams['repetition_frequency_unit'] == 'week'){
251 if(CRM_Utils_Array::value('start_action_condition', $formParams)){
252 $repeats_on = CRM_Utils_Array::value('start_action_condition', $formParams);
253 $dbParams['start_action_condition'] = implode(",", array_keys($repeats_on));
62933949 254 }
b1d1479a 255 }
62933949 256
257 //For Repeats By:(monthly case)
b1d1479a 258 if($formParams['repetition_frequency_unit'] == 'month'){
259 if($formParams['repeats_by'] == 1){
260 if(CRM_Utils_Array::value('limit_to', $formParams)){
261 $dbParams['limit_to'] = $formParams['limit_to'];
62933949 262 }
b1d1479a 263 }
264 if($formParams['repeats_by'] == 2){
265 if(CRM_Utils_Array::value('start_action_date_1', $formParams) && CRM_Utils_Array::value('start_action_date_2', $formParams)){
266 $dbParams['start_action_date'] = $formParams['start_action_date_1']." ".$formParams['start_action_date_2'];
62933949 267 }
268 }
b1d1479a 269 }
62933949 270
271 //For "Ends" - After:
b1d1479a 272 if($formParams['ends'] == 1){
273 if(CRM_Utils_Array::value('start_action_offset', $formParams)){
274 $dbParams['start_action_offset'] = $formParams['start_action_offset'];
62933949 275 }
b1d1479a 276 }
62933949 277
b1d1479a 278 //For "Ends" - On:
279 if($formParams['ends'] == 2){
280 if(CRM_Utils_Array::value('repeat_absolute_date', $formParams)){
281 $dbParams['absolute_date'] = CRM_Utils_Date::processDate($formParams['repeat_absolute_date']);
62933949 282 }
62933949 283 }
b1d1479a 284 return $dbParams;
285 }
62933949 286
b1d1479a 287 static public function getScheduleReminderDetailsById($scheduleReminderId){
288 $query = "SELECT *
289 FROM civicrm_action_schedule WHERE 1";
290 if($scheduleReminderId){
291 $query .= "
292 AND id = %1";
62933949 293 }
b1d1479a 294 $dao = CRM_Core_DAO::executeQuery($query,
295 array(
296 1 => array($scheduleReminderId, 'Integer')
297 )
298 );
299 $dao->fetch();
300 return $dao;
301 }
62933949 302
b1d1479a 303 static function getRecursionFromReminder($scheduleReminderId){
304 if($scheduleReminderId){
305 //Get all the details from schedule reminder table
306 $scheduleReminderDetails = self::getScheduleReminderDetailsById($scheduleReminderId);
307 $scheduleReminderDetails = (array) $scheduleReminderDetails;
308 $recursionDetails = self::getRecursionFromReminderByDBParams($scheduleReminderDetails);
62933949 309 }
b1d1479a 310 return $recursionDetails;
311 }
62933949 312
b1d1479a 313 static function getRecursionFromReminderByDBParams($scheduleReminderDetails = array()){
314 $r = new When();
315 //If there is some data for this id
316 if($scheduleReminderDetails['repetition_frequency_unit']){
317 if($scheduleReminderDetails['entity_status']){
318 $currDate = date('Y-m-d H:i:s', strtotime($scheduleReminderDetails['entity_status']));
319 }else{
62933949 320 $currDate = date("Y-m-d H:i:s");
b1d1479a 321 }
322 $start = new DateTime($currDate);
323 if($scheduleReminderDetails['repetition_frequency_unit']){
324 $repetition_frequency_unit = $scheduleReminderDetails['repetition_frequency_unit'];
325 if($repetition_frequency_unit == "day"){
326 $repetition_frequency_unit = "dai";
62933949 327 }
b1d1479a 328 $repetition_frequency_unit = $repetition_frequency_unit.'ly';
329 $r->recur($start, $repetition_frequency_unit);
330 }
62933949 331
b1d1479a 332 if($scheduleReminderDetails['repetition_frequency_interval']){
333 $r->interval($scheduleReminderDetails['repetition_frequency_interval']);
334 }else{
335 $r->errors[] = 'Repeats every: is a required field';
336 }
62933949 337
b1d1479a 338 //week
339 if($scheduleReminderDetails['repetition_frequency_unit'] == 'week'){
340 if($scheduleReminderDetails['start_action_condition']){
341 $startActionCondition = $scheduleReminderDetails['start_action_condition'];
342 $explodeStartActionCondition = explode(',', $startActionCondition);
343 $buildRuleArray = array();
344 foreach($explodeStartActionCondition as $key => $val){
345 $buildRuleArray[] = strtoupper(substr($val, 0, 2));
62933949 346 }
b1d1479a 347 $r->wkst('MO')->byday($buildRuleArray);
62933949 348 }
b1d1479a 349 }
62933949 350
b1d1479a 351 //month
352 if($scheduleReminderDetails['repetition_frequency_unit'] == 'month'){
b1d1479a 353 if($scheduleReminderDetails['start_action_date']){
354 $startActionDate = explode(" ", $scheduleReminderDetails['start_action_date']);
355 switch ($startActionDate[0]) {
356 case 'first':
357 $startActionDate1 = 1;
358 break;
359 case 'second':
360 $startActionDate1 = 2;
361 break;
362 case 'third':
363 $startActionDate1 = 3;
364 break;
365 case 'fourth':
366 $startActionDate1 = 4;
367 break;
368 case 'last':
369 $startActionDate1 = -1;
370 break;
c828f12a 371 }
b1d1479a 372 $concatStartActionDateBits = $startActionDate1.strtoupper(substr($startActionDate[1], 0, 2));
373 $r->byday(array($concatStartActionDateBits));
4cf90acd 374 }else if($scheduleReminderDetails['limit_to']){
375 $r->bymonthday(array($scheduleReminderDetails['limit_to']));
62933949 376 }
b1d1479a 377 }
62933949 378
b1d1479a 379 //Ends
380 if($scheduleReminderDetails['start_action_offset']){
381 if($scheduleReminderDetails['start_action_offset'] > 30){
382 $r->errors[] = 'Occurrences should be less than or equal to 30';
62933949 383 }
b1d1479a 384 $r->count($scheduleReminderDetails['start_action_offset']);
385 }
62933949 386
b1d1479a 387 if($scheduleReminderDetails['absolute_date']){
388 $absoluteDate = CRM_Utils_Date::setDateDefaults($scheduleReminderDetails['absolute_date']);
389 $endDate = new DateTime($absoluteDate[0].' '.$absoluteDate[1]);
390 $r->until($endDate);
391 }
392
393 if(!$scheduleReminderDetails['start_action_offset'] && !$scheduleReminderDetails['absolute_date']){
394 $r->errors[] = 'Ends: is a required field';
395 }
396 }else{
397 $r->errors[] = 'Repeats: is a required field';
398 }
399 return $r;
400 }
62933949 401
d10273dc 402 /*
403 * Get Reminder id based on event id
404 */
405 static public function getReminderDetailsByEventId($eventId, $used_for){
406 if($eventId){
407 $query = "
408 SELECT *
409 FROM civicrm_action_schedule
410 WHERE entity_value = %1";
411 if($used_for){
412 $query .= " AND used_for = %2";
413 }
414 $params = array(
415 1 => array($eventId, 'Integer'),
416 2 => array($used_for, 'String')
417 );
418 $dao = CRM_Core_DAO::executeQuery($query, $params);
419 $dao->fetch();
420 }
421 return $dao;
422 }
423
424 static public function getInterval($startDate, $endDate) {
425 if ($startDate && $endDate) {
426 $startDate = new DateTime($startDate);
c5a70e15 427 $endDate = new DateTime($endDate);
d10273dc 428
c5a70e15 429 return $startDate->diff($endDate);
fd1abec4 430 }
fd1abec4 431 }
2eeee284 432
ad82cae1 433 static public function generateRecursions($recursionObj, $params = array(), $excludeDates = array()) {
2eeee284 434 $newParams = $recursionResult = array();
9d2af827 435 if (is_a($recursionObj, 'When')) {
ad82cae1 436 $initialCount = CRM_Utils_Array::value('start_action_offset', $params);
d10273dc 437 $interval = CRM_Utils_Array::value('interval', $params);
438
439 $count = 1;
440 while ($result = $recursionObj->next()) {
441 $recursionResult[$count]['start_date'] = CRM_Utils_Date::processDate($result->format('Y-m-d H:i:s'));
442
443 if($interval){
444 $endDate = new DateTime($recursionResult[$count]['start_date']);
445 $endDate->add($interval);
446 $recursionResult[$count]['end_date'] = CRM_Utils_Date::processDate($endDate->format('Y-m-d H:i:s'));
447 }
448
449 $skip = FALSE;
450 foreach ($excludeDates as $date) {
451 $date = CRM_Utils_Date::processDate($date, NULL, FALSE, 'Ymd');
452 if (($date == $result->format('Ymd')) ||
453 ($endDate && ($date > $result->format('Ymd')) && ($date <= $endDate->format('Ymd')))
454 ) {
455 $skip = TRUE;
456 break;
ad82cae1 457 }
d10273dc 458 }
ad82cae1 459
d10273dc 460 if ($skip) {
461 unset($recursionResult[$count]);
462 if ($initialCount && ($initialCount > 0)) {
463 // lets increase the counter, so we get correct number of occurrences
464 $initialCount++;
465 $recursionObj->count($initialCount);
ad82cae1 466 }
d10273dc 467 continue;
2eeee284 468 }
d10273dc 469 $count++;
2eeee284 470 }
471 }
472 return $recursionResult;
473 }
2eeee284 474
8dc6e78a 475
476 static public function delEntityRelations($entityId, $entityTable){
477 if(!$entityId && !$entityTable){
478 return FALSE;
479 }
480 $parentID = self::getParentFor($entityId, $entityTable);
481 if($parentID){
482 $dao = new CRM_Core_DAO_RecurringEntity();
483 $dao->parent_id = $parentID;
484 return $dao->delete();
485 }
486 }
f51acb7a 487
488 static public function getParticipantCountforEvent($listOfRelatedEntities = array()){
489 if(!empty($listOfRelatedEntities)){
490 $implodeRelatedEntities = implode(',', array_map(function($entity){
491 return $entity['id'];
492 }, $listOfRelatedEntities));
493 if($implodeRelatedEntities){
494 $query = "SELECT p.event_id as event_id,
495 concat_ws(' ', e.title, concat_ws(' - ', DATE_FORMAT(e.start_date, '%b %d %Y %h:%i %p'), DATE_FORMAT(e.end_date, '%b %d %Y %h:%i %p'))) as event_data,
496 count(p.id) as participant_count
497 FROM civicrm_participant p, civicrm_event e
498 WHERE p.event_id = e.id AND p.event_id IN ({$implodeRelatedEntities})
499 GROUP BY p.event_id";
500 $dao = CRM_Core_DAO::executeQuery($query);
501 $participantDetails = array();
502 while($dao->fetch()) {
503 $participantDetails['countByID'][$dao->event_id] = $dao->participant_count;
504 $participantDetails['countByName'][$dao->event_id][$dao->event_data] = $dao->participant_count;
505 }
506 }
507 }
508 return $participantDetails;
509 }
62933949 510
fd1abec4 511}