surveyChaining.php 37.7 KB
Newer Older
1 2 3 4 5
<?php
/**
 * Chaining survey
 *
 * @author Denis Chenu <denis@sondages.pro>
6
 * @copyright 2018 Denis Chenu <http://www.sondages.pro>
Chenu Denis's avatar
Chenu Denis committed
7
 * @copyright 2018 DRAAF Bourgogne-Franche-Comte <http://draaf.bourgogne-franche-comte.agriculture.gouv.fr/>
8
 * @license GPL v3
9
 * @version 0.13.1
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU AFFERO 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.
 */
class surveyChaining extends PluginBase {

    protected $storage = 'DbStorage';
    static protected $description = 'Chaining surveys';
    static protected $name = 'surveyChaining';

27 28 29 30 31 32 33 34 35 36
    /**
     * @var integer dbversion
     */
    var $dbversion = 1;

    /**
     * @var boolean did this done (see issue in limesurvey)
     */
    var $done = false;

37
    public function init() {
38
        /* Config must be set before all other */
39 40 41 42
        $oPlugin = Plugin::model()->find("name = :name",array("name"=>get_class($this)));
        if($oPlugin && $oPlugin->active) {
          $this->_setConfig();
        }
43 44 45 46 47
        /* Add menu in tool menu */
        $this->subscribe('beforeToolsMenuRender');
        /* Add menu in tool menu */
        $this->subscribe('afterSurveyComplete');
        /* */
48
        $this->subscribe('beforeControllerAction');
49
        /* */
50
        $this->subscribe('newDirectRequest');
51 52
        /* when survey is deleted : must delete all related links */
        $this->subscribe('afterSurveyDelete');
53
    }
54

55 56 57 58
    /** @inheritdoc **/
    public function beforeControllerAction()
    {
      $this->_setDb();
59 60
    }

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    /** */
    public function newDirectRequest()
    {
        if($this->getEvent()->get('target') != get_class($this)) {
            return;
        }
        if(!Permission::model()->getUserId() ) {
            throw new CHttpException(401);
        }
        
        $surveyId = App()->getRequest()->getParam('sid');
        $destSurveyId = App()->getRequest()->getParam('destsid');
        if(!$surveyId || !$destSurveyId) {
            throw new CHttpException(500,$this->gT("This action need a survey and a destination survey id"));
        }
        $oAnswersAsReadonly = Plugin::model()->find("name = :name",array(":name"=>'answersAsReadonly'));
        if (!$oAnswersAsReadonly || !$oAnswersAsReadonly->active) {
            $this->_renderJson(array('error'=>array('message'=>$this->gT("answersAsReadonly plugin didn't exist or is not activated."))));
        }
        if(!Permission::model()->hasSurveyPermission($surveyId,'surveysettings','update')){
            throw new CHttpException(403);
        }
        if(!Permission::model()->hasSurveyPermission($destSurveyId,'surveysettings','update')){
            throw new CHttpException(403,sprintf($this->gT("You don't have permission on survey %s"),$destSurveyId));
        }
        //~ $oSurvey = Survey::model()->findByPk($surveyId);
        //~ $oDestSurvey = Survey::model()->findByPk($destSurveyId);
        $aSameCode = $this->_getSameCodes($surveyId,$destSurveyId);
        if(empty($aSameCode)) {
            $this->_renderJson(array('error'=>array('message'=>$this->gT("Survey selected and current survey didn't have any correspondig question."))));
        }
        $aQidColumnsToCode = \surveyChaining\helpers\surveyCodeHelper::getColumnsToCode($destSurveyId,true);
        $aQidToDo = array_filter($aQidColumnsToCode, function($aColumnToCode) use ($aSameCode) {
            return count(array_intersect($aSameCode,$aColumnToCode));
        });
        foreach(array_keys($aQidToDo) as $qid) {
            QuestionAttribute::model()->setQuestionAttribute($qid,'readonly',1);
        }
        $this->_renderJson(array(
            'success'=>sprintf($this->gT("Question(s) %s are set to readonly"),implode(',',array_keys($aQidToDo)))
        ));
    }
103
    /** @inheritdoc **/
104
    public function afterSurveyDelete()
105 106
    {
        /* Delete all link when set a survey is deleted */
107 108 109 110 111
        $oSurvey = $this->getEvent()->get('model');
            if($oSurvey->sid) {
                $deleted = \surveyChaining\models\chainingResponseLink::model()->deleteAll("prevsid = :prevsid OR nextsid =:nextsid",array(':prevsid'=>$oSurvey->sid,':nextsid'=>$oSurvey->sid));
                if($deleted>0) { // Don't log each time, can be saved for something other …
                $this->log(sprintf("%d chainingResponseLink deleted for %d",$deleted,$oSurvey->sid),CLogger::LEVEL_INFO);
112 113 114 115 116
            }
        }
    }

