4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 * This class generates form components to transfer an Event to another participant
23 class CRM_Event_Form_SelfSvcTransfer
extends CRM_Core_Form
{
30 protected $_from_participant_id;
37 protected $_from_contact_id;
39 * last name of the particpant to transfer to
44 protected $_to_contact_last_name;
46 * first name of the particpant to transfer to
51 protected $_to_contact_first_name;
53 * email of participant
58 protected $_to_contact_email;
64 protected $_to_contact_id;
66 * event to be cancelled/transferred
76 protected $_event_title;
82 protected $_event_start_date;
94 protected $_participant = [];
100 protected $_part_values;
106 protected $_details = [];
112 protected $_line_items = [];
118 protected $contact_id;
121 * Is backoffice form?
125 protected $isBackoffice = FALSE;
128 * Get source values for transfer based on participant id in URL. Line items will
129 * be transferred to this participant - at this point no transaction changes processed
133 * @throws \CRM_Core_Exception
135 public function preProcess() {
136 $session = CRM_Core_Session
::singleton();
137 $this->_userContext
= $session->readUserContext();
138 $this->_from_participant_id
= CRM_Utils_Request
::retrieve('pid', 'Positive', $this, FALSE, NULL, 'REQUEST');
139 $this->_userChecksum
= CRM_Utils_Request
::retrieve('cs', 'String', $this, FALSE, NULL, 'REQUEST');
140 $this->isBackoffice
= CRM_Utils_Request
::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST');
141 $params = ['id' => $this->_from_participant_id
];
142 $participant = $values = [];
143 $this->_participant
= CRM_Event_BAO_Participant
::getValues($params, $values, $participant);
144 $this->_part_values
= $values[$this->_from_participant_id
];
145 $this->set('values', $this->_part_values
);
146 $this->_event_id
= $this->_part_values
['event_id'];
147 $url = CRM_Utils_System
::url('civicrm/event/info', "reset=1&id={$this->_event_id}");
148 $this->_from_contact_id
= $this->_part_values
['participant_contact_id'];
149 $validUser = CRM_Contact_BAO_Contact_Utils
::validChecksum($this->_from_contact_id
, $this->_userChecksum
);
150 if (!$validUser && !CRM_Core_Permission
::check('edit all events')) {
151 CRM_Core_Error
::statusBounce(ts('You do not have sufficient permission to transfer/cancel this participant.'), $url);
153 $this->assign('action', $this->_action
);
154 if ($this->_from_participant_id
) {
155 $this->assign('participantId', $this->_from_participant_id
);
159 $this->_event_title
= CRM_Event_BAO_Event
::getFieldValue('CRM_Event_DAO_Event', $this->_event_id
, $daoName);
160 $daoName = 'start_date';
161 $this->_event_start_date
= CRM_Event_BAO_Event
::getFieldValue('CRM_Event_DAO_Event', $this->_event_id
, $daoName);
162 list($displayName, $email) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($this->_from_contact_id
);
163 $this->_contact_name
= $displayName;
164 $this->_contact_email
= $email;
166 $details = CRM_Event_BAO_Participant
::participantDetails($this->_from_participant_id
);
167 $selfServiceDetails = CRM_Event_BAO_Participant
::getSelfServiceEligibility($this->_from_participant_id
, $url, $this->isBackoffice
);
168 $details = array_merge($details, $selfServiceDetails);
169 $this->assign('details', $details);
170 //This participant row will be cancelled. Get line item(s) to cancel
171 $this->selfsvctransferUrl
= CRM_Utils_System
::url('civicrm/event/selfsvcupdate',
172 "reset=1&id={$this->_from_participant_id}&id=0");
173 $this->selfsvctransferText
= ts('Update');
174 $this->selfsvctransferButtonText
= ts('Update');
178 * Build form for input of transferree email, name
182 public function buildQuickForm() {
183 // use entityRef select field for contact when this form is used by staff/admin user
184 if ($this->isBackoffice
) {
185 $this->addEntityRef("contact_id", ts('Select Contact'), ['create' => TRUE], TRUE);
187 // for front-end user show and use the basic three fields used to create a contact
189 $this->add('text', 'email', ts('To Email'), $this->_contact_email
, TRUE);
190 $this->add('text', 'last_name', ts('To Last Name'), $this->_to_contact_last_name
, TRUE);
191 $this->add('text', 'first_name', ts('To First Name'), $this->_to_contact_first_name
, TRUE);
197 'name' => ts('Transfer Registration'),
200 $this->addFormRule(['CRM_Event_Form_SelfSvcTransfer', 'formRule'], $this);
201 parent
::buildQuickForm();
207 * return @array _defaults
209 public function setDefaultValues() {
210 $this->_defaults
= [];
211 return $this->_defaults
;
215 * Validate email and name input
217 * return array $errors
219 public static function formRule($fields, $files, $self) {
221 if (!empty($fields['contact_id'])) {
222 $to_contact_id = $fields['contact_id'];
225 //check that either an email or firstname+lastname is included in the form(CRM-9587)
226 $to_contact_id = self
::checkProfileComplete($fields, $errors, $self);
228 //To check if the user is already registered for the event(CRM-2426)
229 if (!empty($to_contact_id)) {
230 self
::checkRegistration($fields, $self, $to_contact_id, $errors);
232 //return parent::formrule($fields, $files, $self);
233 return empty($errors) ?
TRUE : $errors;
237 * Check whether profile (name, email) is complete
241 public static function checkProfileComplete($fields, &$errors, $self) {
243 foreach ($fields as $fieldname => $fieldvalue) {
244 if (substr($fieldname, 0, 5) == 'email' && $fieldvalue) {
245 $email = $fieldvalue;
248 if (!$email && !(CRM_Utils_Array
::value('first_name', $fields) &&
249 !empty($fields['last_name']))) {
250 $defaults = $params = ['id' => $eventId];
251 CRM_Event_BAO_Event
::retrieve($params, $defaults);
252 $message = ts("Mandatory fields (first name and last name, OR email address) are missing from this form.");
253 $errors['_qf_default'] = $message;
255 $contact = CRM_Contact_BAO_Contact
::matchContactOnEmail($email, "");
256 $contact_id = empty($contact->contact_id
) ?
NULL : $contact->contact_id
;
257 if (!CRM_Utils_Rule
::email($fields['email'])) {
258 $errors['email'] = ts('Enter valid email address.');
260 if (empty($errors) && empty($contact_id)) {
262 'email-Primary' => $fields['email'] ??
NULL,
263 'first_name' => $fields['first_name'] ??
NULL,
264 'last_name' => $fields['last_name'] ??
NULL,
265 'is_deleted' => $fields['is_deleted'] ??
FALSE,
267 //create new contact for this name/email pair
268 //if new contact, no need to check for contact already registered
269 $contact_id = CRM_Contact_BAO_Contact
::createProfileContact($params, $fields, $contact_id);
275 * Check contact details
279 public static function checkRegistration($fields, $self, $contact_id, &$errors) {
280 // verify whether this contact already registered for this event
281 $contact_details = CRM_Contact_BAO_Contact
::getContactDetails($contact_id);
282 $display_name = $contact_details[0];
283 $query = 'select event_id from civicrm_participant where contact_id = ' . $contact_id;
284 $dao = CRM_Core_DAO
::executeQuery($query);
285 while ($dao->fetch()) {
286 $to_event_id[] = $dao->event_id
;
288 if (!empty($to_event_id)) {
289 foreach ($to_event_id as $id) {
290 if ($id == $self->_event_id
) {
291 $errors['email'] = $display_name . ts(" is already registered for this event");
298 * Process transfer - first add the new participant to the event, then cancel
299 * source participant - send confirmation email to transferee
301 * @throws \CiviCRM_API3_Exception
303 public function postProcess() {
304 //For transfer, process form to allow selection of transferree
305 $params = $this->controller
->exportValues($this->_name
);
306 if (!empty($params['contact_id'])) {
307 $contact_id = $params['contact_id'];
310 //cancel 'from' participant row
311 $contact_id_result = civicrm_api3('Contact', 'get', [
314 'email' => $params['email'],
315 'options' => ['limit' => 1],
317 $contact_id_result = $contact_id_result['values'][0];
318 $contact_id = $contact_id_result['contact_id'];
319 $contact_is_deleted = $contact_id_result['contact_is_deleted'];
320 if ($contact_is_deleted ||
!is_numeric($contact_id)) {
321 CRM_Core_Error
::statusBounce(ts('Contact does not exist.'));
325 $this->transferParticipantRegistration($contact_id, $this->_from_participant_id
, $this->_event_id
);
327 $contact_details = CRM_Contact_BAO_Contact
::getContactDetails($contact_id);
328 $display_name = current($contact_details);
329 $this->assign('to_participant', $display_name);
330 $this->sendCancellation();
331 list($displayName, $email) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($contact_id);
332 $statusMsg = ts('Event registration information for %1 has been updated.', [1 => $displayName]);
333 $statusMsg .= ' ' . ts('A confirmation email has been sent to %1.', [1 => $email]);
334 CRM_Core_Session
::setStatus($statusMsg, ts('Registration Transferred'), 'success');
335 if ($this->isBackoffice
) {
338 $url = CRM_Utils_System
::url('civicrm/event/info', "reset=1&id={$this->_event_id}");
339 CRM_Utils_System
::redirect($url);
343 * Based on input, create participant row for transferee and send email
347 * @throws \CRM_Core_Exception
349 public function participantTransfer($participant) {
350 $contactDetails = [];
351 $contactIds[] = $participant->contact_id
;
352 list($currentContactDetails) = CRM_Utils_Token
::getTokenDetails($contactIds, NULL,
353 FALSE, FALSE, NULL, [], 'CRM_Event_BAO_Participant');
354 foreach ($currentContactDetails as $contactId => $contactValues) {
355 $contactDetails[$contactId] = $contactValues;
357 $participantRoles = CRM_Event_PseudoConstant
::participantRole();
358 $participantDetails = [];
359 $query = "SELECT * FROM civicrm_participant WHERE id = " . $participant->id
;
360 $dao = CRM_Core_DAO
::executeQuery($query);
361 while ($dao->fetch()) {
362 $participantDetails[$dao->id
] = [
364 'role' => $participantRoles[$dao->role_id
],
365 'is_test' => $dao->is_test
,
366 'event_id' => $dao->event_id
,
367 'status_id' => $dao->status_id
,
368 'fee_amount' => $dao->fee_amount
,
369 'contact_id' => $dao->contact_id
,
370 'register_date' => $dao->register_date
,
371 'registered_by_id' => $dao->registered_by_id
,
375 if (empty($domainValues)) {
376 $domain = CRM_Core_BAO_Domain
::getDomain();
385 'contact' => CRM_Core_SelectValues
::contactTokens(),
387 foreach ($tokens['domain'] as $token) {
388 $domainValues[$token] = CRM_Utils_Token
::getDomainTokenReplacement($token, $domain);
392 $eventParams = ['id' => $participant->event_id
];
393 CRM_Event_BAO_Event
::retrieve($eventParams, $eventDetails);
394 //get default participant role.
395 $eventDetails['participant_role'] = $participantRoles[$eventDetails['default_role_id']] ??
NULL;
396 //get the location info
398 'entity_id' => $participant->event_id
,
399 'entity_table' => 'civicrm_event',
401 $eventDetails['location'] = CRM_Core_BAO_Location
::getValues($locParams, TRUE);
402 $toEmail = $contactDetails[$participant->contact_id
]['email'] ??
NULL;
404 //take a receipt from as event else domain.
405 $receiptFrom = $domainValues['name'] . ' <' . $domainValues['email'] . '>';
406 if (!empty($eventDetails['confirm_from_name']) && !empty($eventDetails['confirm_from_email'])) {
407 $receiptFrom = $eventDetails['confirm_from_name'] . ' <' . $eventDetails['confirm_from_email'] . '>';
409 $participantName = $contactDetails[$participant->contact_id
]['display_name'];
411 'event' => $eventDetails,
412 'participant' => $participantDetails[$participant->id
],
413 'participantID' => $participant->id
,
414 'participant_status' => 'Registered',
417 $sendTemplateParams = [
418 'groupName' => 'msg_tpl_workflow_event',
419 'valueName' => 'event_online_receipt',
420 'contactId' => $participantDetails[$participant->id
]['contact_id'],
421 'tplParams' => $tplParams,
422 'from' => $receiptFrom,
423 'toName' => $participantName,
424 'toEmail' => $toEmail,
425 'cc' => $eventDetails['cc_confirm'] ??
NULL,
426 'bcc' => $eventDetails['bcc_confirm'] ??
NULL,
428 CRM_Core_BAO_MessageTemplate
::sendTemplate($sendTemplateParams);
433 * Send confirmation of cancellation to source participant
437 public function sendCancellation() {
439 $domain = CRM_Core_BAO_Domain
::getDomain();
448 'contact' => CRM_Core_SelectValues
::contactTokens(),
450 foreach ($tokens['domain'] as $token) {
451 $domainValues[$token] = CRM_Utils_Token
::getDomainTokenReplacement($token, $domain);
454 $participantRoles = CRM_Event_PseudoConstant
::participantRole();
455 $participantDetails = [];
456 $query = "SELECT * FROM civicrm_participant WHERE id = {$this->_from_participant_id}";
457 $dao = CRM_Core_DAO
::executeQuery($query);
458 while ($dao->fetch()) {
459 $participantDetails[$dao->id
] = [
461 'role' => $participantRoles[$dao->role_id
],
462 'is_test' => $dao->is_test
,
463 'event_id' => $dao->event_id
,
464 'status_id' => $dao->status_id
,
465 'fee_amount' => $dao->fee_amount
,
466 'contact_id' => $dao->contact_id
,
467 'register_date' => $dao->register_date
,
468 'registered_by_id' => $dao->registered_by_id
,
472 $eventParams = ['id' => $this->_event_id
];
473 CRM_Event_BAO_Event
::retrieve($eventParams, $eventDetails[$this->_event_id
]);
474 //get default participant role.
475 $eventDetails[$this->_event_id
]['participant_role'] = $participantRoles[$eventDetails[$this->_event_id
]['default_role_id']] ??
NULL;
476 //get the location info
477 $locParams = ['entity_id' => $this->_event_id
, 'entity_table' => 'civicrm_event'];
478 $eventDetails[$this->_event_id
]['location'] = CRM_Core_BAO_Location
::getValues($locParams, TRUE);
479 //get contact details
480 $contactIds[$this->_from_contact_id
] = $this->_from_contact_id
;
481 list($currentContactDetails) = CRM_Utils_Token
::getTokenDetails($contactIds, NULL,
482 FALSE, FALSE, NULL, [],
483 'CRM_Event_BAO_Participant'
485 foreach ($currentContactDetails as $contactId => $contactValues) {
486 $contactDetails[$this->_from_contact_id
] = $contactValues;
488 //send a 'cancelled' email to user, and cc the event's cc_confirm email
489 $mail = CRM_Event_BAO_Participant
::sendTransitionParticipantMail($this->_from_participant_id
,
490 $participantDetails[$this->_from_participant_id
],
491 $eventDetails[$this->_event_id
],
492 $contactDetails[$this->_from_contact_id
],
497 $statusMsg = ts('Event registration information for %1 has been updated.', [1 => $this->_contact_name
]);
498 $statusMsg .= ' ' . ts('A cancellation email has been sent to %1.', [1 => $this->_contact_email
]);
499 CRM_Core_Session
::setStatus($statusMsg, ts('Thanks'), 'success');
503 * Move Participant registration to new contact.
505 * @param int $toContactID
506 * @param int $fromParticipantID
507 * @param int $eventID
509 * @throws \CRM_Core_Exception
510 * @throws \CiviCRM_API3_Exception
512 public function transferParticipantRegistration($toContactID, $fromParticipantID, $eventID) {
513 $query = 'select role_id, source, fee_level, is_test, is_pay_later, fee_amount, discount_id, fee_currency,campaign_id, discount_amount from civicrm_participant where id = ' . $fromParticipantID;
514 $dao = CRM_Core_DAO
::executeQuery($query);
516 while ($dao->fetch()) {
517 $value_to['role_id'] = $dao->role_id
;
518 $value_to['source'] = $dao->source
;
519 $value_to['fee_level'] = $dao->fee_level
;
520 $value_to['is_test'] = $dao->is_test
;
521 $value_to['is_pay_later'] = $dao->is_pay_later
;
522 $value_to['fee_amount'] = $dao->fee_amount
;
524 $value_to['contact_id'] = $toContactID;
525 $value_to['event_id'] = $eventID;
526 $value_to['status_id'] = CRM_Core_PseudoConstant
::getKey(
527 'CRM_Event_BAO_Participant',
531 $value_to['register_date'] = date("Y-m-d");
532 //first create the new participant row -don't set registered_by yet or email won't be sent
533 $participant = CRM_Event_BAO_Participant
::create($value_to);
534 //send a confirmation email to the new participant
535 $this->participantTransfer($participant);
536 //now update registered_by_id
537 $query = "UPDATE civicrm_participant cp SET cp.registered_by_id = %1 WHERE cp.id = ({$participant->id})";
538 $params = [1 => [$fromParticipantID, 'Integer']];
539 $dao = CRM_Core_DAO
::executeQuery($query, $params);
540 //copy line items to new participant
541 $line_items = CRM_Price_BAO_LineItem
::getLineItems($this->_from_participant_id
);
542 foreach ($line_items as $item) {
543 $item['entity_id'] = $participant->id
;
545 $item['entity_table'] = "civicrm_participant";
546 $new_item = CRM_Price_BAO_LineItem
::create($item);
548 //now cancel the from participant record, leaving the original line-item(s)
550 $value_from['id'] = $fromParticipantID;
551 $tansferId = array_search('Transferred', CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'"));
552 $value_from['status_id'] = $tansferId;
553 $value_from['transferred_to_contact_id'] = $toContactID;
554 CRM_Event_BAO_Participant
::create($value_from);