question_list_admin.inc.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use ChamiloSession as Session;
  4. /**
  5. * Code library for HotPotatoes integration.
  6. *
  7. * @package chamilo.exercise
  8. *
  9. * @author Olivier Brouckaert & Julio Montoya & Hubert Borderiou 21-10-2011 (Question by category)
  10. * QUESTION LIST ADMINISTRATION
  11. *
  12. * This script allows to manage the question list
  13. * It is included from the script admin.php
  14. */
  15. $limitTeacherAccess = api_get_configuration_value('limit_exercise_teacher_access');
  16. // deletes a question from the exercise (not from the data base)
  17. if ($deleteQuestion) {
  18. if ($limitTeacherAccess && !api_is_platform_admin()) {
  19. exit;
  20. }
  21. // if the question exists
  22. if ($objQuestionTmp = Question::read($deleteQuestion)) {
  23. $objQuestionTmp->delete($exerciseId);
  24. // if the question has been removed from the exercise
  25. if ($objExercise->removeFromList($deleteQuestion)) {
  26. $nbrQuestions--;
  27. }
  28. }
  29. // destruction of the Question object
  30. unset($objQuestionTmp);
  31. }
  32. $ajax_url = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&exercise_id='.intval($exerciseId);
  33. ?>
  34. <div id="dialog-confirm"
  35. title="<?php echo get_lang('Please confirm your choice'); ?>"
  36. style="display:none;">
  37. <p>
  38. <?php echo get_lang('Are you sure you want to delete'); ?>
  39. </p>
  40. </div>
  41. <script>
  42. $(function () {
  43. $("#dialog:ui-dialog").dialog("destroy");
  44. $("#dialog-confirm").dialog({
  45. autoOpen: false,
  46. show: "blind",
  47. resizable: false,
  48. height: 150,
  49. modal: false
  50. });
  51. $(".opener").click(function () {
  52. var targetUrl = $(this).attr("href");
  53. $("#dialog-confirm").dialog({
  54. modal: true,
  55. buttons: {
  56. "<?php echo get_lang('Yes'); ?>": function () {
  57. location.href = targetUrl;
  58. $(this).dialog("close");
  59. },
  60. "<?php echo get_lang('No'); ?>": function () {
  61. $(this).dialog("close");
  62. }
  63. }
  64. });
  65. $("#dialog-confirm").dialog("open");
  66. return false;
  67. });
  68. var stop = false;
  69. $("#question_list h3").click(function (event) {
  70. if (stop) {
  71. event.stopImmediatePropagation();
  72. event.preventDefault();
  73. stop = false;
  74. }
  75. });
  76. /* We can add links in the accordion header */
  77. $(".btn-actions .edition a.btn").click(function () {
  78. //Avoid the redirecto when selecting the delete button
  79. if (this.id.indexOf('delete') == -1) {
  80. newWind = window.open(this.href, "_self");
  81. newWind.focus();
  82. return false;
  83. }
  84. });
  85. $("#question_list").accordion({
  86. icons: null,
  87. heightStyle: "content",
  88. active: false, // all items closed by default
  89. collapsible: true,
  90. header: ".header_operations",
  91. beforeActivate: function (e, ui) {
  92. var data = ui.newHeader.data();
  93. if (typeof data === 'undefined') {
  94. return;
  95. }
  96. var exerciseId = data.exercise || 0,
  97. questionId = data.question || 0;
  98. if (!questionId || !exerciseId) {
  99. return;
  100. }
  101. var $pnlQuestion = $('#pnl-question-' + questionId);
  102. if ($pnlQuestion.html().trim().length) {
  103. return;
  104. }
  105. $pnlQuestion.html('<span class="fa fa-spinner fa-spin fa-3x fa-fw" aria-hidden="true"></span>');
  106. $.get('<?php echo api_get_path(WEB_AJAX_PATH); ?>exercise.ajax.php?<?php echo api_get_cidreq(); ?>', {
  107. a: 'show_question',
  108. exercise: exerciseId,
  109. question: questionId
  110. }, function (response) {
  111. $pnlQuestion.html(response)
  112. });
  113. }
  114. })
  115. .sortable({
  116. cursor: "move", // works?
  117. update: function (event, ui) {
  118. var order = $(this).sortable("serialize") + "&a=update_question_order&exercise_id=<?php echo $exerciseId; ?>";
  119. $.post("<?php echo $ajax_url; ?>", order, function (result) {
  120. $("#message").html(result);
  121. });
  122. },
  123. axis: "y",
  124. placeholder: "ui-state-highlight", //defines the yellow highlight
  125. handle: ".moved", //only the class "moved"
  126. stop: function () {
  127. stop = true;
  128. }
  129. });
  130. });
  131. </script>
  132. <?php
  133. // Filter the type of questions we can add
  134. Question::displayTypeMenu($objExercise);
  135. echo '<div id="message"></div>';
  136. $token = Security::get_token();
  137. //deletes a session when using don't know question type (ugly fix)
  138. Session::erase('less_answer');
  139. // If we are in a test
  140. $inATest = isset($exerciseId) && $exerciseId > 0;
  141. if (!$inATest) {
  142. echo Display::return_message(get_lang('Choose question type'), 'warning');
  143. } else {
  144. if ($nbrQuestions) {
  145. // In the building exercise mode show question list ordered as is.
  146. $objExercise->setCategoriesGrouping(false);
  147. // In building mode show all questions not render by teacher order.
  148. $objExercise->questionSelectionType = EX_Q_SELECTION_ORDERED;
  149. $allowQuestionOrdering = true;
  150. $showPagination = api_get_configuration_value('show_question_pagination');
  151. if (!empty($showPagination) && $nbrQuestions > $showPagination) {
  152. $length = api_get_configuration_value('question_pagination_length');
  153. $url = api_get_self().'?'.api_get_cidreq();
  154. // Use pagination for exercise with more than 200 questions.
  155. $allowQuestionOrdering = false;
  156. $start = ($page - 1) * $length;
  157. $questionList = $objExercise->getQuestionForTeacher($start, $length);
  158. $paginator = new Knp\Component\Pager\Paginator();
  159. $pagination = $paginator->paginate([]);
  160. $pagination->setTotalItemCount($nbrQuestions);
  161. $pagination->setItemNumberPerPage($length);
  162. $pagination->setCurrentPageNumber($page);
  163. $pagination->renderer = function ($data) use ($url) {
  164. $render = '<ul class="pagination">';
  165. for ($i = 1; $i <= $data['pageCount']; $i++) {
  166. $pageContent = '<li><a href="'.$url.'&page='.$i.'">'.$i.'</a></li>';
  167. if ($data['current'] == $i) {
  168. $pageContent = '<li class="active"><a href="#" >'.$i.'</a></li>';
  169. }
  170. $render .= $pageContent;
  171. }
  172. $render .= '</ul>';
  173. return $render;
  174. };
  175. echo $pagination;
  176. } else {
  177. // Classic order
  178. $questionList = $objExercise->selectQuestionList(true, true);
  179. }
  180. echo '
  181. <div class="row hidden-xs">
  182. <div class="col-sm-5"><strong>'.get_lang('Questions').'</strong></div>
  183. <div class="col-sm-1 text-center"><strong>'.get_lang('Type').'</strong></div>
  184. <div class="col-sm-2"><strong>'.get_lang('Category').'</strong></div>
  185. <div class="col-sm-1 text-right"><strong>'.get_lang('Difficulty').'</strong></div>
  186. <div class="col-sm-1 text-right"><strong>'.get_lang('Score').'</strong></div>
  187. <div class="col-sm-2 text-right"><strong>'.get_lang('Detail').'</strong></div>
  188. </div>
  189. <div id="question_list">
  190. ';
  191. $category_list = TestCategory::getListOfCategoriesNameForTest($objExercise->id, false);
  192. if (is_array($questionList)) {
  193. foreach ($questionList as $id) {
  194. // To avoid warning messages.
  195. if (!is_numeric($id)) {
  196. continue;
  197. }
  198. /** @var Question $objQuestionTmp */
  199. $objQuestionTmp = Question::read($id);
  200. if (empty($objQuestionTmp)) {
  201. continue;
  202. }
  203. $clone_link = Display::url(
  204. Display::return_icon(
  205. 'cd.png',
  206. get_lang('Copy'),
  207. [],
  208. ICON_SIZE_TINY
  209. ),
  210. api_get_self().'?'.api_get_cidreq().'&clone_question='.$id.'&page='.$page,
  211. ['class' => 'btn btn-default btn-sm']
  212. );
  213. $edit_link = $objQuestionTmp->selectType() == CALCULATED_ANSWER && $objQuestionTmp->isAnswered()
  214. ? Display::span(
  215. Display::return_icon(
  216. 'edit_na.png',
  217. get_lang('Question edition is not available because the question has been already answered. However, you can copy and modify it.'),
  218. [],
  219. ICON_SIZE_TINY
  220. ),
  221. ['class' => 'btn btn-default btn-sm']
  222. )
  223. : Display::url(
  224. Display::return_icon(
  225. 'edit.png',
  226. get_lang('Edit'),
  227. [],
  228. ICON_SIZE_TINY
  229. ),
  230. api_get_self().'?'.api_get_cidreq().'&'
  231. .http_build_query([
  232. 'type' => $objQuestionTmp->selectType(),
  233. 'editQuestion' => $id,
  234. 'page' => $page,
  235. ]),
  236. ['class' => 'btn btn-default btn-sm']
  237. );
  238. $delete_link = null;
  239. if ($objExercise->edit_exercise_in_lp == true) {
  240. $delete_link = Display::url(
  241. Display::return_icon(
  242. 'delete.png',
  243. get_lang('Remove from test'),
  244. [],
  245. ICON_SIZE_TINY
  246. ),
  247. api_get_self().'?'.api_get_cidreq().'&'
  248. .http_build_query([
  249. 'exerciseId' => $exerciseId,
  250. 'deleteQuestion' => $id,
  251. 'page' => $page,
  252. ]),
  253. [
  254. 'id' => "delete_$id",
  255. 'class' => 'opener btn btn-default btn-sm',
  256. ]
  257. );
  258. }
  259. if ($limitTeacherAccess && !api_is_platform_admin()) {
  260. $delete_link = '';
  261. }
  262. $btnDetail = implode(
  263. PHP_EOL,
  264. [$edit_link, $clone_link, $delete_link]
  265. );
  266. $title = Security::remove_XSS($objQuestionTmp->selectTitle());
  267. $title = strip_tags($title);
  268. $move = '&nbsp;';
  269. if ($allowQuestionOrdering) {
  270. $move = Display::returnFontAwesomeIcon('arrows moved', 1, true);
  271. }
  272. // Question name
  273. $questionName =
  274. '<a href="#" title = "'.Security::remove_XSS($title).'">
  275. '.$move.' '.cut($title, 42).'
  276. </a>';
  277. // Question type
  278. $typeImg = $objQuestionTmp->getTypePicture();
  279. $typeExpl = $objQuestionTmp->getExplanation();
  280. $questionType = Display::return_icon($typeImg, $typeExpl);
  281. // Question category
  282. $txtQuestionCat = Security::remove_XSS(
  283. TestCategory::getCategoryNameForQuestion($objQuestionTmp->id)
  284. );
  285. if (empty($txtQuestionCat)) {
  286. $txtQuestionCat = '-';
  287. }
  288. // Question level
  289. $txtQuestionLevel = $objQuestionTmp->getLevel();
  290. if (empty($objQuestionTmp->level)) {
  291. $txtQuestionLevel = '-';
  292. }
  293. $questionLevel = $txtQuestionLevel;
  294. // Question score
  295. $questionScore = $objQuestionTmp->selectWeighting();
  296. echo '<div id="question_id_list_'.$id.'">
  297. <div class="header_operations" data-exercise="'.$objExercise->selectId().'"
  298. data-question="'.$id.'">
  299. <div class="row">
  300. <div class="question col-sm-5 col-xs-12">'
  301. .$questionName.'
  302. </div>
  303. <div class="type text-center col-sm-1 col-xs-12">
  304. <span class="visible-xs-inline">'.get_lang('Type').' </span>'
  305. .$questionType.'
  306. </div>
  307. <div class="category col-sm-2 col-xs-12" title="'.$txtQuestionCat.'">
  308. <span class="visible-xs-inline">'.get_lang('Category').' </span>'
  309. .cut($txtQuestionCat, 42).'
  310. </div>
  311. <div class="level text-right col-sm-1 col-xs-6">
  312. <span class="visible-xs-inline">'.get_lang('Difficulty').' </span>'
  313. .$questionLevel.'
  314. </div>
  315. <div class="score text-right col-sm-1 col-xs-6">
  316. <span class="visible-xs-inline">'.get_lang('Score').' </span>'
  317. .$questionScore.'
  318. </div>
  319. <div class="btn-actions text-right col-sm-2 col-xs-6">
  320. <div class="edition">'.$btnDetail.'</div>
  321. </div>
  322. </div>
  323. </div>
  324. <div class="question-list-description-block" id="pnl-question-'.$id.'">
  325. </div>
  326. </div>
  327. ';
  328. unset($objQuestionTmp);
  329. }
  330. }
  331. echo '</div>'; //question list div
  332. } else {
  333. echo Display::return_message(get_lang('Questions list (there is no question so far).'), 'warning');
  334. }
  335. }