    /** @inheritdoc **/
117 118 119 120 121
    public function beforeToolsMenuRender()
    {
        $event = $this->getEvent();
        $surveyId = $event->get('surveyId');
        $aMenuItem = array(
122
            'label' => $this->gT('Survey chaining'),
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
            'iconClass' => 'fa fa-recycle',
            'href' => Yii::app()->createUrl(
                'admin/pluginhelper',
                array(
                    'sa' => 'sidebody',
                    'plugin' => get_class($this),
                    'method' => 'actionSettings',
                    'surveyId' => $surveyId
                )
            ),
        );
        if (class_exists("\LimeSurvey\Menu\MenuItem")) {
            $menuItem = new \LimeSurvey\Menu\MenuItem($aMenuItem);
        } else {
            $menuItem = new \ls\menu\MenuItem($aMenuItem);
        }
        $event->append('menuItems', array($menuItem));
    }
    /**
     * Main function
     * @param int $surveyId Survey id
     *
     * @return string
     */
    public function actionSettings($surveyId)
    {
        $oSurvey=Survey::model()->findByPk($surveyId);
        if(!$oSurvey) {
            throw new CHttpException(404,$this->translate("This survey does not seem to exist."));
        }
        if(!Permission::model()->hasSurveyPermission($surveyId,'surveysettings','update')){
154
            throw new CHttpException(403);
155 156 157 158 159
        }

        if(App()->getRequest()->getPost('save'.get_class($this))) {
            PluginSetting::model()->deleteAll("plugin_id = :pluginid AND model = :model AND model_id = :sid",array(":pluginid"=>$this->id,":model"=>'Survey',':sid'=>$surveyId));
            $this->set('nextSurvey', App()->getRequest()->getPost('nextSurvey'), 'Survey', $surveyId);
160
            $this->set('findExistingLink', App()->getRequest()->getPost('findExistingLink'), 'Survey', $surveyId);
161 162 163 164 165 166
            $this->set('nextEmail', App()->getRequest()->getPost('nextEmail'), 'Survey', $surveyId);
            $this->set('nextMessage', App()->getRequest()->getPost('nextMessage'), 'Survey', $surveyId);

            /* Don't update old choice only of choice are updated */
            $oldChoice = $this->get('choiceQuestion', 'Survey', $surveyId,null);
            $this->set('choiceQuestion', App()->getRequest()->getPost('choiceQuestion'), 'Survey', $surveyId);
Chenu Denis's avatar
Chenu Denis committed
167 168 169 170 171 172 173 174 175 176 177
            if($this->get('choiceQuestion', 'Survey', $surveyId,null)) {
                $title = $this->get('choiceQuestion', 'Survey', $surveyId,null);
                $oQuestion = Question::model()->find("title=:title and language=:language",array(":title"=>$title,":language"=>$oSurvey->language));
                $aoAnswers = Answer::model()->findAll(array(
                    'condition' => "qid=:qid and language=:language",
                    'order' => 'sortorder ASC',
                    'params' => array(":qid"=>$oQuestion->qid,":language"=>$oSurvey->language)
                ));
                foreach($aoAnswers as $oAnswers) {
                    $code = $oAnswers->code;
                    $this->set('nextSurvey_'.$code, App()->getRequest()->getPost('nextSurvey_'.$code), 'Survey', $surveyId);
178
                    $this->set('findExistingLink_'.$code, App()->getRequest()->getPost('findExistingLink_'.$code), 'Survey', $surveyId);
Chenu Denis's avatar
Chenu Denis committed
179 180 181 182
                    $this->set('nextEmail_'.$code, App()->getRequest()->getPost('nextEmail_'.$code), 'Survey', $surveyId);
                    $this->set('nextMessage_'.$code, App()->getRequest()->getPost('nextMessage_'.$code), 'Survey', $surveyId);
                }
            }
Chenu Denis's avatar
Chenu Denis committed
183
            if(App()->getRequest()->getPost('save'.get_class($this))=='redirect') {
184 185 186
                Yii::app()->getController()->redirect(Yii::app()->getController()->createUrl('admin/survey',array('sa'=>'view','surveyid'=>$surveyId)));
            }
        }
187

188 189 190 191 192 193 194 195 196
        $aData=array();
        $aData['warningString'] = null;
        $aSettings=array();
        /* Basic settings */
        //$aWholeSurveys = Survey::model()->with('permission')
        $aWholeSurveys = Survey::model()
            ->permission(Yii::app()->user->getId())
            ->with('defaultlanguage')
            ->findAll(array('order'=>'surveyls_title'));
197 198
        $sHelpSurveyTable = null;
        $iNextSurvey = $this->get('nextSurvey', 'Survey', $surveyId,null);
199

200 201 202 203 204 205 206 207 208
        $aNextSettings = array(
            'nextSurvey' => array(
                'type'=>'select',
                'htmlOptions'=>array(
                    'empty'=>$this->gT("None"),
                ),
                'label'=>$this->gT("Next survey (by default)."),
                'options'=>CHtml::listData($aWholeSurveys,'sid','defaultlanguage.surveyls_title'),
                'current'=>$this->get('nextSurvey', 'Survey', $surveyId,null),
209
                'help' => $this->_getHelpFoSurveySetting($surveyId,$this->get('nextSurvey', 'Survey', $surveyId,null)),
210
            ),
211 212 213
            'findExistingLink' => array(
                'type' => 'boolean',
                'label' => $this->gT("Update existing response if exist."),
214 215
                'help' => $this->gT("If you check this settings and a chain already exist with this respone, previous response was updated, else a new empty response was updated. If you want to keep history : disable this setting."),
                'current'=>$this->get('findExistingLink', 'Survey', $surveyId,1),
216 217
                'current'=>$this->get('findExistingLink', 'Survey', $surveyId,1),
            ),
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
            'nextEmail' => array(
                'type' => 'string',
                'label' => $this->gT("Send email to"),
                'help' => $this->gT("You can use Expression Manager with question code"),
                'current'=>$this->get('nextEmail', 'Survey', $surveyId,null),

            ),
            'nextMessage' => array(
                'type' => 'select',
                'label' => $this->gT("Mail template to use"),
                'htmlOptions'=>array(
                    'empty'=>$this->gT("Invitation (Default)"),
                ),
                'options'=>array(
                    "invite" => $this->gT("Invitation"),
                    "remind" => $this->gT("Reminder"),
                    "register" => $this->gT("Register"),
                    "admin_notification" => $this->gT("Admin notification"),
                    "admin_responses" => $this->gT("Admin detailed response"),
                ),
                'current'=>$this->get('nextMessage', 'Survey', $surveyId,null),
            ),
        );
        $aSettings[$this->gT("Next survey")] = $aNextSettings;
        $oQuestionCriteria = new CDbCriteria();
        $oQuestionCriteria->condition = "t.sid =:sid and t.language=:language and parent_qid = 0";
        $oQuestionCriteria->params = array(":sid"=>$surveyId,":language"=>$oSurvey->language);
        $oQuestionCriteria->order = "group_order ASC, question_order ASC";
        $oQuestionCriteria->addInCondition("type",['L','O','!']);
        $oaQuestion = Question::model()->with('groups')->findAll($oQuestionCriteria);
248

249 250 251 252 253 254
        $aNextQuestionSettings = array(
            'choiceQuestion' => array(
                'type'=>'select',
                'htmlOptions'=>array(
                    'empty'=>$this->gT("None"),
                ),
255
                'label'=>$this->gT("Question determining the following survey"),
256 257 258 259 260
                'options'=>CHtml::listData($oaQuestion,'title',
                    function($oQuestion) {
                        return "[".$oQuestion->title."] ".viewHelper::flatEllipsizeText($oQuestion->question,1,40,"…");
                    }
                ),
261
                'current'=>$this->get('choiceQuestion', 'Survey', $surveyId,null),
262
                'help' => $this->gT("Only single choice question type can be used for survey selection. The list of available answer update after save this settings."),
263 264
            ),
        );
265
        $aSettings[$this->gT("Surveys determined by a question inside this survey")] = $aNextQuestionSettings;
266

267 268 269 270 271
        /* Text for default */
        $sDefaultText = $this->gT("None");
        if(!empty($oNextSurvey)) {
            $sDefaultText = $this->gT("Current default");
        }
272 273 274 275 276 277 278 279 280 281 282 283
        if($this->get('choiceQuestion', 'Survey', $surveyId,null)) {
            $title = $this->get('choiceQuestion', 'Survey', $surveyId,null);
            $oQuestion = Question::model()->find("title=:title and language=:language",array(":title"=>$title,":language"=>$oSurvey->language));
            $aoAnswers = Answer::model()->findAll(array(
                'condition' => "qid=:qid and language=:language",
                'order' => 'sortorder ASC',
                'params' => array(":qid"=>$oQuestion->qid,":language"=>$oSurvey->language)
            ));
            foreach($aoAnswers as $oAnswers) {
                $code = $oAnswers->code;
                $aNextSettings = array(
                    'nextSurvey_'.$code => array(
284 285 286
                        'type' => 'select',
                        'htmlOptions' => array(
                            'empty' => $sDefaultText,
287
                        ),
288 289
                        'label' => $this->gT("Next survey according to the choice"),
                        'options' =>CHtml::listData($aWholeSurveys,'sid','defaultlanguage.surveyls_title'),
290
                        'current'=> intval($this->get('nextSurvey_'.$code, 'Survey', $surveyId,null)),
291
                        'help' => $this->_getHelpFoSurveySetting($surveyId,$this->get('nextSurvey_'.$code, 'Survey', $surveyId,null)),
292
                    ),
293 294 295 296 297
                    'findExistingLink_'.$code => array(
                        'type' => 'boolean',
                        'label' => $this->gT("Update existing response if exist."),
                        'current'=>$this->get('findExistingLink_'.$code, 'Survey', $surveyId,1),
                    ),
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
                    'nextEmail_'.$code => array(
                        'type' => 'string',
                        'label' => $this->gT("Send email to"),
                        'help' => $this->gT("You can use Expression Manager with question code"),
                        'current'=>$this->get('nextEmail_'.$code, 'Survey', $surveyId,null),

                    ),
                    'nextMessage_'.$code => array(
                        'type' => 'select',
                        'label' => $this->gT("Mail template to use"),
                        'htmlOptions'=>array(
                            'empty'=>$this->gT("Invitation (Default)"),
                        ),
                        'options'=>array(
                            "invite" => $this->gT("Invitation"),
                            "remind" => $this->gT("Reminder"),
                            "register" => $this->gT("Register"),
                            "admin_notification" => $this->gT("Admin notification"),
                            "admin_responses" => $this->gT("Admin detailed response"),
                        ),
                        'current'=>$this->get('nextMessage_'.$code, 'Survey', $surveyId,null),
                    ),
                );
321
                //~ tracevar($aNextSettings);
322
                $aSettings[sprintf($this->gT("Next survey for %s (%s)"),$code,viewHelper::flatEllipsizeText($oAnswers->answer,1,60,"…"))] = $aNextSettings;
323 324 325 326 327 328 329 330 331
            }

        }

        $aData['pluginClass']=get_class($this);
        $aData['surveyId']=$surveyId;
        $aData['title']=$this->gT("Survey chaining settings");
        $aData['aSettings']=$aSettings;
        $aData['assetUrl']=Yii::app()->assetManager->publish(dirname(__FILE__) . '/assets/');
332 333 334 335
        if(App()->getConfig("debug")) {
            $aData['assetUrl'] = Yii::app()->request->getBaseUrl()."/plugins/surveyChaining/assets";
        }

336 337
        $aSettings=array();
        $content = $this->renderPartial('settings', $aData, true);
338

339 340
        return $content;
    }
341 342 343 344 345 346 347 348 349 350

