CRM-12046 add missing campaign id on petition signatures
[civicrm-core.git] / CRM / Campaign / Form / Petition / Signature.php
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 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2013
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; function __construct() {
142 parent::__construct();
143 // this property used by civicrm_fb module and if true, forces thank you email to be sent
144 // for users signing in via Facebook connect; also sets Fb email to check against
145 $this->forceEmailConfirmed['flag'] = FALSE;
146 $this->forceEmailConfirmed['email'] = '';
147 }
148
149 function getContactID() {
150 $tempID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
151
152 // force to ignore the authenticated user
153 if ($tempID === '0') {
154 return $tempID;
155 }
156
157 //check if this is a checksum authentication
158 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this);
159 if ($userChecksum) {
160 //check for anonymous user.
161 $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($tempID, $userChecksum);
162 if ($validUser) {
163 return $tempID;
164 }
165 }
166
167 // check if the user is registered and we have a contact ID
168 $session = CRM_Core_Session::singleton();
169 return $session->get('userID');
170 }
171
172 public function preProcess() {
173 $this->bao = new CRM_Campaign_BAO_Petition();
174 $this->_mode = self::MODE_CREATE;
175
176 //get the survey id
177 $this->_surveyId = CRM_Utils_Request::retrieve('sid', 'Positive', $this);
178
179 //some sanity checks
180 if (!$this->_surveyId) {
181 CRM_Core_Error::fatal('Petition id is not valid. (it needs a "sid" in the url).');
182 return;
183 }
184 //check petition is valid and active
185 $params['id'] = $this->_surveyId;
186 $this->petition = array();
187 CRM_Campaign_BAO_Survey::retrieve($params, $this->petition);
188 if (empty($this->petition)) {
189 CRM_Core_Error::fatal('Petition doesn\'t exist.');
190 }
191 if ($this->petition['is_active'] == 0) {
192 CRM_Core_Error::fatal('Petition is no longer active.');
193 }
194
195 //get userID from session
196 $session = CRM_Core_Session::singleton();
197
198 //get the contact id for this user if logged in
199 $this->_contactId = $this->getContactId();
200 if (isset($this->_contactId)) {
201 $this->_loggedIn = TRUE;
202 }
203
204 // add the custom contact and activity profile fields to the signature form
205
206 $ufJoinParams = array(
207 'entity_id' => $this->_surveyId,
208 'entity_table' => 'civicrm_survey',
209 'module' => 'CiviCampaign',
210 'weight' => 2,
211 );
212
213 $this->_contactProfileId = CRM_Core_BAO_UFJoin::findUFGroupId($ufJoinParams);
214 if ($this->_contactProfileId) {
215 $this->_contactProfileFields = CRM_Core_BAO_UFGroup::getFields($this->_contactProfileId, FALSE, CRM_Core_Action::ADD);
216 }
217 if (!isset($this->_contactProfileFields['email-Primary'])) {
218 CRM_Core_Error::fatal('The contact profile needs to contain the primary email address field');
219 }
220
221
222 $ufJoinParams['weight'] = 1;
223 $this->_activityProfileId = CRM_Core_BAO_UFJoin::findUFGroupId($ufJoinParams);
224
225 if ($this->_activityProfileId) {
226 $this->_activityProfileFields = CRM_Core_BAO_UFGroup::getFields($this->_activityProfileId, FALSE, CRM_Core_Action::ADD);
227 }
228
229 $this->setDefaultValues();
230 CRM_Utils_System::setTitle($this->petition['title']);
231 }
232
233 /**
234 * This function sets the default values for the form.
235 *
236 * @access public
237 *
238 * @return None
239 */
240 function setDefaultValues() {
241 $this->_defaults = array();
242 if ($this->_contactId) {
243 CRM_Core_BAO_UFGroup::setProfileDefaults($this->_contactId, $this->_contactProfileFields, $this->_defaults, TRUE);
244 if ($this->_activityProfileId) {
245 CRM_Core_BAO_UFGroup::setProfileDefaults($this->_contactId, $this->_activityProfileFields, $this->_defaults, TRUE);
246 }
247 }
248
249 //set custom field defaults
250
251 foreach ($this->_contactProfileFields as $name => $field) {
252 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
253 $htmlType = $field['html_type'];
254
255 if (!isset($this->_defaults[$name])) {
256 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID,
257 $name,
258 $this->_defaults,
259 $this->_contactId,
260 $this->_mode
261 );
262 }
263 }
264 }
265
266 if ($this->_activityProfileFields) {
267 foreach ($this->_activityProfileFields as $name => $field) {
268 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
269 $htmlType = $field['html_type'];
270
271 if (!isset($this->_defaults[$name])) {
272 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID,
273 $name,
274 $this->_defaults,
275 $this->_contactId,
276 $this->_mode
277 );
278 }
279 }
280 }
281 }
282
283 $this->setDefaults($this->_defaults);
284 }
285
286 public function buildQuickForm() {
287 $this->assign('survey_id', $this->_surveyId);
288 $this->assign('petitionTitle', $this->petition['title']);
289 if (isset($_COOKIE['signed_' . $this->_surveyId])) {
290 if (isset($_COOKIE['confirmed_' . $this->_surveyId])) {
291 $this->assign('duplicate', "confirmed");
292 }
293 else {
294 $this->assign('duplicate', "unconfirmed");
295 }
296 return;
297 }
298
299 $this->applyFilter('__ALL__', 'trim');
300
301 $this->buildCustom($this->_contactProfileId, 'petitionContactProfile');
302 if ($this->_activityProfileId) {
303 $this->buildCustom($this->_activityProfileId, 'petitionActivityProfile');
304 }
305 // add buttons
306 $this->addButtons(array(
307 array(
308 'type' => 'next',
309 'name' => ts('Sign the Petition'),
310 'isDefault' => TRUE,
311 ),
312 )
313 );
314 }
315
316 /**
317 * This function is used to add the rules (mainly global rules) for form.
318 * All local rules are added near the element
319 *
320 * @return None
321 * @access public
322 * @see valid_date
323 */
324
325 static function formRule($fields, $files, $errors) {
326 $errors = array();
327
328 return empty($errors) ? TRUE : $errors;
329 }
330
331 /**
332 * Form submission of petition signature
333 *
334 * @access public
335 *
336 * @return None
337 */
338 public function postProcess() {
339 $tag_name = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME,
340 'tag_unconfirmed'
341 );
342
343
344 if ($tag_name) {
345 // Check if contact 'email confirmed' tag exists, else create one
346 // This should be in the petition module initialise code to create a default tag for this
347 $tag_params['name'] = $tag_name;
348 $tag_params['version'] = 3;
349 $tag = civicrm_api('tag', 'get', $tag_params);
350 if ($tag['count'] == 0) {
351 //create tag
352 $tag_params['description'] = $tag_name;
353 $tag_params['is_reserved'] = 1;
354 $tag_params['used_for'] = 'civicrm_contact';
355 $tag = civicrm_api('tag', 'create', $tag_params);
356 }
357 $this->_tagId = $tag['id'];
358 }
359
360 // export the field values to be used for saving the profile form
361 $params = $this->controller->exportValues($this->_name);
362
363 $session = CRM_Core_Session::singleton();
364 // format params
365 $params['last_modified_id'] = $session->get('userID');
366 $params['last_modified_date'] = date('YmdHis');
367
368 if ($this->_action & CRM_Core_Action::ADD) {
369 $params['created_id'] = $session->get('userID');
370 $params['created_date'] = date('YmdHis');
371 }
372
373 if (isset($this->_surveyId)) {
374 $params['sid'] = $this->_surveyId;
375 }
376
377 if (isset($this->_contactId)) {
378 $params['contactId'] = $this->_contactId;
379 }
380
381 // if logged in user, skip dedupe
382 if ($this->_loggedIn) {
383 $ids[0] = $this->_contactId;
384 }
385 else {
386 // dupeCheck - check if contact record already exists
387 // code modified from api/v2/Contact.php-function civicrm_contact_check_params()
388 $params['contact_type'] = $this->_ctype;
389 //TODO - current dedupe finds soft deleted contacts - adding param is_deleted not working
390 // ignore soft deleted contacts
391 //$params['is_deleted'] = 0;
392 $dedupeParams = CRM_Dedupe_Finder::formatParams($params, $params['contact_type']);
393 $dedupeParams['check_permission'] = '';
394
395 //dupesByParams($params, $ctype, $level = 'Unsupervised', $except = array())
396 $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $params['contact_type']);
397 }
398
399 $petition_params['id'] = $this->_surveyId;
400 $petition = array();
401 CRM_Campaign_BAO_Survey::retrieve($petition_params, $petition);
402
403 switch (count($ids)) {
404 case 0:
405 //no matching contacts - create a new contact
406 // Add a source for this new contact
407 $params['source'] = ts('Petition Signature') . ' ' . $this->petition['title'];
408
409 if ($this->petition['bypass_confirm']) {
410 // send thank you email directly, bypassing confirmation
411 $this->_sendEmailMode = self::EMAIL_THANK;
412 // Set status for signature activity to completed
413 $params['statusId'] = 2;
414 }
415 else {
416 $this->_sendEmailMode = self::EMAIL_CONFIRM;
417
418 // Set status for signature activity to scheduled until email is verified
419 $params['statusId'] = 1;
420 }
421 break;
422
423 case 1:
424 $this->_contactId = $params['contactId'] = $ids[0];
425
426 // check if user has already signed this petition - redirects to Thank You if true
427 $this->redirectIfSigned($params);
428
429 if ($this->petition['bypass_confirm']) {
430 // send thank you email directly, bypassing confirmation
431 $this->_sendEmailMode = self::EMAIL_THANK;
432 // Set status for signature activity to completed
433 $params['statusId'] = 2;
434 break;
435 }
436
437 // dedupe matched single contact, check for 'unconfirmed' tag
438 if ($tag_name) {
439 $tag = new CRM_Core_DAO_EntityTag();
440 $tag->entity_id = $this->_contactId;
441 $tag->tag_id = $this->_tagId;
442
443 if (!($tag->find())) {
444 // send thank you email directly, the user is known and validated
445 $this->_sendEmailMode = self::EMAIL_THANK;
446 // Set status for signature activity to completed
447 $params['statusId'] = 2;
448 }
449 else {
450 // send email verification email
451 $this->_sendEmailMode = self::EMAIL_CONFIRM;
452 // Set status for signature activity to scheduled until email is verified
453 $params['statusId'] = 1;
454 }
455 }
456 break;
457
458 default:
459 // more than 1 matching contact
460 // for time being, take the first matching contact (not sure that's the best strategy, but better than creating another duplicate)
461 $this->_contactId = $params['contactId'] = $ids[0];
462
463 // check if user has already signed this petition - redirects to Thank You if true
464 $this->redirectIfSigned($params);
465
466 if ($this->petition['bypass_confirm']) {
467 // send thank you email directly, bypassing confirmation
468 $this->_sendEmailMode = self::EMAIL_THANK;
469 // Set status for signature activity to completed
470 $params['statusId'] = 2;
471 break;
472 }
473
474 if ($tag_name) {
475 $tag = new CRM_Core_DAO_EntityTag();
476 $tag->entity_id = $this->_contactId;
477 $tag->tag_id = $this->_tagId;
478
479 if (!($tag->find())) {
480 // send thank you email
481 $this->_sendEmailMode = self::EMAIL_THANK;
482 // Set status for signature activity to completed
483 $params['statusId'] = 2;
484 }
485 else {
486 // send email verification email
487 $this->_sendEmailMode = self::EMAIL_CONFIRM;
488 // Set status for signature activity to scheduled until email is verified
489 $params['statusId'] = 1;
490 }
491 }
492 break;
493 }
494
495
496
497 $transaction = new CRM_Core_Transaction();
498
499 $addToGroupID = isset($this->_addToGroupID) ? $this->_addToGroupID : NULL;
500 $this->_contactId = CRM_Contact_BAO_Contact::createProfileContact($params, $this->_contactProfileFields,
501 $this->_contactId, $addToGroupID,
502 $this->_contactProfileId, $this->_ctype,
503 TRUE
504 );
505
506 // get additional custom activity profile field data
507 // to save with new signature activity record
508 $surveyInfo = $this->bao->getSurveyInfo($this->_surveyId);
509 $customActivityFields = CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
510 $surveyInfo['activity_type_id']
511 );
512 $customActivityFields = CRM_Utils_Array::crmArrayMerge($customActivityFields,
513 CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
514 NULL, NULL, TRUE
515 )
516 );
517
518 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
519 $customActivityFields,
520 NULL,
521 'Activity'
522 );
523
524 // create the signature activity record
525 $params['contactId'] = $this->_contactId;
526 $params['activity_campaign_id'] = $this->petition['campaign_id'];
527 $result = $this->bao->createSignature($params);
528
529 // send thank you or email verification emails
530
531 // if logged in using Facebook connect and email on form matches Fb email,
532 // no need for email confirmation, send thank you email
533 if ($this->forceEmailConfirmed['flag'] &&
534 ($this->forceEmailConfirmed['email'] == $params['email-Primary'])
535 ) {
536 $this->_sendEmailMode = self::EMAIL_THANK;
537 }
538
539 switch ($this->_sendEmailMode) {
540 case self::EMAIL_THANK:
541 // mark the signature activity as completed and set confirmed cookie
542 $this->bao->confirmSignature($result->id, $this->_contactId, $this->_surveyId);
543 break;
544
545 case self::EMAIL_CONFIRM:
546 // set 'Unconfirmed' tag for this new contact
547 if ($tag_name) {
548 unset($tag_params);
549 $tag_params['contact_id'] = $this->_contactId;
550 $tag_params['tag_id'] = $this->_tagId;
551 $tag_value = civicrm_api('entity_tag', 'create', $tag_params);
552 }
553 break;
554 }
555
556 //send email
557 $params['activityId'] = $result->id;
558 $params['tagId'] = $this->_tagId;
559 $this->bao->sendEmail($params, $this->_sendEmailMode);
560
561 $transaction->commit();
562
563 if ($result) {
564 // call the hook before we redirect
565 $this->postProcessHook();
566
567 // set the template to thank you
568 $url =
569 CRM_Utils_System::url(
570 'civicrm/petition/thankyou',
571 'pid=' . $this->_surveyId . '&id=' . $this->_sendEmailMode . '&reset=1'
572 );
573 CRM_Utils_System::redirect($url);
574 }
575 }
576
577 /**
578 * Function to build the petition profile form
579 *
580 * @return None
581 * @access public
582 */
583 function buildCustom($id, $name, $viewOnly = FALSE) {
584
585 if ($id) {
586 $session = CRM_Core_Session::singleton();
587 $this->assign("petition", $this->petition);
588 //$contactID = $this->_contactId;
589 $contactID = NULL;
590 $this->assign('contact_id', $this->_contactId);
591
592 $fields = NULL;
593 // TODO: contactID is never set (commented above)
594 if ($contactID) {
595 if (CRM_Core_BAO_UFGroup::filterUFGroups($id, $contactID)) {
596 $fields = CRM_Core_BAO_UFGroup::getFields($id, FALSE, CRM_Core_Action::ADD);
597 }
598 }
599 else {
600 $fields = CRM_Core_BAO_UFGroup::getFields($id, FALSE, CRM_Core_Action::ADD);
601 }
602
603 if ($fields) {
604 /*
605 // unset any email-* fields since we already collect it, CRM-2888
606 foreach ( array_keys( $fields ) as $fieldName ) {
607 if ( substr( $fieldName, 0, 6 ) == 'email-' ) {
608 unset( $fields[$fieldName] );
609 }
610 }
611 */
612
613
614 $this->assign($name, $fields);
615
616 $addCaptcha = FALSE;
617 foreach ($fields as $key => $field) {
618 if ($viewOnly &&
619 isset($field['data_type']) &&
620 $field['data_type'] == 'File' || ($viewOnly && $field['name'] == 'image_URL')
621 ) {
622 // ignore file upload fields
623 continue;
624 }
625
626
627 CRM_Core_BAO_UFGroup::buildProfile($this, $field, CRM_Profile_Form::MODE_CREATE, $contactID, TRUE);
628 $this->_fields[$key] = $field;
629 if ($field['add_captcha']) {
630 $addCaptcha = TRUE;
631 }
632 }
633
634 if ($addCaptcha &&
635 !$viewOnly
636 ) {
637 $captcha = CRM_Utils_ReCAPTCHA::singleton();
638 $captcha->add($this);
639 $this->assign("isCaptcha", TRUE);
640 }
641 }
642 }
643 }
644
645 function getTemplateFileName() {
646 if (isset($this->thankyou)) {
647 return ('CRM/Campaign/Page/Petition/ThankYou.tpl');
648 }
649 else {}
650 return parent::getTemplateFileName();
651 }
652
653 // check if user has already signed this petition
654 function redirectIfSigned($params) {
655 $signature = $this->bao->checkSignature($this->_surveyId, $this->_contactId);
656 //TODO: error case when more than one signature found for this petition and this contact
657 if (!empty($signature) && (count($signature) == 1)) {
658 $signature_id = array_keys($signature);
659 switch ($signature[$signature_id[0]]['status_id']) {
660 case 1:
661 //status is scheduled - email is unconfirmed
662 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId . '&id=4&reset=1'));
663 break;
664
665 case 2:
666 //status is completed
667 $this->bao->sendEmail($params, 1);
668 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/petition/thankyou', 'pid=' . $this->_surveyId . '&id=5&reset=1'));
669 break;
670 }
671 }
672 }
673 }
674
675
676