CRM-15172 - Refactor forms to use addChainSelect method
[civicrm-core.git] / CRM / Campaign / Form / Petition / Signature.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 /**
37 * This class generates form components for processing a petition signature
38 *
39 */
40 class CRM_Campaign_Form_Petition_Signature extends CRM_Core_Form {
41 CONST EMAIL_THANK = 1, EMAIL_CONFIRM = 2, MODE_CREATE = 4;
42
43 protected $_mode;
44
45 /**
46 * the id of the contact associated with this signature
47 *
48 * @var int
49 * @public
50 */
51 public $_contactId;
52
53 /**
54 * Is this a logged in user
55 *
56 * @var int
57 */
58 protected $_loggedIn = FALSE;
59
60 /**
61 * The contact type
62 *
63 * @var string ("Individual"/"Household"/"Organization"). Never been tested for something else than Individual
64 */
65 protected $_ctype = 'Individual';
66
67 /**
68 * The contact profile id attached with this petition
69 *
70 * @var int
71 */
72 protected $_contactProfileId;
73
74 /**
75 * the contact profile fields used for this petition
76 *
77 * @var array
78 */
79 public $_contactProfileFields;
80
81 /**
82 * The activity profile id attached with this petition
83 *
84 * @var int
85 */
86 protected $_activityProfileId;
87
88 /**
89 * the activity profile fields used for this petition
90 *
91 * @var array
92 */
93 public $_activityProfileFields;
94
95 /**
96 * the id of the survey (petition) we are proceessing
97 *
98 * @var int
99 * @protected
100 */
101 public $_surveyId;
102
103 /**
104 * The tag id used to set against contacts with unconfirmed email
105 *
106 * @var int
107 */
108 protected $_tagId;
109
110 /**
111 * values to use for custom profiles
112 *
113 * @var array
114 * @protected
115 */
116 public $_values;
117
118 /**
119 * The params submitted by the form
120 *
121 * @var array
122 * @protected
123 */
124 protected $_params;
125
126 /**
127 * which email send mode do we use
128 *
129 * @var int
130 * EMAIL_THANK = 1,
131 * connected user via login/pwd - thank you
132 * or dedupe contact matched who doesn't have a tag CIVICRM_TAG_UNCONFIRMED - thank you
133 * or login using fb connect - thank you + click to add msg to fb wall
134 * EMAIL_CONFIRM = 2;
135 * send a confirmation request email
136 */
137 protected $_sendEmailMode;
138
139 protected $_image_URL;
140
141 protected $_defaults = NULL;
142
143 /**
144 *
145 */
146 function __construct() {
147 parent::__construct();
148 // this property used by civicrm_fb module and if true, forces thank you email to be sent
149 // for users signing in via Facebook connect; also sets Fb email to check against
150 $this->forceEmailConfirmed['flag'] = FALSE;
151 $this->forceEmailConfirmed['email'] = '';
152 }
153
154 /**
155 * @return mixed
156 */
157 function getContactID() {
158 $tempID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
159
160 // force to ignore the authenticated user
161 if ($tempID === '0') {
162 return $tempID;
163 }
164
165 //check if this is a checksum authentication
166 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
167 if ($userChecksum) {
168 //check for anonymous user.
169 $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($tempID, $userChecksum);
170 if ($validUser) {
171 return $tempID;
172 }
173 }
174
175 // check if the user is registered and we have a contact ID
176 $session = CRM_Core_Session::singleton();
177 return $session->get('userID');
178 }
179
180 public function preProcess() {
181 $this->bao = new CRM_Campaign_BAO_Petition();
182 $this->_mode = self::MODE_CREATE;
183
184 //get the survey id
185 $this->_surveyId = CRM_Utils_Request::retrieve('sid', 'Positive', $this);
186
187 //some sanity checks
188 if (!$this->_surveyId) {
189 CRM_Core_Error::fatal('Petition id is not valid. (it needs a "sid" in the url).');
190 return;
191 }
192 //check petition is valid and active
193 $params['id'] = $this->_surveyId;
194 $this->petition = array();
195 CRM_Campaign_BAO_Survey::retrieve($params, $this->petition);
196 if (empty($this->petition)) {
197 CRM_Core_Error::fatal('Petition doesn\'t exist.');
198 }
199 if ($this->petition['is_active'] == 0) {
200 CRM_Core_Error::fatal('Petition is no longer active.');
201 }
202
203 //get userID from session
204 $session = CRM_Core_Session::singleton();
205
206 //get the contact id for this user if logged in
207 $this->_contactId = $this->getContactId();
208 if (isset($this->_contactId)) {
209 $this->_loggedIn = TRUE;
210 }
211
212 // add the custom contact and activity profile fields to the signature form
213
214 $ufJoinParams = array(
215 'entity_id' => $this->_surveyId,
216 'entity_table' => 'civicrm_survey',
217 'module' => 'CiviCampaign',
218 'weight' => 2,
219 );
220
221 $this->_contactProfileId = CRM_Core_BAO_UFJoin::findUFGroupId($ufJoinParams);
222 if ($this->_contactProfileId) {
223 $this->_contactProfileFields = CRM_Core_BAO_UFGroup::getFields($this->_contactProfileId, FALSE, CRM_Core_Action::ADD);
224 }
225 if (!isset($this->_contactProfileFields['email-Primary'])) {
226 CRM_Core_Error::fatal('The contact profile needs to contain the primary email address field');
227 }
228
229
230 $ufJoinParams['weight'] = 1;
231 $this->_activityProfileId = CRM_Core_BAO_UFJoin::findUFGroupId($ufJoinParams);
232
233 if ($this->_activityProfileId) {
234 $this->_activityProfileFields = CRM_Core_BAO_UFGroup::getFields($this->_activityProfileId, FALSE, CRM_Core_Action::ADD);
235 }
236
237 $this->setDefaultValues();
238 CRM_Utils_System::setTitle($this->petition['title']);
239 }
240
241 /**
242 * This function sets the default values for the form.
243 *
244 * @access public
245 *
246 * @return void
247 */
248 function setDefaultValues() {
249 $this->_defaults = array();
250 if ($this->_contactId) {
251 CRM_Core_BAO_UFGroup::setProfileDefaults($this->_contactId, $this->_contactProfileFields, $this->_defaults, TRUE);
252 if ($this->_activityProfileId) {
253 CRM_Core_BAO_UFGroup::setProfileDefaults($this->_contactId, $this->_activityProfileFields, $this->_defaults, TRUE);
254 }
255 }
256
257 //set custom field defaults
258
259 foreach ($this->_contactProfileFields as $name => $field) {
260 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
261 $htmlType = $field['html_type'];
262
263 if (!isset($this->_defaults[$name])) {
264 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID,
265 $name,
266 $this->_defaults,
267 $this->_contactId,
268 $this->_mode
269 );
270 }
271 }
272 }
273
274 if ($this->_activityProfileFields) {
275 foreach ($this->_activityProfileFields as $name => $field) {
276 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
277 $htmlType = $field['html_type'];
278
279 if (!isset($this->_defaults[$name])) {
280 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID,
281 $name,
282 $this->_defaults,
283 $this->_contactId,
284 $this->_mode
285 );
286 }
287 }
288 }
289 }
290
291 $this->setDefaults($this->_defaults);
292 }
293
294 public function buildQuickForm() {
295 $this->assign('survey_id', $this->_surveyId);
296 $this->assign('petitionTitle', $this->petition['title']);
297 if (isset($_COOKIE['signed_' . $this->_surveyId])) {
298 if (isset($_COOKIE['confirmed_' . $this->_surveyId])) {
299 $this->assign('duplicate', "confirmed");
300 }
301 else {
302 $this->assign('duplicate', "unconfirmed");
303 }
304 return;
305 }
306
307 $this->applyFilter('__ALL__', 'trim');
308
309 $this->buildCustom($this->_contactProfileId, 'petitionContactProfile');
310 if ($this->_activityProfileId) {
311 $this->buildCustom($this->_activityProfileId, 'petitionActivityProfile');
312 }
313 // add buttons
314 $this->addButtons(array(
315 array(
316 'type' => 'next',
317 'name' => ts('Sign the Petition'),
318 'isDefault' => TRUE,
319 ),
320 )
321 );
322 }
323
324 /**
325 * This function is used to add the rules (mainly global rules) for form.
326 * All local rules are added near the element
327 *
328 * @param $fields
329 * @param $files
330 * @param $errors
331 *
332 * @return void
333 * @access public
334 * @see valid_date
335 */
336
337 static function formRule($fields, $files, $errors) {
338 $errors = array();
339
340 return empty($errors) ? TRUE : $errors;
341 }
342
343 /**
344 * Form submission of petition signature
345 *
346 * @access public
347 *
348 * @return void
349 */
350 public function postProcess() {
351 $tag_name = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME,
352 'tag_unconfirmed'
353 );
354
355
356 if ($tag_name) {
357 // Check if contact 'email confirmed' tag exists, else create one
358 // This should be in the petition module initialise code to create a default tag for this
359 $tag_params['name'] = $tag_name;
360 $tag_params['version'] = 3;
361 $tag = civicrm_api('tag', 'get', $tag_params);
362 if ($tag['count'] == 0) {
363 //create tag
364 $tag_params['description'] = $tag_name;
365 $tag_params['is_reserved'] = 1;
366 $tag_params['used_for'] = 'civicrm_contact';
367 $tag = civicrm_api('tag', 'create', $tag_params);
368 }
369 $this->_tagId = $tag['id'];
370 }
371
372 // export the field values to be used for saving the profile form
373 $params = $this->controller->exportValues($this->_name);
374
375 $session = CRM_Core_Session::singleton();
376 // format params
377 $params['last_modified_id'] = $session->get('userID');
378 $params['last_modified_date'] = date('YmdHis');
379
380 if ($this->_action & CRM_Core_Action::ADD) {
381 $params['created_id'] = $session->get('userID');
382 $params['created_date'] = date('YmdHis');
383 }
384
385 if (isset($this->_surveyId)) {
386 $params['sid'] = $this->_surveyId;
387 }
388
389 if (isset($this->_contactId)) {
390 $params['contactId'] = $this->_contactId;
391 }
392
393 // if logged in user, skip dedupe
394 if ($this->_loggedIn) {
395 $ids[0] = $this->_contactId;
396 }
397 else {
398 // dupeCheck - check if contact record already exists
399 // code modified from api/v2/Contact.php-function civicrm_contact_check_params()
400 $params['contact_type'] = $this->_ctype;
401 //TODO - current dedupe finds soft deleted contacts - adding param is_deleted not working
402 // ignore soft deleted contacts
403 //$params['is_deleted'] = 0;
404 $dedupeParams = CRM_Dedupe_Finder::formatParams($params, $params['contact_type']);
405 $dedupeParams['check_permission'] = '';
406
407 //dupesByParams($params, $ctype, $level = 'Unsupervised', $except = array())
408 $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $params['contact_type']);
409 }
410
411 $petition_params['id'] = $this->_surveyId;
412 $petition = array();
413 CRM_Campaign_BAO_Survey::retrieve($petition_params, $petition);
414
415 switch (count($ids)) {
416 case 0:
417 //no matching contacts - create a new contact
418 // Add a source for this new contact
419 $params['source'] = ts('Petition Signature') . ' ' . $this->petition['title'];
420
421 if ($this->petition['bypass_confirm']) {
422 // send thank you email directly, bypassing confirmation
423 $this->_sendEmailMode = self::EMAIL_THANK;
424 // Set status for signature activity to completed
425 $params['statusId'] = 2;
426 }
427 else {
428 $this->_sendEmailMode = self::EMAIL_CONFIRM;
429
430 // Set status for signature activity to scheduled until email is verified
431 $params['statusId'] = 1;
432 }
433 break;
434
435 case 1:
436 $this->_contactId = $params['contactId'] = $ids[0];
437
438 // check if user has already signed this petition - redirects to Thank You if true
439 $this->redirectIfSigned($params);
440
441 if ($this->petition['bypass_confirm']) {
442 // send thank you email directly, bypassing confirmation
443 $this->_sendEmailMode = self::EMAIL_THANK;
444 // Set status for signature activity to completed
445 $params['statusId'] = 2;
446 break;
447 }
448
449 // dedupe matched single contact, check for 'unconfirmed' tag
450 if ($tag_name) {
451 $tag = new CRM_Core_DAO_EntityTag();
452 $tag->entity_id = $this->_contactId;
453 $tag->tag_id = $this->_tagId;
454
455 if (!($tag->find())) {
456 // send thank you email directly, the user is known and validated
457 $this->_sendEmailMode = self::EMAIL_THANK;
458 // Set status for signature activity to completed
459 $params['statusId'] = 2;
460 }
461 else {
462 // send email verification email
463 $this->_sendEmailMode = self::EMAIL_CONFIRM;
464 // Set status for signature activity to scheduled until email is verified
465 $params['statusId'] = 1;
466 }
467 }
468 break;
469
470 default:
471 // more than 1 matching contact
472 // for time being, take the first matching contact (not sure that's the best strategy, but better than creating another duplicate)
473 $this->_contactId = $params['contactId'] = $ids[0];
474
475 // check if user has already signed this petition - redirects to Thank You if true
476 $this->redirectIfSigned($params);
477
478 if ($this->petition['bypass_confirm']) {
479 // send thank you email directly, bypassing confirmation
480 $this->_sendEmailMode = self::EMAIL_THANK;
481 // Set status for signature activity to completed
482 $params['statusId'] = 2;
483 break;
484 }
485
486 if ($tag_name) {
487 $tag = new CRM_Core_DAO_EntityTag();
488 $tag->entity_id = $this->_contactId;
489 $tag->tag_id = $this->_tagId;
490
491 if (!($tag->find())) {
492 // send thank you email
493 $this->_sendEmailMode = self::EMAIL_THANK;
494 // Set status for signature activity to completed
495 $params['statusId'] = 2;
496 }
497 else {
498 // send email verification email
499 $this->_sendEmailMode = self::EMAIL_CONFIRM;
500 // Set status for signature activity to scheduled until email is verified
501 $params['statusId'] = 1;
502 }
503 }
504 break;
505 }
506
507
508
509 $transaction = new CRM_Core_Transaction();
510
511 $addToGroupID = isset($this->_addToGroupID) ? $this->_addToGroupID : NULL;
512 $this->_contactId = CRM_Contact_BAO_Contact::createProfileContact($params, $this->_contactProfileFields,
513 $this->_contactId, $addToGroupID,
514 $this->_contactProfileId, $this->_ctype,
515 TRUE
516 );
517
518 // get additional custom activity profile field data
519 // to save with new signature activity record
520 $surveyInfo = $this->bao->getSurveyInfo($this->_surveyId);
521 $customActivityFields = CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
522 $surveyInfo['activity_type_id']
523 );
524 $customActivityFields = CRM_Utils_Array::crmArrayMerge($customActivityFields,
525 CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
526 NULL, NULL, TRUE
527 )
528 );
529
530 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
531 $customActivityFields,
532 NULL,
533 'Activity'
534 );
535
536 // create the signature activity record
537 $params['contactId'] = $this->_contactId;
538 $params['activity_campaign_id'] = CRM_Utils_Array::value('campaign_id', $this->petition);
539 $result = $this->bao->createSignature($params);
540
541 // send thank you or email verification emails
542
543 // if logged in using Facebook connect and email on form matches Fb email,
544 // no need for email confirmation, send thank you email
545 if ($this->forceEmailConfirmed['flag'] &&
546 ($this->forceEmailConfirmed['email'] == $params['email-Primary'])
547 ) {
548 $this->_sendEmailMode = self::EMAIL_THANK;
549 }
550
551 switch ($this->_sendEmailMode) {
552 case self::EMAIL_THANK:
553 // mark the signature activity as completed and set confirmed cookie
554 $this->bao->confirmSignature($result->id, $this->_contactId, $this->_surveyId);
555 break;
556
557 case self::EMAIL_CONFIRM:
558 // set 'Unconfirmed' tag for this new contact
559 if ($tag_name) {
560 unset($tag_params);
561 $tag_params['contact_id'] = $this->_contactId;
562 $tag_params['tag_id'] = $this->_tagId;
563 $tag_params['version'] = 3;
564 $tag_value = civicrm_api('entity_tag', 'create', $tag_params);
565 }
566 break;
567 }
568
569 //send email
570 $params['activityId'] = $result->id;
571 $params['tagId'] = $this->_tagId;
572
573 $transaction->commit();
574
575 $this->bao->sendEmail($params, $this->_sendEmailMode);
576
577 if ($result) {
578 // call the hook before we redirect
579 $this->postProcessHook();
580
581 // set the template to thank you
582 $url =
583 CRM_Utils_System::url(
584 'civicrm/petition/thankyou',
585 'pid=' . $this->_surveyId . '&id=' . $this->_sendEmailMode . '&reset=1'
586 );
587 CRM_Utils_System::redirect($url);
588 }
589 }
590
591 /**
592 * Function to build the petition profile form
593 *
594 * @param $id
595 * @param $name
596 * @param bool $viewOnly
597 *
598 * @return void
599 * @access public
600 */
601 function buildCustom($id, $name, $viewOnly = FALSE) {
602 if ($id) {
603 $session = CRM_Core_Session::singleton();
604 $this->assign("petition", $this->petition);
605 //$contactID = $this->_contactId;
606 $contactID = NULL;
607 $this->assign('contact_id', $this->_contactId);
608
609 $fields = NULL;
610 // TODO: contactID is never set (commented above)
611 if ($contactID) {
612 if (CRM_Core_BAO_UFGroup::filterUFGroups($id, $contactID)) {
613 $fields = CRM_Core_BAO_UFGroup::getFields($id, FALSE, CRM_Core_Action::ADD);
614 }
615 }
616 else {
617 $fields = CRM_Core_BAO_UFGroup::getFields($id, FALSE, CRM_Core_Action::ADD);
618 }
619
620 if ($fields) {
621 /*
622 // unset any email-* fields since we already collect it, CRM-2888
623 foreach ( array_keys( $fields ) as $fieldName ) {
624 if ( substr( $fieldName, 0, 6 ) == 'email-' ) {
625 unset( $fields[$fieldName] );
626 }
627 }
628 */
629
630
631 $this->assign($name, $fields);
632
633 $addCaptcha = FALSE;
634 foreach ($fields as $key => $field) {
635 if ($viewOnly &&
636 isset($field['data_type']) &&
637 $field['data_type'] == 'File' || ($viewOnly && $field['name'] == 'image_URL')
638 ) {
639 // ignore file upload fields
640 continue;
641 }
642
643 // if state or country in the profile, create map
644 list($prefixName, $index) = CRM_Utils_System::explode('-', $key, 2);
645
646 CRM_Core_BAO_UFGroup::buildProfile($this, $field, CRM_Profile_Form::MODE_CREATE, $contactID, TRUE);
647 $this->_fields[$key] = $field;
648 // CRM-11316 Is ReCAPTCHA enabled for this profile AND is this an anonymous visitor
649 if ($field['add_captcha'] && !$this->_contactId) {
650 $addCaptcha = TRUE;
651 }
652 }
653
654 if ($addCaptcha && !$viewOnly) {
655 $captcha = CRM_Utils_ReCAPTCHA::singleton();
656 $captcha->add($this);
657 $this->assign("isCaptcha", TRUE);
658 }
659 }
660 }
661 }
662
663 /**
664 * @return string
665 */
666 function getTemplateFileName() {
667 if (isset($this->thankyou)) {
668 return ('CRM/Campaign/Page/Petition/ThankYou.tpl');
669 }
670 else {}
671 return parent::getTemplateFileName();
672 }
673
674 // check if user has already signed this petition
675 /**
676 * @param $params
677 */
678 function redirectIfSigned($params) {
679 $signature = $this->bao->checkSignature($this->_surveyId, $this->_contactId);
680 //TODO: error case when more than one signature found for this petition and this contact
681 if (!empty($signature) && (count($signature) == 1)) {
682 $signature_id = array_keys($signature);
683 switch ($signature[$signature_id[0]]['status_id']) {
684 case 1:
685 //status is scheduled - email is unconfirmed
686 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId . '&id=4&reset=1'));
687 break;
688
689 case 2:
690 //status is completed
691 $this->bao->sendEmail($params, 1);
692 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId . '&id=5&reset=1'));
693 break;
694 }
695 }
696 }
697 }
698
699
700