    /**
     * Action to do when survey is completed
     */
    public function afterSurveyComplete()
    {
        $nextSurvey = $oNextSurvey = null;
        $surveyId = $this->getEvent()->get('surveyId');
        $responseId = $this->getEvent()->get('responseId');
        $choiceQuestion = $this->get('choiceQuestion', 'Survey', $surveyId,null);
351 352
        $nextEmailSetting = 'nextEmail';
        $nextMessageSetting = 'nextMessage';
353
        $existingLinkSetting = 'findExistingLink';
354

355 356
        $currentResponse = $this->pluginManager->getAPI()->getResponse($surveyId, $responseId);
        $currentChoice = null;
357 358
        if($choiceQuestion) {
            // Find current value of choiceQuestion -if exist)
359 360 361 362 363 364
            if(!empty($currentResponse[$choiceQuestion])) {
                $currentChoice = strval($currentResponse[$choiceQuestion]);
                $nextSurvey = $this->get('nextSurvey_'.$currentChoice, 'Survey', $surveyId,null);
                if($nextSurvey) {
                    $nextEmailSetting = 'nextEmail_'.$currentChoice;
                    $nextMessageSetting = 'nextMessage_'.$currentChoice;
365
                    $existingLinkSetting = 'findExistingLink_'.$currentChoice;
366 367
                }
            }
368 369 370 371 372 373 374
        }
        if(!$nextSurvey) {
            $nextSurvey = $this->get('nextSurvey', 'Survey', $surveyId,null);
        }
        if(!$nextSurvey) {
            return;
        }
375 376

        $this->log($this->gT("Survey selected for $surveyId : $nextSurvey"),\CLogger::LEVEL_INFO);
377 378
        $oNextSurvey = Survey::model()->findByPk($nextSurvey);
        if(!$oNextSurvey) {
379
            $this->log($this->gT("Invalid survey selected for $surveyId (didn{t exist)"),\CLogger::LEVEL_WARNING);
380 381
            return;
        }
382
        if(!$oNextSurvey->getHasTokensTable() && !$this->_reloadAnyResponseExist()) {
383
            $this->log($this->gT("Invalid survey selected for $surveyId (No token table) and reloadAnyResponse plugin not installed."),\CLogger::LEVEL_WARNING);
384 385
            return;
        }
386
        $sEmail = LimeExpressionManager::ProcessStepString($this->get($nextEmailSetting, 'Survey', $surveyId,""),array(),3,1);
387
        /* Ok we get here : do action */
388
        //~ $oSurvey = Survey::model()->findByPk($surveyId);
389
        //~ $currentColumnsToCode = \surveyChaining\surveyCodeHelper::getColumnsToCode($surveyId);
390
        $nextCodeToColumn =  array_flip(\surveyChaining\helpers\surveyCodeHelper::getColumnsToCode($nextSurvey));
391 392
        $nextExistingCodeToColumn = array_intersect_key($nextCodeToColumn,$currentResponse);
        if(empty($nextExistingCodeToColumn)) {
393
            $this->log($this->gT("No question code corresponding for $surveyId"),\CLogger::LEVEL_WARNING);
394 395
            return;
        }
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        $nextsrid = null;
        if($this->get($existingLinkSetting, 'Survey', $surveyId,1)) {
            /* Find if previous response */
            $chainingResponseLink = \surveyChaining\models\chainingResponseLink::model()->find(
                "prevsid = :prevsid AND nextsid = :nextsid AND prevsrid = :prevsrid",
                array(':prevsid'=>$nextSurvey,':nextsid'=>$surveyId,':prevsrid'=>$responseId)
            );
            if(!empty($chainingResponseLink)) {
                $nextsrid = $chainingResponseLink->nextsrid;
            }
            /* If don't have : get the inverse */
            if(empty($chainingResponseLink)) {
                $chainingResponseLinkInverse = \surveyChaining\models\chainingResponseLink::model()->find(
                    "prevsid = :prevsid AND nextsid = :nextsid AND nextsrid = :nextsrid",
                    array(':prevsid'=>$nextSurvey,':nextsid'=>$surveyId,':nextsrid'=>$responseId)
                );
                if(!empty($chainingResponseLinkInverse)) {
                    $nextsrid = $chainingResponseLinkInverse->prevsrid;
                }
            }
        }
417
        $oResponse = null;
418 419 420 421 422 423 424 425 426 427 428 429 430
        if($nextsrid) {
            $oResponse = Response::model($nextSurvey)->findByPk($nextsrid);
            if(empty($oResponse)) {
                $this->log($this->gT("A chaining between $surveyId and $nextSurvey but {$chainingResponseLink->nextsrid} not found. We delete all links."),\CLogger::LEVEL_WARNING);
                \surveyChaining\models\chainingResponseLink::model()->deleteAll(
                    "prevsid = :prevsid AND nextsid = :nextsid AND prevsrid = :prevsrid",
                    array(':prevsid'=>$nextSurvey,':nextsid'=>$surveyId,':prevsrid'=>$responseId)
                );
                \surveyChaining\models\chainingResponseLink::model()->deleteAll(
                    "prevsid = :prevsid AND nextsid = :nextsid AND nextsrid = :nextsrid",
                    array(':prevsid'=>$nextSurvey,':nextsid'=>$surveyId,':nextsrid'=>$responseId)
                );
            }
431
        }
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
        $oToken = null;
        if($oNextSurvey->getHasTokensTable() && !$oNextSurvey->getIsAnonymized()) {
            /* find token */
            if($oResponse) {
                $oToken = Token::model($nextSurvey)->find("token = :token",array(':token'=>$oResponse->token));
                $oToken->email = $sEmail;
            }
            /* Else create the token */
            if(!$oToken) {
                /* To set attributes ? */
                $aAttributes = array();
                $oToken = $this->_createToken($nextSurvey,$sEmail,$aAttributes);
            }
        }
        /* Create response */
        if (!$oResponse) {
            $oResponse = Response::create($nextSurvey);
449 450
        }
        foreach($nextExistingCodeToColumn as $code=>$column) {
451
            $oResponse->$column = $currentResponse[$code];
452
        }
453 454 455 456
        $oResponse->startlanguage = App()->getLanguage();
        if($oToken && !$oNextSurvey->getIsAnonymized()) {
            $oResponse->token = $oToken->token;
        }
457
        if(!$oResponse->save()) {
458 459
            $this->log("Unable to save response for survey {$surveyId}, response {$responseId} for $nextSurvey",\CLogger::LEVEL_ERROR);
            $this->log(CVarDumper::dumpAsString($oResponse->getErrors()),CLogger::LEVEL_ERROR);
460 461
            return;
        }
462

463 464 465 466 467 468 469 470 471 472 473 474
        /* save links between responses */
        if(!$chainingResponseLink) {
            $chainingResponseLink = new \surveyChaining\models\chainingResponseLink;
            $chainingResponseLink->prevsid = $surveyId;
            $chainingResponseLink->prevsrid = $responseId;
            $chainingResponseLink->nextsid = $nextSurvey;
        }
        $chainingResponseLink->nextsrid = $oResponse->id;
        if(!$chainingResponseLink->save()) {
            $this->log("Unable to save response link for {$oResponse->id} survey {$surveyId} linked with response {$responseId} for $nextSurvey",\CLogger::LEVEL_ERROR);
            $this->log(CVarDumper::dumpAsString($chainingResponseLink->getErrors()),CLogger::LEVEL_ERROR);
        }
475 476
        /* Get email and send */
        $nextMessage = $this->get($nextMessageSetting, 'Survey', $surveyId, "invite");
477 478 479
        if($currentChoice && $nextMessage==='') {
            $nextMessage = $this->get('nextMessage', 'Survey', $surveyId, "invite");
        }
480 481 482
        if($nextMessage==='') {
            $nextMessage = 'invite';
        }
483

484
        if($oNextSurvey->getHasTokensTable() && !$oNextSurvey->getIsAnonymized() && $oToken) {
485
            if($this->_sendSurveyChainingTokenEmail($nextSurvey,$oToken,$nextMessage,$oResponse->id)) {
486 487 488 489
                // All done
                return;
            }
        }
490
        if($this->_reloadAnyResponseExist()) {
491
            $this->_sendSurveyChainingReloadEmail($nextSurvey,$oResponse->id,$sEmail,$nextMessage,$oToken);
492 493
        } else {
            $this->log("reloadAnyResponse not activated for surveyChaining, can create next reponse",'error');
494 495 496
        }
    }

497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
    /**
     * send email with SURVEYURL to new survey using reloadAnyResponse plugin
     * @param integer $iSurvey
     * @param integer $iResponse
     * @param string $sEmail
     * @param string $mailType
     * @throw Exception
     * @return boolean
     */
    private function _sendSurveyChainingReloadEmail($nextSurvey,$iResponse,$sEmail,$mailType = 'invite')
    {
        $oNextSurvey = Survey::model()->findByPk($nextSurvey);
        if($oNextSurvey->getHasTokensTable()) {
            /* Always create token */
            $oToken = $this->_createToken($nextSurvey);
            /* Set token to exiting response if needed */
            if(!$oNextSurvey->getIsAnonymized()) {
                $oResponse = Response::model($nextSurvey)->findByPk($iResponse);
                if($oResponse && $oResponse->token) {
                    $oToken->token = $oResponse->token;
                    $oToken->save();
                }
            }
        }
        $aReplacement = array();
        /* Get survey link */
        $token = isset($oToken->token) ? $oToken->token : null;
        $responseLink = \reloadAnyResponse\models\responseLink::setResponseLink($nextSurvey,$iResponse,$token);
525
        if($responseLink->hasErrors()) {
526 527 528 529 530 531 532 533 534 535 536 537 538
            $this->log("Unable to save response reload link for {$iResponse} survey {$nextSurvey}",\CLogger::LEVEL_ERROR);
            $this->log(CVarDumper::dumpAsString($responseLink->getErrors()),CLogger::LEVEL_ERROR);
            return false;
        }
        $aReplacements = array();
        $aReplacements["SURVEYURL"] = $responseLink->getStartUrl();
        $this->log("Try to send an email to $sEmail for $nextSurvey with responseLink",\CLogger::LEVEL_INFO);
        if($this->_sendSurveyChainingEmail($nextSurvey,$sEmail,Yii::app()->getLanguage(),$mailType,$aReplacements) ) {
            return true;
        }
        return false;
    }

539 540 541
    /**
     * send email with SURVEYURL to new survey
     * @param integer $iSurvey
542
     * @param \Tokent $oToken \Token
543
     * @param string $mailType
544
     * @throw Exception
545 546
     * @return boolean
     */
547
    private function _sendSurveyChainingTokenEmail($nextSurvey,$oToken,$mailType = 'invite',$srid=null)
548 549 550 551 552 553 554
    {
        $sToken = $oToken->token;
        $sLanguage = $oToken->language;
        $aReplacements = array();
        foreach($oToken->attributes as $attribute=>$value){
            $aReplacements[strtoupper($attribute)]=$value;
        }
555
        $aReplacements["SURVEYURL"] = Yii::app()->getController()->createAbsoluteUrl("/survey/index",array('sid'=>$nextSurvey,'lang'=>$sLanguage,'token'=>$sToken,'srid'=>$srid));
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
        if($this->_sendSurveyChainingEmail($nextSurvey,$oToken->email,$sLanguage,$mailType,$aReplacements) ) {
            /* @todo did we need to test sent ? */
            $oToken->sent = dateShift(date("Y-m-d H:i:s"), "Y-m-d H:i", App()->getConfig("timeadjust"));
            $oToken->save();
            return true;
        }
        return false;
    }

