HEX
Server: Apache
System: Linux WWW 6.1.0-40-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20) x86_64
User: web11 (1011)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/apklausos/application/models/services/Quotas.php
<?php

namespace LimeSurvey\Models\Services;

use CHtml;
use LimeSurvey\PluginManager\PluginEvent;
use LimeSurvey\PluginManager\PluginEventContent;
use QuestionAttribute;
use Quota;
use Response;
use Survey;

/**
 * @todo Possible remove this warning
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
 */
class Quotas
{
    /** @var \Survey the survey */
    private $survey;


    /**
     * Quicktranslation constructor.
     *
     * @param \Survey $survey the survey object
     *
     */
    public function __construct(\Survey $survey)
    {
        $this->survey = $survey;
    }


    /**
     * In the array are
     * qoutaId[
     *   aQuotaItems = [
     *      oQuestion,
     *      answerTitle,
     *      quotaMember
     *      valid ???
     *   ]
     * ]
     *
     * @return array
     */
    public function getQuotaStructure()
    {
        $totalquotas = 0;
        $totalcompleted = 0;
        if (!empty($this->survey->quotas)) {
            $aQuotaItems = array();
            //loop through all quotas
            foreach ($this->survey->quotas as $oQuota) {
                $totalquotas += $oQuota->qlimit;
                $totalcompleted += $oQuota->completeCount;

                //loop through all quotaMembers
                foreach ($oQuota->quotaMembers as $oQuotaMember) {
                    $aQuestionAnswers = self::getQuotaAnswers($oQuotaMember['qid'], $oQuota['id']);
                    if ($oQuotaMember->question->type == '*') {
                        $answerText = $oQuotaMember->code;
                    } else {
                        $answerText = isset($aQuestionAnswers[$oQuotaMember['code']]) ? flattenText($aQuestionAnswers[$oQuotaMember['code']]['Display']) : null;
                    }

                    $aQuotaItems[$oQuota['id']][] = array(
                        'oQuestion' => \Question::model()
                            ->with('questionl10ns', array('language' => $this->survey->language))
                            ->findByPk(array('qid' => $oQuotaMember['qid'])),
                        'answer_title' => $answerText,
                        'oQuotaMember' => $oQuotaMember,
                        'valid' => isset($answerText),
                    );
                }
            }
            $aData['aQuotaItems'] = $aQuotaItems;

            // take the last quota as base for bulk edits
            $aData['oQuota'] = $oQuota;
            $aData['aQuotaLanguageSettings'] = array();
            foreach ($oQuota->languagesettings as $languagesetting) {
                $aData['aQuotaLanguageSettings'][$languagesetting->quotals_language] = $languagesetting;
            }
        }
        $aData['totalquotas'] = $totalquotas;
        $aData['totalcompleted'] = $totalcompleted;

        return $aData;
    }

