3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class generates form components for processing a petition signature.
21 class CRM_Campaign_Form_Petition_Signature
extends CRM_Core_Form
{
22 const EMAIL_THANK
= 1, EMAIL_CONFIRM
= 2, MODE_CREATE
= 4;
27 * The id of the contact associated with this signature
34 * Is this a logged in user
38 protected $_loggedIn = FALSE;
44 * ("Individual"/"Household"/"Organization"). Never been tested for something else than Individual
46 protected $_ctype = 'Individual';
49 * The contact profile id attached with this petition
53 protected $_contactProfileId;
56 * The contact profile fields used for this petition
60 public $_contactProfileFields;
63 * The activity profile id attached with this petition
67 protected $_activityProfileId;
70 * The activity profile fields used for this petition
74 public $_activityProfileFields;
77 * The id of the survey (petition) we are proceessing
84 * The tag id used to set against contacts with unconfirmed email
91 * Values to use for custom profiles
98 * The params submitted by the form
105 * Which email send mode do we use
109 * connected user via login/pwd - thank you
110 * or dedupe contact matched who doesn't have a tag CIVICRM_TAG_UNCONFIRMED - thank you
111 * or login using fb connect - thank you + click to add msg to fb wall
113 * send a confirmation request email
115 protected $_sendEmailMode;
117 protected $_image_URL;
121 public function __construct() {
122 parent
::__construct();
123 // this property used by civicrm_fb module and if true, forces thank you email to be sent
124 // for users signing in via Facebook connect; also sets Fb email to check against
125 $this->forceEmailConfirmed
['flag'] = FALSE;
126 $this->forceEmailConfirmed
['email'] = '';
132 public function getContactID() {
133 $tempID = CRM_Utils_Request
::retrieve('cid', 'Positive', $this);
135 // force to ignore the authenticated user
136 if ($tempID === '0') {
140 //check if this is a checksum authentication
141 $userChecksum = CRM_Utils_Request
::retrieve('cs', 'String', $this);
143 //check for anonymous user.
144 $validUser = CRM_Contact_BAO_Contact_Utils
::validChecksum($tempID, $userChecksum);
150 // check if the user is registered and we have a contact ID
151 $session = CRM_Core_Session
::singleton();
152 return $session->get('userID');
155 public function preProcess() {
156 $this->bao
= new CRM_Campaign_BAO_Petition();
157 $this->_mode
= self
::MODE_CREATE
;
160 $this->_surveyId
= CRM_Utils_Request
::retrieve('sid', 'Positive', $this);
163 if (!$this->_surveyId
) {
164 CRM_Core_Error
::statusBounce('Petition id is not valid. (it needs a "sid" in the url).');
167 //check petition is valid and active
168 $params['id'] = $this->_surveyId
;
169 $this->petition
= [];
170 CRM_Campaign_BAO_Survey
::retrieve($params, $this->petition
);
171 if (empty($this->petition
)) {
172 CRM_Core_Error
::statusBounce('Petition doesn\'t exist.');
174 if ($this->petition
['is_active'] == 0) {
175 CRM_Core_Error
::statusBounce('Petition is no longer active.');
178 //get userID from session
179 $session = CRM_Core_Session
::singleton();
181 //get the contact id for this user if logged in
182 $this->_contactId
= $this->getContactId();
183 if (isset($this->_contactId
)) {
184 $this->_loggedIn
= TRUE;
187 // add the custom contact and activity profile fields to the signature form
190 'entity_id' => $this->_surveyId
,
191 'entity_table' => 'civicrm_survey',
192 'module' => 'CiviCampaign',
196 $this->_contactProfileId
= CRM_Core_BAO_UFJoin
::findUFGroupId($ufJoinParams);
197 if ($this->_contactProfileId
) {
198 $this->_contactProfileFields
= CRM_Core_BAO_UFGroup
::getFields($this->_contactProfileId
, FALSE, CRM_Core_Action
::ADD
);
200 if (!isset($this->_contactProfileFields
['email-Primary'])) {
201 CRM_Core_Error
::statusBounce('The contact profile needs to contain the primary email address field');
204 $ufJoinParams['weight'] = 1;
205 $this->_activityProfileId
= CRM_Core_BAO_UFJoin
::findUFGroupId($ufJoinParams);
207 if ($this->_activityProfileId
) {
208 $this->_activityProfileFields
= CRM_Core_BAO_UFGroup
::getFields($this->_activityProfileId
, FALSE, CRM_Core_Action
::ADD
);
211 $this->setDefaultValues();
212 CRM_Utils_System
::setTitle($this->petition
['title']);
216 * Set default values for the form.
218 public function setDefaultValues() {
219 $this->_defaults
= [];
220 if ($this->_contactId
) {
221 CRM_Core_BAO_UFGroup
::setProfileDefaults($this->_contactId
, $this->_contactProfileFields
, $this->_defaults
, TRUE);
222 if ($this->_activityProfileId
) {
223 CRM_Core_BAO_UFGroup
::setProfileDefaults($this->_contactId
, $this->_activityProfileFields
, $this->_defaults
, TRUE);
227 //set custom field defaults
229 foreach ($this->_contactProfileFields
as $name => $field) {
230 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
231 $htmlType = $field['html_type'];
233 if (!isset($this->_defaults
[$name])) {
234 CRM_Core_BAO_CustomField
::setProfileDefaults($customFieldID,
244 if ($this->_activityProfileFields
) {
245 foreach ($this->_activityProfileFields
as $name => $field) {
246 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
247 $htmlType = $field['html_type'];
249 if (!isset($this->_defaults
[$name])) {
250 CRM_Core_BAO_CustomField
::setProfileDefaults($customFieldID,
261 $this->setDefaults($this->_defaults
);
264 public function buildQuickForm() {
265 $this->assign('survey_id', $this->_surveyId
);
266 $this->assign('petitionTitle', $this->petition
['title']);
267 if (isset($_COOKIE['signed_' . $this->_surveyId
])) {
268 if (isset($_COOKIE['confirmed_' . $this->_surveyId
])) {
269 $this->assign('duplicate', "confirmed");
272 $this->assign('duplicate', "unconfirmed");
277 $this->applyFilter('__ALL__', 'trim');
279 $this->buildCustom($this->_contactProfileId
, 'petitionContactProfile');
280 if ($this->_activityProfileId
) {
281 $this->buildCustom($this->_activityProfileId
, 'petitionActivityProfile');
287 'name' => ts('Sign the Petition'),
294 * add the rules (mainly global rules) for form.
295 * All local rules are added near the element
304 public static function formRule($fields, $files, $errors) {
307 return empty($errors) ?
TRUE : $errors;
311 * Form submission of petition signature.
313 public function postProcess() {
314 $tag_name = Civi
::settings()->get('tag_unconfirmed');
317 // Check if contact 'email confirmed' tag exists, else create one
318 // This should be in the petition module initialise code to create a default tag for this
319 $tag_params['name'] = $tag_name;
320 $tag_params['version'] = 3;
321 $tag = civicrm_api('tag', 'get', $tag_params);
322 if ($tag['count'] == 0) {
324 $tag_params['description'] = $tag_name;
325 $tag_params['is_reserved'] = 1;
326 $tag_params['used_for'] = 'civicrm_contact';
327 $tag = civicrm_api('tag', 'create', $tag_params);
329 $this->_tagId
= $tag['id'];
332 // export the field values to be used for saving the profile form
333 $params = $this->controller
->exportValues($this->_name
);
335 $session = CRM_Core_Session
::singleton();
337 $params['last_modified_id'] = $session->get('userID');
338 $params['last_modified_date'] = date('YmdHis');
340 if ($this->_action
& CRM_Core_Action
::ADD
) {
341 $params['created_id'] = $session->get('userID');
342 $params['created_date'] = date('YmdHis');
345 if (isset($this->_surveyId
)) {
346 $params['sid'] = $this->_surveyId
;
349 if (isset($this->_contactId
)) {
350 $params['contactId'] = $this->_contactId
;
353 // if logged in user, skip dedupe
354 if ($this->_loggedIn
) {
355 $ids[0] = $this->_contactId
;
358 $ids = CRM_Contact_BAO_Contact
::getDuplicateContacts($params, $this->_ctype
, 'Unsupervised', [], FALSE);
361 $petition_params['id'] = $this->_surveyId
;
363 CRM_Campaign_BAO_Survey
::retrieve($petition_params, $petition);
365 switch (count($ids)) {
367 //no matching contacts - create a new contact
368 // Add a source for this new contact
369 $params['source'] = ts('Petition Signature') . ' ' . $this->petition
['title'];
371 if ($this->petition
['bypass_confirm']) {
372 // send thank you email directly, bypassing confirmation
373 $this->_sendEmailMode
= self
::EMAIL_THANK
;
374 // Set status for signature activity to completed
375 $params['statusId'] = 2;
378 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
380 // Set status for signature activity to scheduled until email is verified
381 $params['statusId'] = 1;
386 $this->_contactId
= $params['contactId'] = $ids[0];
388 // check if user has already signed this petition - redirects to Thank You if true
389 $this->redirectIfSigned($params);
391 if ($this->petition
['bypass_confirm']) {
392 // send thank you email directly, bypassing confirmation
393 $this->_sendEmailMode
= self
::EMAIL_THANK
;
394 // Set status for signature activity to completed
395 $params['statusId'] = 2;
399 // dedupe matched single contact, check for 'unconfirmed' tag
401 $tag = new CRM_Core_DAO_EntityTag();
402 $tag->entity_id
= $this->_contactId
;
403 $tag->tag_id
= $this->_tagId
;
405 if (!($tag->find())) {
406 // send thank you email directly, the user is known and validated
407 $this->_sendEmailMode
= self
::EMAIL_THANK
;
408 // Set status for signature activity to completed
409 $params['statusId'] = 2;
412 // send email verification email
413 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
414 // Set status for signature activity to scheduled until email is verified
415 $params['statusId'] = 1;
421 // more than 1 matching contact
422 // for time being, take the first matching contact (not sure that's the best strategy, but better than creating another duplicate)
423 $this->_contactId
= $params['contactId'] = $ids[0];
425 // check if user has already signed this petition - redirects to Thank You if true
426 $this->redirectIfSigned($params);
428 if ($this->petition
['bypass_confirm']) {
429 // send thank you email directly, bypassing confirmation
430 $this->_sendEmailMode
= self
::EMAIL_THANK
;
431 // Set status for signature activity to completed
432 $params['statusId'] = 2;
437 $tag = new CRM_Core_DAO_EntityTag();
438 $tag->entity_id
= $this->_contactId
;
439 $tag->tag_id
= $this->_tagId
;
441 if (!($tag->find())) {
442 // send thank you email
443 $this->_sendEmailMode
= self
::EMAIL_THANK
;
444 // Set status for signature activity to completed
445 $params['statusId'] = 2;
448 // send email verification email
449 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
450 // Set status for signature activity to scheduled until email is verified
451 $params['statusId'] = 1;
457 $transaction = new CRM_Core_Transaction();
459 // CRM-17029 - get the add_to_group_id from the _contactProfileFields array.
460 // There's a much more elegant solution with
461 // array_values($this->_contactProfileFields)[0] but it's PHP 5.4+ only.
462 $slice = array_slice($this->_contactProfileFields
, 0, 1);
463 $firstField = array_shift($slice);
464 $addToGroupID = $firstField['add_to_group_id'] ??
NULL;
465 $this->_contactId
= CRM_Contact_BAO_Contact
::createProfileContact($params, $this->_contactProfileFields
,
466 $this->_contactId
, $addToGroupID,
467 $this->_contactProfileId
, $this->_ctype
,
471 // get additional custom activity profile field data
472 // to save with new signature activity record
473 $surveyInfo = $this->bao
->getSurveyInfo($this->_surveyId
);
474 $customActivityFields = CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
475 $surveyInfo['activity_type_id']
477 $customActivityFields = CRM_Utils_Array
::crmArrayMerge($customActivityFields,
478 CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
483 $params['custom'] = CRM_Core_BAO_CustomField
::postProcess($params,
488 // create the signature activity record
489 $params['contactId'] = $this->_contactId
;
490 $params['activity_campaign_id'] = CRM_Utils_Array
::value('campaign_id', $this->petition
);
491 $result = $this->bao
->createSignature($params);
493 // send thank you or email verification emails
495 // if logged in using Facebook connect and email on form matches Fb email,
496 // no need for email confirmation, send thank you email
497 if ($this->forceEmailConfirmed
['flag'] &&
498 ($this->forceEmailConfirmed
['email'] == $params['email-Primary'])
500 $this->_sendEmailMode
= self
::EMAIL_THANK
;
503 switch ($this->_sendEmailMode
) {
504 case self
::EMAIL_THANK
:
505 // mark the signature activity as completed and set confirmed cookie
506 $this->bao
->confirmSignature($result->id
, $this->_contactId
, $this->_surveyId
);
509 case self
::EMAIL_CONFIRM
:
510 // set 'Unconfirmed' tag for this new contact
513 $tag_params['contact_id'] = $this->_contactId
;
514 $tag_params['tag_id'] = $this->_tagId
;
515 $tag_params['version'] = 3;
516 $tag_value = civicrm_api('entity_tag', 'create', $tag_params);
522 $params['activityId'] = $result->id
;
523 $params['tagId'] = $this->_tagId
;
525 $transaction->commit();
527 $this->bao
->sendEmail($params, $this->_sendEmailMode
);
530 // call the hook before we redirect
531 $this->postProcessHook();
533 // set the template to thank you
534 $url = CRM_Utils_System
::url(
535 'civicrm/petition/thankyou',
536 'pid=' . $this->_surveyId
. '&id=' . $this->_sendEmailMode
. '&reset=1'
538 CRM_Utils_System
::redirect($url);
543 * Build the petition profile form.
546 * @param string $name
547 * @param bool $viewOnly
549 public function buildCustom($id, $name, $viewOnly = FALSE) {
551 $session = CRM_Core_Session
::singleton();
552 $this->assign("petition", $this->petition
);
553 //$contactID = $this->_contactId;
555 $this->assign('contact_id', $this->_contactId
);
558 // TODO: contactID is never set (commented above)
560 if (CRM_Core_BAO_UFGroup
::filterUFGroups($id, $contactID)) {
561 $fields = CRM_Core_BAO_UFGroup
::getFields($id, FALSE, CRM_Core_Action
::ADD
);
565 $fields = CRM_Core_BAO_UFGroup
::getFields($id, FALSE, CRM_Core_Action
::ADD
);
569 $this->assign($name, $fields);
572 foreach ($fields as $key => $field) {
574 isset($field['data_type']) &&
575 $field['data_type'] == 'File' ||
($viewOnly && $field['name'] == 'image_URL')
577 // ignore file upload fields
581 // if state or country in the profile, create map
582 list($prefixName, $index) = CRM_Utils_System
::explode('-', $key, 2);
584 CRM_Core_BAO_UFGroup
::buildProfile($this, $field, CRM_Profile_Form
::MODE_CREATE
, $contactID, TRUE);
585 $this->_fields
[$key] = $field;
586 // CRM-11316 Is ReCAPTCHA enabled for this profile AND is this an anonymous visitor
587 if ($field['add_captcha'] && !$this->_contactId
) {
592 if ($addCaptcha && !$viewOnly) {
593 CRM_Utils_ReCAPTCHA
::enableCaptchaOnForm($this);
602 public function getTemplateFileName() {
603 if (isset($this->thankyou
)) {
604 return ('CRM/Campaign/Page/Petition/ThankYou.tpl');
608 return parent
::getTemplateFileName();
612 * check if user has already signed this petition.
613 * @param array $params
615 public function redirectIfSigned($params) {
616 $signature = $this->bao
->checkSignature($this->_surveyId
, $this->_contactId
);
617 //TODO: error case when more than one signature found for this petition and this contact
618 if (!empty($signature) && (count($signature) == 1)) {
619 $signature_id = array_keys($signature);
620 switch ($signature[$signature_id[0]]['status_id']) {
622 //status is scheduled - email is unconfirmed
623 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId
. '&id=4&reset=1'));
627 //status is completed
628 $this->bao
->sendEmail($params, 1);
629 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId
. '&id=5&reset=1'));