1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/limesurvey_ynh.git synced 2024-09-03 19:36:32 +02:00
limesurvey_ynh/sources/application/models/Question.php

641 lines
27 KiB
PHP

<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/*
* LimeSurvey
* Copyright (C) 2013 The LimeSurvey Project Team / Carsten Schmitz
* All rights reserved.
* License: GNU/GPL License v2 or later, see LICENSE.php
* LimeSurvey is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*
* Files Purpose: lots of common functions
*/
class Question extends LSActiveRecord
{
/**
* Returns the static model of Settings table
*
* @static
* @access public
* @param string $class
* @return CActiveRecord
*/
public static function model($class = __CLASS__)
{
return parent::model($class);
}
/**
* Returns the setting's table name to be used by the model
*
* @access public
* @return string
*/
public function tableName()
{
return '{{questions}}';
}
/**
* Returns the primary key of this table
*
* @access public
* @return string
*/
public function primaryKey()
{
return array('qid', 'language');
}
/**
* Defines the relations for this model
*
* @access public
* @return array
*/
public function relations()
{
$alias = $this->getTableAlias();
return array(
'groups' => array(self::HAS_ONE, 'QuestionGroup', '', 'on' => "$alias.gid = groups.gid AND $alias.language = groups.language"),
'parents' => array(self::HAS_ONE, 'Question', '', 'on' => "$alias.parent_qid = parents.qid"),
'subquestions' => array(self::HAS_MANY, 'Question', 'parent_qid', 'on' => "$alias.language = subquestions.language")
);
}
/**
* Returns this model's validation rules
*
*/
public function rules()
{
$clang = Yii::app()->lang;
$aRules= array(
array('title','required','on' => 'update, insert'),// 140207 : Before was commented, put only on update/insert ?
array('title','length', 'min' => 1, 'max'=>20,'on' => 'update, insert'),
array('qid', 'numerical','integerOnly'=>true),
array('qid', 'unique', 'criteria'=>array(
'condition'=>'language=:language',
'params'=>array(':language'=>$this->language)
),
'message'=>'{attribute} "{value}" is already in use.'),
array('language','length', 'min' => 2, 'max'=>20),// in array languages ?
array('title,question,help','LSYii_Validators'),
array('other', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
array('mandatory', 'in','range'=>array('Y','N'), 'allowEmpty'=>true),
array('question_order','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
array('scale_id','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
array('same_default','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
);
if($this->parent_qid)// Allways enforce unicity on Sub question code (DB issue).
{
$aRules[]=array('title', 'unique', 'caseSensitive'=>false, 'criteria'=>array(
'condition' => 'language=:language AND sid=:sid AND parent_qid=:parent_qid and scale_id=:scale_id',
'params' => array(
':language' => $this->language,
':sid' => $this->sid,
':parent_qid' => $this->parent_qid,
':scale_id' => $this->scale_id
)
),
'message' => gT('Subquestion codes must be unique.'));
}
if($this->qid && $this->language)
{
$oActualValue=Question::model()->findByPk(array("qid"=>$this->qid,'language'=>$this->language));
if($oActualValue && $oActualValue->title==$this->title)
{
return $aRules; // We don't change title, then don't put rules on title
}
}
if(!$this->parent_qid)// 0 or empty
{
$aRules[]=array('title', 'unique', 'caseSensitive'=>true, 'criteria'=>array(
'condition' => 'language=:language AND sid=:sid AND parent_qid=0',
'params' => array(
':language' => $this->language,
':sid' => $this->sid
)
),
'message' => gT('Question codes must be unique.'), 'except' => 'archiveimport');
$aRules[]= array('title', 'match', 'pattern' => '/^[a-z,A-Z][[:alnum:]]*$/', 'message' => gT('Question codes must start with a letter and may only contain alphanumeric characters.'), 'except' => 'archiveimport');
}
else
{
$aRules[]= array('title', 'match', 'pattern' => '/^[[:alnum:]]*$/', 'message' => gT('Subquestion codes may only contain alphanumeric characters.'), 'except' => 'archiveimport');
}
return $aRules;
}
/**
* Rewrites sort order for questions in a group
*
* @static
* @access public
* @param int $gid
* @param int $surveyid
* @return void
*/
public static function updateSortOrder($gid, $surveyid)
{
$questions = self::model()->findAllByAttributes(array('gid' => $gid, 'sid' => $surveyid, 'language' => Survey::model()->findByPk($surveyid)->language));
$p = 0;
foreach ($questions as $question)
{
$question->question_order = $p;
$question->save();
$p++;
}
}
/**
* Fixe sort order for questions in a group
*
* @static
* @access public
* @param int $gid
* @param int $surveyid
* @return void
*/
function updateQuestionOrder($gid,$language,$position=0)
{
$data=Yii::app()->db->createCommand()->select('qid')
->where(array('and','gid=:gid','language=:language'))
->order('question_order, title ASC')
->from('{{questions}}')
->bindParam(':gid', $gid, PDO::PARAM_INT)
->bindParam(':language', $language, PDO::PARAM_STR)
->query();
$position = intval($position);
foreach($data->readAll() as $row)
{
Yii::app()->db->createCommand()->update($this->tableName(),array('question_order' => $position),'qid='.$row['qid']);
$position++;
}
}
/**
* This function returns an array of the advanced attributes for the particular question
* including their values set in the database
*
* @access public
* @param int $iQuestionID The question ID - if 0 then all settings will use the default value
* @param string $sQuestionType The question type
* @param int $iSurveyID
* @param string $sLanguage If you give a language then only the attributes for that language are returned
* @return array
*/
public function getAdvancedSettingsWithValues($iQuestionID, $sQuestionType, $iSurveyID, $sLanguage=null)
{
if (is_null($sLanguage))
{
$aLanguages = array_merge(array(Survey::model()->findByPk($iSurveyID)->language), Survey::model()->findByPk($iSurveyID)->additionalLanguages);
}
else
{
$aLanguages = array($sLanguage);
}
if ($iQuestionID)
{
$oAttributeValues = QuestionAttribute::model()->findAll("qid=:qid",array('qid'=>$iQuestionID));
$aAttributeValues=array();
foreach($oAttributeValues as $oAttributeValue)
{
if($oAttributeValue->language){
$aAttributeValues[$oAttributeValue->attribute][$oAttributeValue->language]=$oAttributeValue->value;
}else{
$aAttributeValues[$oAttributeValue->attribute]=$oAttributeValue->value;
}
}
}
$aAttributeNames = questionAttributes();
$aAttributeNames = $aAttributeNames[$sQuestionType];
uasort($aAttributeNames, 'categorySort');
foreach ($aAttributeNames as $iKey => $aAttribute)
{
if ($aAttribute['i18n'] == false)
{
if (isset($aAttributeValues[$aAttribute['name']]))
{
$aAttributeNames[$iKey]['value'] = $aAttributeValues[$aAttribute['name']];
}
else
{
$aAttributeNames[$iKey]['value'] = $aAttribute['default'];
}
}
else
{
foreach ($aLanguages as $sLanguage)
{
if (isset($aAttributeValues[$aAttribute['name']][$sLanguage]))
{
$aAttributeNames[$iKey][$sLanguage]['value'] = $aAttributeValues[$aAttribute['name']][$sLanguage];
}
else
{
$aAttributeNames[$iKey][$sLanguage]['value'] = $aAttribute['default'];
}
}
}
}
return $aAttributeNames;
}
function getQuestions($sid, $gid, $language)
{
return Yii::app()->db->createCommand()
->select()
->from(self::tableName())
->where(array('and', 'sid=:sid', 'gid=:gid', 'language=:language', 'parent_qid=0'))
->order('question_order asc')
->bindParam(":sid", $sid, PDO::PARAM_INT)
->bindParam(":gid", $gid, PDO::PARAM_INT)
->bindParam(":language", $language, PDO::PARAM_STR)
->query();
}
function getSubQuestions($parent_qid)
{
return Yii::app()->db->createCommand()
->select()
->from(self::tableName())
->where('parent_qid=:parent_qid')
->bindParam(":parent_qid", $parent_qid, PDO::PARAM_INT)
->order('question_order asc')
->query();
}
function getQuestionsWithSubQuestions($iSurveyID, $sLanguage, $sCondition = FALSE)
{
$command = Yii::app()->db->createCommand()
->select('{{questions}}.*, q.qid as sqid, q.title as sqtitle, q.question as sqquestion, ' . '{{groups}}.*')
->from($this->tableName())
->leftJoin('{{questions}} q', "q.parent_qid = {{questions}}.qid AND q.language = {{questions}}.language")
->join('{{groups}}', "{{groups}}.gid = {{questions}}.gid AND {{questions}}.language = {{groups}}.language");
$command->where("({{questions}}.sid = '$iSurveyID' AND {{questions}}.language = '$sLanguage' AND {{questions}}.parent_qid = 0)");
if ($sCondition != FALSE)
{
$command->where("({{questions}}.sid = :iSurveyID AND {{questions}}.language = :sLanguage AND {{questions}}.parent_qid = 0) AND {$sCondition}")
->bindParam(":iSurveyID", $iSurveyID, PDO::PARAM_STR)
->bindParam(":sLanguage", $sLanguage, PDO::PARAM_STR);
}
$command->order("{{groups}}.group_order asc, {{questions}}.question_order asc");
return $command->query()->readAll();
}
/**
* Insert an array into the questions table
* Returns false if insertion fails, otherwise the new QID
*
* @param array $data
*/
function insertRecords($data)
{
// This function must be deprecated : don't find a way to have getErrors after (Shnoulle on 131206)
$questions = new self;
foreach ($data as $k => $v){
$questions->$k = $v;
}
try
{
$questions->save();
return $questions->qid;
}
catch(Exception $e)
{
return false;
}
}
public static function deleteAllById($questionsIds)
{
if ( !is_array($questionsIds) )
{
$questionsIds = array($questionsIds);
}
Yii::app()->db->createCommand()->delete(Condition::model()->tableName(), array('in', 'qid', $questionsIds));
Yii::app()->db->createCommand()->delete(QuestionAttribute::model()->tableName(), array('in', 'qid', $questionsIds));
Yii::app()->db->createCommand()->delete(Answer::model()->tableName(), array('in', 'qid', $questionsIds));
Yii::app()->db->createCommand()->delete(Question::model()->tableName(), array('in', 'parent_qid', $questionsIds));
Yii::app()->db->createCommand()->delete(Question::model()->tableName(), array('in', 'qid', $questionsIds));
Yii::app()->db->createCommand()->delete(DefaultValue::model()->tableName(), array('in', 'qid', $questionsIds));
Yii::app()->db->createCommand()->delete(QuotaMember::model()->tableName(), array('in', 'qid', $questionsIds));
}
function getAllRecords($condition, $order=FALSE)
{
$command=Yii::app()->db->createCommand()->select('*')->from($this->tableName())->where($condition);
if ($order != FALSE)
{
$command->order($order);
}
return $command->query();
}
public function getQuestionsForStatistics($fields, $condition, $orderby=FALSE)
{
$command = Yii::app()->db->createCommand()
->select($fields)
->from(self::tableName())
->where($condition);
if ($orderby != FALSE)
{
$command->order($orderby);
}
return $command->queryAll();
}
public function getQuestionList($surveyid, $language)
{
$query = "SELECT questions.*, groups.group_name, groups.group_order"
." FROM {{questions}} as questions, {{groups}} as groups"
." WHERE groups.gid=questions.gid"
." AND groups.language=:language1"
." AND questions.language=:language2"
." AND questions.parent_qid=0"
." AND questions.sid=:sid";
return Yii::app()->db->createCommand($query)->bindParam(":language1", $language, PDO::PARAM_STR)
->bindParam(":language2", $language, PDO::PARAM_STR)
->bindParam(":sid", $surveyid, PDO::PARAM_INT)->queryAll();
}
/**
* Returns the type of subquestions for this question:
* 0: No subquestions,
* 1: Subquestions on 1 scale.
* 2: Subquestions on 2 scales. (Used only by array(texts) and array(numbers) question types)
* @return integer
* @throws CException
*/
public function getHasSubQuestions()
{
$types = self::typeList();
if (isset($types[$this->type]))
{
return $types[$this->type]['subquestions'];
}
throw new CException("Unknown question type: '{$this->type}'");
}
/**
* Returns the type of answers for this question:
* 0: No answers (ie open text question),
* 1: Answers on 1 scale.
* 2: Answers on 2 scales. (Used only by array(dual scale) question type)
* @return integer
* @throws CException
*/
public function getHasAnswerScales()
{
$types = self::typeList();
if (isset($types[$this->type]))
{
return $types[$this->type]['answerscales'] == 1;
}
throw new CException("Unknown question type: '{$this->type}'");
}
/**
* This function contains the question type definitions.
* @return array The question type definitions
*
* Explanation of questiontype array:
*
* description : Question description
* subquestions : 0= Does not support subquestions x=Number of subquestion scales
* answerscales : 0= Does not need answers x=Number of answer scales (usually 1, but e.g. for dual scale question set to 2)
* assessable : 0=Does not support assessment values when editing answerd 1=Support assessment values
*/
public static function typeList()
{
$questionTypes = array(
"1" => array(
'description' => gT("Array dual scale"),
'group' => gT('Arrays'),
'subquestions' => 1,
'assessable' => 1,
'hasdefaultvalues' => 0,
'answerscales' => 2),
"5" => array(
'description' => gT("5 Point Choice"),
'group' => gT("Single choice questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"A" => array(
'description' => gT("Array (5 Point Choice)"),
'group' => gT('Arrays'),
'subquestions' => 1,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 0),
"B" => array(
'description' => gT("Array (10 Point Choice)"),
'group' => gT('Arrays'),
'subquestions' => 1,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 0),
"C" => array(
'description' => gT("Array (Yes/No/Uncertain)"),
'group' => gT('Arrays'),
'subquestions' => 1,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 0),
"D" => array(
'description' => gT("Date/Time"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"E" => array(
'description' => gT("Array (Increase/Same/Decrease)"),
'group' => gT('Arrays'),
'subquestions' => 1,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 0),
"F" => array(
'description' => gT("Array"),
'group' => gT('Arrays'),
'subquestions' => 1,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 1),
"G" => array(
'description' => gT("Gender"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"H" => array(
'description' => gT("Array by column"),
'group' => gT('Arrays'),
'hasdefaultvalues' => 0,
'subquestions' => 1,
'assessable' => 1,
'answerscales' => 1),
"I" => array(
'description' => gT("Language Switch"),
'group' => gT("Mask questions"),
'hasdefaultvalues' => 0,
'subquestions' => 0,
'assessable' => 0,
'answerscales' => 0),
"K" => array(
'description' => gT("Multiple Numerical Input"),
'group' => gT("Mask questions"),
'hasdefaultvalues' => 1,
'subquestions' => 1,
'assessable' => 1,
'answerscales' => 0),
"L" => array(
'description' => gT("List (Radio)"),
'group' => gT("Single choice questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 1,
'answerscales' => 1),
"M" => array(
'description' => gT("Multiple choice"),
'group' => gT("Multiple choice questions"),
'subquestions' => 1,
'hasdefaultvalues' => 1,
'assessable' => 1,
'answerscales' => 0),
"N" => array(
'description' => gT("Numerical Input"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"O" => array(
'description' => gT("List with comment"),
'group' => gT("Single choice questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 1,
'answerscales' => 1),
"P" => array(
'description' => gT("Multiple choice with comments"),
'group' => gT("Multiple choice questions"),
'subquestions' => 1,
'hasdefaultvalues' => 1,
'assessable' => 1,
'answerscales' => 0),
"Q" => array(
'description' => gT("Multiple Short Text"),
'group' => gT("Text questions"),
'subquestions' => 1,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"R" => array(
'description' => gT("Ranking"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 1),
"S" => array(
'description' => gT("Short Free Text"),
'group' => gT("Text questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"T" => array(
'description' => gT("Long Free Text"),
'group' => gT("Text questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"U" => array(
'description' => gT("Huge Free Text"),
'group' => gT("Text questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 0,
'answerscales' => 0),
"X" => array(
'description' => gT("Text display"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"Y" => array(
'description' => gT("Yes/No"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"!" => array(
'description' => gT("List (Dropdown)"),
'group' => gT("Single choice questions"),
'subquestions' => 0,
'hasdefaultvalues' => 1,
'assessable' => 1,
'answerscales' => 1),
":" => array(
'description' => gT("Array (Numbers)"),
'group' => gT('Arrays'),
'subquestions' => 2,
'hasdefaultvalues' => 0,
'assessable' => 1,
'answerscales' => 0),
";" => array(
'description' => gT("Array (Texts)"),
'group' => gT('Arrays'),
'subquestions' => 2,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"|" => array(
'description' => gT("File upload"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
"*" => array(
'description' => gT("Equation"),
'group' => gT("Mask questions"),
'subquestions' => 0,
'hasdefaultvalues' => 0,
'assessable' => 0,
'answerscales' => 0),
);
/**
* @todo Check if this actually does anything, since the values are arrays.
*/
asort($questionTypes);
return $questionTypes;
}
}
?>