PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/application/libraries/PluginManager/Question/QuestionBase.php

https://gitlab.com/mariadb-corporation/LimeSurvey
PHP | 330 lines | 198 code | 32 blank | 100 comment | 19 complexity | d9d2f25a0a9d80df352aaf1fd39d19b5 MD5 | raw file
  1. <?php
  2. abstract class QuestionBase implements iQuestion {
  3. /**
  4. * @var LimesurveyApi
  5. */
  6. protected $api;
  7. /**
  8. * Array containing meta data for supported question attributes.
  9. * @var array
  10. */
  11. protected $attributes;
  12. /**
  13. * Array containing an array for each column.
  14. * The supported keys for column meta data are:
  15. * - type
  16. * - name
  17. * - description
  18. *
  19. * @var array
  20. */
  21. protected $columns;
  22. /**
  23. * Array containing attributes that all question types share.
  24. * @var array
  25. */
  26. private $defaultAttributes;
  27. /**
  28. * Array containing default attributes that are merged into the attribute
  29. * arrays.
  30. * @var array
  31. */
  32. protected $defaultAttributeProperties = array(
  33. 'localized' => false,
  34. 'advanced' => false
  35. );
  36. public static $info = array();
  37. /**
  38. *
  39. * @var iPlugin
  40. */
  41. protected $plugin;
  42. /**
  43. *
  44. * @var int The question id for this question object instance.
  45. */
  46. protected $questionId = null;
  47. /**
  48. * @var int The response id for this question object instance.
  49. */
  50. protected $responseId = null;
  51. /**
  52. * Contains the subquestion objects for this question.
  53. * @var iQuestion[]
  54. */
  55. protected $subQuestions;
  56. /**
  57. * The signature array is used for deriving a unique identifier for
  58. * a question type.
  59. * After initial release the contents of this array may NEVER be changed.
  60. * Changing the contents of the array will identify the question object
  61. * as a new question type and will break many if not all existing surveys.
  62. *
  63. *
  64. * - Add more keys & values to make it more unique.
  65. * @var array
  66. */
  67. protected static $signature = array();
  68. /**
  69. * @param iPlugin $plugin The plugin to which this question belongs.
  70. * @param int $questionId
  71. * @param int $responseId Pass a response id to load results.
  72. */
  73. public function __construct(iPlugin $plugin, LimesurveyApi $api, $questionId = null, $responseId = null) {
  74. $this->plugin = $plugin;
  75. $this->api = $api;
  76. $this->responseId = $responseId;
  77. $this->questionId = $questionId;
  78. if (isset($questionId))
  79. {
  80. $this->loadSubQuestions($questionId);
  81. }
  82. $this->defaultAttributes = array(
  83. 'questiontype' => array(
  84. 'type' => 'select',
  85. 'localized' => false,
  86. 'advanced' => false,
  87. 'label' => gt('Question type:'),
  88. 'options' => CHtml::listData(App()->getPluginManager()->loadQuestionObjects(), 'guid', 'name')
  89. ),
  90. 'code' => array(
  91. 'type' => 'string',
  92. 'localized' => false,
  93. 'advanced' => false,
  94. 'label' => gT('Question code:')
  95. ),
  96. 'gid' => array(
  97. 'type' => 'select',
  98. 'localized' => false,
  99. 'advanced' => false,
  100. 'label' => gT('Question group:'),
  101. 'options' => function($this) { return $this->api->getGroupList($this->get('sid')); }
  102. ),
  103. 'relevance' => array(
  104. 'type' => 'relevance',
  105. 'localized' => false,
  106. 'advanced' => false,
  107. 'label' => gT('Relevance equation:')
  108. ),
  109. 'randomization' => array(
  110. 'type' => 'string',
  111. 'localized' => false,
  112. 'advanced' => false,
  113. 'label' => gT("Randomization group:")
  114. )
  115. );
  116. }
  117. /**
  118. * This function retrieves question data. Do not cache this data; the plugin storage
  119. * engine will handling caching. After the first call to this function, subsequent
  120. * calls will only consist of a few function calls and array lookups.
  121. *
  122. * @param string $key String identifier for data.
  123. * @param mixed $default Default value.
  124. * @param string $language Language to retrieve.
  125. * @param int $questionId By default uses the question id for the current instance. Override this to read from another question.
  126. * @return boolean
  127. */
  128. protected function get($key = null, $default = null, $language = null, $questionId = null)
  129. {
  130. if (!isset($questionId) && isset($this->questionId))
  131. {
  132. $questionId = $this->questionId;
  133. return $this->plugin->getStore()->get($this->plugin, $key, 'Question', $questionId, $default, $language);
  134. }
  135. else
  136. {
  137. return false;
  138. }
  139. }
  140. /**
  141. * Gets the meta data for question attributes.
  142. * Optionally pass one or more languages to also get current values.
  143. * Pass * to get all stored languages.
  144. * @param type $language
  145. * @return type
  146. */
  147. public function getAttributes($languages = null)
  148. {
  149. $allAttributes = array_merge($this->defaultAttributes, $this->attributes);
  150. if (count($allAttributes) != count($this->defaultAttributes) + count($this->attributes))
  151. {
  152. throw new Exception(get_class($this) . " must not redefine default attributes");
  153. }
  154. foreach ($allAttributes as $name => &$metaData)
  155. {
  156. $metaData = array_merge($this->defaultAttributeProperties, $metaData);
  157. if (isset($this->questionId))
  158. {
  159. if (is_array($languages))
  160. {
  161. foreach ($languages as $language)
  162. {
  163. $metaData['current'][$language] = $this->get($name, null, $language);
  164. }
  165. }
  166. else
  167. {
  168. $metaData['current'] = $this->get($name, null, $languages);
  169. }
  170. // Populate select fields with a list.
  171. if ($metaData['type'] == 'select' && is_callable($metaData['options']))
  172. {
  173. $metaData['options'] = call_user_func($metaData['options'], $this);
  174. }
  175. }
  176. }
  177. return $allAttributes;
  178. }
  179. public function getColumns()
  180. {
  181. return $this->columns;
  182. }
  183. public function getCount()
  184. {
  185. return 1;
  186. }
  187. /**
  188. * This function derives a unique identifier for identifying a question type.
  189. */
  190. public static function getGUID()
  191. {
  192. // We use json_encode because it is faster than serialize.
  193. return md5(json_encode(static::$signature));
  194. }
  195. /**
  196. * Gets the response for the current response id.
  197. * @return type
  198. */
  199. public function getResponse()
  200. {
  201. if (isset($this->responseId))
  202. {
  203. $surveyId = Question::model()->findFieldByPk($this->questionId, 'sid');
  204. $response = SurveyDynamic::model($surveyId)->findByPk($this->responseId);
  205. $columns = $this->getColumns();
  206. foreach ($columns as &$column)
  207. {
  208. if (isset($response->$column))
  209. {
  210. $column['response'] = $response->$column;
  211. }
  212. }
  213. return $columns;
  214. }
  215. }
  216. public function getVariables()
  217. {
  218. if (isset($this->questionId))
  219. {
  220. return array(
  221. $this->get('code') => array(
  222. 'id' => $this->questionId,
  223. 'relevance' => $this->get('relevance')
  224. )
  225. );
  226. }
  227. return array();
  228. }
  229. /**
  230. * Load the question data from the questions model.
  231. * @param type $questionId
  232. */
  233. public function loadSubQuestions($questionId)
  234. {
  235. $subQuestions = Question::model()->findAllByAttributes(array(
  236. 'parent_id' => $questionId
  237. ));
  238. foreach ($subQuestions as $subQuestion)
  239. {
  240. /**
  241. * @todo Alter this so that subquestion can be of another type.
  242. */
  243. $this->subQuestions[] = new self($subQuestion->qid, $this->responseId);
  244. }
  245. }
  246. public function saveAttributes(array $attributeValues, $qid = null)
  247. {
  248. $attributes = $this->getAttributes();
  249. $result = true;
  250. foreach ($attributeValues as $key => $value)
  251. {
  252. // Check if the attribute is valid for the question.
  253. if (isset($attributes[$key]))
  254. {
  255. // If the attribute is localized, save each language.
  256. if ($attributes[$key]['localized'])
  257. {
  258. foreach ($value as $language => $localizedValue)
  259. {
  260. if (!$this->set($key, $localizedValue, $language, $qid))
  261. {
  262. $result = false;
  263. }
  264. }
  265. }
  266. else
  267. {
  268. if (!$this->set($key, $value, $qid))
  269. {
  270. $result = false;
  271. }
  272. }
  273. }
  274. }
  275. return $result;
  276. }
  277. /**
  278. * This function saves question data.
  279. * @param int $qid Question id.
  280. * @param string $key
  281. * @param string $language
  282. * @param mixed $value
  283. * @return boolean
  284. */
  285. protected function set($key, $value, $language = null, $questionId = null)
  286. {
  287. if (!isset($questionId) && isset($this->questionId))
  288. {
  289. $questionId = $this->questionId;
  290. return $this->plugin->getStore()->set($this->plugin, $key, $value, 'Question', $questionId, $language);
  291. }
  292. else
  293. {
  294. return false;
  295. }
  296. }
  297. }
  298. ?>