    /**
     * Returns an answerlist for a specific question type and marks already used answers as such.
     *
     * @todo Refactor and remove phpmd warning
     * @param integer $iQuestionId
     * @param integer $iQuotaId
     * @return array
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function getQuotaAnswers(int $iQuestionId, int $iQuotaId)
    {
        $iQuestionId = sanitize_int($iQuestionId);
        $iQuotaId    = sanitize_int($iQuotaId);
        $sBaseLang = $this->survey->language;

        $aQuestion = \Question::model()
            ->with('questionl10ns', array('language' => $sBaseLang))
            ->findByPk(array('qid' => $iQuestionId));
        $aQuestionType = $aQuestion['type'];
        $aAnswerList = [];
        switch ($aQuestionType) {
            case \Question::QT_M_MULTIPLE_CHOICE:
                $aResults = \Question::model()
                    ->with('questionl10ns', array('language' => $sBaseLang))
                    ->findAllByAttributes(array('parent_qid' => $iQuestionId));
                foreach ($aResults as $oDbAnsList) {
                    $tmparrayans = array('Title' => $aQuestion['title'],
                        'Display' => substr((string) $oDbAnsList->questionl10ns[$sBaseLang]->question, 0, 40),
                        'code' => $oDbAnsList->title);
                    $aAnswerList[$oDbAnsList->title] = $tmparrayans;
                }
                break;
            case \Question::QT_G_GENDER:
                $aAnswerList = array(
                    'M' => array('Title' => $aQuestion['title'], 'Display' => gT("Male"), 'code' => 'M'),
                    'F' => array('Title' => $aQuestion['title'], 'Display' => gT("Female"), 'code' => 'F'));
                break;
            case \Question::QT_L_LIST:
            case \Question::QT_O_LIST_WITH_COMMENT:
            case \Question::QT_EXCLAMATION_LIST_DROPDOWN:
                $aAnsResults = \Answer::model()
                ->with('answerl10ns', array('language' => $sBaseLang))
                ->findAllByAttributes(array('qid' => $iQuestionId));

                foreach ($aAnsResults as $aDbAnsList) {
                    $aAnswerList[$aDbAnsList['code']] = array('Title' => $aQuestion['title'],
                    'Display' => $aDbAnsList->answerl10ns[$sBaseLang]->answer,
                    'code' => $aDbAnsList['code']);
                }
                break;
            case \Question::QT_A_ARRAY_5_POINT:
                $aAnsResults = \Question::model()
                    ->with('questionl10ns', array('language' => $sBaseLang))
                    ->findAllByAttributes(array('parent_qid' => $iQuestionId));

                foreach ($aAnsResults as $aDbAnsList) {
                    for ($x = 1; $x < 6; $x++) {
                        $tmparrayans = array('Title' => $aQuestion['title'],
                            'Display' => substr((string) $aDbAnsList->questionl10ns[$sBaseLang]->question, 0, 40) . ' [' . $x . ']',
                            'code' => $aDbAnsList['title']);
                        $aAnswerList[$aDbAnsList['title'] . "-" . $x] = $tmparrayans;
                    }
                }
                break;
            case \Question::QT_B_ARRAY_10_CHOICE_QUESTIONS:
                $aAnsResults = \Question::model()
                    ->with('questionl10ns', array('language' => $sBaseLang))
                    ->findAllByAttributes(array('parent_qid' => $iQuestionId));

                foreach ($aAnsResults as $aDbAnsList) {
                    for ($x = 1; $x < 11; $x++) {
                        $tmparrayans = array('Title' => $aQuestion['title'],
                            'Display' => substr((string) $aDbAnsList->questionl10ns[$sBaseLang]->question, 0, 40) . ' [' . $x . ']',
                            'code' => $aDbAnsList['title']);
                        $aAnswerList[$aDbAnsList['title'] . "-" . $x] = $tmparrayans;
                    }
                }
                break;
            case \Question::QT_Y_YES_NO_RADIO:
                $aAnswerList = array(
                    'Y' => array('Title' => $aQuestion['title'], 'Display' => gT("Yes"), 'code' => 'Y'),
                    'N' => array('Title' => $aQuestion['title'], 'Display' => gT("No"), 'code' => 'N'));
                break;
            case \Question::QT_I_LANGUAGE:
                $slangs = $this->survey->allLanguages;

                foreach ($slangs as $key => $value) {
                    $tmparrayans = array('Title' => $aQuestion['title'],
                        'Display' => getLanguageNameFromCode($value, false), $value);
                    $aAnswerList[$value] = $tmparrayans;
                }
                break;
        }

        if (!empty($aAnswerList)) {
            // Now we mark answers already used in this quota as such
            $aExistsingAnswers = \QuotaMember::model()->findAllByAttributes(array('sid' => $this->survey->sid,
                'qid' => $iQuestionId, 'quota_id' => $iQuotaId));
            foreach ($aExistsingAnswers as $aAnswerRow) {
                if (array_key_exists($aAnswerRow['code'], $aAnswerList)) {
                    $aAnswerList[$aAnswerRow['code']]['rowexists'] = '1';
                }
            }
        }

        return  $aAnswerList;
    }

    /**
     * Saves the new quota and it's language settings.
     *
     * @param $quotaParams array the quota attributes
     * @return Quota the new quota with added QuotaLanguageSettings, or Quota with errors
     * @throws \CDbException
     */
    public function saveNewQuota(array $quotaParams): Quota
    {
        $oQuota = new Quota();
        $oQuota->sid = $this->survey->sid;
        /* new quota : remove pk */
        unset($quotaParams['id']);
        $oQuota->attributes = $quotaParams;
        if ($oQuota->save()) {
            $postQuotaLanguageSettings = (array) App()->getRequest()->getPost('QuotaLanguageSetting');
            foreach ($postQuotaLanguageSettings as $language => $settingAttributes) {
                $oQuotaLanguageSetting = new \QuotaLanguageSetting();
                $oQuotaLanguageSetting->attributes = $settingAttributes;
                $oQuotaLanguageSetting->quotals_quota_id = $oQuota->primaryKey;
                $oQuotaLanguageSetting->quotals_language = $language;

                //Clean XSS - Automatically provided by CI
                $oQuotaLanguageSetting->quotals_message = html_entity_decode($oQuotaLanguageSetting->quotals_message, ENT_QUOTES, "UTF-8");
                // Fix bug with FCKEditor saving strange BR types
                $oQuotaLanguageSetting->quotals_message = fixCKeditorText($oQuotaLanguageSetting->quotals_message);

                $oQuotaLanguageSetting->save(false);
                if (!$oQuotaLanguageSetting->validate()) {
                    $oQuota->addErrors($oQuotaLanguageSetting->getErrors());
                }
            }
            //delete quota and language settings for this qouta if errors
            if ($oQuota->getErrors()) {
                //delete quotalanguagesettings if any for this qouta
                foreach ($oQuota->languagesettings as $languageSetting) {
                    $languageSetting->delete();
                }
                $oQuota->delete();
            }
        }

        return $oQuota;
    }