    /**
     * function to send email
     * @param int $nextSurvey
     * @param string $language
     * @param string $sSendTo email or list of emails separated by ;
     * @param string $mailType
     * @param array $aReplacements
     * @return boolean (success or not)
     */
    private function _sendSurveyChainingEmail($nextSurvey,$sSendTo,$sLanguage = '',$mailType = 'invite',$aReplacements = array()) {
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
        global $maildebug;
        if(!in_array($mailType,array('invite','remind','register','confirm','admin_notification','admin_responses')) ) {
            if(defined('YII_DEBUG') && YII_DEBUG) {
                throw new Exception("Invalid mail type set ({$mailType}).");
            }
            return false;
        }
        $oSurvey = Survey::model()->findByPk($nextSurvey);
        if(!in_array($sLanguage,$oSurvey->getAllLanguages())) {
            $sLanguage = $oSurvey->language;
        }
        $oSurveyLanguage = SurveyLanguageSetting::model()->findByPk(array('surveyls_survey_id'=>$nextSurvey,'surveyls_language'=>$sLanguage));

        $attSubject = 'surveyls_email_'.$mailType.'_subj';
        $attMessage = 'surveyls_email_'.$mailType;
        $sSubject = $oSurveyLanguage->$attSubject;
        $sMessage = $oSurveyLanguage->$attMessage;
592 593
        $useHtmlEmail = $oSurvey->getIsHtmlEmail();
        $aReplacementFields=$aReplacements;
594 595 596 597 598
        $aReplacementFields["ADMINNAME"]=$oSurvey->admin;
        $aReplacementFields["ADMINEMAIL"]=$oSurvey->adminemail;
        $aReplacementFields["SURVEYNAME"]=$oSurveyLanguage->surveyls_title;
        $aReplacementFields["SURVEYDESCRIPTION"]=$oSurveyLanguage->surveyls_description;
        $aReplacementFields["EXPIRY"]=$oSurvey->expires;
599 600
        if(isset($aReplacementFields["SURVEYURL"])) {
            $url = $aReplacementFields["SURVEYURL"];
601
            if ($useHtmlEmail) {
602
                $aReplacementFields["SURVEYURL"] = "<a href='{$url}'>" . htmlspecialchars($url) . '</a>';
603
            }
604 605 606 607
            $sSubject = str_replace("@@SURVEYURL@@", $url, $sSubject);
            $sMessage = str_replace("@@SURVEYURL@@", $url, $sMessage);
        } // Send a warning if not set ?

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
        $sSubject = LimeExpressionManager::ProcessStepString($sSubject,$aReplacementFields,3,1);
        $sMessage = LimeExpressionManager::ProcessStepString($sMessage,$aReplacementFields,3,1);
        $mailFromName = $oSurvey->admin;
        $mailFromMail = empty($oSurvey->adminemail) ? App()->getConfig('siteadminemail') : $oSurvey->adminemail;
        $sFrom = !empty($mailFromName) ? "{$mailFromName} <{$mailFromMail}>" : $mailFromMail;
        $sBounce=$oSurvey->bounce_email;
        $aRecipient = explode(";", LimeExpressionManager::ProcessStepString($sSendTo,$aReplacementFields,3,1));
        foreach ($aRecipient as $sRecipient) {
            $sRecipient = trim($sRecipient);
            if (validateEmailAddress($sRecipient)) {
                $aEmailTo[] = $sRecipient;
            }
        }
        $sended = false;
        if(!empty($aEmailTo)) {
            foreach($aEmailTo as $sEmail) {
624
                $this->log("SendEmailMessage to $sEmail",\CLogger::LEVEL_TRACE);
625 626
                if(SendEmailMessage($sMessage, $sSubject, $sEmail, $sFrom, App()->getConfig("sitename"), $useHtmlEmail, $sBounce)) {
                    $sended = true;
627 628
                } else {
                    $this->log($this->gT("Unable to send email with debug : {$maildebug}"),\CLogger::LEVEL_ERROR);
629 630 631
                }
            }
        }
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
        return $sended;
    }
    /**
     * Create a token for a survey
     * @param integer $nextSurvey id
     * @param string $email
     * @param array $aAttributes to create
     * @return \Token|null
     */
    private function _createToken($nextSurvey,$email="",$aAttributes=array())
    {
        $oToken = Token::create($nextSurvey);
        $oToken->validfrom = date("Y-m-d H:i:s");
        $oToken->email = $email;
        $oToken->generateToken();
        $language = App()->getLanguage();
        if(!in_array($language,Survey::model()->findByPk($nextSurvey)->getAllLanguages())) {
            $language = Survey::model()->findByPk($nextSurvey)->language;
650
        }
651 652 653 654 655 656 657
        $oToken->language = $language;
        /* @todo : set attribute */
        if(!$oToken->save()) {
            $this->log($this->gT("Unable to create token for $nextSurvey"),\CLogger::LEVEL_WARNING);
            return null;
        }
        return $oToken;
658
    }
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
    /**
     * Update some Yii and LS config
     * Set some var
     */
    private function _setConfig()
    {
        Yii::setPathOfAlias('surveyChaining', dirname(__FILE__));
    }

