registerQuick.php 19.2 KB
Newer Older
Chenu Denis's avatar
Chenu Denis committed
1 2
<?php
/**
3
 * Alternative solution for registering
Chenu Denis's avatar
Chenu Denis committed
4 5
 *
 * @author Denis Chenu <denis@sondages.pro>
6
 * @copyright 2018 Denis Chenu <http://www.sondages.pro>
Chenu Denis's avatar
Chenu Denis committed
7
 * @copyright 2017 SICODA GmbH <http://www.sicoda.de>
8
 * @copyright 2017 www.marketaccess.ca <https://www.marketaccess.ca/>
Chenu Denis's avatar
Chenu Denis committed
9
 * @license AGPL v3
10
 * @version 1.2.1
Chenu Denis's avatar
Chenu Denis committed
11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
22
class registerQuick extends PluginBase {
Chenu Denis's avatar
Chenu Denis committed
23 24 25 26 27 28

    protected $storage = 'DbStorage';

    static protected $description = 'Quick register system, replace default register system by a quickest way.';
    static protected $name = 'registerQuick';

29

Chenu Denis's avatar
Chenu Denis committed
30 31
    public function init()
    {
32 33
        $this->subscribe('beforeActivate');

Chenu Denis's avatar
Chenu Denis committed
34 35 36 37 38 39
        $this->subscribe('beforeSurveySettings');
        $this->subscribe('newSurveySettings');

        $this->subscribe('beforeRegisterForm');
        $this->subscribe('beforeRegister');

40
        $this->subscribe('getPluginTwigPath');
41
        $this->subscribe('getValidScreenFiles');
42 43 44 45 46 47 48 49 50

    }

    /**
     * test if this plugin can be used, log as error and as vardump if not able
     * @return boolean
     */
    private function _canBeUsed()
    {
51
        $lsVersion = floatval(Yii::app()->getConfig('versionnumber'));
52 53
        $alsVersion = array_replace(array(0,0,0),explode(".",$lsVersion));
        if($alsVersion[0] >= 3 && $alsVersion[1] > 9) {
54 55
            return true;
        }
56 57 58 59 60 61
        if($lsVersion < 3) {
            $this->log("Only for LimeSurvey 3.0.0 and up version",'error');
            return false;
        }
        $oTwigExtendByPlugins = Plugin::model()->find("name=:name",array(":name"=>'twigExtendByPlugins'));
        if(!$oTwigExtendByPlugins) {
62
            $this->log("You must download twigExtendByPlugins plugin",'error');
63 64
            return false;
        } elseif(!$oTwigExtendByPlugins->active) {
65
            $this->log("You must activate twigExtendByPlugins plugin",'error');
66 67 68 69 70 71 72 73 74
            return false;
        }
        return true;
    }
    /**
     * Activate or not
     */
    public function beforeActivate()
    {
75
        $lsVersion = floatval(Yii::app()->getConfig('versionnumber'));
76 77 78
        $alsVersion = array_replace(array(0,0,0),explode(".",$lsVersion));
        if($alsVersion[0] >= 3 && $alsVersion[1] > 9) {
            return true;
79
        }
80 81 82 83 84 85 86 87 88 89 90 91
        if($lsVersion < 3) {
            $this->getEvent()->set('message', gT("Only for LimeSurvey 3.0.0 and up version"));
            $this->getEvent()->set('success', false);
        }
        $oTwigExtendByPlugins = Plugin::model()->find("name=:name",array(":name"=>'twigExtendByPlugins'));
        if(!$oTwigExtendByPlugins) {
            $this->getEvent()->set('message', gT("You must download twigExtendByPlugins plugin"));
            $this->getEvent()->set('success', false);
        } elseif(!$oTwigExtendByPlugins->active) {
            $this->getEvent()->set('message', gT("You must activate twigExtendByPlugins plugin"));
            $this->getEvent()->set('success', false);
        }
Chenu Denis's avatar
Chenu Denis committed
92 93 94 95 96 97 98 99 100 101 102 103 104
    }

    /**
     * var string[] The registered erros for this plugin
     */
    private $_aRegisterError=array();

    /**
    * @see beforeSurveySettings
    */
    public function beforeSurveySettings()
    {
        $oEvent = $this->event;
105 106 107 108 109 110 111 112 113 114 115 116
        if(!$this->_canBeUsed()) {
            $oEvent->set("surveysettings.{$this->id}", array(
                'name' => get_class($this),
                'settings' => array(
                    'warning'=> array(
                        'type'=>'info',
                        'content'=>\CHtml::tag("div",array('class'=>'alert alert-danger'),$this->gT("You can not use this plugin with your current LimeSurvey version and active plugin")),
                    ),
                ),
            ));
            return;
        }
Chenu Denis's avatar
Chenu Denis committed
117 118 119 120 121
        $oEvent->set("surveysettings.{$this->id}", array(
            'name' => get_class($this),
            'settings' => array(
                'quickRegistering'=>array(
                    'type'=>'boolean',
122
                    'label'=>$this->gT('Use quick registering'),
Chenu Denis's avatar
Chenu Denis committed
123 124 125 126
                    'current'=>$this->get('quickRegistering','Survey',$oEvent->get('survey'),0)
                ),
                'emailValidation'=>array(
                    'type'=>'select',
127
                    'label'=>$this->gT('Email settings'),
Chenu Denis's avatar
Chenu Denis committed
128
                    'options'=>array(
129 130
                            'show'=>$this->gT("Shown but allow empty"),
                            'hide'=>$this->gT("Hide and don't use it"),
Chenu Denis's avatar
Chenu Denis committed
131 132
                        ),
                    'htmlOptions'=>array(
133
                        'empty'=>$this->gT("Validate like LimeSurvey core (default)"),
Chenu Denis's avatar
Chenu Denis committed
134 135 136
                    ),
                    'current'=>$this->get('emailValidation','Survey',$oEvent->get('survey'),"")
                ),
137 138
                'emailMultiple' => array(
                    'type'=>'select',
139
                    'label'=>$this->gT('Existing Email'),
140
                    'options'=>array(
141 142
                        'getold' => $this->gT("Reload previous response."),
                        'renew'=> $this->gT("Create a new one if already completed"),
143 144
                    ),
                    'htmlOptions'=>array(
145
                        'empty'=>$this->gT("Create a new token each time."),
146 147 148
                    ),
                    'current'=>$this->get('emailMultiple','Survey',$oEvent->get('survey'),"")
                ),
149 150
                'emailSecurity'=>array(
                    'type'=>'boolean',
151 152
                    'label'=>$this->gT('Privacy of response'),
                    'help'=>$this->gT("If email exist : disable reloading survey without token. Warning: enabling reload of survey only with the e-mail address can cause privacy issue. The message will be sent if the email address exists."),
153 154
                    'current'=>$this->get('emailSecurity','Survey',$oEvent->get('survey'),1)
                ),
Chenu Denis's avatar
Chenu Denis committed
155 156
                'emailSend'=>array(
                    'type'=>'boolean',
157 158
                    'label'=>$this->gT('Send the email.'),
                    'help'=>$this->gT("If user put an email address, the register message will be sent."),
Chenu Denis's avatar
Chenu Denis committed
159 160
                    'current'=>$this->get('emailSend','Survey',$oEvent->get('survey'),0)
                ),
161 162
                'showTokenForm'=>array(
                    'type'=>'boolean',
163
                    'label'=>$this->gT('Show the token form.'),
164 165
                    'current'=>$this->get('showTokenForm','Survey',$oEvent->get('survey'),0)
                ),
Chenu Denis's avatar
Chenu Denis committed
166 167 168 169 170 171 172 173 174 175
            )
        ));
    }

    /**
    * @see newSurveySettings
    */
    public function newSurveySettings()
    {
        $event = $this->event;
176
        foreach ($event->get('settings') as $name => $value) {
Chenu Denis's avatar
Chenu Denis committed
177 178 179 180 181 182 183 184 185
            $this->set($name, $value, 'Survey', $event->get('survey'));
        }
    }

    /**
     * @see beforeRegister
     */
    public function beforeRegister()
    {
186 187 188
        if(!$this->_canBeUsed()) {
            return;
        }
Chenu Denis's avatar
Chenu Denis committed
189 190
        $iSurveyId=$this->getEvent()->get('surveyid');
        if($this->get('quickRegistering','Survey',$iSurveyId)){
191
            
192 193 194 195 196 197 198 199 200
            /* Control survey access and Fix langage according to survey @see https://bugs.limesurvey.org/view.php?id=12641 */
            $oSurvey=Survey::model()->findByPK($iSurveyId);
            if (!$oSurvey) {
                throw new CHttpException(404, "The survey in which you are trying to participate does not seem to exist. It may have been deleted or the link you were given is outdated or incorrect.");
            } elseif($oSurvey->allowregister!='Y' || !tableExists("{{tokens_{$iSurveyId}}}")) {
                throw new CHttpException(404,"The survey in which you are trying to register don't accept registration. It may have been updated or the link you were given is outdated or incorrect.");
            } elseif(!is_null($oSurvey->expires) && $oSurvey->expires < dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i", Yii::app()->getConfig('timeadjust'))) {
                $this->redirect(array('survey/index','sid'=>$iSurveyId,'lang'=>$sLanguage));
            }
201
            $this->_fixLanguage($iSurveyId);
Chenu Denis's avatar
Chenu Denis committed
202 203 204 205 206
            if((Yii::app()->request->getPost('register'))){
                $this->_validateForm($iSurveyId);
            }
        }
    }