    /**
     *
     * @param Quota $oQuota
     * @param array $quotaParams
     * @return bool|mixed
     */
    public function editQuota($oQuota, array $quotaParams)
    {
        $oQuota->attributes = $quotaParams;
        if ($oQuota->save()) {
            foreach ($_POST['QuotaLanguageSetting'] as $language => $settingAttributes) {
                $oQuotaLanguageSetting = $oQuota->languagesettings[$language];
                $oQuotaLanguageSetting->attributes = $settingAttributes;

                //Clean XSS - Automatically provided by CI
                $oQuotaLanguageSetting->quotals_message = html_entity_decode($oQuotaLanguageSetting->quotals_message, ENT_QUOTES, "UTF-8");
                // Fix bug with FCKEditor saving strange BR types
                $oQuotaLanguageSetting->quotals_message = fixCKeditorText($oQuotaLanguageSetting->quotals_message);

                if (!$oQuotaLanguageSetting->save()) {
                    $oQuota->addErrors($oQuotaLanguageSetting->getErrors());
                }
            }
        }

        return $oQuota;
    }

    /**
     * Retunr
     *
     * @param Quota $oQuota
     * @param $language
     * @return \QuotaLanguageSetting
     */
    public function newQuotaLanguageSetting(Quota $oQuota, $language)
    {
        $oQuotaLanguageSetting = new \QuotaLanguageSetting();
        $oQuotaLanguageSetting->quotals_name = $oQuota->name;
        $oQuotaLanguageSetting->quotals_quota_id = $oQuota->primaryKey;
        $oQuotaLanguageSetting->quotals_language = $language;
        $oQuotaLanguageSetting->quotals_url = $this->survey->languagesettings[$language]->surveyls_url;
        $siteLanguage = \Yii::app()->language;
        // Switch language temporarily to get the default text in right language
        \Yii::app()->language = $language;
        $oQuotaLanguageSetting->quotals_message = gT("Sorry your responses have exceeded a quota on this survey.");
        \Yii::app()->language = $siteLanguage;

        return $oQuotaLanguageSetting;
    }

    /**
     * Checks if all possible answers are already selected.
     *
     * @param \Question $oQuestion
     * @param array $aQuestionAnswers  array list with possible question answers
     *                                  and already used answers (see getQuotaAnswer)
     * @return bool true if all possible answers are alreday selected, false otherwise
     */
    public function allAnswersSelected(\Question $oQuestion, array $aQuestionAnswers)
    {
        $cntQuestionAnswer = 0;
        foreach ($aQuestionAnswers as $aQACheck) {
            if (isset($aQACheck['rowexists'])) {
                $cntQuestionAnswer++;
            }
        }

        return ($oQuestion->type != "*" && count($aQuestionAnswers) == $cntQuestionAnswer);
    }

