question_list_admin.inc.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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('ConfirmYourChoice'); ?>"
  36. style="display:none;">
  37. <p>
  38. <?php echo get_lang('AreYouSureToDelete'); ?>
  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. //we 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('ChoiceQuestionType'), '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('MaximumScore').'</strong></div>
  187. <div class="col-sm-2 text-right"><strong>'.get_lang('Actions').'</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. $clone_link = Display::url(
  201. Display::return_icon(
  202. 'cd.png',
  203. get_lang('Copy'),
  204. [],
  205. ICON_SIZE_TINY
  206. ),
  207. api_get_self().'?'.api_get_cidreq().'&clone_question='.$id.'&page='.$page,
  208. ['class' => 'btn btn-default btn-sm']
  209. );
  210. $edit_link = $objQuestionTmp->type == CALCULATED_ANSWER && $objQuestionTmp->isAnswered()
  211. ? Display::span(
  212. Display::return_icon(
  213. 'edit_na.png',
  214. get_lang('QuestionEditionNotAvailableBecauseItIsAlreadyAnsweredHoweverYouCanCopyItAndModifyTheCopy'),
  215. [],
  216. ICON_SIZE_TINY
  217. ),
  218. ['class' => 'btn btn-default btn-sm']
  219. )
  220. : Display::url(
  221. Display::return_icon(
  222. 'edit.png',
  223. get_lang('Modify'),
  224. [],
  225. ICON_SIZE_TINY
  226. ),
  227. api_get_self().'?'.api_get_cidreq().'&'
  228. .http_build_query([
  229. 'type' => $objQuestionTmp->selectType(),
  230. 'editQuestion' => $id,
  231. 'page' => $page,
  232. ]),
  233. ['class' => 'btn btn-default btn-sm']
  234. );
  235. $delete_link = null;
  236. if ($objExercise->edit_exercise_in_lp == true) {
  237. $delete_link = Display::url(
  238. Display::return_icon(
  239. 'delete.png',
  240. get_lang('RemoveFromTest'),
  241. [],
  242. ICON_SIZE_TINY
  243. ),
  244. api_get_self().'?'.api_get_cidreq().'&'
  245. .http_build_query([
  246. 'exerciseId' => $exerciseId,
  247. 'deleteQuestion' => $id,
  248. 'page' => $page,
  249. ]),
  250. [
  251. 'id' => "delete_$id",
  252. 'class' => 'opener btn btn-default btn-sm',
  253. ]
  254. );
  255. }
  256. if ($limitTeacherAccess && !api_is_platform_admin()) {
  257. $delete_link = '';
  258. }
  259. $btnActions = implode(
  260. PHP_EOL,
  261. [$edit_link, $clone_link, $delete_link]
  262. );
  263. $title = Security::remove_XSS($objQuestionTmp->selectTitle());
  264. $title = strip_tags($title);
  265. $move = '&nbsp;';
  266. if ($allowQuestionOrdering) {
  267. $move = Display::returnFontAwesomeIcon('arrows moved', 1, true);
  268. }
  269. // Question name
  270. $questionName =
  271. '<a href="#" title = "'.Security::remove_XSS($title).'">
  272. '.$move.' '.cut($title, 42).'
  273. </a>';
  274. // Question type
  275. list($typeImg, $typeExpl) = $objQuestionTmp->get_type_icon_html();
  276. $questionType = Display::return_icon($typeImg, $typeExpl);
  277. // Question category
  278. $txtQuestionCat = Security::remove_XSS(
  279. TestCategory::getCategoryNameForQuestion($objQuestionTmp->id)
  280. );
  281. if (empty($txtQuestionCat)) {
  282. $txtQuestionCat = "-";
  283. }
  284. // Question level
  285. $txtQuestionLevel = $objQuestionTmp->level;
  286. if (empty($objQuestionTmp->level)) {
  287. $txtQuestionLevel = '-';
  288. }
  289. $questionLevel = $txtQuestionLevel;
  290. // Question score
  291. $questionScore = $objQuestionTmp->selectWeighting();
  292. echo '<div id="question_id_list_'.$id.'">
  293. <div class="header_operations" data-exercise="'.$objExercise->selectId().'"
  294. data-question="'.$id.'">
  295. <div class="row">
  296. <div class="question col-sm-5 col-xs-12">'
  297. .$questionName.'
  298. </div>
  299. <div class="type text-center col-sm-1 col-xs-12">
  300. <span class="visible-xs-inline">'.get_lang('Type').' </span>'
  301. .$questionType.'
  302. </div>
  303. <div class="category col-sm-2 col-xs-12" title="'.$txtQuestionCat.'">
  304. <span class="visible-xs-inline">'.get_lang('Category').' </span>'
  305. .cut($txtQuestionCat, 42).'
  306. </div>
  307. <div class="level text-right col-sm-1 col-xs-6">
  308. <span class="visible-xs-inline">'.get_lang('Difficulty').' </span>'
  309. .$questionLevel.'
  310. </div>
  311. <div class="score text-right col-sm-1 col-xs-6">
  312. <span class="visible-xs-inline">'.get_lang('Score').' </span>'
  313. .$questionScore.'
  314. </div>
  315. <div class="btn-actions text-right col-sm-2 col-xs-6">
  316. <div class="edition">'.$btnActions.'</div>
  317. </div>
  318. </div>
  319. </div>
  320. <div class="question-list-description-block" id="pnl-question-'.$id.'">
  321. </div>
  322. </div>
  323. ';
  324. unset($objQuestionTmp);
  325. }
  326. }
  327. } else {
  328. echo Display::return_message(get_lang('NoQuestion'), 'warning');
  329. }
  330. echo '</div>'; //question list div
  331. }