207

Chenu Denis's avatar
Chenu Denis committed
208 209 210 211 212
    /**
    * @see beforeRegisterForm
    */
    public function beforeRegisterForm()
    {
213 214 215
        if(!$this->_canBeUsed()) {
            return;
        }
Chenu Denis's avatar
Chenu Denis committed
216 217 218 219 220 221
        $iSurveyId=$this->getEvent()->get('surveyid');
        if($this->get('quickRegistering','Survey',$iSurveyId)){
            $this->getEvent()->set('registerForm',$this->_getRegisterForm($iSurveyId));
        }
    }

222 223 224
    public function getPluginTwigPath()
    {
        $viewPath = dirname(__FILE__)."/views";
225
        $forcedPath = dirname(__FILE__)."/forced";
226
        $this->getEvent()->append('TwigExtendOption', array($viewPath));
227
        $this->getEvent()->append('TwigExtendForced', array($forcedPath));
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        $this->getEvent()->append('add', array($viewPath));
        $this->getEvent()->append('replace', array($forcedPath));
    }

    public function getValidScreenFiles()
    {
        if(
            $this->getEvent()->get("type")!='view' ||
            ($this->getEvent()->get("screen") && $this->getEvent()->get("screen")!="register")
        ){
            return;
        }
        if($this->getEvent()->get("screen")) {
            $this->getEvent()->append('remove', array("subviews/registration/register_form.twig"));
        }
        $this->getEvent()->append('add', array("subviews/registration/registerquick_form.twig","subviews/registration/registerquick_token_form.twig"));
244
    }