    /**
     *
     * @param integer[] $aQuotaIds
     * @param string $action
     * @param null|array $languageSettings
     * @return null|array errors or null if no errors
     * @throws \CDbException
     */
    public function multipleItemsAction($aQuotaIds, $action, $languageSettings = [])
    {
        $errors = null;
        foreach ($aQuotaIds as $iQuotaId) {
            /** @var Quota $oQuota */
            $oQuota = Quota::model()->findByPk($iQuotaId);
            if (empty($oQuota) || $oQuota->sid != $this->survey->sid) {
                $errors [] = gT("Invalid quota ID");
            }
            switch ($action) {
                case 'activate':
                case 'deactivate':
                    $oQuota->active = ($action == 'activate' ? 1 : 0);
                    if (!$oQuota->save()) {
                        $errors[] = $oQuota->errors;
                    }
                    break;
                case 'delete':
                    $oQuota->delete();
                    \QuotaLanguageSetting::model()->deleteAllByAttributes(array('quotals_quota_id' => $iQuotaId));
                    \QuotaMember::model()->deleteAllByAttributes(array('quota_id' => $iQuotaId));
                    break;
                case 'changeLanguageSettings':
                    if (!empty($languageSettings)) {
                        $oQuotaLanguageSettings = $oQuota->languagesettings;
                        foreach ($_POST['QuotaLanguageSetting'] as $language => $aQuotaLanguageSettingAttributes) {
                            $oQuotaLanguageSetting = $oQuota->languagesettings[$language];
                            $oQuotaLanguageSetting->attributes = $aQuotaLanguageSettingAttributes;
                            if (!$oQuotaLanguageSetting->save()) {
                                // save errors
                                $oQuotaLanguageSettings[$language] = $oQuotaLanguageSetting;
                                $errors[] = $oQuotaLanguageSetting->errors;
                            }
                        }
                    }
                    break;
                default:
                    $errors [] = gT('No valid action');
            }
        }

        return $errors;
    }

    /**
     * Checks for a specific action if current user has the permission for it.
     *
     * @param $action
     * @return bool true if user has permission for action, false otherwise
     */
    public function checkActionPermissions($action)
    {
        switch ($action) {
            case 'activate':
            case 'deactivate':
            case 'changeLanguageSettings':
                $permissionOk = \Permission::model()->hasSurveyPermission($this->survey->sid, 'quotas', 'update');
                break;
            case 'delete':
                $permissionOk = \Permission::model()->hasSurveyPermission(
                    $this->survey->sid,
                    'quotas',
                    'delete'
                );
                break;
            default:
                $permissionOk = false;
        }
        return $permissionOk;
    }

