3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2019
35 * This class generates form components for processing a petition signature.
37 class CRM_Campaign_Form_Petition_Signature
extends CRM_Core_Form
{
38 const EMAIL_THANK
= 1, EMAIL_CONFIRM
= 2, MODE_CREATE
= 4;
43 * The id of the contact associated with this signature
50 * Is this a logged in user
54 protected $_loggedIn = FALSE;
59 * @var string ("Individual"/"Household"/"Organization"). Never been tested for something else than Individual
61 protected $_ctype = 'Individual';
64 * The contact profile id attached with this petition
68 protected $_contactProfileId;
71 * The contact profile fields used for this petition
75 public $_contactProfileFields;
78 * The activity profile id attached with this petition
82 protected $_activityProfileId;
85 * The activity profile fields used for this petition
89 public $_activityProfileFields;
92 * The id of the survey (petition) we are proceessing
99 * The tag id used to set against contacts with unconfirmed email
106 * Values to use for custom profiles
113 * The params submitted by the form
120 * Which email send mode do we use
124 * connected user via login/pwd - thank you
125 * or dedupe contact matched who doesn't have a tag CIVICRM_TAG_UNCONFIRMED - thank you
126 * or login using fb connect - thank you + click to add msg to fb wall
128 * send a confirmation request email
130 protected $_sendEmailMode;
132 protected $_image_URL;
136 public function __construct() {
137 parent
::__construct();
138 // this property used by civicrm_fb module and if true, forces thank you email to be sent
139 // for users signing in via Facebook connect; also sets Fb email to check against
140 $this->forceEmailConfirmed
['flag'] = FALSE;
141 $this->forceEmailConfirmed
['email'] = '';
147 public function getContactID() {
148 $tempID = CRM_Utils_Request
::retrieve('cid', 'Positive', $this);
150 // force to ignore the authenticated user
151 if ($tempID === '0') {
155 //check if this is a checksum authentication
156 $userChecksum = CRM_Utils_Request
::retrieve('cs', 'String', $this);
158 //check for anonymous user.
159 $validUser = CRM_Contact_BAO_Contact_Utils
::validChecksum($tempID, $userChecksum);
165 // check if the user is registered and we have a contact ID
166 $session = CRM_Core_Session
::singleton();
167 return $session->get('userID');
170 public function preProcess() {
171 $this->bao
= new CRM_Campaign_BAO_Petition();
172 $this->_mode
= self
::MODE_CREATE
;
175 $this->_surveyId
= CRM_Utils_Request
::retrieve('sid', 'Positive', $this);
178 if (!$this->_surveyId
) {
179 CRM_Core_Error
::fatal('Petition id is not valid. (it needs a "sid" in the url).');
182 //check petition is valid and active
183 $params['id'] = $this->_surveyId
;
184 $this->petition
= array();
185 CRM_Campaign_BAO_Survey
::retrieve($params, $this->petition
);
186 if (empty($this->petition
)) {
187 CRM_Core_Error
::fatal('Petition doesn\'t exist.');
189 if ($this->petition
['is_active'] == 0) {
190 CRM_Core_Error
::fatal('Petition is no longer active.');
193 //get userID from session
194 $session = CRM_Core_Session
::singleton();
196 //get the contact id for this user if logged in
197 $this->_contactId
= $this->getContactId();
198 if (isset($this->_contactId
)) {
199 $this->_loggedIn
= TRUE;
202 // add the custom contact and activity profile fields to the signature form
204 $ufJoinParams = array(
205 'entity_id' => $this->_surveyId
,
206 'entity_table' => 'civicrm_survey',
207 'module' => 'CiviCampaign',
211 $this->_contactProfileId
= CRM_Core_BAO_UFJoin
::findUFGroupId($ufJoinParams);
212 if ($this->_contactProfileId
) {
213 $this->_contactProfileFields
= CRM_Core_BAO_UFGroup
::getFields($this->_contactProfileId
, FALSE, CRM_Core_Action
::ADD
);
215 if (!isset($this->_contactProfileFields
['email-Primary'])) {
216 CRM_Core_Error
::fatal('The contact profile needs to contain the primary email address field');
219 $ufJoinParams['weight'] = 1;
220 $this->_activityProfileId
= CRM_Core_BAO_UFJoin
::findUFGroupId($ufJoinParams);
222 if ($this->_activityProfileId
) {
223 $this->_activityProfileFields
= CRM_Core_BAO_UFGroup
::getFields($this->_activityProfileId
, FALSE, CRM_Core_Action
::ADD
);
226 $this->setDefaultValues();
227 CRM_Utils_System
::setTitle($this->petition
['title']);
231 * Set default values for the form.
233 public function setDefaultValues() {
234 $this->_defaults
= array();
235 if ($this->_contactId
) {
236 CRM_Core_BAO_UFGroup
::setProfileDefaults($this->_contactId
, $this->_contactProfileFields
, $this->_defaults
, TRUE);
237 if ($this->_activityProfileId
) {
238 CRM_Core_BAO_UFGroup
::setProfileDefaults($this->_contactId
, $this->_activityProfileFields
, $this->_defaults
, TRUE);
242 //set custom field defaults
244 foreach ($this->_contactProfileFields
as $name => $field) {
245 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
246 $htmlType = $field['html_type'];
248 if (!isset($this->_defaults
[$name])) {
249 CRM_Core_BAO_CustomField
::setProfileDefaults($customFieldID,
259 if ($this->_activityProfileFields
) {
260 foreach ($this->_activityProfileFields
as $name => $field) {
261 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
262 $htmlType = $field['html_type'];
264 if (!isset($this->_defaults
[$name])) {
265 CRM_Core_BAO_CustomField
::setProfileDefaults($customFieldID,
276 $this->setDefaults($this->_defaults
);
279 public function buildQuickForm() {
280 $this->assign('survey_id', $this->_surveyId
);
281 $this->assign('petitionTitle', $this->petition
['title']);
282 if (isset($_COOKIE['signed_' . $this->_surveyId
])) {
283 if (isset($_COOKIE['confirmed_' . $this->_surveyId
])) {
284 $this->assign('duplicate', "confirmed");
287 $this->assign('duplicate', "unconfirmed");
292 $this->applyFilter('__ALL__', 'trim');
294 $this->buildCustom($this->_contactProfileId
, 'petitionContactProfile');
295 if ($this->_activityProfileId
) {
296 $this->buildCustom($this->_activityProfileId
, 'petitionActivityProfile');
299 $this->addButtons(array(
302 'name' => ts('Sign the Petition'),
310 * add the rules (mainly global rules) for form.
311 * All local rules are added near the element
320 public static function formRule($fields, $files, $errors) {
323 return empty($errors) ?
TRUE : $errors;
327 * Form submission of petition signature.
329 public function postProcess() {
330 $tag_name = Civi
::settings()->get('tag_unconfirmed');
333 // Check if contact 'email confirmed' tag exists, else create one
334 // This should be in the petition module initialise code to create a default tag for this
335 $tag_params['name'] = $tag_name;
336 $tag_params['version'] = 3;
337 $tag = civicrm_api('tag', 'get', $tag_params);
338 if ($tag['count'] == 0) {
340 $tag_params['description'] = $tag_name;
341 $tag_params['is_reserved'] = 1;
342 $tag_params['used_for'] = 'civicrm_contact';
343 $tag = civicrm_api('tag', 'create', $tag_params);
345 $this->_tagId
= $tag['id'];
348 // export the field values to be used for saving the profile form
349 $params = $this->controller
->exportValues($this->_name
);
351 $session = CRM_Core_Session
::singleton();
353 $params['last_modified_id'] = $session->get('userID');
354 $params['last_modified_date'] = date('YmdHis');
356 if ($this->_action
& CRM_Core_Action
::ADD
) {
357 $params['created_id'] = $session->get('userID');
358 $params['created_date'] = date('YmdHis');
361 if (isset($this->_surveyId
)) {
362 $params['sid'] = $this->_surveyId
;
365 if (isset($this->_contactId
)) {
366 $params['contactId'] = $this->_contactId
;
369 // if logged in user, skip dedupe
370 if ($this->_loggedIn
) {
371 $ids[0] = $this->_contactId
;
374 $ids = CRM_Contact_BAO_Contact
::getDuplicateContacts($params, $this->_ctype
, 'Unsupervised', array(), FALSE);
377 $petition_params['id'] = $this->_surveyId
;
379 CRM_Campaign_BAO_Survey
::retrieve($petition_params, $petition);
381 switch (count($ids)) {
383 //no matching contacts - create a new contact
384 // Add a source for this new contact
385 $params['source'] = ts('Petition Signature') . ' ' . $this->petition
['title'];
387 if ($this->petition
['bypass_confirm']) {
388 // send thank you email directly, bypassing confirmation
389 $this->_sendEmailMode
= self
::EMAIL_THANK
;
390 // Set status for signature activity to completed
391 $params['statusId'] = 2;
394 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
396 // Set status for signature activity to scheduled until email is verified
397 $params['statusId'] = 1;
402 $this->_contactId
= $params['contactId'] = $ids[0];
404 // check if user has already signed this petition - redirects to Thank You if true
405 $this->redirectIfSigned($params);
407 if ($this->petition
['bypass_confirm']) {
408 // send thank you email directly, bypassing confirmation
409 $this->_sendEmailMode
= self
::EMAIL_THANK
;
410 // Set status for signature activity to completed
411 $params['statusId'] = 2;
415 // dedupe matched single contact, check for 'unconfirmed' tag
417 $tag = new CRM_Core_DAO_EntityTag();
418 $tag->entity_id
= $this->_contactId
;
419 $tag->tag_id
= $this->_tagId
;
421 if (!($tag->find())) {
422 // send thank you email directly, the user is known and validated
423 $this->_sendEmailMode
= self
::EMAIL_THANK
;
424 // Set status for signature activity to completed
425 $params['statusId'] = 2;
428 // send email verification email
429 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
430 // Set status for signature activity to scheduled until email is verified
431 $params['statusId'] = 1;
437 // more than 1 matching contact
438 // for time being, take the first matching contact (not sure that's the best strategy, but better than creating another duplicate)
439 $this->_contactId
= $params['contactId'] = $ids[0];
441 // check if user has already signed this petition - redirects to Thank You if true
442 $this->redirectIfSigned($params);
444 if ($this->petition
['bypass_confirm']) {
445 // send thank you email directly, bypassing confirmation
446 $this->_sendEmailMode
= self
::EMAIL_THANK
;
447 // Set status for signature activity to completed
448 $params['statusId'] = 2;
453 $tag = new CRM_Core_DAO_EntityTag();
454 $tag->entity_id
= $this->_contactId
;
455 $tag->tag_id
= $this->_tagId
;
457 if (!($tag->find())) {
458 // send thank you email
459 $this->_sendEmailMode
= self
::EMAIL_THANK
;
460 // Set status for signature activity to completed
461 $params['statusId'] = 2;
464 // send email verification email
465 $this->_sendEmailMode
= self
::EMAIL_CONFIRM
;
466 // Set status for signature activity to scheduled until email is verified
467 $params['statusId'] = 1;
473 $transaction = new CRM_Core_Transaction();
475 // CRM-17029 - get the add_to_group_id from the _contactProfileFields array.
476 // There's a much more elegant solution with
477 // array_values($this->_contactProfileFields)[0] but it's PHP 5.4+ only.
478 $slice = array_slice($this->_contactProfileFields
, 0, 1);
479 $firstField = array_shift($slice);
480 $addToGroupID = isset($firstField['add_to_group_id']) ?
$firstField['add_to_group_id'] : NULL;
481 $this->_contactId
= CRM_Contact_BAO_Contact
::createProfileContact($params, $this->_contactProfileFields
,
482 $this->_contactId
, $addToGroupID,
483 $this->_contactProfileId
, $this->_ctype
,
487 // get additional custom activity profile field data
488 // to save with new signature activity record
489 $surveyInfo = $this->bao
->getSurveyInfo($this->_surveyId
);
490 $customActivityFields = CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
491 $surveyInfo['activity_type_id']
493 $customActivityFields = CRM_Utils_Array
::crmArrayMerge($customActivityFields,
494 CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
499 $params['custom'] = CRM_Core_BAO_CustomField
::postProcess($params,
504 // create the signature activity record
505 $params['contactId'] = $this->_contactId
;
506 $params['activity_campaign_id'] = CRM_Utils_Array
::value('campaign_id', $this->petition
);
507 $result = $this->bao
->createSignature($params);
509 // send thank you or email verification emails
511 // if logged in using Facebook connect and email on form matches Fb email,
512 // no need for email confirmation, send thank you email
513 if ($this->forceEmailConfirmed
['flag'] &&
514 ($this->forceEmailConfirmed
['email'] == $params['email-Primary'])
516 $this->_sendEmailMode
= self
::EMAIL_THANK
;
519 switch ($this->_sendEmailMode
) {
520 case self
::EMAIL_THANK
:
521 // mark the signature activity as completed and set confirmed cookie
522 $this->bao
->confirmSignature($result->id
, $this->_contactId
, $this->_surveyId
);
525 case self
::EMAIL_CONFIRM
:
526 // set 'Unconfirmed' tag for this new contact
529 $tag_params['contact_id'] = $this->_contactId
;
530 $tag_params['tag_id'] = $this->_tagId
;
531 $tag_params['version'] = 3;
532 $tag_value = civicrm_api('entity_tag', 'create', $tag_params);
538 $params['activityId'] = $result->id
;
539 $params['tagId'] = $this->_tagId
;
541 $transaction->commit();
543 $this->bao
->sendEmail($params, $this->_sendEmailMode
);
546 // call the hook before we redirect
547 $this->postProcessHook();
549 // set the template to thank you
550 $url = CRM_Utils_System
::url(
551 'civicrm/petition/thankyou',
552 'pid=' . $this->_surveyId
. '&id=' . $this->_sendEmailMode
. '&reset=1'
554 CRM_Utils_System
::redirect($url);
559 * Build the petition profile form.
562 * @param string $name
563 * @param bool $viewOnly
565 public function buildCustom($id, $name, $viewOnly = FALSE) {
567 $session = CRM_Core_Session
::singleton();
568 $this->assign("petition", $this->petition
);
569 //$contactID = $this->_contactId;
571 $this->assign('contact_id', $this->_contactId
);
574 // TODO: contactID is never set (commented above)
576 if (CRM_Core_BAO_UFGroup
::filterUFGroups($id, $contactID)) {
577 $fields = CRM_Core_BAO_UFGroup
::getFields($id, FALSE, CRM_Core_Action
::ADD
);
581 $fields = CRM_Core_BAO_UFGroup
::getFields($id, FALSE, CRM_Core_Action
::ADD
);
585 $this->assign($name, $fields);
588 foreach ($fields as $key => $field) {
590 isset($field['data_type']) &&
591 $field['data_type'] == 'File' ||
($viewOnly && $field['name'] == 'image_URL')
593 // ignore file upload fields
597 // if state or country in the profile, create map
598 list($prefixName, $index) = CRM_Utils_System
::explode('-', $key, 2);
600 CRM_Core_BAO_UFGroup
::buildProfile($this, $field, CRM_Profile_Form
::MODE_CREATE
, $contactID, TRUE);
601 $this->_fields
[$key] = $field;
602 // CRM-11316 Is ReCAPTCHA enabled for this profile AND is this an anonymous visitor
603 if ($field['add_captcha'] && !$this->_contactId
) {
608 if ($addCaptcha && !$viewOnly) {
609 $captcha = CRM_Utils_ReCAPTCHA
::singleton();
610 $captcha->add($this);
611 $this->assign("isCaptcha", TRUE);
620 public function getTemplateFileName() {
621 if (isset($this->thankyou
)) {
622 return ('CRM/Campaign/Page/Petition/ThankYou.tpl');
626 return parent
::getTemplateFileName();
630 * check if user has already signed this petition.
631 * @param array $params
633 public function redirectIfSigned($params) {
634 $signature = $this->bao
->checkSignature($this->_surveyId
, $this->_contactId
);
635 //TODO: error case when more than one signature found for this petition and this contact
636 if (!empty($signature) && (count($signature) == 1)) {
637 $signature_id = array_keys($signature);
638 switch ($signature[$signature_id[0]]['status_id']) {
640 //status is scheduled - email is unconfirmed
641 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId
. '&id=4&reset=1'));
645 //status is completed
646 $this->bao
->sendEmail($params, 1);
647 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId
. '&id=5&reset=1'));