245

Chenu Denis's avatar
Chenu Denis committed
246 247 248 249 250 251 252 253 254 255
    /**
     * Validate the register form and do action if needed
     * @param integer $iSurveyId
     * @return void
     */
    private function _validateForm($iSurveyId)
    {
        $this->_aRegisterError=$this->_getRegisterErrors($iSurveyId);
        if(empty($this->_aRegisterError)){
            $iTokenId=$this->_getTokenId($iSurveyId);
256 257 258 259 260
            if(empty($this->_aRegisterError)){
                if($this->get('emailSend','Survey',$iSurveyId)){
                    $this->_sendRegistrationEmail($iSurveyId,$iTokenId);
                }
                $this->_redirectToToken($iSurveyId,$iTokenId);
Chenu Denis's avatar
Chenu Denis committed
261 262 263 264
            }
        }
    }

265

Chenu Denis's avatar
Chenu Denis committed
266 267 268 269 270 271 272 273
    /**
     * Construct the new form with option
     * @see RegisterController->getRegisterForm()
     * @param integer $iSurveyId
     * @return string
     */
    private function _getRegisterForm($iSurveyId)
    {
274
        $this->unsubscribe('beforeRegisterForm');
Chenu Denis's avatar
Chenu Denis committed
275 276
        Yii::import('application.controllers.RegisterController');
        $RegisterController= new RegisterController('register');
277 278
        $this->_fixLanguage($iSurveyId);
        $aRegisterFormInfo = $RegisterController->getRegisterForm($iSurveyId); // Here come a array, not a form
Chenu Denis's avatar
Chenu Denis committed
279
        $emailValidation=$this->get('emailValidation','Survey',$iSurveyId,'');
280 281
        $aRegisterFormInfo['showEmail'] = (empty($emailValidation) || $emailValidation=='show');
        $aRegisterFormInfo['requiredEmail'] = ($emailValidation=='');
282 283 284
        $aRegisterFormInfo['showTokenForm'] = (bool) $this->get('showTokenForm','Survey',$iSurveyId,0);
        $aRegisterFormInfo['surveyUrl'] = App()->createUrl("/survey/index/",array('sid'=>$iSurveyId,'lang'=>App()->getLanguage()));;

285 286 287 288
        /* Complete by register errors */
        if(App()->getRequest()->getPost('register')) {
            $registerErrors = $this->_getRegisterErrors($iSurveyId);
            $aRegisterFormInfo['aErrors'] = $registerErrors;
289
        }
290
        $aRegisterFormInfo['options']['ajaxmode'] = "off";/* Seem to be replaced after, then adding a script in twig file */
291
        $aRegisterFormInfo['surveyls_title'] = sprintf($this->gT("Registering to : %s"),Survey::model()->findByPk($iSurveyId)->getLocalizedTitle());
292
        return $aRegisterFormInfo;
Chenu Denis's avatar
Chenu Denis committed
293 294 295 296 297 298 299 300 301 302
    }

    /**
     * Create the token and return it
     * @param integer iSurveyId
     * @return integer the token id
     */
    private function _getTokenId($iSurveyId){
        Yii::import('application.controllers.RegisterController');
        $RegisterController= new RegisterController('register');
303 304
        $this->_fixLanguage($iSurveyId);
        $aSurveyInfo=getSurveyInfo($iSurveyId,App()->getLanguage());
Chenu Denis's avatar
Chenu Denis committed
305
        $aFieldValue=$RegisterController->getFieldValue($iSurveyId);
306 307 308 309 310 311 312 313 314 315 316 317 318
        if($aFieldValue['sEmail']!="" && $this->get('emailMultiple','Survey',$iSurveyId)) {
            $oToken=Token::model($iSurveyId)->findByAttributes(array('email' => $aFieldValue['sEmail']));
            if($oToken) {
                if($oToken->usesleft<1 && $aSurveyInfo['alloweditaftercompletion']!='Y') {
                    switch (trim($this->get('emailMultiple','Survey',$iSurveyId)) ) {
                        case 'renew' :
                            $oToken=null;
                            break;
                        case 'getold' :
                        default :
                            $this->_aRegisterError[]=gT("The email address you have entered is already registered and the survey has been completed.");
                    }
                } elseif(strtolower(substr(trim($oToken->emailstatus),0,6))==="optout") {
319
                    $this->_aRegisterError[]=$this->gT("This email address cannot be used because it was opted out of this survey.");
320
                } elseif(!$oToken->emailstatus && $oToken->emailstatus!="OK") {
321
                    $this->_aRegisterError[]=$this->gT("This email address is already registered but the email adress was bounced.");
322 323
                } elseif($aSurveyInfo['alloweditaftercompletion']=='Y' || $oToken->usesleft > 0) {
                    if($this->get('emailSecurity','Survey',$iSurveyId,1)) {
324
                        $this->_aRegisterError[]=$this->gT("This email address is already registered, entering in survey is only allowed with token.");
325 326
                        $this->_sendRegistrationEmail($iSurveyId,$oToken->tid);
                    }
327 328 329
                }
            }
        }
330
        if(!empty($oToken)) {
331 332
            return $oToken->tid;
        }
Chenu Denis's avatar
Chenu Denis committed
333 334 335 336 337 338 339 340
        $oToken= Token::create($iSurveyId);
        $oToken->firstname = sanitize_xss_string($aFieldValue['sFirstName']);
        $oToken->lastname = sanitize_xss_string($aFieldValue['sLastName']);
        $oToken->email = $aFieldValue['sEmail'];
        $oToken->emailstatus = 'OK';
        $oToken->language = $sLanguage;
        $aFieldValue['aAttribute']=array_map('sanitize_xss_string',$aFieldValue['aAttribute']);
        $oToken->setAttributes($aFieldValue['aAttribute']);
341
        if ($aSurveyInfo['startdate']) {
Chenu Denis's avatar
Chenu Denis committed
342 343
            $oToken->validfrom = $aSurveyInfo['startdate'];
        }
344
        if ($aSurveyInfo['expires']) {
Chenu Denis's avatar
Chenu Denis committed
345 346 347 348 349 350 351 352 353 354
            $oToken->validuntil = $aSurveyInfo['expires'];
        }
        $oToken->generateToken();
        $oToken->save();
        return $oToken->tid;
    }

    /**
     * Send the registration email
     * @see RegisterController->sendRegistrationEmail();
355 356 357
     * @param integer $iSureyId
     * @param integer $iTokenId
     * @return boolean 
Chenu Denis's avatar
Chenu Denis committed
358 359 360
     */
    private function _sendRegistrationEmail($iSurveyId,$iTokenId){
        Yii::import('application.controllers.RegisterController');
361 362 363
        $RegisterController = new RegisterController('register');
        $this->_fixLanguage($iSurveyId);
        $done = $RegisterController->sendRegistrationEmail($iSurveyId,$iTokenId);
Chenu Denis's avatar
Chenu Denis committed
364 365 366 367 368 369 370 371 372 373 374 375
        return $done;
    }
    /**
    * Validate a register form
    * Because we need validating before default action happen
    * @param $iSurveyId Survey Id to register
    * @return array of errors when try to register (empty array => no error)
    */
    private function _getRegisterErrors($iSurveyId){
        $aSurveyInfo=getSurveyInfo($iSurveyId,App()->language);
        $aRegisterErrors=array();
        // Check the security question's answer
376
        if (function_exists("ImageCreate") && isCaptchaEnabled('registrationscreen',$aSurveyInfo['usecaptcha']) ) {
Chenu Denis's avatar
Chenu Denis committed
377 378 379 380
            $sLoadSecurity=Yii::app()->request->getPost('loadsecurity','');
            $captcha=Yii::app()->getController()->createAction("captcha");
            $captchaCorrect = $captcha->validate( $sLoadSecurity, false);

381
            if (!$captchaCorrect) {
Chenu Denis's avatar
Chenu Denis committed
382 383 384 385 386
                $aRegisterErrors[] = gT("Your answer to the security question was not correct - please try again.");
            }
        }
        Yii::import('application.controllers.RegisterController');
        $RegisterController= new RegisterController('register');
387
        $this->_fixLanguage($iSurveyId);
Chenu Denis's avatar
Chenu Denis committed
388 389 390 391 392 393 394 395 396 397
        $aFieldValue=$RegisterController->getFieldValue($iSurveyId);
        $aRegisterAttributes=$RegisterController->getExtraAttributeInfo($iSurveyId);

        //Check that the email is a valid style address
        if ($aFieldValue['sEmail']!="" && !validateEmailAddress($aFieldValue['sEmail'])) {
            $aRegisterErrors[]= gT("The email you used is not valid. Please try again.");
        } elseif ($aFieldValue['sEmail']=="" && $this->get('emailValidation','Survey',$iSurveyId,'')=="") {
            $aRegisterErrors[]= gT("You must enter a valid email. Please try again.");
        }
        //Check and validate attribute
398
        foreach ($aRegisterAttributes as $key => $aAttribute) {
Chenu Denis's avatar
Chenu Denis committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
            if ($aAttribute['show_register'] == 'Y' && $aAttribute['mandatory'] == 'Y' && empty($aFieldValue['aAttribute'][$key]))
            {
                $aRegisterErrors[]= sprintf(gT("%s cannot be left empty").".", $aAttribute['caption']);
            }
        }
        return $aRegisterErrors;
    }
    /**
     * redirect to survey with the new token
     * @param integer $iSurveyId
     * @param integer $iTokenId
     * @return void (but redirect)
     */
    private function _redirectToToken($iSurveyId,$iTokenId){
        $oToken = Token::model($iSurveyId)->findByPk($iTokenId);
        $sToken=$oToken->token;
        $sLanguage=App()->language;
416 417 418
        $redirectUrl = App()->createUrl("/survey/index/",array('sid'=>$iSurveyId,'lang'=>$sLanguage,'token'=>$sToken,'newtest'=>'Y'));
        Yii::app()->getController()->redirect($redirectUrl);
        Yii::app()->end();
Chenu Denis's avatar
Chenu Denis committed
419
    }