    /**
     * checkCompletedQuota() returns matched quotas information for the current response
     * @param int $surveyid Survey ID
     * @param array $updatedValues The fields to be updated in the current request
     * @param bool $return Set to true to return information or false to execute the quota and display a message
     * @return array|void Nested array, Quotas->Members->Fields, includes quota information matched in session
     * @throws \CException
     * @throws \CHttpException
     * @throws \Throwable
     * @throws \WrongTemplateVersionException
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public static function checkCompletedQuota(int $surveyid, array $updatedValues = [], bool $return = false)
    {
        /* Check if session is set */
        if (!isset(App()->session['survey_' . $surveyid]['srid'])) {
            return;
        }
        /* Check if Response is already submitted : only when "do" the quota: allow to send information about quota */
        $oResponse = Response::model($surveyid)->findByPk(App()->session['survey_' . $surveyid]['srid']);
        if (!$return && $oResponse && !is_null($oResponse->submitdate)) {
            return;
        }
        // EM call 2 times quotas with 3 lines of php code, then use static.
        static $aMatchedQuotas;
        if (is_null($aMatchedQuotas)) {
            $aMatchedQuotas = [];
            /** @var Quota[] $aQuotas */
            $aQuotas = Quota::model()
                ->with('languagesettings', 'quotaMembers.question')
                ->findAllByAttributes(['sid' => $surveyid]);
            if (empty($aQuotas)) {
                return $aMatchedQuotas;
            }

            // OK, we have some quota, then find if this $_SESSION have some set
            // foreach ($aQuotasInfos as $aQuotaInfo)
            foreach ($aQuotas as $oQuota) {
                // if(!$aQuotaInfo['active']) {
                if (!$oQuota->active) {
                    continue;
                }
                // if(count($aQuotaInfo['members'])===0) {
                if (count($oQuota->quotaMembers) === 0) {
                    continue;
                }
                $iMatchedAnswers = 0;
                $bPostedField = false;

                ////Create filtering
                // Array of field with quota array value
                $aQuotaFields = [];
                // Array of fieldnames with relevance value : EM fill $_SESSION with default value even is irrelevant (em_manager_helper line 6548)
                $aQuotaRelevantFieldnames = [];
                // To count number of hidden questions
                $aQuotaQid = [];
                //Fill the necessary filter arrays
                foreach ($oQuota->quotaMembers as $oQuotaMember) {
                    $aQuotaMember = $oQuotaMember->getMemberInfo();
                    $aQuotaFields[$aQuotaMember['fieldname']][] = $aQuotaMember['value'];
                    $aQuotaRelevantFieldnames[$aQuotaMember['fieldname']] = isset($_SESSION['survey_' . $surveyid]['relevanceStatus'][$aQuotaMember['qid']]) && $_SESSION['survey_' . $surveyid]['relevanceStatus'][$aQuotaMember['qid']];
                    $aQuotaQid[] = $aQuotaMember['qid'];
                }
                $aQuotaQid = array_unique($aQuotaQid);

                // Filter
                // For each field : test if actual responses is in quota (and is relevant)
                foreach ($aQuotaFields as $sFieldName => $aValues) {
                    $bInQuota = isset($_SESSION['survey_' . $surveyid][$sFieldName])
                        && in_array($_SESSION['survey_' . $surveyid][$sFieldName], $aValues);
                    if ($bInQuota && $aQuotaRelevantFieldnames[$sFieldName]) {
                        $iMatchedAnswers++;
                    }
                    if (!is_null(App()->request->getPost($sFieldName))) {
                        // Need only one posted value
                        $bPostedField = true;
                        $aPostedQuotaFields[$sFieldName] = App()->getRequest()->getPost($sFieldName);
                    }
                }

                ################ QUOTA ACTIONS START ################
                $quotaAction = (int)$oQuota->action;
                $actionBasedTriggerCondition = false;

                if ($quotaAction === Quota::TERMINATE_VISIBLE_QUOTA_QUESTIONS) {
                    // if all quota members are hidden questions, the quota will be triggered as well
                    // if we have quota members that are hidden mixed with visible ones, they will never be triggered.
                    $actionBasedTriggerCondition = (int)QuestionAttribute::model()
                            ->countByAttributes([
                                'qid'       => $aQuotaQid,
                                'attribute' => 'hidden',
                                'value'     => '1',
                            ]) === count($aQuotaQid);
                }
                if ($quotaAction === Quota::TERMINATE_VISIBLE_AND_HIDDEN_QUOTA_QUESTIONS) {
                    $actionBasedTriggerCondition = !empty(array_intersect_key($updatedValues, $aQuotaRelevantFieldnames));
                }
                if ($quotaAction === Quota::TERMINATE_ALL_PAGES) {
                    $actionBasedTriggerCondition = true;
                }
                if ($quotaAction === Quota::SOFT_TERMINATE_VISIBLE_QUOTA_QUESTIONS) {
                    $actionBasedTriggerCondition = false;
                }
                ################ QUOTA ACTIONS END ################

                // condition to count quota
                // check if all answers match the quota AND check if the question was answered on this page OR if all questions are hidden
                if (
                    $iMatchedAnswers === count($aQuotaFields)
                    && ($bPostedField || $actionBasedTriggerCondition)
                ) {
                    if ($oQuota->qlimit === 0) {
                        // Always add the quota if qlimit is 0
                        $aMatchedQuotas[] = $oQuota->getViewArray();
                    } else {
                        $iCompleted = $oQuota->completeCount;
                        if (!is_null($iCompleted) && ($iCompleted >= $oQuota->qlimit)) {
                            // This remove invalid quota and not completed
                            $aMatchedQuotas[] = $oQuota->getViewArray();
                        }
                    }
                }
            }
        }
        if ($return) {
            return $aMatchedQuotas;
        }
        if (empty($aMatchedQuotas)) {
            return;
        }
        // Now we have all the information we need about the quotas and their status.
        // We need to construct the page and do all needed action
        $aSurveyInfo = getSurveyInfo($surveyid, $_SESSION['survey_' . $surveyid]['s_lang']);
        $sClientToken = $_SESSION['survey_' . $surveyid]['token'] ?? "";
        // $redata for templatereplace
        $aDataReplacement = [
            'thissurvey'  => $aSurveyInfo,
            'clienttoken' => $sClientToken,
            'token'       => $sClientToken,
        ];
        // We take only the first matched quota, no need for each
        $aMatchedQuota = $aMatchedQuotas[0];
        // If a token is used then mark the token as completed, do it before event : this allow plugin to update token information
        $event = new PluginEvent('afterSurveyQuota');
        $event->set('surveyId', $surveyid);
        $event->set('responseId', $_SESSION['survey_' . $surveyid]['srid']); // We always have a responseId
        $event->set('aMatchedQuotas', $aMatchedQuotas); // Give all the matched quota : the first is the active
        App()->getPluginManager()->dispatchEvent($event);
        $blocks = [];
        foreach ($event->getAllContent() as $blockData) {
            /* @var $blockData PluginEventContent */
            $blocks[] = CHtml::tag(
                'div',
                ['id' => $blockData->getCssId(), 'class' => $blockData->getCssClass()],
                $blockData->getContent()
            );
        }
        // Allow plugin to update message, url, url description and action
        $sMessage = $event->get('message', $aMatchedQuota['quotals_message']);
        $sUrl = $event->get('url', $aMatchedQuota['quotals_url']);
        $sUrlDescription = $event->get('urldescrip', $aMatchedQuota['quotals_urldescrip']);
        $sAction = (int) $event->get('action', $aMatchedQuota['action']);
        // close the survey only when the action is a terminate type or when confirmquota is called as move action
        $closeSurvey = ($sAction !== Quota::SOFT_TERMINATE_VISIBLE_QUOTA_QUESTIONS || App()->getRequest()->getPost('move') === 'confirmquota');
        $sAutoloadUrl = $event->get('autoloadurl', $aMatchedQuota['autoload_url']);
        // Doing the action and show the page
        if (
            $sClientToken
            && in_array($sAction, [
                Quota::TERMINATE_VISIBLE_QUOTA_QUESTIONS,
                Quota::TERMINATE_VISIBLE_AND_HIDDEN_QUOTA_QUESTIONS,
                Quota::TERMINATE_ALL_PAGES
            ], true)
        ) {
            submittokens(true);
        }
        // Construct the default message
        $sMessage = templatereplace(
            $sMessage,
            [],
            $aDataReplacement,
            null,
            null,
            null,
            null,
            true
        );
        $sUrl = passthruReplace($sUrl, $aSurveyInfo);
        $sUrl = templatereplace(
            $sUrl,
            [],
            $aDataReplacement,
            null,
            null,
            null,
            null,
            true
        );
        $sUrlDescription = templatereplace(
            $sUrlDescription,
            [],
            $aDataReplacement,
            null,
            null,
            null,
            null,
            true
        );