  /**
   * @inheritdoc adding string, by default current event
   * @param string
   */
  public function log($message, $level = \CLogger::LEVEL_TRACE,$logDetail = null)
  {
    if(!$logDetail && $this->getEvent()) {
      $logDetail = $this->getEvent()->getEventName();
    } // What to put if no event ?
    //parent::log($message, $level);
    Yii::log($message, $level,'application.plugins.'.get_class($this).".".$logDetail);
  }

  /**
   * set and fix DB and table
   * @return void
   */
  private function _setDb()
  {
    if($this->get('dbversion',null,null,0) >= $this->dbversion) {
      return;
    }
    if (!$this->api->tableExists($this, 'chainingResponseLink')) {
      $this->api->createTable($this, 'chainingResponseLink', array(
          'id' => 'pk',
          'prevsid'=>'int',
          'prevsrid'=>'int',
          'nextsid'=>'int',
          'nextsrid'=>'int',
      ));
      $this->set('dbversion',$this->dbversion);
      Notification::broadcast(array(
          'title' => gT('Database update'),
          'message' => sprintf($this->gT('The database for plugin %s has been created (version %s).'),get_class($this),$this->dbversion),
          'display_class' => 'success',
      ),User::model()->getSuperAdmins());
      $this->log(sprintf('The database for plugin %s has been created (version %s).',get_class($this),$this->dbversion),\CLogger::LEVEL_INFO);
      return; // No need upgrade if created
    }
    /* Not used currently, adding function when need update */
    $this->set('dbversion',$this->dbversion);
    Notification::broadcast(array(
        'title' => gT('Database update'),
        'message' => sprintf($this->gT('The database for plugin %s has been upgraded to version %s.'),get_class($this),$this->dbversion),
        'display_class' => 'success',
    ),User::model()->getSuperAdmins());
    $this->log(sprintf('The database for plugin %s has been upgraded to version %s.',get_class($this),$this->dbversion),\CLogger::LEVEL_INFO);
  }
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732