420

Chenu Denis's avatar
Chenu Denis committed
421
    /**
422 423
     * Log message
     * @return void
Chenu Denis's avatar
Chenu Denis committed
424
     */
425 426
    public function log($message, $level = \CLogger::LEVEL_TRACE)
    {
427 428 429
        if(is_callable("parent::log")) {
            parent::log($message, $level);
        }
430
        Yii::log("[".get_class($this)."] ".$message, $level, 'vardump');
Chenu Denis's avatar
Chenu Denis committed
431
    }
432

Chenu Denis's avatar
Chenu Denis committed
433
    /**
434 435 436
     * fix the language
     * @param $iSurveyId
     * @return void
Chenu Denis's avatar
Chenu Denis committed
437
     */
438 439 440 441 442 443 444 445 446
    private function _fixLanguage($iSurveyId)
    {
        $oSurvey = Survey::model()->findByPk($iSurveyId);
        $language = $oSurvey->language;
        $userLanguage = App()->getRequest()->getParam('lang',App()->getRequest()->getPost('lang'));
        if(in_array($userLanguage,$oSurvey->getAllLanguages()) ) {
            $language = $userLanguage;
        }
        App()->setLanguage($language);
Chenu Denis's avatar
Chenu Denis committed
447
    }
448
    
449 450 451 452 453 454 455 456
    /**
     * @inheritdoc
     * With default escape mode to 'unescaped'
     */
    public function gT($sToTranslate, $sEscapeMode = 'unescaped', $sLanguage = null)
    {
        return parent::gT($sToTranslate, $sEscapeMode, $sLanguage);
    }
Chenu Denis's avatar
Chenu Denis committed
457
}