CRM-10859 rename BAO classes that dont follow naming std (at least those for which...
[civicrm-core.git] / CRM / Event / BAO / Participant.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
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 *
31 *
32 * @package CRM
33 * @copyright CiviCRM LLC (c) 2004-2013
34 * $Id$
35 *
36 */
37class CRM_Event_BAO_Participant extends CRM_Event_DAO_Participant {
38
39 /**
40 * static field for all the participant information that we can potentially import
41 *
42 * @var array
43 * @static
44 */
45 static $_importableFields = NULL;
46
47 /**
48 * static field for all the participant information that we can potentially export
49 *
50 * @var array
51 * @static
52 */
53 static $_exportableFields = NULL;
54
55 /**
56 * static array for valid status transitions rules
57 *
58 * @var array
59 * @static
60 */
61 static $_statusTransitionsRules = array(
62 'Pending from pay later' => array('Registered', 'Cancelled'),
63 'Pending from incomplete transaction' => array('Registered', 'Cancelled'),
64 'On waitlist' => array('Cancelled', 'Pending from waitlist'),
65 'Pending from waitlist' => array('Registered', 'Cancelled'),
66 'Awaiting approval' => array('Cancelled', 'Pending from approval'),
67 'Pending from approval' => array('Registered', 'Cancelled'),
68 );
69 function __construct() {
70 parent::__construct();
71 }
72
73 /**
74 * takes an associative array and creates a participant object
75 *
76 * the function extract all the params it needs to initialize the create a
77 * participant object. the params array could contain additional unused name/value
78 * pairs
79 *
80 * @param array $params (reference ) an assoc array of name/value pairs
81 * @param array $ids the array that holds all the db ids
82 *
83 * @return object CRM_Event_BAO_Participant object
84 * @access public
85 * @static
86 */
87 static function &add(&$params) {
88
89 if (CRM_Utils_Array::value('id', $params)) {
90 CRM_Utils_Hook::pre('edit', 'Participant', $params['id'], $params);
91 }
92 else {
93 CRM_Utils_Hook::pre('create', 'Participant', NULL, $params);
94 }
95
96 // converting dates to mysql format
97 if (CRM_Utils_Array::value('register_date', $params)) {
98 $params['register_date'] = CRM_Utils_Date::isoToMysql($params['register_date']);
99 }
100
101 if (CRM_Utils_Array::value('participant_fee_amount', $params)) {
102 $params['participant_fee_amount'] = CRM_Utils_Rule::cleanMoney($params['participant_fee_amount']);
103 }
104
105 if (CRM_Utils_Array::value('fee_amount', $params)) {
106 $params['fee_amount'] = CRM_Utils_Rule::cleanMoney($params['fee_amount']);
107 }
108
109 $participantBAO = new CRM_Event_BAO_Participant;
110 if (CRM_Utils_Array::value('id', $params)) {
111 $participantBAO->id = CRM_Utils_Array::value('id', $params);
112 $participantBAO->find(TRUE);
113 $participantBAO->register_date = CRM_Utils_Date::isoToMysql($participantBAO->register_date);
114 }
115
116 $participantBAO->copyValues($params);
117
118 //CRM-6910
119 //1. If currency present, it should be valid one.
120 //2. We should have currency when amount is not null.
121 $currency = $participantBAO->fee_currency;
122 if ($currency ||
123 !CRM_Utils_System::isNull($participantBAO->fee_amount)
124 ) {
125 if (!CRM_Utils_Rule::currencyCode($currency)) {
126 $config = CRM_Core_Config::singleton();
127 $currency = $config->defaultCurrency;
128 }
129 }
130 $participantBAO->fee_currency = $currency;
131
132 $participantBAO->save();
133
134 $session = CRM_Core_Session::singleton();
135
136 // reset the group contact cache for this group
137 CRM_Contact_BAO_GroupContactCache::remove();
138
139 if (CRM_Utils_Array::value('id', $params)) {
140 CRM_Utils_Hook::post('edit', 'Participant', $participantBAO->id, $participantBAO);
141 }
142 else {
143 CRM_Utils_Hook::post('create', 'Participant', $participantBAO->id, $participantBAO);
144 }
145
146 return $participantBAO;
147 }
148
149 /**
150 * Given the list of params in the params array, fetch the object
151 * and store the values in the values array
152 *
153 * @param array $params input parameters to find object
154 * @param array $values output values of the object
155 *
156 * @return CRM_Event_BAO_Participant|null the found object or null
157 * @access public
158 * @static
159 */
160 static function getValues(&$params, &$values, &$ids) {
161 if (empty($params)) {
162 return NULL;
163 }
164 $participant = new CRM_Event_BAO_Participant();
165 $participant->copyValues($params);
166 $participant->find();
167 $participants = array();
168 while ($participant->fetch()) {
169 $ids['participant'] = $participant->id;
170 CRM_Core_DAO::storeValues($participant, $values[$participant->id]);
171 $participants[$participant->id] = $participant;
172 }
173 return $participants;
174 }
175
176 /**
177 * takes an associative array and creates a participant object
178 *
179 * @param array $params (reference ) an assoc array of name/value pairs
180 * @param array $ids the array that holds all the db ids
181 *
182 * @return object CRM_Event_BAO_Participant object
183 * @access public
184 * @static
185 */
186 static function &create(&$params) {
187
188 $transaction = new CRM_Core_Transaction();
189 $status = NULL;
190
191 if (CRM_Utils_Array::value('id', $params)) {
192 $status = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $params['id'], 'status_id');
193 }
194
195 $participant = self::add($params);
196
197 if (is_a($participant, 'CRM_Core_Error')) {
198 $transaction->rollback();
199 return $participant;
200 }
201
202 if ((!CRM_Utils_Array::value('id', $params)) ||
203 ($params['status_id'] != $status)
204 ) {
205 CRM_Activity_BAO_Activity::addActivity($participant);
206 }
207
208 //CRM-5403
209 //for update mode
210 if (self::isPrimaryParticipant($participant->id) && $status) {
211 self::updateParticipantStatus($participant->id, $status, $participant->status_id);
212 }
213
214 $session = CRM_Core_Session::singleton();
215 $id = $session->get('userID');
216 if (!$id) {
217 $id = $params['contact_id'];
218 }
219
220 // add custom field values
221 if (CRM_Utils_Array::value('custom', $params) &&
222 is_array($params['custom'])
223 ) {
224 CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_participant', $participant->id);
225 }
226
227 //process note, CRM-7634
228 $noteId = NULL;
229 if (CRM_Utils_Array::value('id', $params)) {
230 $note = CRM_Core_BAO_Note::getNote($params['id'], 'civicrm_participant');
231 $noteId = key($note);
232 }
233 $noteValue = NULL;
234 $hasNoteField = FALSE;
235 foreach (array(
236 'note', 'participant_note') as $noteFld) {
237 if (array_key_exists($noteFld, $params)) {
238 $noteValue = $params[$noteFld];
239 $hasNoteField = TRUE;
240 break;
241 }
242 }
243 if ($noteId || $noteValue) {
244 if ($noteValue) {
245 $noteParams = array(
246 'entity_table' => 'civicrm_participant',
247 'note' => $noteValue,
248 'entity_id' => $participant->id,
249 'contact_id' => $id,
250 'modified_date' => date('Ymd'),
251 );
252 $noteIDs = array();
253 if ($noteId) {
254 $noteIDs['id'] = $noteId;
255 }
256 CRM_Core_BAO_Note::add($noteParams, $noteIDs);
257 }
258 elseif ($noteId && $hasNoteField) {
259 CRM_Core_BAO_Note::del($noteId, FALSE);
260 }
261 }
262
263 // Log the information on successful add/edit of Participant data.
264 $logParams = array(
265 'entity_table' => 'civicrm_participant',
266 'entity_id' => $participant->id,
267 'data' => CRM_Event_PseudoConstant::participantStatus($participant->status_id),
268 'modified_id' => $id,
269 'modified_date' => date('Ymd'),
270 );
271
272 CRM_Core_BAO_Log::add($logParams);
273
274 $params['participant_id'] = $participant->id;
275
276 $transaction->commit();
277
278 // do not add to recent items for import, CRM-4399
279 if (!CRM_Utils_Array::value('skipRecentView', $params)) {
280
281 $url = CRM_Utils_System::url('civicrm/contact/view/participant',
282 "action=view&reset=1&id={$participant->id}&cid={$participant->contact_id}&context=home"
283 );
284
285 $recentOther = array();
286 if (CRM_Core_Permission::check('edit event participants')) {
287 $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/contact/view/participant',
288 "action=update&reset=1&id={$participant->id}&cid={$participant->contact_id}&context=home"
289 );
290 }
291 if (CRM_Core_Permission::check('delete in CiviEvent')) {
292 $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/participant',
293 "action=delete&reset=1&id={$participant->id}&cid={$participant->contact_id}&context=home"
294 );
295 }
296
297 $participantRoles = CRM_Event_PseudoConstant::participantRole();
298
299 if ($participant->role_id) {
300 $role = explode(CRM_Core_DAO::VALUE_SEPARATOR, $participant->role_id);
301
302 foreach ($role as & $roleValue) {
303 if (isset($roleValue)) {
304 $roleValue = $participantRoles[$roleValue];
305 }
306 }
307 $roles = implode(', ', $role);
308 }
309
310 $roleString = empty($roles) ? '' : $roles;
311 $eventTitle = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $participant->event_id, 'title');
312 $title = CRM_Contact_BAO_Contact::displayName($participant->contact_id) . ' (' . $roleString . ' - ' . $eventTitle . ')';
313
314 // add the recently created Participant
315 CRM_Utils_Recent::add($title,
316 $url,
317 $participant->id,
318 'Participant',
319 $participant->contact_id,
320 NULL,
321 $recentOther
322 );
323 }
324
325 return $participant;
326 }
327
328 /**
329 * Check whether the event is full for participation and return as
330 * per requirements.
331 *
332 * @param int $eventId event id.
333 * @param boolean $returnEmptySeats are we require number if empty seats.
334 * @param boolean $includeWaitingList consider waiting list in event full
335 * calculation or not. (it is for cron job purpose)
336 *
337 * @return
338 * 1. false => If event having some empty spaces.
339 * 2. null => If no registration yet or no limit.
340 * 3. Event Full Message => If event is full.
341 * 4. Number of Empty Seats => If we are interested in empty spaces.( w/ include/exclude waitings. )
342 *
343 * @static
344 * @access public
345 */
346 static function eventFull(
347 $eventId,
348 $returnEmptySeats = FALSE,
349 $includeWaitingList = TRUE,
350 $returnWaitingCount = FALSE,
351 $considerTestParticipant = FALSE
352 ) {
353 $result = NULL;
354 if (!$eventId) {
355 return $result;
356 }
357
358 // consider event is full when.
359 // 1. (count(is_counted) >= event_size) or
360 // 2. (count(participants-with-status-on-waitlist) > 0)
361 // It might be case there are some empty spaces and still event
362 // is full, as waitlist might represent group require spaces > empty.
363
364 $participantRoles = CRM_Event_PseudoConstant::participantRole(NULL, 'filter = 1');
365 $countedStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1');
366 $waitingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Waiting'");
367 $onWaitlistStatusId = array_search('On waitlist', $waitingStatuses);
368
369 //when we do require only waiting count don't consider counted.
370 if (!$returnWaitingCount && !empty($countedStatuses)) {
371 $allStatusIds = array_keys($countedStatuses);
372 }
373
374 $where = array(' event.id = %1 ');
375 if (!$considerTestParticipant) {
376 $where[] = ' ( participant.is_test = 0 OR participant.is_test IS NULL ) ';
377 }
378 if (!empty($participantRoles)) {
379 $where[] = ' participant.role_id IN ( ' . implode(', ', array_keys($participantRoles)) . ' ) ';
380 }
381
382 $eventParams = array(1 => array($eventId, 'Positive'));
383
384 //in case any waiting, straight forward event is full.
385 if ($includeWaitingList && $onWaitlistStatusId) {
386
387 //build the where clause.
388 $whereClause = ' WHERE ' . implode(' AND ', $where);
389 $whereClause .= " AND participant.status_id = $onWaitlistStatusId ";
390 $eventSeatsWhere = implode(' AND ', $where) . " AND ( participant.status_id = $onWaitlistStatusId )";
391
392 $query = "
393 SELECT participant.id id,
394 event.event_full_text as event_full_text
395 FROM civicrm_participant participant
396INNER JOIN civicrm_event event ON ( event.id = participant.event_id )
397 {$whereClause}";
398
399 $eventFullText = ts('This event is full!!!');
400 $participants = CRM_Core_DAO::executeQuery($query, $eventParams);
401 while ($participants->fetch()) {
402 //oops here event is full and we don't want waiting count.
403 if ($returnWaitingCount) {
404 return CRM_Event_BAO_Event::eventTotalSeats($eventId, $eventSeatsWhere);
405 }
406 else {
407 return ($participants->event_full_text) ? $participants->event_full_text : $eventFullText;
408 }
409 }
410 }
411
412 //consider only counted participants.
413 $where[] = ' participant.status_id IN ( ' . implode(', ', array_keys($countedStatuses)) . ' ) ';
414 $whereClause = ' WHERE ' . implode(' AND ', $where);
415 $eventSeatsWhere = implode(' AND ', $where);
416
417 $query = "
418 SELECT participant.id id,
419 event.event_full_text as event_full_text,
420 event.max_participants as max_participants
421 FROM civicrm_participant participant
422INNER JOIN civicrm_event event ON ( event.id = participant.event_id )
423 {$whereClause}";
424
425 $eventMaxSeats = NULL;
426 $eventFullText = ts('This event is full !!!');
427 $participants = CRM_Core_DAO::executeQuery($query, $eventParams);
428 while ($participants->fetch()) {
429 if ($participants->event_full_text) {
430 $eventFullText = $participants->event_full_text;
431 }
432 $eventMaxSeats = $participants->max_participants;
433 //don't have limit for event seats.
434 if ($participants->max_participants == NULL) {
435 return $result;
436 }
437 }
438
439 //get the total event seats occupied by these participants.
440 $eventRegisteredSeats = CRM_Event_BAO_Event::eventTotalSeats($eventId, $eventSeatsWhere);
441
442 if ($eventRegisteredSeats) {
443 if ($eventRegisteredSeats >= $eventMaxSeats) {
444 $result = $eventFullText;
445 }
446 elseif ($returnEmptySeats) {
447 $result = $eventMaxSeats - $eventRegisteredSeats;
448 }
449 return $result;
450 }
451 else {
452 $query = '
453SELECT event.event_full_text,
454 event.max_participants
455 FROM civicrm_event event
456 WHERE event.id = %1';
457 $event = CRM_Core_DAO::executeQuery($query, $eventParams);
458 while ($event->fetch()) {
459 $eventFullText = $event->event_full_text;
460 $eventMaxSeats = $event->max_participants;
461 }
462 }
463
464 // no limit for registration.
465 if ($eventMaxSeats == NULL) {
466 return $result;
467 }
468 if ($eventMaxSeats) {
469 return ($returnEmptySeats) ? (int) $eventMaxSeats : FALSE;
470 }
471
472 return $evenFullText;
473 }
474
475 /**
476 * Return the array of all price set field options,
477 * with total participant count that field going to carry.
478 *
479 * @param int $eventId event id.
480 * @param array $skipParticipants an array of participant ids those we should skip.
481 * @param int $isTest would you like to consider test participants.
482 *
483 * @return array $optionsCount an array of each option id and total count
484 * @static
485 * @access public
486 */
487 static function priceSetOptionsCount(
488 $eventId,
489 $skipParticipantIds = array(),
490 $considerCounted = TRUE,
491 $considerWaiting = TRUE,
492 $considerTestParticipants = FALSE
493 ) {
494 $optionsCount = array();
495 if (!$eventId) {
496 return $optionsCount;
497 }
498
499 $allStatusIds = array();
500 if ($considerCounted) {
501 $countedStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1');
502 $allStatusIds = array_merge($allStatusIds, array_keys($countedStatuses));
503 }
504 if ($considerWaiting) {
505 $waitingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Waiting'");
506 $allStatusIds = array_merge($allStatusIds, array_keys($waitingStatuses));
507 }
508 $statusIdClause = NULL;
509 if (!empty($allStatusIds)) {
510 $statusIdClause = ' AND participant.status_id IN ( ' . implode(', ', array_values($allStatusIds)) . ')';
511 }
512
513 $isTestClause = NULL;
514 if (!$considerTestParticipants) {
515 $isTestClause = ' AND ( participant.is_test IS NULL OR participant.is_test = 0 )';
516 }
517
518 $skipParticipantClause = NULL;
519 if (is_array($skipParticipantIds) && !empty($skipParticipantIds)) {
520 $skipParticipantClause = ' AND participant.id NOT IN ( ' . implode(', ', $skipParticipantIds) . ')';
521 }
522
523 $sql = "
524 SELECT line.id as lineId,
525 line.entity_id as entity_id,
526 line.qty,
527 value.id as valueId,
528 value.count,
529 field.html_type
530 FROM civicrm_line_item line
531INNER JOIN civicrm_participant participant ON ( line.entity_table = 'civicrm_participant'
532 AND participant.id = line.entity_id )
533INNER JOIN civicrm_price_field_value value ON ( value.id = line.price_field_value_id )
534INNER JOIN civicrm_price_field field ON ( value.price_field_id = field.id )
535 WHERE participant.event_id = %1
536 {$statusIdClause}
537 {$isTestClause}
538 {$skipParticipantClause}";
539
540 $lineItem = CRM_Core_DAO::executeQuery($sql, array(1 => array($eventId, 'Positive')));
541 while ($lineItem->fetch()) {
542 $count = $lineItem->count;
543 if (!$count) {
544 $count = 1;
545 }
546 if ($lineItem->html_type == 'Text') {
547 $count *= $lineItem->qty;
548 }
549 $optionsCount[$lineItem->valueId] = $count + CRM_Utils_Array::value($lineItem->valueId, $optionsCount, 0);
550 }
551
552 return $optionsCount;
553 }
554
555 /**
556 * Get the empty spaces for event those we can allocate
557 * to pending participant to become confirm.
558 *
559 * @param int $eventId event id.
560 *
561 * @return int $spaces Number of Empty Seats/null.
562 * @static
563 * @access public
564 */
565 static function pendingToConfirmSpaces($eventId) {
566 $emptySeats = 0;
567 if (!$eventId) {
568 return $emptySeats;
569 }
570
571 $positiveStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Positive'");
572 $statusIds = '(' . implode(',', array_keys($positiveStatuses)) . ')';
573
574 $query = "
575 SELECT count(participant.id) as registered,
576 civicrm_event.max_participants
577 FROM civicrm_participant participant, civicrm_event
578 WHERE participant.event_id = {$eventId}
579 AND civicrm_event.id = participant.event_id
580 AND participant.status_id IN {$statusIds}
581GROUP BY participant.event_id
582";
583 $dao = CRM_Core_DAO::executeQuery($query);
584 if ($dao->fetch()) {
585
586 //unlimited space.
587 if ($dao->max_participants == NULL || $dao->max_participants <= 0) {
588 return NULL;
589 }
590
591 //no space.
592 if ($dao->registered >= $dao->max_participants) {
593 return $emptySeats;
594 }
595
596 //difference.
597 return $dao->max_participants - $dao->registered;
598 }
599
600 //space in case no registeration yet.
601 return CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $eventId, 'max_participants');
602 }
603
604 /**
605 * combine all the importable fields from the lower levels object
606 *
607 * @return array array of importable Fields
608 * @access public
609 * @static
610 */
611 static function &importableFields($contactType = 'Individual', $status = TRUE, $onlyParticipant = FALSE) {
612 if (!self::$_importableFields) {
613 if (!$onlyParticipant) {
614 if (!$status) {
615 $fields = array('' => array('title' => ts('- do not import -')));
616 }
617 else {
618 $fields = array('' => array('title' => ts('- Participant Fields -')));
619 }
620 }
621 else {
622 $fields = array();
623 }
624
625 $tmpFields = CRM_Event_DAO_Participant::import();
626
627 $note = array(
628 'participant_note' => array(
629 'title' => 'Participant Note',
630 'name' => 'participant_note',
631 'headerPattern' => '/(participant.)?note$/i',
632 ));
633
634 $participantStatus = array(
635 'participant_status' => array(
636 'title' => 'Participant Status',
637 'name' => 'participant_status',
638 'data_type' => CRM_Utils_Type::T_STRING,
639 ));
640
641 $participantRole = array(
642 'participant_role' => array(
643 'title' => 'Participant Role',
644 'name' => 'participant_role',
645 'data_type' => CRM_Utils_Type::T_STRING,
646 ));
647
648 $eventType = array(
649 'event_type' => array(
650 'title' => 'Event Type',
651 'name' => 'event_type',
652 'data_type' => CRM_Utils_Type::T_STRING,
653 ));
654
655 $tmpContactField = $contactFields = array();
656 $contactFields = array( );
657 if (!$onlyParticipant) {
658 $contactFields = CRM_Contact_BAO_Contact::importableFields($contactType, NULL);
659
660 // Using new Dedupe rule.
661 $ruleParams = array(
662 'contact_type' => $contactType,
663 'used' => 'Unsupervised',
664 );
665 $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams);
666
667 if (is_array($fieldsArray)) {
668 foreach ($fieldsArray as $value) {
669 $customFieldId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField',
670 $value,
671 'id',
672 'column_name'
673 );
674 $value = $customFieldId ? 'custom_' . $customFieldId : $value;
675 $tmpContactField[trim($value)] = CRM_Utils_Array::value(trim($value), $contactFields);
676 if (!$status) {
677 $title = $tmpContactField[trim($value)]['title'] . ' (match to contact)';
678 }
679 else {
680 $title = $tmpContactField[trim($value)]['title'];
681 }
682
683 $tmpContactField[trim($value)]['title'] = $title;
684 }
685 }
686 }
687 $extIdentifier = CRM_Utils_Array::value('external_identifier', $contactFields);
688 if ($extIdentifier) {
689 $tmpContactField['external_identifier'] = $extIdentifier;
690 $tmpContactField['external_identifier']['title'] =
691 CRM_Utils_Array::value('title', $extIdentifier) . ' (match to contact)';
692 }
693 $tmpFields['participant_contact_id']['title'] =
694 $tmpFields['participant_contact_id']['title'] . ' (match to contact)';
695
696 //campaign fields.
697 if (isset($tmpFields['participant_campaign_id'])) {
698 $tmpFields['participant_campaign'] = array('title' => ts('Campaign Title'));
699 }
700
701 $fields = array_merge($fields, $tmpContactField);
702 $fields = array_merge($fields, $tmpFields);
703 $fields = array_merge($fields, $note, $participantStatus, $participantRole, $eventType);
704 $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Participant'));
705
706 self::$_importableFields = $fields;
707 }
708
709 return self::$_importableFields;
710 }
711
712 /**
713 * combine all the exportable fields from the lower levels object
714 *
715 * @return array array of exportable Fields
716 * @access public
717 * @static
718 */
719 static function &exportableFields() {
720 if (!self::$_exportableFields) {
721 if (!self::$_exportableFields) {
722 self::$_exportableFields = array();
723 }
724
725 $fields = array();
726
727 $participantFields = CRM_Event_DAO_Participant::export();
728 $noteField = array(
729 'participant_note' => array('title' => 'Participant Note',
730 'name' => 'participant_note',
731 ));
732
733 $participantStatus = array(
734 'participant_status' => array('title' => 'Participant Status',
735 'name' => 'participant_status',
736 ));
737
738 $participantRole = array(
739 'participant_role' => array('title' => 'Participant Role',
740 'name' => 'participant_role',
741 ));
742
743 //campaign fields.
744 if (isset($participantFields['participant_campaign_id'])) {
745 $participantFields['participant_campaign'] = array('title' => ts('Campaign Title'));
746 }
747
748 $discountFields = CRM_Core_DAO_Discount::export();
749
750 $fields = array_merge($participantFields, $participantStatus, $participantRole, $noteField, $discountFields);
751
752 // add custom data
753 $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Participant'));
754 self::$_exportableFields = $fields;
755 }
756
757 return self::$_exportableFields;
758 }
759
760 /**
761 * function to get the event name/sort name for a particular participation / participant
762 *
763 * @param int $participantId id of the participant
764
765 *
766 * @return array $name associated array with sort_name and event title
767 * @static
768 * @access public
769 */
770 static function participantDetails($participantId) {
771 $query = "
772SELECT civicrm_contact.sort_name as name, civicrm_event.title as title, civicrm_contact.id as cid
773FROM civicrm_participant
774 LEFT JOIN civicrm_event ON (civicrm_participant.event_id = civicrm_event.id)
775 LEFT JOIN civicrm_contact ON (civicrm_participant.contact_id = civicrm_contact.id)
776WHERE civicrm_participant.id = {$participantId}
777";
778 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
779
780 $details = array();
781 while ($dao->fetch()) {
782 $details['name'] = $dao->name;
783 $details['title'] = $dao->title;
784 $details['cid'] = $dao->cid;
785 }
786
787 return $details;
788 }
789
790 /**
791 * Get the values for pseudoconstants for name->value and reverse.
792 *
793 * @param array $defaults (reference) the default values, some of which need to be resolved.
794 * @param boolean $reverse true if we want to resolve the values in the reverse direction (value -> name)
795 *
796 * @return void
797 * @access public
798 * @static
799 */
800 static function resolveDefaults(&$defaults, $reverse = FALSE) {
801 self::lookupValue($defaults, 'event', CRM_Event_PseudoConstant::event(), $reverse);
802 self::lookupValue($defaults, 'status', CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label'), $reverse);
803 self::lookupValue($defaults, 'role', CRM_Event_PseudoConstant::participantRole(), $reverse);
804 }
805
806 /**
807 * This function is used to convert associative array names to values
808 * and vice-versa.
809 *
810 * This function is used by both the web form layer and the api. Note that
811 * the api needs the name => value conversion, also the view layer typically
812 * requires value => name conversion
813 */
814 static function lookupValue(&$defaults, $property, &$lookup, $reverse) {
815 $id = $property . '_id';
816
817 $src = $reverse ? $property : $id;
818 $dst = $reverse ? $id : $property;
819
820 if (!array_key_exists($src, $defaults)) {
821 return FALSE;
822 }
823
824 $look = $reverse ? array_flip($lookup) : $lookup;
825
826 if (is_array($look)) {
827 if (!array_key_exists($defaults[$src], $look)) {
828 return FALSE;
829 }
830 }
831 $defaults[$dst] = $look[$defaults[$src]];
832 return TRUE;
833 }
834
835 /**
836 * Delete the record that are associated with this participation
837 *
838 * @param int $id id of the participation to delete
839 *
840 * @return void
841 * @access public
842 * @static
843 */
844 static function deleteParticipant($id) {
845 CRM_Utils_Hook::pre('delete', 'Participant', $id, CRM_Core_DAO::$_nullArray);
846
847 $transaction = new CRM_Core_Transaction();
848
849 //delete activity record
850 $params = array(
851 'source_record_id' => $id,
852 // activity type id for event registration
853 'activity_type_id' => 5,
854 );
855
856 CRM_Activity_BAO_Activity::deleteActivity($params);
857
858 // delete the participant payment record
859 // we need to do this since the cascaded constraints
860 // dont work with join tables
861 $p = array('participant_id' => $id);
862 CRM_Event_BAO_ParticipantPayment::deleteParticipantPayment($p);
863
864 // cleanup line items.
865 $participantsId = array();
866 $participantsId = self::getAdditionalParticipantIds($id);
867 $participantsId[] = $id;
868 CRM_Price_BAO_LineItem::deleteLineItems($participantsId, 'civicrm_participant');
869
870 //delete note when participant deleted.
871 $note = CRM_Core_BAO_Note::getNote($id, 'civicrm_participant');
872 $noteId = key($note);
873 if ($noteId) {
874 CRM_Core_BAO_Note::del($noteId, FALSE);
875 }
876
877 $participant = new CRM_Event_DAO_Participant();
878 $participant->id = $id;
879 $participant->delete();
880
881 $transaction->commit();
882
883 CRM_Utils_Hook::post('delete', 'Participant', $participant->id, $participant);
884
885 // delete the recently created Participant
886 $participantRecent = array(
887 'id' => $id,
888 'type' => 'Participant',
889 );
890
891 CRM_Utils_Recent::del($participantRecent);
892
893 return $participant;
894 }
895
896 /**
897 *Checks duplicate participants
898 *
899 * @param array $duplicates (reference ) an assoc array of name/value pairs
900 * @param array $input an assosiative array of name /value pairs
901 * from other function
902 *
903 * @return object CRM_Contribute_BAO_Contribution object
904 * @access public
905 * @static
906 */
907 static function checkDuplicate($input, &$duplicates) {
908 $eventId = CRM_Utils_Array::value('event_id', $input);
909 $contactId = CRM_Utils_Array::value('contact_id', $input);
910
911 $clause = array();
912 $input = array();
913
914 if ($eventId) {
915 $clause[] = "event_id = %1";
916 $input[1] = array($eventId, 'Integer');
917 }
918
919 if ($contactId) {
920 $clause[] = "contact_id = %2";
921 $input[2] = array($contactId, 'Integer');
922 }
923
924 if (empty($clause)) {
925 return FALSE;
926 }
927
928 $clause = implode(' AND ', $clause);
929
930 $query = "SELECT id FROM civicrm_participant WHERE $clause";
931 $dao = CRM_Core_DAO::executeQuery($query, $input);
932 $result = FALSE;
933 while ($dao->fetch()) {
934 $duplicates[] = $dao->id;
935 $result = TRUE;
936 }
937 return $result;
938 }
939
940 /**
941 * fix the event level
942 *
943 * When price sets are used as event fee, fee_level is set as ^A
944 * seperated string. We need to change that string to comma
945 * separated string before using fee_level in view mode.
946 *
947 * @param string $eventLevel event_leval string from db
948 *
949 * @static
950 *
951 * @return void
952 */
953 static function fixEventLevel(&$eventLevel) {
954 if ((substr($eventLevel, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR) &&
955 (substr($eventLevel, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR)
956 ) {
957 $eventLevel = implode(', ', explode(CRM_Core_DAO::VALUE_SEPARATOR,
958 substr($eventLevel, 1, -1)
959 ));
960 if ($pos = strrpos($eventLevel, '(multiple participants)', 0)) {
961 $eventLevel = substr_replace($eventLevel, "", $pos - 3, 1);
962 }
963 }
964 elseif ((substr($eventLevel, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR)) {
965 $eventLevel = implode(', ', explode(CRM_Core_DAO::VALUE_SEPARATOR,
966 substr($eventLevel, 0, 1)
967 ));
968 }
969 elseif ((substr($eventLevel, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR)) {
970 $eventLevel = implode(', ', explode(CRM_Core_DAO::VALUE_SEPARATOR,
971 substr($eventLevel, 0, -1)
972 ));
973 }
974 }
975
976 /**
977 * get the additional participant ids.
978 *
979 * @param int $primaryParticipantId primary partycipant Id
980 * @param boolean $excludeCancel do not include participant those are cancelled.
981 *
982 * @return array $additionalParticipantIds
983 * @static
984 */
985 static function getAdditionalParticipantIds($primaryParticipantId, $excludeCancel = TRUE, $oldStatusId = NULL) {
986 $additionalParticipantIds = array();
987 if (!$primaryParticipantId) {
988 return $additionalParticipantIds;
989 }
990
991 $where = "participant.registered_by_id={$primaryParticipantId}";
992 if ($excludeCancel) {
993 $cancelStatusId = 0;
994 $negativeStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Negative'");
995 $cancelStatusId = array_search('Cancelled', $negativeStatuses);
996 $where .= " AND participant.status_id != {$cancelStatusId}";
997 }
998
999 if ($oldStatusId) {
1000 $where .= " AND participant.status_id = {$oldStatusId}";
1001 }
1002
1003 $query = "
1004 SELECT participant.id
1005 FROM civicrm_participant participant
1006 WHERE {$where}";
1007
1008 $dao = CRM_Core_DAO::executeQuery($query);
1009 while ($dao->fetch()) {
1010 $additionalParticipantIds[$dao->id] = $dao->id;
1011 }
1012 return $additionalParticipantIds;
1013 }
1014
1015 /**
1016 * Get the event fee info for given participant ids
1017 * either from line item table / participant table.
1018 *
1019 * @param array $participantIds participant ids.
1020 * @param boolean $hasLineItems do fetch from line items.
1021 *
1022 * @return array $feeDetails
1023 * @static
1024 */
1025 function getFeeDetails($participantIds, $hasLineItems = FALSE) {
1026 $feeDetails = array();
1027 if (!is_array($participantIds) || empty($participantIds)) {
1028 return $feeDetails;
1029 }
1030
1031 $select = '
1032SELECT participant.id as id,
1033 participant.fee_level as fee_level,
1034 participant.fee_amount as fee_amount';
1035 $from = 'FROM civicrm_participant participant';
1036 if ($hasLineItems) {
1037 $select .= ' ,
1038lineItem.id as lineId,
1039lineItem.label as label,
1040lineItem.qty as qty,
1041lineItem.unit_price as unit_price,
1042lineItem.line_total as line_total,
1043field.label as field_title,
1044field.html_type as html_type,
1045field.id as price_field_id,
1046value.id as price_field_value_id,
1047value.description as description,
1048IF( value.count, value.count, 0 ) as participant_count';
1049 $from .= "
1050INNER JOIN civicrm_line_item lineItem ON ( lineItem.entity_table = 'civicrm_participant'
1051 AND lineItem.entity_id = participant.id )
1052INNER JOIN civicrm_price_field field ON ( field.id = lineItem.price_field_id )
1053INNER JOIN civicrm_price_field_value value ON ( value.id = lineItem.price_field_value_id )
1054";
1055 }
1056 $where = 'WHERE participant.id IN ( ' . implode(', ', $participantIds) . ' )';
1057 $query = "$select $from $where";
1058
1059 $feeInfo = CRM_Core_DAO::executeQuery($query);
1060 $feeProperties = array('fee_level', 'fee_amount');
1061 $lineProperties = array(
1062 'lineId', 'label', 'qty', 'unit_price',
1063 'line_total', 'field_title', 'html_type',
1064 'price_field_id', 'participant_count', 'price_field_value_id', 'description',
1065 );
1066 while ($feeInfo->fetch()) {
1067 if ($hasLineItems) {
1068 foreach ($lineProperties as $property) {
1069 $feeDetails[$feeInfo->id][$feeInfo->lineId][$property] = $feeInfo->$property;
1070 }
1071 }
1072 else {
1073 foreach ($feeProperties as $property) $feeDetails[$feeInfo->id][$property] = $feeInfo->$property;
1074 }
1075 }
1076
1077 return $feeDetails;
1078 }
1079
1080 /**
1081 * Retrieve additional participants display-names and URL to view their participant records.
1082 * (excludes cancelled participants automatically)
1083 *
1084 * @param int $primaryParticipantID id of primary participant record
1085 *
1086 * @return array $additionalParticipants $displayName => $viewUrl
1087 * @static
1088 */
1089 static function getAdditionalParticipants($primaryParticipantID) {
1090 $additionalParticipantIDs = array();
1091 $additionalParticipantIDs = self::getAdditionalParticipantIds($primaryParticipantID);
1092 if (!empty($additionalParticipantIDs)) {
1093 foreach ($additionalParticipantIDs as $additionalParticipantID) {
1094 $additionalContactID = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant',
1095 $additionalParticipantID,
1096 'contact_id', 'id'
1097 );
1098 $additionalContactName = CRM_Contact_BAO_Contact::displayName($additionalContactID);
1099 $pViewURL = CRM_Utils_System::url('civicrm/contact/view/participant',
1100 "action=view&reset=1&id={$additionalParticipantID}&cid={$additionalContactID}"
1101 );
1102
1103 $additionalParticipants[$additionalContactName] = $pViewURL;
1104 }
1105 }
1106 return $additionalParticipants;
1107 }
1108
1109 /**
1110 * Function for update primary and additional participant status
1111 *
1112 * @param int $participantID primary participant's id
1113 * @param int $statusId status id for participant
1114 * return void
1115 * @access public
1116 * @static
1117 */
1118 static function updateParticipantStatus($participantID, $oldStatusID, $newStatusID = NULL, $updatePrimaryStatus = FALSE) {
1119 if (!$participantID || !$oldStatusID) {
1120 return;
1121 }
1122
1123 if (!$newStatusID) {
1124 $newStatusID = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $participantID, 'status_id');
1125 }
1126 elseif ($updatePrimaryStatus) {
1127 CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Participant', $participantID, 'status_id', $newStatusID);
1128 }
1129
1130 $cascadeAdditionalIds = self::getValidAdditionalIds($participantID, $oldStatusID, $newStatusID);
1131
1132 if (!empty($cascadeAdditionalIds)) {
1133 $cascadeAdditionalIds = implode(',', $cascadeAdditionalIds);
1134 $query = "UPDATE civicrm_participant cp SET cp.status_id = %1 WHERE cp.id IN ({$cascadeAdditionalIds})";
1135 $params = array(1 => array($newStatusID, 'Integer'));
1136 $dao = CRM_Core_DAO::executeQuery($query, $params);
1137 return TRUE;
1138 }
1139 return FALSE;
1140 }
1141
1142 /**
1143 * Function for update status for given participant ids
1144 *
1145 * @param int $participantIds array of participant ids
1146 * @param int $statusId status id for participant
1147 * @params boolean $updateRegisterDate way to track when status changed.
1148 *
1149 * return void
1150 * @access public
1151 * @static
1152 */
1153 static function updateStatus($participantIds, $statusId, $updateRegisterDate = FALSE) {
1154 if (!is_array($participantIds) || empty($participantIds) || !$statusId) {
1155 return;
1156 }
1157
1158 //lets update register date as we update status to keep track
1159 //when we did update status, useful for moving participant
1160 //from pending to expired.
1161 $setClause = "status_id = {$statusId}";
1162 if ($updateRegisterDate) {
1163 $setClause .= ", register_date = NOW()";
1164 }
1165
1166 $participantIdClause = '( ' . implode(',', $participantIds) . ' )';
1167
1168 $query = "
1169UPDATE civicrm_participant
1170 SET {$setClause}
1171 WHERE id IN {$participantIdClause}";
1172
1173 $dao = CRM_Core_DAO::executeQuery($query);
1174 }
1175
1176 /*
1177 * Function takes participant ids and statuses
1178 * update status from $fromStatusId to $toStatusId
1179 * and send mail + create activities.
1180 *
1181 * @param array $participantIds participant ids.
1182 * @param int $toStatusId update status id.
1183 * @param int $fromStatusId from status id
1184 *
1185 * return void
1186 * @access public
1187 * @static
1188 */
1189 static function transitionParticipants($participantIds, $toStatusId,
1190 $fromStatusId = NULL, $returnResult = FALSE, $skipCascadeRule = FALSE
1191 ) {
1192 if (!is_array($participantIds) || empty($participantIds) || !$toStatusId) {
1193 return;
1194 }
1195
1196 //thumb rule is if we triggering primary participant need to triggered additional
1197 $allParticipantIds = $primaryANDAdditonalIds = array();
1198 foreach ($participantIds as $id) {
1199 $allParticipantIds[] = $id;
1200 if (self::isPrimaryParticipant($id)) {
1201 //filter additional as per status transition rules, CRM-5403
1202 if ($skipCascadeRule) {
1203 $additionalIds = self::getAdditionalParticipantIds($id);
1204 }
1205 else {
1206 $additionalIds = self::getValidAdditionalIds($id, $fromStatusId, $toStatusId);
1207 }
1208 if (!empty($additionalIds)) {
1209 $allParticipantIds = array_merge($allParticipantIds, $additionalIds);
1210 $primaryANDAdditonalIds[$id] = $additionalIds;
1211 }
1212 }
1213 }
1214
1215 //get the unique participant ids,
1216 $allParticipantIds = array_unique($allParticipantIds);
1217
1218 //pull required participants, contacts, events data, if not in hand
1219 static $eventDetails = array();
1220 static $domainValues = array();
1221 static $contactDetails = array();
1222
1223 $contactIds = $eventIds = $participantDetails = array();
1224
1225 $statusTypes = CRM_Event_PseudoConstant::participantStatus();
1226 $participantRoles = CRM_Event_PseudoConstant::participantRole();
1227 $pendingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL,
1228 "class = 'Pending'"
1229 );
1230
1231 //first thing is pull all necessory data from db.
1232 $participantIdClause = '(' . implode(',', $allParticipantIds) . ')';
1233
1234 //get all participants data.
1235 $query = "SELECT * FROM civicrm_participant WHERE id IN {$participantIdClause}";
1236 $dao = CRM_Core_DAO::executeQuery($query);
1237 while ($dao->fetch()) {
1238 $participantDetails[$dao->id] = array(
1239 'id' => $dao->id,
1240 'role' => $participantRoles[$dao->role_id],
1241 'is_test' => $dao->is_test,
1242 'event_id' => $dao->event_id,
1243 'status_id' => $dao->status_id,
1244 'fee_amount' => $dao->fee_amount,
1245 'contact_id' => $dao->contact_id,
1246 'register_date' => $dao->register_date,
1247 'registered_by_id' => $dao->registered_by_id,
1248 );
1249 if (!array_key_exists($dao->contact_id, $contactDetails)) {
1250 $contactIds[$dao->contact_id] = $dao->contact_id;
1251 }
1252
1253 if (!array_key_exists($dao->event_id, $eventDetails)) {
1254 $eventIds[$dao->event_id] = $dao->event_id;
1255 }
1256 }
1257
1258 //get the domain values.
1259 if (empty($domainValues)) {
1260 // making all tokens available to templates.
1261 $domain = CRM_Core_BAO_Domain::getDomain();
1262 $tokens = array('domain' => array('name', 'phone', 'address', 'email'),
1263 'contact' => CRM_Core_SelectValues::contactTokens(),
1264 );
1265
1266 foreach ($tokens['domain'] as $token) {
1267 $domainValues[$token] = CRM_Utils_Token::getDomainTokenReplacement($token, $domain);
1268 }
1269 }
1270
1271 //get all required contacts detail.
1272 if (!empty($contactIds)) {
1273 // get the contact details.
1274 list($currentContactDetails) = CRM_Utils_Token::getTokenDetails($contactIds, NULL,
1275 FALSE, FALSE, NULL,
1276 array(),
1277 'CRM_Event_BAO_Participant'
1278 );
1279 foreach ($currentContactDetails as $contactId => $contactValues) {
1280 $contactDetails[$contactId] = $contactValues;
1281 }
1282 }
1283
1284 //get all required events detail.
1285 if (!empty($eventIds)) {
1286 foreach ($eventIds as $eventId) {
1287 //retrieve event information
1288 $eventParams = array('id' => $eventId);
1289 CRM_Event_BAO_Event::retrieve($eventParams, $eventDetails[$eventId]);
1290
1291 //get default participant role.
1292 $eventDetails[$eventId]['participant_role'] = CRM_Utils_Array::value($eventDetails[$eventId]['default_role_id'], $participantRoles);
1293
1294 //get the location info
1295 $locParams = array('entity_id' => $eventId, 'entity_table' => 'civicrm_event');
1296 $eventDetails[$eventId]['location'] = CRM_Core_BAO_Location::getValues($locParams, TRUE);
1297 }
1298 }
1299
1300 //now we are ready w/ all required data.
1301 //take a decision as per statuses.
1302
1303 $emailType = NULL;
1304 $toStatus = $statusTypes[$toStatusId];
1305 $fromStatus = CRM_Utils_Array::value($fromStatusId, $statusTypes);
1306
1307 switch ($toStatus) {
1308 case 'Pending from waitlist':
1309 case 'Pending from approval':
1310 switch ($fromStatus) {
1311 case 'On waitlist':
1312 case 'Awaiting approval':
1313 $emailType = 'Confirm';
1314 break;
1315 }
1316 break;
1317
1318 case 'Expired':
1319 //no matter from where u come send expired mail.
1320 $emailType = $toStatus;
1321 break;
1322
1323 case 'Cancelled':
1324 //no matter from where u come send cancel mail.
1325 $emailType = $toStatus;
1326 break;
1327 }
1328
1329 //as we process additional w/ primary, there might be case if user
1330 //select primary as well as additionals, so avoid double processing.
1331 $processedParticipantIds = array();
1332 $mailedParticipants = array();
1333
1334 //send mails and update status.
1335 foreach ($participantDetails as $participantId => $participantValues) {
1336 $updateParticipantIds = array();
1337 if (in_array($participantId, $processedParticipantIds)) {
1338 continue;
1339 }
1340
1341 //check is it primary and has additional.
1342 if (array_key_exists($participantId, $primaryANDAdditonalIds)) {
1343 foreach ($primaryANDAdditonalIds[$participantId] as $additonalId) {
1344
1345 if ($emailType) {
1346 $mail = self::sendTransitionParticipantMail($additonalId,
1347 $participantDetails[$additonalId],
1348 $eventDetails[$participantDetails[$additonalId]['event_id']],
1349 $contactDetails[$participantDetails[$additonalId]['contact_id']],
1350 $domainValues,
1351 $emailType
1352 );
1353
1354 //get the mail participant ids
1355 if ($mail) {
1356 $mailedParticipants[$additonalId] = $contactDetails[$participantDetails[$additonalId]['contact_id']]['display_name'];
1357 }
1358 }
1359 $updateParticipantIds[] = $additonalId;
1360 $processedParticipantIds[] = $additonalId;
1361 }
1362 }
1363
1364 //now send email appropriate mail to primary.
1365 if ($emailType) {
1366 $mail = self::sendTransitionParticipantMail($participantId,
1367 $participantValues,
1368 $eventDetails[$participantValues['event_id']],
1369 $contactDetails[$participantValues['contact_id']],
1370 $domainValues,
1371 $emailType
1372 );
1373
1374 //get the mail participant ids
1375 if ($mail) {
1376 $mailedParticipants[$participantId] = $contactDetails[$participantValues['contact_id']]['display_name'];
1377 }
1378 }
1379
1380 //now update status of group/one at once.
1381 $updateParticipantIds[] = $participantId;
1382
1383 //update the register date only when we,
1384 //move participant to pending class, CRM-6496
1385 $updateRegisterDate = FALSE;
1386 if (array_key_exists($toStatusId, $pendingStatuses)) {
1387 $updateRegisterDate = TRUE;
1388 }
1389 self::updateStatus($updateParticipantIds, $toStatusId, $updateRegisterDate);
1390 $processedParticipantIds[] = $participantId;
1391 }
1392
1393 //return result for cron.
1394 if ($returnResult) {
1395 $results = array(
1396 'mailedParticipants' => $mailedParticipants,
1397 'updatedParticipantIds' => $processedParticipantIds,
1398 );
1399
1400 return $results;
1401 }
1402 }
1403
1404 /**
1405 * Function to send mail and create activity
1406 * when participant status changed.
1407 *
1408 * @param int $participantId participant id.
1409 * @param array $participantValues participant detail values. status id for participants
1410 * @param array $eventDetails required event details
1411 * @param array $contactDetails required contact details
1412 * @param array $domainValues required domain values.
1413 * @param string $mailType (eg 'approval', 'confirm', 'expired' )
1414 *
1415 * return void
1416 * @access public
1417 * @static
1418 */
1419 static function sendTransitionParticipantMail(
1420 $participantId,
1421 $participantValues,
1422 $eventDetails,
1423 $contactDetails,
1424 &$domainValues,
1425 $mailType
1426 ) {
1427 //send emails.
1428 $mailSent = FALSE;
1429
1430 //don't send confirmation mail to additional
1431 //since only primary able to confirm registration.
1432 if (CRM_Utils_Array::value('registered_by_id', $participantValues) &&
1433 $mailType == 'Confirm'
1434 ) {
1435 return $mailSent;
1436 }
1437
1438 if ($toEmail = CRM_Utils_Array::value('email', $contactDetails)) {
1439
1440 $contactId = $participantValues['contact_id'];
1441 $participantName = $contactDetails['display_name'];
1442
1443 //calculate the checksum value.
1444 $checksumValue = NULL;
1445 if ($mailType == 'Confirm' && !$participantValues['registered_by_id']) {
1446 $checksumLife = 'inf';
1447 if ($endDate = CRM_Utils_Array::value('end_date', $eventDetails)) {
1448 $checksumLife = (CRM_Utils_Date::unixTime($endDate) - time()) / (60 * 60);
1449 }
1450 $checksumValue = CRM_Contact_BAO_Contact_Utils::generateChecksum($contactId, NULL, $checksumLife);
1451 }
1452
1453 //take a receipt from as event else domain.
1454 $receiptFrom = $domainValues['name'] . ' <' . $domainValues['email'] . '>';
1455 if (CRM_Utils_Array::value('confirm_from_name', $eventDetails) &&
1456 CRM_Utils_Array::value('confirm_from_email', $eventDetails)
1457 ) {
1458 $receiptFrom = $eventDetails['confirm_from_name'] . ' <' . $eventDetails['confirm_from_email'] . '>';
1459 }
1460
1461 list($mailSent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplates::sendTemplate(
1462 array(
1463 'groupName' => 'msg_tpl_workflow_event',
1464 'valueName' => 'participant_' . strtolower($mailType),
1465 'contactId' => $contactId,
1466 'tplParams' => array(
1467 'contact' => $contactDetails,
1468 'domain' => $domainValues,
1469 'participant' => $participantValues,
1470 'event' => $eventDetails,
1471 'paidEvent' => CRM_Utils_Array::value('is_monetary', $eventDetails),
1472 'isShowLocation' => CRM_Utils_Array::value('is_show_location', $eventDetails),
1473 'isAdditional' => $participantValues['registered_by_id'],
1474 'isExpired' => $mailType == 'Expired',
1475 'isConfirm' => $mailType == 'Confirm',
1476 'checksumValue' => $checksumValue,
1477 ),
1478 'from' => $receiptFrom,
1479 'toName' => $participantName,
1480 'toEmail' => $toEmail,
1481 'cc' => CRM_Utils_Array::value('cc_confirm', $eventDetails),
1482 'bcc' => CRM_Utils_Array::value('bcc_confirm', $eventDetails),
1483 )
1484 );
1485
1486 // 3. create activity record.
1487 if ($mailSent) {
1488 $now = date('YmdHis');
1489 $activityType = 'Event Registration';
1490 $activityParams = array(
1491 'subject' => $subject,
1492 'source_contact_id' => $contactId,
1493 'source_record_id' => $participantId,
1494 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type',
1495 $activityType,
1496 'name'
1497 ),
1498 'activity_date_time' => CRM_Utils_Date::isoToMysql($now),
1499 'due_date_time' => CRM_Utils_Date::isoToMysql($participantValues['register_date']),
1500 'is_test' => $participantValues['is_test'],
1501 'status_id' => 2,
1502 );
1503
1504 if (is_a(CRM_Activity_BAO_Activity::create($activityParams), 'CRM_Core_Error')) {
1505 CRM_Core_Error::fatal('Failed creating Activity for expiration mail');
1506 }
1507 }
1508 }
1509
1510 return $mailSent;
1511 }
1512
1513 /**
1514 * get participant status change message.
1515 *
1516 * @return string
1517 * @access public
1518 */
1519 function updateStatusMessage($participantId, $statusChangeTo, $fromStatusId) {
1520 $statusMsg = NULL;
1521 $results = self::transitionParticipants(array($participantId),
1522 $statusChangeTo, $fromStatusId, TRUE
1523 );
1524
1525 $allStatuses = CRM_Event_PseudoConstant::participantStatus();
1526 //give user message only when mail has sent.
1527 if (is_array($results) && !empty($results)) {
1528 if (is_array($results['updatedParticipantIds']) && !empty($results['updatedParticipantIds'])) {
1529 foreach ($results['updatedParticipantIds'] as $processedId) {
1530 if (is_array($results['mailedParticipants']) &&
1531 array_key_exists($processedId, $results['mailedParticipants'])
1532 ) {
1533 $statusMsg .= '<br /> ' . ts("Participant status has been updated to '%1'. An email has been sent to %2.",
1534 array(
1535 1 => $allStatuses[$statusChangeTo],
1536 2 => $results['mailedParticipants'][$processedId],
1537 )
1538 );
1539 }
1540 }
1541 }
1542 }
1543
1544 return $statusMsg;
1545 }
1546
1547 /**
1548 * get event full and waiting list message.
1549 *
1550 * @return string
1551 * @access public
1552 */
1553 static function eventFullMessage($eventId, $participantId = NULL) {
1554 $eventfullMsg = $dbStatusId = NULL;
1555 $checkEventFull = TRUE;
1556 if ($participantId) {
1557 $dbStatusId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $participantId, 'status_id');
1558 if (array_key_exists($dbStatusId, CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1'))) {
1559 //participant already in counted status no need to check for event full messages.
1560 $checkEventFull = FALSE;
1561 }
1562 }
1563
1564 //early return.
1565 if (!$eventId || !$checkEventFull) {
1566 return $eventfullMsg;
1567 }
1568
1569 //event is truly full.
1570 $emptySeats = self::eventFull($eventId, FALSE, FALSE);
1571 if (is_string($emptySeats) && $emptySeats !== NULL) {
1572 $maxParticipants = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $eventId, 'max_participants');
1573 $eventfullMsg = ts("This event currently has the maximum number of participants registered (%1). However, you can still override this limit and register additional participants using this form.", array(
1574 1 => $maxParticipants)) . '<br />';
1575 }
1576
1577 $hasWaiting = FALSE;
1578 $waitListedCount = self::eventFull($eventId, FALSE, TRUE, TRUE);
1579 if (is_numeric($waitListedCount)) {
1580 $hasWaiting = TRUE;
1581 //only current processing participant is on waitlist.
1582 if ($waitListedCount == 1 && CRM_Event_PseudoConstant::participantStatus($dbStatusId) == 'On waitlist') {
1583 $hasWaiting = FALSE;
1584 }
1585 }
1586
1587 if ($hasWaiting) {
1588 $waitingStatusId = array_search('On waitlist',
1589 CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Waiting'")
1590 );
1591 $viewWaitListUrl = CRM_Utils_System::url('civicrm/event/search',
1592 "reset=1&force=1&event={$eventId}&status={$waitingStatusId}"
1593 );
1594
1595 $eventfullMsg .= ts("There are %2 people currently on the waiting list for this event. You can <a href='%1'>view waitlisted registrations here</a>, or you can continue and register additional participants using this form.",
1596 array(
1597 1 => $viewWaitListUrl,
1598 2 => $waitListedCount,
1599 )
1600 );
1601 }
1602
1603 return $eventfullMsg;
1604 }
1605
1606 /**
1607 * check for whether participant is primary or not
1608 *
1609 * @param $participantId
1610 *
1611 * @return true if participant is primary
1612 * @access public
1613 */
1614 static function isPrimaryParticipant($participantId) {
1615
1616 $participant = new CRM_Event_DAO_Participant();
1617 $participant->registered_by_id = $participantId;
1618
1619 if ($participant->find(TRUE)) {
1620 return TRUE;
1621 }
1622 return FALSE;
1623 }
1624
1625 /**
1626 * get additional participant Ids for cascading with primary participant status
1627 *
1628 * @param int $participantId participant id.
1629 * @param int $oldStatusId previous status
1630 * @param int $newStatusId new status
1631 *
1632 * @return true if allowed
1633 * @access public
1634 */
1635 static function getValidAdditionalIds($participantId, $oldStatusId, $newStatusId) {
1636
1637 $additionalParticipantIds = array();
1638
1639 static $participantStatuses = array();
1640
1641 if (empty($participantStatuses)) {
1642 $participantStatuses = CRM_Event_PseudoConstant::participantStatus();
1643 }
1644
1645 if (CRM_Utils_Array::value($participantStatuses[$oldStatusId], self::$_statusTransitionsRules) &&
1646 in_array($participantStatuses[$newStatusId], self::$_statusTransitionsRules[$participantStatuses[$oldStatusId]])
1647 ) {
1648 $additionalParticipantIds = self::getAdditionalParticipantIds($participantId, TRUE, $oldStatusId);
1649 }
1650
1651 return $additionalParticipantIds;
1652 }
1653
1654 /**
1655 * Function to get participant record count for a Contact
1656 *
1657 * @param int $contactId Contact ID
1658 *
1659 * @return int count of participant records
1660 * @access public
1661 * @static
1662 */
1663 static function getContactParticipantCount($contactID) {
1664 $query = "SELECT count(*)
1665FROM civicrm_participant
1666WHERE civicrm_participant.contact_id = {$contactID} AND
1667 civicrm_participant.is_test = 0";
1668 return CRM_Core_DAO::singleValueQuery($query);
1669 }
1670
1671 /**
1672 * Function to get participant ids by contribution id
1673 *
1674 * @param int $contributionId Contribution Id
1675 * @param bool $excludeCancelled Exclude cancelled additional participant
1676 *
1677 * @return int $participantsId
1678 * @access public
1679 * @static
1680 */
1681 static function getParticipantIds($contributionId, $excludeCancelled = FALSE) {
1682
1683 $ids = array();
1684 if (!$contributionId) {
1685 return $ids;
1686 }
1687
1688 // get primary participant id
1689 $query = "SELECT participant_id FROM civicrm_participant_payment WHERE contribution_id = {$contributionId}";
1690 $participantId = CRM_Core_DAO::singleValueQuery($query);
1691
1692 // get additional participant ids (including cancelled)
1693 if ($participantId) {
1694 $ids = array_merge(array(
1695 $participantId), self::getAdditionalParticipantIds($participantId,
1696 $excludeCancelled
1697 ));
1698 }
1699
1700 return $ids;
1701 }
1702
1703 /**
1704 * Function to get additional Participant edit & view url .
1705 *
1706 * @param array $paticipantIds an array of additional participant ids.
1707 *
1708 * @return array of Urls.
1709 * @access public
1710 * @static
1711 */
1712 static function getAdditionalParticipantUrl($participantIds) {
1713 foreach ($participantIds as $value) {
1714 $links = array();
1715 $details = self::participantDetails($value);
1716 $viewUrl = CRM_Utils_System::url('civicrm/contact/view/participant',
1717 "action=view&reset=1&id={$value}&cid={$details['cid']}"
1718 );
1719 $editUrl = CRM_Utils_System::url('civicrm/contact/view/participant',
1720 "action=update&reset=1&id={$value}&cid={$details['cid']}"
1721 );
1722 $links[] = "<td><a href='{$viewUrl}'>" . $details['name'] . "</a></td><td></td><td><a href='{$editUrl}'>" . ts('Edit') . "</a></td>";
1723 $links = "<table><tr>" . implode("</tr><tr>", $links) . "</tr></table>";
1724 return $links;
1725 }
1726 }
1727
1728 /**
1729 * to create trxn entry if an event has discount.
1730 *
1731 * @param int $eventID event id
1732 * @param array $contributionParams contribution params.
1733 *
1734 * @static
1735 */
1736 static function createDiscountTrxn($eventID, $contributionParams, $feeLevel) {
1737 // CRM-11124
1738 $checkDiscount = CRM_Core_BAO_Discount::findSet($eventID,'civicrm_event');
1739 if (!empty($checkDiscount)) {
1740 $feeLevel = current($feeLevel);
9da8dc8c 1741 $priceSetId = CRM_Price_BAO_PriceSet::getFor('civicrm_event', $eventID, NULL);
6a488035
TO
1742 $query = "SELECT cpfv.amount FROM `civicrm_price_field_value` cpfv
1743LEFT JOIN civicrm_price_field cpf ON cpfv.price_field_id = cpf.id
1744WHERE cpf.price_set_id = %1 AND cpfv.label LIKE %2";
1745 $params = array(1 => array($priceSetId, 'Integer'),
1746 2 => array($feeLevel, 'String'));
1747 $mainAmount = CRM_Core_DAO::singleValueQuery($query, $params);
f743a6eb 1748 $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Discounts Account is' "));
6a488035
TO
1749 $contributionParams['trxnParams']['from_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType(
1750 $contributionParams['contribution']->financial_type_id, $relationTypeId);
1751 if (CRM_Utils_Array::value('from_financial_account_id', $contributionParams['trxnParams'])) {
1752 $contributionParams['trxnParams']['total_amount'] = $mainAmount - $contributionParams['total_amount'];
1753 $contributionParams['trxnParams']['payment_processor_id'] = $contributionParams['trxnParams']['payment_instrument_id'] =
1754 $contributionParams['trxnParams']['check_number'] = $contributionParams['trxnParams']['trxn_id'] =
1755 $contributionParams['trxnParams']['net_amount'] = $contributionParams['trxnParams']['fee_amount'] = NULL;
1756
1757 CRM_Core_BAO_FinancialTrxn::create($contributionParams['trxnParams']);
1758 }
1759 }
1760 return;
1761 }
c3d24ba7
PN
1762
1763 /**
1764 * Function to delete participants of contact
1765 *
1766 * CRM-12155
1767 *
03e04002 1768 * @param integer $contactId contact id
c3d24ba7
PN
1769 *
1770 * @access public
1771 * @static
1772 */
1773 static function deleteContactParticipant($contactId) {
1774 $participant = new CRM_Event_DAO_Participant();
1775 $participant->contact_id = $contactId;
1776 $participant->find();
1777 while ($participant->fetch()) {
1778 self::deleteParticipant($participant->id);
1779 }
1780 }
6a488035
TO
1781}
1782