exercise.php 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use ChamiloSession as Session;
  4. /**
  5. * Exercise list: This script shows the list of exercises for administrators and students.
  6. *
  7. * @package chamilo.exercise
  8. *
  9. * @author Olivier Brouckaert, original author
  10. * @author Denes Nagy, HotPotatoes integration
  11. * @author Wolfgang Schneider, code/html cleanup
  12. * @author Julio Montoya <gugli100@gmail.com>, lots of cleanup + several improvements
  13. * Modified by hubert.borderiou (question category)
  14. */
  15. // including the global library
  16. require_once __DIR__.'/../inc/global.inc.php';
  17. $current_course_tool = TOOL_QUIZ;
  18. // Setting the tabs
  19. $this_section = SECTION_COURSES;
  20. //$htmlHeadXtra[] = api_get_asset('qtip2/jquery.qtip.min.js');
  21. //$htmlHeadXtra[] = api_get_css_asset('qtip2/jquery.qtip.min.css');
  22. api_protect_course_script(true);
  23. $limitTeacherAccess = api_get_configuration_value('limit_exercise_teacher_access');
  24. require_once 'hotpotatoes.lib.php';
  25. /* Constants and variables */
  26. $is_allowedToEdit = api_is_allowed_to_edit(null, true);
  27. $is_tutor = api_is_allowed_to_edit(true);
  28. $is_tutor_course = api_is_course_tutor();
  29. $courseInfo = api_get_course_info();
  30. $courseId = $courseInfo['real_id'];
  31. $userInfo = api_get_user_info();
  32. $userId = $userInfo['id'];
  33. $sessionId = api_get_session_id();
  34. $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
  35. $userId,
  36. $courseInfo
  37. );
  38. $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
  39. $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  40. $TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  41. $TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST);
  42. $TBL_TRACK_EXERCISES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  43. // document path
  44. $documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
  45. // picture path
  46. $picturePath = $documentPath.'/images';
  47. // audio path
  48. $audioPath = $documentPath.'/audio';
  49. // hot potatoes
  50. $uploadPath = DIR_HOTPOTATOES; //defined in main_api
  51. $exercisePath = api_get_self();
  52. $exfile = explode('/', $exercisePath);
  53. $exfile = strtolower($exfile[sizeof($exfile) - 1]);
  54. $exercisePath = substr($exercisePath, 0, strpos($exercisePath, $exfile));
  55. $exercisePath = $exercisePath.'exercise.php';
  56. // Clear the exercise session
  57. Session::erase('objExercise');
  58. Session::erase('objQuestion');
  59. Session::erase('objAnswer');
  60. Session::erase('questionList');
  61. Session::erase('exerciseResult');
  62. Session::erase('firstTime');
  63. Session::erase('calculatedAnswerId');
  64. Session::erase('duration_time_previous');
  65. Session::erase('duration_time');
  66. //General POST/GET/SESSION/COOKIES parameters recovery
  67. $origin = api_get_origin();
  68. $choice = isset($_REQUEST['choice']) ? Security::remove_XSS($_REQUEST['choice']) : null;
  69. $hpchoice = isset($_REQUEST['hpchoice']) ? Security::remove_XSS($_REQUEST['hpchoice']) : null;
  70. $exerciseId = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : null;
  71. $file = isset($_REQUEST['file']) ? Database::escape_string($_REQUEST['file']) : null;
  72. $learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : null;
  73. $learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) $_REQUEST['learnpath_item_id'] : null;
  74. $page = isset($_REQUEST['page']) ? (int) $_REQUEST['page'] : null;
  75. if ($page < 0) {
  76. $page = 1;
  77. }
  78. if (api_is_in_gradebook()) {
  79. $interbreadcrumb[] = [
  80. 'url' => Category::getUrl(),
  81. 'name' => get_lang('ToolGradebook'),
  82. ];
  83. }
  84. $autoLaunchAvailable = false;
  85. if (api_get_course_setting('enable_exercise_auto_launch') == 1 &&
  86. api_get_configuration_value('allow_exercise_auto_launch')
  87. ) {
  88. $autoLaunchAvailable = true;
  89. }
  90. $nameTools = get_lang('Exercises');
  91. $errorXmlExport = null;
  92. if ($is_allowedToEdit && !empty($choice) && $choice === 'exportqti2') {
  93. if ($limitTeacherAccess && !api_is_platform_admin()) {
  94. api_not_allowed(true);
  95. }
  96. require_once api_get_path(SYS_CODE_PATH).'exercise/export/qti2/qti2_export.php';
  97. $export = export_exercise_to_qti($exerciseId, true);
  98. $archive_path = api_get_path(SYS_ARCHIVE_PATH);
  99. $temp_dir_short = api_get_unique_id();
  100. $temp_zip_dir = $archive_path.$temp_dir_short;
  101. if (!is_dir($temp_zip_dir)) {
  102. mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
  103. }
  104. $temp_zip_file = $temp_zip_dir."/".api_get_unique_id().".zip";
  105. $temp_xml_file = $temp_zip_dir."/qti2export_".$exerciseId.'.xml';
  106. file_put_contents($temp_xml_file, $export);
  107. $xmlReader = new XMLReader();
  108. $xmlReader->open($temp_xml_file);
  109. $xmlReader->setParserProperty(XMLReader::VALIDATE, true);
  110. $isValid = $xmlReader->isValid();
  111. if ($isValid) {
  112. $zip_folder = new PclZip($temp_zip_file);
  113. $zip_folder->add($temp_xml_file, PCLZIP_OPT_REMOVE_ALL_PATH);
  114. $name = 'qti2_export_'.$exerciseId.'.zip';
  115. DocumentManager::file_send_for_download($temp_zip_file, true, $name);
  116. unlink($temp_zip_file);
  117. unlink($temp_xml_file);
  118. rmdir($temp_zip_dir);
  119. exit; //otherwise following clicks may become buggy
  120. } else {
  121. $errorXmlExport = Display::return_message(get_lang('ErrorWritingXMLFile'), 'error');
  122. }
  123. }
  124. if ($origin != 'learnpath') {
  125. //so we are not in learnpath tool
  126. Display :: display_header($nameTools, get_lang('Exercise'));
  127. if (isset($_GET['message']) && in_array($_GET['message'], ['ExerciseEdited'])) {
  128. echo Display::return_message(get_lang('ExerciseEdited'), 'confirmation');
  129. }
  130. } else {
  131. Display::display_reduced_header();
  132. }
  133. Event::event_access_tool(TOOL_QUIZ);
  134. $logInfo = [
  135. 'tool' => TOOL_QUIZ,
  136. 'tool_id' => (int) $exerciseId,
  137. 'tool_id_detail' => 0,
  138. 'action' => isset($_REQUEST['learnpath_id']) ? 'learnpath_id' : '',
  139. 'action_details' => isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : '',
  140. ];
  141. Event::registerLog($logInfo);
  142. // Tool introduction
  143. Display::display_introduction_section(TOOL_QUIZ);
  144. if (!empty($errorXmlExport)) {
  145. echo $errorXmlExport;
  146. }
  147. HotPotGCt($documentPath, 1, $userId);
  148. // Only for administrator
  149. if ($is_allowedToEdit) {
  150. if (!empty($choice)) {
  151. // All test choice, clean all test's results
  152. if ($choice === 'clean_all_test') {
  153. $check = Security::check_token('get');
  154. if ($check) {
  155. // list of exercises in a course/session
  156. // we got variable $courseId $courseInfo session api_get_session_id()
  157. $exerciseList = ExerciseLib::get_all_exercises_for_course_id(
  158. $courseInfo,
  159. $sessionId,
  160. $courseId,
  161. false
  162. );
  163. $quantity_results_deleted = 0;
  164. foreach ($exerciseList as $exeItem) {
  165. // delete result for test, if not in a gradebook
  166. $exercise_action_locked = api_resource_is_locked_by_gradebook($exeItem['id'], LINK_EXERCISE);
  167. if ($exercise_action_locked == false) {
  168. $objExerciseTmp = new Exercise();
  169. if ($objExerciseTmp->read($exeItem['id'])) {
  170. $quantity_results_deleted += $objExerciseTmp->cleanResults(true);
  171. }
  172. }
  173. }
  174. echo Display::return_message(
  175. sprintf(
  176. get_lang('XResultsCleaned'),
  177. $quantity_results_deleted
  178. ),
  179. 'confirm'
  180. );
  181. }
  182. }
  183. // single exercise choice
  184. // construction of Exercise
  185. $objExerciseTmp = new Exercise();
  186. $check = Security::check_token('get');
  187. $exercise_action_locked = api_resource_is_locked_by_gradebook(
  188. $exerciseId,
  189. LINK_EXERCISE
  190. );
  191. if ($objExerciseTmp->read($exerciseId)) {
  192. if ($check) {
  193. switch ($choice) {
  194. case 'enable_launch':
  195. $objExerciseTmp->cleanCourseLaunchSettings();
  196. $objExerciseTmp->enableAutoLaunch();
  197. Display::addFlash(Display::return_message(get_lang('Updated')));
  198. break;
  199. case 'disable_launch':
  200. $objExerciseTmp->cleanCourseLaunchSettings();
  201. break;
  202. case 'delete':
  203. // deletes an exercise
  204. if ($exercise_action_locked == false) {
  205. if ($limitTeacherAccess && !api_is_platform_admin()) {
  206. // Teacher cannot delete an exercise
  207. break;
  208. }
  209. $objExerciseTmp->delete();
  210. $link_info = GradebookUtils::isResourceInCourseGradebook(
  211. api_get_course_id(),
  212. 1,
  213. $exerciseId,
  214. api_get_session_id()
  215. );
  216. if ($link_info !== false) {
  217. GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
  218. }
  219. echo Display::return_message(get_lang('ExerciseDeleted'), 'confirmation');
  220. }
  221. break;
  222. case 'enable':
  223. if ($limitTeacherAccess && !api_is_platform_admin()) {
  224. // Teacher change exercise
  225. break;
  226. }
  227. // enables an exercise
  228. if (empty($sessionId)) {
  229. $objExerciseTmp->enable();
  230. $objExerciseTmp->save();
  231. } else {
  232. if (!empty($objExerciseTmp->sessionId)) {
  233. $objExerciseTmp->enable();
  234. $objExerciseTmp->save();
  235. }
  236. }
  237. api_item_property_update(
  238. $courseInfo,
  239. TOOL_QUIZ,
  240. $objExerciseTmp->id,
  241. 'visible',
  242. $userId
  243. );
  244. // "WHAT'S NEW" notification: update table item_property (previously last_tooledit)
  245. echo Display::return_message(get_lang('VisibilityChanged'), 'confirmation');
  246. break;
  247. case 'disable':
  248. if ($limitTeacherAccess && !api_is_platform_admin()) {
  249. // Teacher change exercise
  250. break;
  251. }
  252. // disables an exercise
  253. if (empty($sessionId)) {
  254. $objExerciseTmp->disable();
  255. $objExerciseTmp->save();
  256. } else {
  257. // Only change active if it belongs to a session
  258. if (!empty($objExerciseTmp->sessionId)) {
  259. $objExerciseTmp->disable();
  260. $objExerciseTmp->save();
  261. }
  262. }
  263. api_item_property_update(
  264. $courseInfo,
  265. TOOL_QUIZ,
  266. $objExerciseTmp->id,
  267. 'invisible',
  268. $userId
  269. );
  270. echo Display::return_message(
  271. get_lang('VisibilityChanged'),
  272. 'confirmation'
  273. );
  274. break;
  275. case 'disable_results':
  276. //disable the results for the learners
  277. $objExerciseTmp->disable_results();
  278. $objExerciseTmp->save();
  279. echo Display::return_message(
  280. get_lang('ResultsDisabled'),
  281. 'confirmation'
  282. );
  283. break;
  284. case 'enable_results':
  285. //disable the results for the learners
  286. $objExerciseTmp->enable_results();
  287. $objExerciseTmp->save();
  288. echo Display::return_message(
  289. get_lang('ResultsEnabled'),
  290. 'confirmation'
  291. );
  292. break;
  293. case 'clean_results':
  294. if ($limitTeacherAccess && !api_is_platform_admin()) {
  295. // Teacher change exercise
  296. break;
  297. }
  298. //clean student results
  299. if ($exercise_action_locked == false) {
  300. $quantity_results_deleted = $objExerciseTmp->cleanResults(true);
  301. $title = $objExerciseTmp->selectTitle();
  302. echo Display::return_message(
  303. $title.': '.sprintf(
  304. get_lang('XResultsCleaned'),
  305. $quantity_results_deleted
  306. ),
  307. 'confirmation'
  308. );
  309. }
  310. break;
  311. case 'copy_exercise': //copy an exercise
  312. $objExerciseTmp->copyExercise();
  313. echo Display::return_message(
  314. get_lang('ExerciseCopied'),
  315. 'confirmation'
  316. );
  317. break;
  318. }
  319. }
  320. }
  321. // destruction of Exercise
  322. unset($objExerciseTmp);
  323. Security::clear_token();
  324. }
  325. if (!empty($hpchoice)) {
  326. switch ($hpchoice) {
  327. case 'delete':
  328. if ($limitTeacherAccess && !api_is_platform_admin()) {
  329. // Teacher change exercise
  330. break;
  331. }
  332. // deletes an exercise
  333. $imgparams = [];
  334. $imgcount = 0;
  335. GetImgParams($file, $documentPath, $imgparams, $imgcount);
  336. $fld = GetFolderName($file);
  337. for ($i = 0; $i < $imgcount; $i++) {
  338. my_delete($documentPath.$uploadPath."/".$fld."/".$imgparams[$i]);
  339. DocumentManager::updateDbInfo("delete", $uploadPath."/".$fld."/".$imgparams[$i]);
  340. }
  341. if (!is_dir($documentPath.$uploadPath."/".$fld."/")) {
  342. my_delete($documentPath.$file);
  343. DocumentManager::updateDbInfo("delete", $file);
  344. } else {
  345. if (my_delete($documentPath.$file)) {
  346. DocumentManager::updateDbInfo("delete", $file);
  347. }
  348. }
  349. /* hotpotatoes folder may contains several tests so
  350. don't delete folder if not empty :
  351. http://support.chamilo.org/issues/2165
  352. */
  353. if (!(strstr($uploadPath, DIR_HOTPOTATOES) && !folder_is_empty($documentPath.$uploadPath."/".$fld."/"))) {
  354. my_delete($documentPath.$uploadPath."/".$fld."/");
  355. }
  356. break;
  357. case 'enable': // enables an exercise
  358. if ($limitTeacherAccess && !api_is_platform_admin()) {
  359. // Teacher change exercise
  360. break;
  361. }
  362. $newVisibilityStatus = '1'; //"visible"
  363. $query = "SELECT id FROM $TBL_DOCUMENT
  364. WHERE c_id = $courseId AND path='".Database :: escape_string($file)."'";
  365. $res = Database::query($query);
  366. $row = Database :: fetch_array($res, 'ASSOC');
  367. api_item_property_update(
  368. $courseInfo,
  369. TOOL_DOCUMENT,
  370. $row['id'],
  371. 'visible',
  372. $userId
  373. );
  374. //$dialogBox = get_lang('ViMod');
  375. break;
  376. case 'disable': // disables an exercise
  377. if ($limitTeacherAccess && !api_is_platform_admin()) {
  378. // Teacher change exercise
  379. break;
  380. }
  381. $newVisibilityStatus = '0'; //"invisible"
  382. $query = "SELECT id FROM $TBL_DOCUMENT
  383. WHERE c_id = $courseId AND path='".Database :: escape_string($file)."'";
  384. $res = Database::query($query);
  385. $row = Database :: fetch_array($res, 'ASSOC');
  386. api_item_property_update(
  387. $courseInfo,
  388. TOOL_DOCUMENT,
  389. $row['id'],
  390. 'invisible',
  391. $userId
  392. );
  393. break;
  394. default:
  395. break;
  396. }
  397. }
  398. }
  399. // Actions div bar
  400. if ($is_allowedToEdit) {
  401. echo '<div class="actions">';
  402. }
  403. // Selects $limit exercises at the same time
  404. // maximum number of exercises on a same page
  405. $limit = 50;
  406. // Display the next and previous link if needed
  407. $from = $page * $limit;
  408. HotPotGCt($documentPath, 1, $userId);
  409. //condition for the session
  410. $course_code = api_get_course_id();
  411. $condition_session = api_get_session_condition($sessionId, true, true);
  412. // Only for administrators
  413. if ($is_allowedToEdit) {
  414. $total_sql = "SELECT count(iid) as count
  415. FROM $TBL_EXERCISES
  416. WHERE c_id = $courseId AND active<>'-1' $condition_session ";
  417. $sql = "SELECT * FROM $TBL_EXERCISES
  418. WHERE c_id = $courseId AND active<>'-1' $condition_session
  419. ORDER BY title
  420. LIMIT ".$from.",".$limit;
  421. } else {
  422. // Only for students
  423. $total_sql = "SELECT count(iid) as count
  424. FROM $TBL_EXERCISES
  425. WHERE c_id = $courseId AND active = '1' $condition_session ";
  426. $sql = "SELECT * FROM $TBL_EXERCISES
  427. WHERE c_id = $courseId AND
  428. active='1' $condition_session
  429. ORDER BY title LIMIT ".$from.",".$limit;
  430. }
  431. $result = Database::query($sql);
  432. $result_total = Database::query($total_sql);
  433. $total_exercises = 0;
  434. if (Database :: num_rows($result_total)) {
  435. $result_total = Database::fetch_array($result_total);
  436. $total_exercises = $result_total['count'];
  437. }
  438. //get HotPotatoes files (active and inactive)
  439. if ($is_allowedToEdit) {
  440. $sql = "SELECT * FROM $TBL_DOCUMENT
  441. WHERE
  442. c_id = $courseId AND
  443. path LIKE '".Database :: escape_string($uploadPath.'/%/%')."'";
  444. $res = Database::query($sql);
  445. $hp_count = Database :: num_rows($res);
  446. } else {
  447. $sql = "SELECT * FROM $TBL_DOCUMENT d
  448. INNER JOIN $TBL_ITEM_PROPERTY ip
  449. ON (d.id = ip.ref AND d.c_id = ip.c_id)
  450. WHERE
  451. ip.tool = '".TOOL_DOCUMENT."' AND
  452. d.path LIKE '".Database :: escape_string($uploadPath.'/%/%')."' AND
  453. ip.visibility ='1' AND
  454. d.c_id = $courseId AND
  455. ip.c_id = $courseId";
  456. $res = Database::query($sql);
  457. $hp_count = Database :: num_rows($res);
  458. }
  459. $total = $total_exercises + $hp_count;
  460. $token = Security::get_token();
  461. if ($is_allowedToEdit && $origin != 'learnpath') {
  462. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise_admin.php?'.api_get_cidreq().'">'.
  463. Display::return_icon('new_exercice.png', get_lang('NewEx'), '', ICON_SIZE_MEDIUM).'</a>';
  464. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/question_create.php?'.api_get_cidreq().'">'.
  465. Display::return_icon('new_question.png', get_lang('AddQ'), '', ICON_SIZE_MEDIUM).'</a>';
  466. // Question category
  467. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/tests_category.php?'.api_get_cidreq().'">';
  468. echo Display::return_icon('green_open.png', get_lang('QuestionCategory'), '', ICON_SIZE_MEDIUM);
  469. echo '</a>';
  470. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/question_pool.php?'.api_get_cidreq().'">';
  471. echo Display::return_icon('database.png', get_lang('QuestionPool'), '', ICON_SIZE_MEDIUM);
  472. echo '</a>';
  473. //echo Display::url(Display::return_icon('looknfeel.png', get_lang('Media')), 'media.php?' . api_get_cidreq());
  474. // end question category
  475. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/hotpotatoes.php?'.api_get_cidreq().'">'.Display::return_icon('import_hotpotatoes.png', get_lang('ImportHotPotatoesQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
  476. // link to import qti2 ...
  477. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/qti2.php?'.api_get_cidreq().'">'.Display::return_icon('import_qti2.png', get_lang('ImportQtiQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
  478. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/aiken.php?'.api_get_cidreq().'">'.Display::return_icon('import_aiken.png', get_lang('ImportAikenQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
  479. echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/upload_exercise.php?'.api_get_cidreq().'">'.Display::return_icon('import_excel.png', get_lang('ImportExcelQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
  480. echo Display::url(
  481. Display::return_icon(
  482. 'clean_all.png',
  483. get_lang('CleanAllStudentsResultsForAllTests'),
  484. '',
  485. ICON_SIZE_MEDIUM
  486. ),
  487. '#',
  488. [
  489. 'data-item-question' => addslashes(get_lang('AreYouSureToEmptyAllTestResults')),
  490. 'data-href' => api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq().'&choice=clean_all_test&sec_token='.$token,
  491. 'data-toggle' => 'modal',
  492. 'data-target' => '#confirm-delete',
  493. ]
  494. );
  495. }
  496. if ($is_allowedToEdit) {
  497. echo '</div>'; // closing the actions div
  498. }
  499. if ($total > $limit) {
  500. echo '<div style="float:right;height:20px;">';
  501. //show pages navigation link for previous page
  502. if ($page) {
  503. echo "<a href=\"".api_get_self()."?".api_get_cidreq()."&page=".($page - 1)."\">".Display::return_icon('action_prev.png', get_lang('PreviousPage'))."</a>";
  504. } elseif ($total_exercises + $hp_count > $limit) {
  505. echo Display::return_icon('action_prev_na.png', get_lang('PreviousPage'));
  506. }
  507. //show pages navigation link for previous page
  508. if ($total_exercises > $from + $limit || $hp_count > $from + $limit) {
  509. echo ' '."<a href=\"".api_get_self()."?".api_get_cidreq()."&page=".($page + 1)."\">".Display::return_icon('action_next.png', get_lang('NextPage'))."</a>";
  510. } elseif ($page) {
  511. echo ' '.Display::return_icon('action_next_na.png', get_lang('NextPage'));
  512. }
  513. echo '</div>';
  514. }
  515. $i = 1;
  516. $online_icon = Display::return_icon(
  517. 'online.png',
  518. get_lang('Visible'),
  519. ['width' => '12px']
  520. );
  521. $offline_icon = Display::return_icon(
  522. 'offline.png',
  523. get_lang('Invisible'),
  524. ['width' => '12px']
  525. );
  526. $exerciseList = [];
  527. $list_ordered = null;
  528. while ($row = Database :: fetch_array($result, 'ASSOC')) {
  529. $exerciseList[$row['iid']] = $row;
  530. }
  531. if (!empty($exerciseList) &&
  532. api_get_setting('exercise_invisible_in_session') === 'true'
  533. ) {
  534. if (!empty($sessionId)) {
  535. $changeDefaultVisibility = true;
  536. if (api_get_setting('configure_exercise_visibility_in_course') === 'true') {
  537. $changeDefaultVisibility = false;
  538. if (api_get_course_setting('exercise_invisible_in_session') == 1) {
  539. $changeDefaultVisibility = true;
  540. }
  541. }
  542. if ($changeDefaultVisibility) {
  543. // Check exercise
  544. foreach ($exerciseList as $exercise) {
  545. if ($exercise['session_id'] == 0) {
  546. $visibilityInfo = api_get_item_property_info(
  547. $courseId,
  548. TOOL_QUIZ,
  549. $exercise['iid'],
  550. $sessionId
  551. );
  552. if (empty($visibilityInfo)) {
  553. // Create a record for this
  554. api_item_property_update(
  555. $courseInfo,
  556. TOOL_QUIZ,
  557. $exercise['iid'],
  558. 'invisible',
  559. api_get_user_id(),
  560. 0,
  561. null,
  562. '',
  563. '',
  564. $sessionId
  565. );
  566. }
  567. }
  568. }
  569. }
  570. }
  571. }
  572. if (isset($list_ordered) && !empty($list_ordered)) {
  573. $new_question_list = [];
  574. foreach ($list_ordered as $exercise_id) {
  575. if (isset($exerciseList[$exercise_id])) {
  576. $new_question_list[] = $exerciseList[$exercise_id];
  577. }
  578. }
  579. $exerciseList = $new_question_list;
  580. }
  581. $tableRows = [];
  582. /* Listing exercises */
  583. if (!empty($exerciseList)) {
  584. if ($origin != 'learnpath') {
  585. //avoid sending empty parameters
  586. $mylpid = (empty($learnpath_id) ? '' : '&learnpath_id='.$learnpath_id);
  587. $mylpitemid = (empty($learnpath_item_id) ? '' : '&learnpath_item_id='.$learnpath_item_id);
  588. $i = 1;
  589. foreach ($exerciseList as $row) {
  590. $my_exercise_id = $row['id'];
  591. $exercise = new Exercise();
  592. $exercise->read($my_exercise_id, false);
  593. if (empty($exercise->id)) {
  594. continue;
  595. }
  596. $locked = $exercise->is_gradebook_locked;
  597. $i++;
  598. // Validation when belongs to a session
  599. $session_img = api_get_session_image($row['session_id'], $userInfo['status']);
  600. $time_limits = false;
  601. if (!empty($row['start_time']) || !empty($row['end_time'])) {
  602. $time_limits = true;
  603. }
  604. $is_actived_time = false;
  605. if ($time_limits) {
  606. // check if start time
  607. $start_time = false;
  608. if (!empty($row['start_time'])) {
  609. $start_time = api_strtotime($row['start_time'], 'UTC');
  610. }
  611. $end_time = false;
  612. if (!empty($row['end_time'])) {
  613. $end_time = api_strtotime($row['end_time'], 'UTC');
  614. }
  615. $now = time();
  616. //If both "clocks" are enable
  617. if ($start_time && $end_time) {
  618. if ($now > $start_time && $end_time > $now) {
  619. $is_actived_time = true;
  620. }
  621. } else {
  622. //we check the start and end
  623. if ($start_time) {
  624. if ($now > $start_time) {
  625. $is_actived_time = true;
  626. }
  627. }
  628. if ($end_time) {
  629. if ($end_time > $now) {
  630. $is_actived_time = true;
  631. }
  632. }
  633. }
  634. }
  635. // Blocking empty start times see BT#2800
  636. global $_custom;
  637. if (isset($_custom['exercises_hidden_when_no_start_date']) &&
  638. $_custom['exercises_hidden_when_no_start_date']
  639. ) {
  640. if (empty($row['start_time'])) {
  641. $time_limits = true;
  642. $is_actived_time = false;
  643. }
  644. }
  645. $cut_title = $exercise->getCutTitle();
  646. $alt_title = '';
  647. if ($cut_title != $row['title']) {
  648. $alt_title = ' title = "'.$exercise->getUnformattedTitle().'" ';
  649. }
  650. // Teacher only
  651. if ($is_allowedToEdit) {
  652. $lp_blocked = null;
  653. if ($exercise->exercise_was_added_in_lp == true) {
  654. $lp_blocked = Display::div(
  655. get_lang('AddedToLPCannotBeAccessed'),
  656. ['class' => 'lp_content_type_label']
  657. );
  658. }
  659. $visibility = api_get_item_visibility(
  660. $courseInfo,
  661. TOOL_QUIZ,
  662. $my_exercise_id,
  663. 0
  664. );
  665. if (!empty($sessionId)) {
  666. $setting = api_get_configuration_value('show_hidden_exercise_added_to_lp');
  667. if ($setting) {
  668. if ($exercise->exercise_was_added_in_lp == false) {
  669. if ($visibility == 0) {
  670. continue;
  671. }
  672. }
  673. } else {
  674. if ($visibility == 0) {
  675. continue;
  676. }
  677. }
  678. $visibility = api_get_item_visibility(
  679. $courseInfo,
  680. TOOL_QUIZ,
  681. $my_exercise_id,
  682. $sessionId
  683. );
  684. }
  685. if ($row['active'] == 0 || $visibility == 0) {
  686. $title = Display::tag('font', $cut_title, ['style' => 'color:grey']);
  687. } else {
  688. $title = $cut_title;
  689. }
  690. $count_exercise_not_validated = (int) Event::count_exercise_result_not_validated(
  691. $my_exercise_id,
  692. $courseId,
  693. $sessionId
  694. );
  695. $move = Display::return_icon(
  696. 'all_directions.png',
  697. get_lang('Move'),
  698. ['class' => 'moved', 'style' => 'margin-bottom:-0.5em;']
  699. );
  700. $move = null;
  701. $class_tip = '';
  702. if (!empty($count_exercise_not_validated)) {
  703. $results_text = $count_exercise_not_validated == 1 ? get_lang('ResultNotRevised') : get_lang('ResultsNotRevised');
  704. $title .= '<span class="exercise_tooltip" style="display: none;">'.$count_exercise_not_validated.' '.$results_text.' </span>';
  705. $class_tip = 'link_tooltip';
  706. }
  707. $url = $move.'<a '.$alt_title.' class="'.$class_tip.'" id="tooltip_'.$row['id'].'" href="overview.php?'.api_get_cidreq().$mylpid.$mylpitemid.'&exerciseId='.$row['id'].'">
  708. '.Display::return_icon('quiz.png', $row['title']).'
  709. '.$title.' </a>';
  710. $item = Display::tag('td', $url.' '.$session_img.$lp_blocked);
  711. // Count number exercise - teacher
  712. $sql = "SELECT count(*) count FROM $TBL_EXERCISE_QUESTION
  713. WHERE c_id = $courseId AND exercice_id = $my_exercise_id";
  714. $sqlresult = Database::query($sql);
  715. $rowi = intval(Database :: result($sqlresult, 0, 0));
  716. if ($sessionId == $row['session_id']) {
  717. // Questions list
  718. $actions = Display::url(
  719. Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL),
  720. 'admin.php?'.api_get_cidreq().'&exerciseId='.$row['id']
  721. );
  722. // Test settings
  723. $settings = Display::url(
  724. Display::return_icon('settings.png', get_lang('Configure'), '', ICON_SIZE_SMALL),
  725. 'exercise_admin.php?'.api_get_cidreq().'&exerciseId='.$row['id']
  726. );
  727. if ($limitTeacherAccess && !api_is_platform_admin()) {
  728. $settings = '';
  729. }
  730. $actions .= $settings;
  731. // Exercise results
  732. $actions .= '<a href="exercise_report.php?'.api_get_cidreq().'&exerciseId='.$row['id'].'">'.
  733. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  734. // Auto launch
  735. if ($autoLaunchAvailable) {
  736. $autoLaunch = $exercise->getAutoLaunch();
  737. if (empty($autoLaunch)) {
  738. $actions .= Display::url(
  739. Display::return_icon(
  740. 'launch_na.png',
  741. get_lang('Enable'),
  742. '',
  743. ICON_SIZE_SMALL
  744. ),
  745. 'exercise.php?'.api_get_cidreq().'&choice=enable_launch&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  746. );
  747. } else {
  748. $actions .= Display::url(
  749. Display::return_icon(
  750. 'launch.png',
  751. get_lang('Disable'),
  752. '',
  753. ICON_SIZE_SMALL
  754. ),
  755. 'exercise.php?'.api_get_cidreq().'&choice=disable_launch&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  756. );
  757. }
  758. }
  759. // Export
  760. $actions .= Display::url(
  761. Display::return_icon('cd.png', get_lang('CopyExercise')),
  762. '',
  763. [
  764. 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToCopy'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;",
  765. 'href' => 'exercise.php?'.api_get_cidreq().'&choice=copy_exercise&sec_token='.$token.'&exerciseId='.$row['id'],
  766. ]
  767. );
  768. // Clean exercise
  769. if ($locked == false) {
  770. $clean = Display::url(
  771. Display::return_icon(
  772. 'clean.png',
  773. get_lang('CleanStudentResults'),
  774. '',
  775. ICON_SIZE_SMALL
  776. ),
  777. '',
  778. [
  779. 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToDeleteResults'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;",
  780. 'href' => 'exercise.php?'.api_get_cidreq().'&choice=clean_results&sec_token='.$token.'&exerciseId='.$row['id'],
  781. ]
  782. );
  783. } else {
  784. $clean = Display::return_icon(
  785. 'clean_na.png',
  786. get_lang('ResourceLockedByGradebook'),
  787. '',
  788. ICON_SIZE_SMALL
  789. );
  790. }
  791. if ($limitTeacherAccess && !api_is_platform_admin()) {
  792. $clean = '';
  793. }
  794. $actions .= $clean;
  795. // Visible / invisible
  796. // Check if this exercise was added in a LP
  797. if ($exercise->exercise_was_added_in_lp == true) {
  798. $visibility = Display::return_icon(
  799. 'invisible.png',
  800. get_lang('AddedToLPCannotBeAccessed'),
  801. '',
  802. ICON_SIZE_SMALL
  803. );
  804. } else {
  805. if ($row['active'] == 0 || $visibility == 0) {
  806. $visibility = Display::url(
  807. Display::return_icon(
  808. 'invisible.png',
  809. get_lang('Activate'),
  810. '',
  811. ICON_SIZE_SMALL
  812. ),
  813. 'exercise.php?'.api_get_cidreq().'&choice=enable&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  814. );
  815. } else {
  816. // else if not active
  817. $visibility = Display::url(
  818. Display::return_icon(
  819. 'visible.png',
  820. get_lang('Deactivate'),
  821. '',
  822. ICON_SIZE_SMALL
  823. ),
  824. 'exercise.php?'.api_get_cidreq().'&choice=disable&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  825. );
  826. }
  827. }
  828. // Export qti ...
  829. if ($limitTeacherAccess && !api_is_platform_admin()) {
  830. $visibility = '';
  831. }
  832. $actions .= $visibility;
  833. // Export qti ...
  834. $export = Display::url(
  835. Display::return_icon(
  836. 'export_qti2.png',
  837. 'IMS/QTI',
  838. '',
  839. ICON_SIZE_SMALL
  840. ),
  841. 'exercise.php?choice=exportqti2&exerciseId='.$row['id'].'&'.api_get_cidreq()
  842. );
  843. if ($limitTeacherAccess && !api_is_platform_admin()) {
  844. $export = '';
  845. }
  846. $actions .= $export;
  847. } else {
  848. // not session
  849. $actions = Display::return_icon(
  850. 'edit_na.png',
  851. get_lang('ExerciseEditionNotAvailableInSession')
  852. );
  853. // Check if this exercise was added in a LP
  854. if ($exercise->exercise_was_added_in_lp == true) {
  855. $visiblity = Display::return_icon(
  856. 'invisible.png',
  857. get_lang('AddedToLPCannotBeAccessed'),
  858. '',
  859. ICON_SIZE_SMALL
  860. );
  861. } else {
  862. if ($row['active'] == 0 || $visibility == 0) {
  863. $visiblity = Display::url(
  864. Display::return_icon(
  865. 'invisible.png',
  866. get_lang('Activate'),
  867. '',
  868. ICON_SIZE_SMALL
  869. ),
  870. 'exercise.php?'.api_get_cidreq().'&choice=enable&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  871. );
  872. } else {
  873. // else if not active
  874. $visiblity = Display::url(
  875. Display::return_icon(
  876. 'visible.png',
  877. get_lang('Deactivate'),
  878. '',
  879. ICON_SIZE_SMALL
  880. ),
  881. 'exercise.php?'.api_get_cidreq().'&choice=disable&sec_token='.$token.'&page='.$page.'&exerciseId='.$row['id']
  882. );
  883. }
  884. }
  885. if ($limitTeacherAccess && !api_is_platform_admin()) {
  886. $visiblity = '';
  887. }
  888. $actions .= $visiblity;
  889. $actions .= '<a href="exercise_report.php?'.api_get_cidreq().'&exerciseId='.$row['id'].'">'.
  890. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  891. $actions .= Display::url(
  892. Display::return_icon('cd.gif', get_lang('CopyExercise')),
  893. '',
  894. [
  895. 'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang('AreYouSureToCopy'), ENT_QUOTES, $charset))." ".addslashes($row['title'])."?"."')) return false;",
  896. 'href' => 'exercise.php?'.api_get_cidreq().'&choice=copy_exercise&sec_token='.$token.'&exerciseId='.$row['id'],
  897. ]
  898. );
  899. }
  900. // Delete
  901. $delete = '';
  902. if ($sessionId == $row['session_id']) {
  903. if ($locked == false) {
  904. $delete = Display::url(
  905. Display::return_icon(
  906. 'delete.png',
  907. get_lang('Delete'),
  908. '',
  909. ICON_SIZE_SMALL
  910. ),
  911. '',
  912. [
  913. 'href' => 'exercise.php?'.api_get_cidreq().'&choice=delete&sec_token='.$token.'&exerciseId='.$row['id'],
  914. 'title' => addslashes(api_htmlentities(get_lang('AreYouSureToDeleteJS'),ENT_QUOTES, $charset))." ".addslashes($exercise->getUnformattedTitle())."?",
  915. 'class' => 'delete-swal'
  916. ]
  917. );
  918. } else {
  919. $delete = Display::return_icon(
  920. 'delete_na.png',
  921. get_lang('ResourceLockedByGradebook'),
  922. '',
  923. ICON_SIZE_SMALL
  924. );
  925. }
  926. }
  927. if ($limitTeacherAccess && !api_is_platform_admin()) {
  928. $delete = '';
  929. }
  930. $actions .= $delete;
  931. // Number of questions
  932. $random_label = null;
  933. if ($row['random'] > 0 || $row['random'] == -1) {
  934. // if random == -1 means use random questions with all questions
  935. $random_number_of_question = $row['random'];
  936. if ($random_number_of_question == -1) {
  937. $random_number_of_question = $rowi;
  938. }
  939. if ($row['random_by_category'] > 0) {
  940. $nbQuestionsTotal = TestCategory::getNumberOfQuestionRandomByCategory(
  941. $my_exercise_id,
  942. $random_number_of_question
  943. );
  944. $number_of_questions = $nbQuestionsTotal.' ';
  945. $number_of_questions .= ($nbQuestionsTotal > 1) ? get_lang('QuestionsLowerCase') : get_lang('QuestionLowerCase');
  946. $number_of_questions .= ' - ';
  947. $number_of_questions .= min(TestCategory::getNumberMaxQuestionByCat($my_exercise_id), $random_number_of_question).' '.get_lang('QuestionByCategory');
  948. } else {
  949. $random_label = ' ('.get_lang('Random').') ';
  950. $number_of_questions = $random_number_of_question.' '.$random_label;
  951. //Bug if we set a random value bigger than the real number of questions
  952. if ($random_number_of_question > $rowi) {
  953. $number_of_questions = $rowi.' '.$random_label;
  954. }
  955. }
  956. } else {
  957. $number_of_questions = $rowi;
  958. }
  959. $item .= Display::tag('td', $number_of_questions);
  960. } else {
  961. // Student only.
  962. $visibility = api_get_item_visibility(
  963. $courseInfo,
  964. TOOL_QUIZ,
  965. $my_exercise_id,
  966. $sessionId
  967. );
  968. if ($visibility == 0) {
  969. continue;
  970. }
  971. $url = '<a '.$alt_title.' href="overview.php?'.api_get_cidreq().$mylpid.$mylpitemid.'&exerciseId='.$row['id'].'">'.
  972. $cut_title.'</a>';
  973. // Link of the exercise.
  974. $item = Display::tag('td', $url.' '.$session_img);
  975. // Count number exercise questions.
  976. /*$sql = "SELECT count(*) FROM $TBL_EXERCISE_QUESTION
  977. WHERE c_id = $courseId AND exercice_id = ".$row['id'];
  978. $sqlresult = Database::query($sql);
  979. $rowi = Database::result($sqlresult, 0);
  980. if ($row['random'] > 0) {
  981. $row['random'].' '.api_strtolower(get_lang(($row['random'] > 1 ? 'Questions' : 'Question')));
  982. } else {
  983. //show results student
  984. $rowi.' '.api_strtolower(get_lang(($rowi > 1 ? 'Questions' : 'Question')));
  985. }*/
  986. // This query might be improved later on by ordering by the new "tms" field rather than by exe_id
  987. // Don't remove this marker: note-query-exe-results
  988. $sql = "SELECT * FROM $TBL_TRACK_EXERCISES
  989. WHERE
  990. exe_exo_id = ".$row['id']." AND
  991. exe_user_id = $userId AND
  992. c_id = ".api_get_course_int_id()." AND
  993. status <> 'incomplete' AND
  994. orig_lp_id = 0 AND
  995. orig_lp_item_id = 0 AND
  996. session_id = '".api_get_session_id()."'
  997. ORDER BY exe_id DESC";
  998. $qryres = Database::query($sql);
  999. $num = Database :: num_rows($qryres);
  1000. // Hide the results.
  1001. $my_result_disabled = $row['results_disabled'];
  1002. // Time limits are on
  1003. if ($time_limits) {
  1004. // Exam is ready to be taken
  1005. if ($is_actived_time) {
  1006. // Show results 697 $attempt_text = get_lang('LatestAttempt').' : ';
  1007. if (
  1008. in_array(
  1009. $my_result_disabled,
  1010. [
  1011. RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS,
  1012. RESULT_DISABLE_SHOW_SCORE_ONLY,
  1013. RESULT_DISABLE_RANKING,
  1014. ]
  1015. )
  1016. ) {
  1017. //More than one attempt
  1018. if ($num > 0) {
  1019. $row_track = Database :: fetch_array($qryres);
  1020. $attempt_text = get_lang('LatestAttempt').' : ';
  1021. $attempt_text .= ExerciseLib::show_score($row_track['score'], $row_track['max_score']);
  1022. } else {
  1023. //No attempts
  1024. $attempt_text = get_lang('NotAttempted');
  1025. }
  1026. } else {
  1027. //$attempt_text = get_lang('CantShowResults');
  1028. $attempt_text = '-';
  1029. }
  1030. } else {
  1031. //Quiz not ready due to time limits 700 $attempt_text = get_lang('NotAttempted');
  1032. //@todo use the is_visible function
  1033. if (!empty($row['start_time']) && !empty($row['end_time'])) {
  1034. $today = time();
  1035. $start_time = api_strtotime($row['start_time'], 'UTC');
  1036. $end_time = api_strtotime($row['end_time'], 'UTC');
  1037. if ($today < $start_time) {
  1038. $attempt_text = sprintf(
  1039. get_lang('ExerciseWillBeActivatedFromXToY'),
  1040. api_convert_and_format_date($row['start_time']),
  1041. api_convert_and_format_date($row['end_time'])
  1042. );
  1043. } else {
  1044. if ($today > $end_time) {
  1045. $attempt_text = sprintf(
  1046. get_lang('ExerciseWasActivatedFromXToY'),
  1047. api_convert_and_format_date($row['start_time']),
  1048. api_convert_and_format_date($row['end_time'])
  1049. );
  1050. }
  1051. }
  1052. } else {
  1053. //$attempt_text = get_lang('ExamNotAvailableAtThisTime');
  1054. if (!empty($row['start_time'])) {
  1055. $attempt_text = sprintf(
  1056. get_lang('ExerciseAvailableFromX'),
  1057. api_convert_and_format_date($row['start_time'])
  1058. );
  1059. }
  1060. if (!empty($row['end_time'])) {
  1061. $attempt_text = sprintf(
  1062. get_lang('ExerciseAvailableUntilX'),
  1063. api_convert_and_format_date($row['end_time'])
  1064. );
  1065. }
  1066. }
  1067. }
  1068. } else {
  1069. // Normal behaviour.
  1070. // Show results.
  1071. if (
  1072. in_array(
  1073. $my_result_disabled,
  1074. [
  1075. RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS,
  1076. RESULT_DISABLE_SHOW_SCORE_ONLY,
  1077. RESULT_DISABLE_RANKING,
  1078. ]
  1079. )
  1080. ) {
  1081. if ($num > 0) {
  1082. $row_track = Database :: fetch_array($qryres);
  1083. $attempt_text = get_lang('LatestAttempt').' : ';
  1084. $attempt_text .= ExerciseLib::show_score(
  1085. $row_track['score'],
  1086. $row_track['max_score']
  1087. );
  1088. } else {
  1089. $attempt_text = get_lang('NotAttempted');
  1090. }
  1091. } else {
  1092. $attempt_text = '-';
  1093. }
  1094. }
  1095. $class_tip = '';
  1096. if (empty($num)) {
  1097. $num = '';
  1098. } else {
  1099. $class_tip = 'link_tooltip';
  1100. //@todo use sprintf and show the results validated by the teacher
  1101. if ($num == 1) {
  1102. $num = $num.' '.get_lang('Result');
  1103. } else {
  1104. $num = $num.' '.get_lang('Results');
  1105. }
  1106. $num = '<span class="tooltip" style="display: none;">'.$num.'</span>';
  1107. }
  1108. $item .= Display::tag('td', $attempt_text);
  1109. }
  1110. if ($is_allowedToEdit) {
  1111. $item .= Display::tag('td', $actions, ['class' => 'td_actions']);
  1112. } else {
  1113. if ($isDrhOfCourse) {
  1114. $actions = '<a href="exercise_report.php?'.api_get_cidreq().'&exerciseId='.$row['id'].'">'.
  1115. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  1116. $item .= Display::tag('td', $actions, ['class' => 'td_actions']);
  1117. }
  1118. }
  1119. $tableRows[] = Display::tag(
  1120. 'tr',
  1121. $item,
  1122. [
  1123. 'id' => 'exercise_list_'.$my_exercise_id,
  1124. ]
  1125. );
  1126. }
  1127. }
  1128. }
  1129. // end exercise list
  1130. // Hotpotatoes results
  1131. $hotpotatoes_exist = false;
  1132. if ($is_allowedToEdit) {
  1133. $sql = "SELECT d.iid, d.path as path, d.comment as comment
  1134. FROM $TBL_DOCUMENT d
  1135. WHERE
  1136. d.c_id = $courseId AND
  1137. (d.path LIKE '%htm%') AND
  1138. d.path LIKE '".Database :: escape_string($uploadPath.'/%/%')."'
  1139. LIMIT ".$from.",".$limit; // only .htm or .html files listed
  1140. } else {
  1141. $sql = "SELECT d.iid, d.path as path, d.comment as comment
  1142. FROM $TBL_DOCUMENT d
  1143. WHERE
  1144. d.c_id = $courseId AND
  1145. (d.path LIKE '%htm%') AND
  1146. d.path LIKE '".Database :: escape_string($uploadPath.'/%/%')."'
  1147. LIMIT ".$from.",".$limit;
  1148. }
  1149. $result = Database::query($sql);
  1150. $attribute = [];
  1151. while ($row = Database :: fetch_array($result, 'ASSOC')) {
  1152. $attribute['id'][] = $row['iid'];
  1153. $attribute['path'][] = $row['path'];
  1154. $attribute['comment'][] = $row['comment'];
  1155. }
  1156. $nbrActiveTests = 0;
  1157. if (isset($attribute['path']) && is_array($attribute['path'])) {
  1158. $hotpotatoes_exist = true;
  1159. foreach ($attribute['path'] as $key => $path) {
  1160. $item = '';
  1161. $title = GetQuizName($path, $documentPath);
  1162. if ($title == '') {
  1163. $title = basename($path);
  1164. }
  1165. // prof only
  1166. if ($is_allowedToEdit) {
  1167. $visibility = api_get_item_visibility(
  1168. ['real_id' => $courseId],
  1169. TOOL_DOCUMENT,
  1170. $attribute['id'][$key],
  1171. 0
  1172. );
  1173. if (!empty($sessionId)) {
  1174. if (0 == $visibility) {
  1175. continue;
  1176. }
  1177. $visibility = api_get_item_visibility(
  1178. ['real_id' => $courseId],
  1179. TOOL_DOCUMENT,
  1180. $attribute['id'][$key],
  1181. $sessionId
  1182. );
  1183. }
  1184. $item = Display::tag(
  1185. 'td',
  1186. implode(PHP_EOL, [
  1187. Display::return_icon('hotpotatoes_s.png', "HotPotatoes"),
  1188. Display::url(
  1189. $title,
  1190. 'showinframes.php?'.api_get_cidreq().'&'.http_build_query([
  1191. 'file' => $path,
  1192. 'uid' => $userId,
  1193. ]),
  1194. ['class' => $visibility == 0 ? 'text-muted' : null]
  1195. ),
  1196. ])
  1197. );
  1198. $item .= Display::tag('td', '-');
  1199. $actions = Display::url(
  1200. Display::return_icon(
  1201. 'edit.png',
  1202. get_lang('Edit'),
  1203. '',
  1204. ICON_SIZE_SMALL
  1205. ),
  1206. 'adminhp.php?'.api_get_cidreq().'&hotpotatoesName='.$path
  1207. );
  1208. $actions .= '<a href="hotpotatoes_exercise_report.php?'.api_get_cidreq().'&path='.$path.'">'.
  1209. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  1210. // if active
  1211. if ($visibility != 0) {
  1212. $nbrActiveTests = $nbrActiveTests + 1;
  1213. $actions .= ' <a href="'.$exercisePath.'?'.api_get_cidreq().'&hpchoice=disable&page='.$page.'&file='.$path.'">'.
  1214. Display::return_icon('visible.png', get_lang('Deactivate'), '', ICON_SIZE_SMALL).'</a>';
  1215. } else { // else if not active
  1216. $actions .= ' <a href="'.$exercisePath.'?'.api_get_cidreq().'&hpchoice=enable&page='.$page.'&file='.$path.'">'.
  1217. Display::return_icon('invisible.png', get_lang('Activate'), '', ICON_SIZE_SMALL).'</a>';
  1218. }
  1219. $actions .= '<a href="'.$exercisePath.'?'.api_get_cidreq().'&hpchoice=delete&file='.$path.'" onclick="javascript:if(!confirm(\''.addslashes(api_htmlentities(get_lang('AreYouSureToDeleteJS'), ENT_QUOTES, $charset).' '.$title."?").'\')) return false;">'.
  1220. Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL).'</a>';
  1221. $item .= Display::tag('td', $actions);
  1222. $tableRows[] = Display::tag('tr', $item);
  1223. } else {
  1224. $visibility = api_get_item_visibility(
  1225. ['real_id' => $courseId],
  1226. TOOL_DOCUMENT,
  1227. $attribute['id'][$key],
  1228. $sessionId
  1229. );
  1230. if (0 == $visibility) {
  1231. continue;
  1232. }
  1233. // Student only
  1234. $attempt = ExerciseLib::getLatestHotPotatoResult(
  1235. $path,
  1236. $userId,
  1237. api_get_course_int_id(),
  1238. api_get_session_id()
  1239. );
  1240. $nbrActiveTests = $nbrActiveTests + 1;
  1241. $item .= Display::tag(
  1242. 'td',
  1243. Display::url(
  1244. $title,
  1245. 'showinframes.php?'.api_get_cidreq().'&'.http_build_query([
  1246. 'file' => $path,
  1247. 'cid' => api_get_course_id(),
  1248. 'uid' => $userId,
  1249. ])
  1250. )
  1251. );
  1252. if (!empty($attempt)) {
  1253. $actions = '<a href="hotpotatoes_exercise_report.php?'.api_get_cidreq().'&path='.$path.'&filter_by_user='.$userId.'">'.Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  1254. $attemptText = get_lang('LatestAttempt').' : ';
  1255. $attemptText .= ExerciseLib::show_score(
  1256. $attempt['score'],
  1257. $attempt['max_score']
  1258. ).' ';
  1259. $attemptText .= $actions;
  1260. } else {
  1261. // No attempts.
  1262. $attemptText = get_lang('NotAttempted').' ';
  1263. }
  1264. $item .= Display::tag('td', $attemptText);
  1265. if ($isDrhOfCourse) {
  1266. $actions = '<a href="hotpotatoes_exercise_report.php?'.api_get_cidreq().'&path='.$path.'">'.
  1267. Display::return_icon('test_results.png', get_lang('Results'), '', ICON_SIZE_SMALL).'</a>';
  1268. $item .= Display::tag(
  1269. 'td',
  1270. $actions,
  1271. ['class' => 'td_actions']
  1272. );
  1273. }
  1274. $tableRows[] = Display::tag('tr', $item);
  1275. }
  1276. }
  1277. }
  1278. if (empty($exerciseList) && $hotpotatoes_exist == false) {
  1279. if ($is_allowedToEdit && $origin != 'learnpath') {
  1280. echo '<div id="no-data-view">';
  1281. echo '<h3>'.get_lang('Quiz').'</h3>';
  1282. echo Display::return_icon('quiz.png', '', [], 64);
  1283. echo '<div class="controls">';
  1284. echo Display::url(
  1285. '<em class="fa fa-plus"></em> '.get_lang('NewEx'),
  1286. 'exercise_admin.php?'.api_get_cidreq(),
  1287. ['class' => 'btn btn-primary']
  1288. );
  1289. echo '</div>';
  1290. echo '</div>';
  1291. }
  1292. } else {
  1293. if ($is_allowedToEdit) {
  1294. $headers = [
  1295. get_lang('ExerciseName'),
  1296. get_lang('QuantityQuestions'),
  1297. get_lang('Actions'),
  1298. ];
  1299. } else {
  1300. $headers = [
  1301. get_lang('ExerciseName'),
  1302. get_lang('Status'),
  1303. ];
  1304. if ($isDrhOfCourse) {
  1305. $headers[] = get_lang('Actions');
  1306. }
  1307. }
  1308. $headerList = '';
  1309. foreach ($headers as $header) {
  1310. $headerList .= Display::tag('th', $header);
  1311. }
  1312. echo '<div class="table-responsive">';
  1313. echo '<table class="table table-hover">';
  1314. echo Display::tag(
  1315. 'thead',
  1316. Display::tag('tr', $headerList)
  1317. );
  1318. echo '<tbody>';
  1319. foreach ($tableRows as $row) {
  1320. echo $row;
  1321. }
  1322. echo '</tbody>';
  1323. echo '</table>';
  1324. echo '</div>';
  1325. }
  1326. if ($origin != 'learnpath') { //so we are not in learnpath tool
  1327. Display :: display_footer();
  1328. }