        // Datas for twig view
        $thissurvey['sid'] = $surveyid;
        $thissurvey['aQuotas'] = [];
        $thissurvey['aQuotas']['sMessage'] = $sMessage;
        $thissurvey['aQuotas']['bShowNavigator'] = !$closeSurvey;
        $thissurvey['aQuotas']['sClientToken'] = $sClientToken;
        $thissurvey['aQuotas']['sQuotaStep'] = 'returnfromquota';
        $thissurvey['aQuotas']['aPostedQuotaFields'] = $aPostedQuotaFields ?? '';
        $thissurvey['aQuotas']['sPluginBlocks'] = implode("\n", $blocks);
        $thissurvey['aQuotas']['sUrlDescription'] = $sUrlDescription;
        $thissurvey['aQuotas']['sUrl'] = $sUrl;
        $thissurvey['active'] = 'Y';
        $thissurvey['aQuotas']['hiddeninputs'] = '<input type="hidden" name="sid"      value="' . $surveyid . '" />
                                              <input type="hidden" name="token"    value="' . $thissurvey['aQuotas']['sClientToken'] . '" />
                                              <input type="hidden" name="thisstep" value="' . ($_SESSION['survey_' . $surveyid]['step'] ?? 0) . '" />';
        if (!empty($thissurvey['aQuotas']['aPostedQuotaFields'])) {
            foreach ($thissurvey['aQuotas']['aPostedQuotaFields'] as $field => $post) {
                $thissurvey['aQuotas']['hiddeninputs'] .= '<input type="hidden" name="' . $field . '"   value="' . $post . '" />';
            }
        }
        //field,post in aSurveyInfo.aQuotas.aPostedQuotaFields %}
        if ($closeSurvey) {
            killSurveySession($surveyid);

            if ((int) $sAutoloadUrl === 1 && $sUrl !== "") {
                /* Same than end url of survey */
                $headToSurveyUrl = htmlspecialchars_decode($sUrl);
                header("Location: " . $headToSurveyUrl);
            }
        }
        $thissurvey['include_content'] = 'quotas';
        App()->twigRenderer->renderTemplateFromFile(
            "layout_global.twig",
            ['oSurvey' => Survey::model()->findByPk($surveyid), 'aSurveyInfo' => $thissurvey],
            false
        );
    }
}