    /**
     * Get the help for a survey in settings
    * @param $surveyId
    * @param $selectedSurveyId
    * @return null|string html to be shown
    */
    private function _getHelpFoSurveySetting($surveyId,$selectedSurveyId)
    {
        if(!$selectedSurveyId) {
            return null;
        }
        $oNextSurvey = Survey::model()->findByPk($selectedSurveyId);
        if(!$oNextSurvey) {
            return CHtml::tag("div",array('class'=>"text-warning"),$this->gT("Warning : previous survey selected don't exist currently."));
        }
        $aStringReturn = array();
733 734
        $surveyLink = CHtml::link($this->gT("survey selected"),array('admin/survey/sa/view','surveyid'=>$selectedSurveyId));
        tracevar($surveyLink);
735
        if(!$oNextSurvey->getHasTokensTable() && !$this->_reloadAnyResponseExist()) {
736
            $aStringReturn[] = CHtml::tag("div",array('class'=>"text-danger"),sprintf($this->gT("Warning : current %s don't have token table, you must enable token before."),$surveyLink));
737 738
        }
        if($oNextSurvey->getHasTokensTable() && $oNextSurvey->tokenanswerspersistence!="Y" && $this->_reloadAnyResponseExist()) {
739
            $aStringReturn[] = CHtml::tag("div",array('class'=>"text-danger"),sprintf($this->gT("Warning : current %s don't have token answer persistance enable."),$surveyLink));
740 741 742
        }
        $aSameCodes = $this->_getSameCodes($surveyId,$selectedSurveyId);
        if(empty($aSameCodes)) {
743
            $aStringReturn[] = CHtml::tag("div",array('class'=>"text-danger"),sprintf($this->gT("Warning : %s and current survey didn't have any correspondig question."),$surveyLink));
744
        } else {
745
            $aStringReturn[] = CHtml::tag("div",array('class'=>"text-info"),sprintf($this->gT("The %s and current survey have this correspondig question: %s"),$surveyLink,implode(",",$aSameCodes)));
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
            /* Find if answersAsReadonly is activated */
            $oAnswersAsReadonly = Plugin::model()->find("name = :name",array(":name"=>'answersAsReadonly'));
            if ($oAnswersAsReadonly && $oAnswersAsReadonly->active) {
                /* Link broke lsadminpanel … */
                /* @todo : control if user have permission on $selectedSurveyId */
                if(Permission::model()->hasSurveyPermission($selectedSurveyId,'surveysettings','update')){
                    $aStringReturn[] = CHtml::link(
                        $this->gT("Set common question to read only"),
                        array("plugins/direct",'plugin' => get_class(),'sid'=>$surveyId,'destsid'=>$selectedSurveyId),
                        array('class'=>'btn btn-warning btn-xs ajax-surveychaining')
                    );
                } else {
                    $aStringReturn[] = CHtml::tag("div",array('class'=>"text-warning"),$this->gT("Warning : You don't have enough permission on selected survey."));
                }
                //~ $url = Yii::app()->createUrl("plugins/direct", array('plugin' => get_class(),'sid'=>$surveyId));
                //~ $aStringReturn[] = CHtml::htmlButton(
                    //~ $this->gT("Set common question to read only"),
                    //~ array('class'=>'btn btn-warning btn-xs ajax-surveychaining','data-url'=>$url)//)
                //~ );
            }
766 767 768 769 770 771 772
        }
        return implode("\n",$aStringReturn);
    }
    /**
    * Get corresponding questioon code correponding for 2 surveys
    * @param $firstSurveyId
    * @param $secondSurveyId
773
    * @return string[] key is column of 1st survey, value is EM code of question
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    */
    private function _getSameCodes($firstSurveyId,$secondSurveyId)
    {
        $firstSurveyCodes =  \surveyChaining\helpers\surveyCodeHelper::getColumnsToCode($firstSurveyId);
        $secondSurveyCodes =  \surveyChaining\helpers\surveyCodeHelper::getColumnsToCode($secondSurveyId);
        return array_intersect($firstSurveyCodes,$secondSurveyCodes);
    }

    /**
     * Check if reloadResponseExist
     */
    private function _reloadAnyResponseExist()
    {
        return (bool) Yii::getPathOfAlias('reloadAnyResponse');
    }
789 790 791 792 793 794 795 796 797 798 799 800 801 802

    /**
     * render json
     * @param mixed
     * @return void
     */
    private function _renderJson($data=null) {
        if(is_array($data)) {
            $data = array_merge(array('hasPermission'=>true,'loggedIn'=>true),$data);
        }
        header('Content-type: application/json; charset=utf-8');
        echo json_encode($data);
        Yii::app()->end();
    }
803
}