1 | <?php |
||
2 | |||
3 | /* For licensing terms, see /license.txt */ |
||
4 | |||
5 | use Chamilo\CoreBundle\Entity\SequenceResource; |
||
6 | use Chamilo\CourseBundle\Entity\CToolIntro; |
||
7 | |||
8 | /** |
||
9 | * The INTRODUCTION MICRO MODULE is used to insert and edit |
||
10 | * an introduction section on a Chamilo module or on the course homepage. |
||
11 | * It can be inserted on any Chamilo module, provided the corresponding setting |
||
12 | * is enabled in the administration section. |
||
13 | * |
||
14 | * The introduction content are stored in a table called "tool_intro" |
||
15 | * in the course Database. Each module introduction has an Id stored in |
||
16 | * the table, which matches a specific module. |
||
17 | * |
||
18 | * '(c_)tool_intro' table description |
||
19 | * c_id: int |
||
20 | * id : int |
||
21 | * intro_text :text |
||
22 | * session_id: int |
||
23 | * |
||
24 | * usage : |
||
25 | * |
||
26 | * $moduleId = 'XX'; // specifying the module tool (string value) |
||
27 | * include(introductionSection.inc.php); |
||
28 | * |
||
29 | * This script is also used since Chamilo 1.9 to show course progress (from the |
||
30 | * course_progress module) |
||
31 | */ |
||
32 | $em = Database::getManager(); |
||
33 | $intro_editAllowed = $is_allowed_to_edit = api_is_allowed_to_edit(); |
||
34 | $session_id = api_get_session_id(); |
||
35 | $blogParam = isset($_GET['blog_id']) ? ('&blog_id='.(int) $_GET['blog_id']) : ''; |
||
36 | $cidReq = api_get_cidreq(); |
||
37 | |||
38 | $introduction_section = ''; |
||
39 | |||
40 | global $charset; |
||
41 | $intro_cmdEdit = empty($_GET['intro_cmdEdit']) ? '' : $_GET['intro_cmdEdit']; |
||
42 | $intro_cmdUpdate = isset($_POST['intro_cmdUpdate']); |
||
43 | $intro_cmdDel = empty($_GET['intro_cmdDel']) ? '' : $_GET['intro_cmdDel']; |
||
44 | $intro_cmdAdd = empty($_GET['intro_cmdAdd']) ? '' : $_GET['intro_cmdAdd']; |
||
45 | $courseId = api_get_course_id(); |
||
46 | |||
47 | if (!empty($courseId)) { |
||
48 | $form = new FormValidator( |
||
49 | 'introduction_text', |
||
50 | 'post', |
||
51 | api_get_self().'?'.$cidReq.$blogParam |
||
52 | ); |
||
53 | } else { |
||
54 | $form = new FormValidator('introduction_text'); |
||
55 | } |
||
56 | |||
57 | $config = [ |
||
58 | 'ToolbarSet' => 'IntroductionSection', |
||
59 | 'Width' => '100%', |
||
60 | 'Height' => '300', |
||
61 | ]; |
||
62 | |||
63 | $form->addHtmlEditor('intro_content', null, false, false, $config); |
||
64 | $form->addButtonSave(get_lang('SaveIntroText'), 'intro_cmdUpdate'); |
||
65 | |||
66 | /* INTRODUCTION MICRO MODULE - COMMANDS SECTION (IF ALLOWED) */ |
||
67 | $course_id = api_get_course_int_id(); |
||
68 | |||
69 | if ($intro_editAllowed) { |
||
70 | /** @var CToolIntro $toolIntro */ |
||
71 | $toolIntro = $em |
||
72 | ->getRepository('ChamiloCourseBundle:CToolIntro') |
||
73 | ->findOneBy(['cId' => $course_id, 'id' => $moduleId, 'sessionId' => $session_id]); |
||
74 | |||
75 | /* Replace command */ |
||
76 | if ($intro_cmdUpdate) { |
||
77 | if ($form->validate()) { |
||
78 | $form_values = $form->exportValues(); |
||
79 | $intro_content = $form_values['intro_content']; |
||
80 | if (!empty($intro_content)) { |
||
81 | if (!$toolIntro) { |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
82 | $toolIntro = new CToolIntro(); |
||
83 | $toolIntro |
||
84 | ->setSessionId($session_id) |
||
85 | ->setCId($course_id) |
||
86 | ->setId($moduleId); |
||
87 | } |
||
88 | |||
89 | $toolIntro->setIntroText($intro_content); |
||
90 | |||
91 | $em->persist($toolIntro); |
||
92 | $em->flush(); |
||
93 | Display::addFlash(Display::return_message(get_lang('IntroductionTextUpdated'), 'confirmation', false)); |
||
94 | } else { |
||
95 | // got to the delete command |
||
96 | $intro_cmdDel = true; |
||
97 | } |
||
98 | } else { |
||
99 | $intro_cmdEdit = true; |
||
100 | } |
||
101 | } |
||
102 | |||
103 | /* Delete Command */ |
||
104 | if ($intro_cmdDel && $toolIntro) { |
||
105 | $em->remove($toolIntro); |
||
106 | $em->flush(); |
||
107 | |||
108 | Display::addFlash(Display::return_message(get_lang('IntroductionTextDeleted'), 'confirmation')); |
||
109 | } |
||
110 | } |
||
111 | |||
112 | /* INTRODUCTION MICRO MODULE - DISPLAY SECTION */ |
||
113 | |||
114 | /* Retrieves the module introduction text, if exist */ |
||
115 | // Getting course intro |
||
116 | /** @var CToolIntro $toolIntro */ |
||
117 | $toolIntro = $em |
||
118 | ->getRepository('ChamiloCourseBundle:CToolIntro') |
||
119 | ->findOneBy(['cId' => $course_id, 'id' => $moduleId, 'sessionId' => 0]); |
||
120 | |||
121 | $intro_content = $toolIntro ? $toolIntro->getIntroText() : ''; |
||
0 ignored issues
–
show
|
|||
122 | if ($session_id) { |
||
123 | /** @var CToolIntro $toolIntro */ |
||
124 | $toolIntro = $em |
||
125 | ->getRepository('ChamiloCourseBundle:CToolIntro') |
||
126 | ->findOneBy(['cId' => $course_id, 'id' => $moduleId, 'sessionId' => $session_id]); |
||
127 | |||
128 | $introSessionContent = $toolIntro && $toolIntro->getIntroText() ? $toolIntro->getIntroText() : ''; |
||
129 | $intro_content = $introSessionContent ?: $intro_content; |
||
130 | } |
||
131 | |||
132 | // Default behaviour show iframes. |
||
133 | $userStatus = COURSEMANAGERLOWSECURITY; |
||
134 | |||
135 | // Allows to do a remove_XSS in course introduction with user status COURSEMANAGERLOWSECURITY |
||
136 | // Block embed type videos (like vimeo, wistia, etc) - see BT#12244 BT#12556 |
||
137 | if (api_get_configuration_value('course_introduction_html_strict_filtering')) { |
||
138 | $userStatus = COURSEMANAGER; |
||
139 | } |
||
140 | |||
141 | // Ignore editor.css |
||
142 | $cssEditor = api_get_path(WEB_CSS_PATH).'editor.css'; |
||
143 | $linkToReplace = [ |
||
144 | '<link href="'.$cssEditor.'" rel="stylesheet" type="text/css" />', |
||
145 | '<link href="'.$cssEditor.'" media="screen" rel="stylesheet" type="text/css" />', |
||
146 | ]; |
||
147 | $intro_content = str_replace($linkToReplace, '', $intro_content); |
||
148 | $intro_content = Security::remove_XSS($intro_content, $userStatus); |
||
149 | |||
150 | /* Determines the correct display */ |
||
151 | if ($intro_cmdEdit || $intro_cmdAdd) { |
||
152 | $intro_dispDefault = false; |
||
153 | $intro_dispForm = true; |
||
154 | $intro_dispCommand = false; |
||
155 | } else { |
||
156 | $intro_dispDefault = true; |
||
157 | $intro_dispForm = false; |
||
158 | |||
159 | if ($intro_editAllowed) { |
||
160 | $intro_dispCommand = true; |
||
161 | } else { |
||
162 | $intro_dispCommand = false; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /* Executes the display */ |
||
167 | |||
168 | // display thematic advance inside a postit |
||
169 | if ($intro_dispForm) { |
||
170 | $default['intro_content'] = $intro_content; |
||
171 | $form->setDefaults($default); |
||
172 | $introduction_section .= '<div id="courseintro" style="width: 98%">'; |
||
173 | $introduction_section .= $form->returnForm(); |
||
174 | $introduction_section .= '</div>'; |
||
175 | } |
||
176 | |||
177 | $thematic_description_html = ''; |
||
178 | $thematicItemTwo = ''; |
||
179 | |||
180 | if ($tool == TOOL_COURSE_HOMEPAGE && !isset($_GET['intro_cmdEdit'])) { |
||
181 | // Only show this if we're on the course homepage, and we're not currently editing |
||
182 | $thematic = new Thematic(); |
||
183 | $displayMode = api_get_course_setting('display_info_advance_inside_homecourse'); |
||
184 | $class1 = ''; |
||
185 | if ($displayMode == '1') { |
||
186 | // Show only the current course progress step |
||
187 | $last_done_advance = $thematic->get_last_done_thematic_advance(); |
||
188 | $thematic_advance_info = $thematic->get_thematic_advance_list($last_done_advance); |
||
189 | $subTitle1 = get_lang('CurrentTopic'); |
||
190 | $class1 = ' current'; |
||
191 | } elseif ($displayMode == '2') { |
||
192 | // Show only the two next course progress steps |
||
193 | $last_done_advance = $thematic->get_next_thematic_advance_not_done(); |
||
194 | $next_advance_not_done = $thematic->get_next_thematic_advance_not_done(2); |
||
195 | $thematic_advance_info = $thematic->get_thematic_advance_list($last_done_advance); |
||
196 | $thematic_advance_info2 = $thematic->get_thematic_advance_list($next_advance_not_done); |
||
197 | $subTitle1 = $subTitle2 = get_lang('NextTopic'); |
||
198 | } elseif ($displayMode == '3') { |
||
199 | // Show the current and next course progress steps |
||
200 | $last_done_advance = $thematic->get_last_done_thematic_advance(); |
||
201 | $next_advance_not_done = $thematic->get_next_thematic_advance_not_done(); |
||
202 | $thematic_advance_info = $thematic->get_thematic_advance_list($last_done_advance); |
||
203 | $thematic_advance_info2 = $thematic->get_thematic_advance_list($next_advance_not_done); |
||
204 | $subTitle1 = get_lang('CurrentTopic'); |
||
205 | $subTitle2 = get_lang('NextTopic'); |
||
206 | $class1 = ' current'; |
||
207 | } |
||
208 | |||
209 | if (!empty($thematic_advance_info)) { |
||
210 | $thematic_advance = get_lang('CourseThematicAdvance'); |
||
211 | $thematicScore = $thematic->get_total_average_of_thematic_advances().'%'; |
||
212 | $thematicUrl = api_get_path(WEB_CODE_PATH).'course_progress/index.php?action=thematic_details&'.$cidReq; |
||
213 | |||
214 | $thematic_advance_info['thematic_id'] = $thematic_advance_info['thematic_id'] ?? 0; |
||
215 | $thematic_advance_info['start_date'] = $thematic_advance_info['start_date'] ?? null; |
||
216 | $thematic_advance_info['content'] = $thematic_advance_info['content'] ?? ''; |
||
217 | $thematic_advance_info['duration'] = $thematic_advance_info['duration'] ?? 0; |
||
218 | |||
219 | $thematic_info = $thematic->get_thematic_list($thematic_advance_info['thematic_id']); |
||
220 | $thematic_info['title'] = $thematic_info['title'] ?? ''; |
||
221 | |||
222 | if (!empty($thematic_advance_info['start_date'])) { |
||
223 | $thematic_advance_info['start_date'] = api_get_local_time( |
||
224 | $thematic_advance_info['start_date'] |
||
225 | ); |
||
226 | } |
||
227 | |||
228 | $thematic_advance_info['start_date'] = api_format_date( |
||
229 | $thematic_advance_info['start_date'], |
||
230 | DATE_TIME_FORMAT_LONG |
||
231 | ); |
||
232 | $userInfo = api_get_user_info(); |
||
233 | $courseInfo = api_get_course_info(); |
||
234 | $titleThematic = $thematic_advance.' : '.$courseInfo['name'].' <b>( '.$thematicScore.' )</b>'; |
||
235 | |||
236 | $infoUser = '<div class="thematic-avatar"><img src="'.$userInfo['avatar'].'" class="img-circle img-responsive"></div>'; |
||
237 | $infoUser .= '<div class="progress"> |
||
238 | <div class="progress-bar progress-bar-primary" role="progressbar" style="width: '.$thematicScore.';"> |
||
239 | '.$thematicScore.' |
||
240 | </div> |
||
241 | </div>'; |
||
242 | |||
243 | $thematicItemOne = ' |
||
244 | <div class="col-md-6 items-progress"> |
||
245 | <div class="thematic-cont '.$class1.'"> |
||
246 | <div class="topics">'.$subTitle1.'</div> |
||
247 | <h4 class="title-topics">'.Display::returnFontAwesomeIcon('book').strip_tags($thematic_info['title']).'</h4> |
||
248 | <p class="date">'.Display::returnFontAwesomeIcon('calendar-o').$thematic_advance_info['start_date'].'</p> |
||
249 | <div class="views">'.Display::returnFontAwesomeIcon('file-text-o').strip_tags($thematic_advance_info['content']).'</div> |
||
250 | <p class="time">'.Display::returnFontAwesomeIcon('clock-o').get_lang('DurationInHours').' : '.$thematic_advance_info['duration'].' - <a href="'.$thematicUrl.'">'.get_lang('SeeDetail').'</a></p> |
||
251 | </div> |
||
252 | </div>'; |
||
253 | |||
254 | if (!empty($thematic_advance_info2)) { |
||
255 | $thematic_info2 = $thematic->get_thematic_list($thematic_advance_info2['thematic_id']); |
||
256 | $thematic_advance_info2['start_date'] = api_get_local_time($thematic_advance_info2['start_date']); |
||
257 | $thematic_advance_info2['start_date'] = api_format_date($thematic_advance_info2['start_date'], DATE_TIME_FORMAT_LONG); |
||
258 | |||
259 | $thematicItemTwo = ' |
||
260 | <div class="col-md-6 items-progress"> |
||
261 | <div class="thematic-cont"> |
||
262 | <div class="topics">'.$subTitle2.'</div> |
||
263 | <h4 class="title-topics">'.Display::returnFontAwesomeIcon('book').$thematic_info2['title'].'</h4> |
||
264 | <p class="date">'.Display::returnFontAwesomeIcon('calendar-o').$thematic_advance_info2['start_date'].'</p> |
||
265 | <div class="views">'.Display::returnFontAwesomeIcon('file-text-o').strip_tags($thematic_advance_info2['content']).'</div> |
||
266 | <p class="time">'.Display::returnFontAwesomeIcon('clock-o').get_lang('DurationInHours').' : '.$thematic_advance_info2['duration'].' - <a href="'.$thematicUrl.'">'.get_lang('SeeDetail').'</a></p> |
||
267 | </div> |
||
268 | </div>'; |
||
269 | } |
||
270 | $thematicPanel = '<div class="row">'; |
||
271 | $thematicPanel .= '<div class="col-md-2">'.$infoUser.'</div>'; |
||
272 | $thematicPanel .= '<div class="col-md-10"><div class="row">'.$thematicItemOne.$thematicItemTwo.'</div></div>'; |
||
273 | $thematicPanel .= '</div>'; |
||
274 | $thematicPanel .= '<div class="separate"> |
||
275 | <a href="'.$thematicUrl.'" class="btn btn-default btn-block">'.get_lang('ShowFullCourseAdvance').'</a> |
||
276 | </div>'; |
||
277 | |||
278 | $thematicProgress = Display::panelCollapse( |
||
279 | $titleThematic, |
||
280 | $thematicPanel, |
||
281 | 'thematic', |
||
282 | null, |
||
283 | 'accordion-thematic', |
||
284 | 'collapse-thematic', |
||
285 | false |
||
286 | ); |
||
287 | } |
||
288 | } |
||
289 | $introduction_section .= '<div class="row">'; |
||
290 | if (!empty($thematic_advance_info)) { |
||
291 | $introduction_section .= '<div class="col-md-12">'; |
||
292 | $introduction_section .= $thematic_description_html; |
||
293 | $introduction_section .= $thematicProgress; |
||
294 | $introduction_section .= '</div>'; |
||
295 | } |
||
296 | $editIconButton = ''; |
||
297 | if (api_is_allowed_to_edit() && empty($session_id)) { |
||
298 | $editIconButton = Display::url( |
||
299 | '<em class="fa fa-wrench"></em> ', |
||
300 | api_get_path(WEB_CODE_PATH).'course_info/tools.php?'.$cidReq, |
||
301 | ['class' => 'btn btn-default', 'title' => get_lang('CustomizeIcons')] |
||
302 | ); |
||
303 | } |
||
304 | /* Tool to show /hide all tools on course */ |
||
305 | $toolAllShowHide = ''; |
||
306 | if (api_is_allowed_to_edit() && empty($session_id)) { |
||
307 | $toolAllShowHide = '<button class="btn btn-default hidden visible-all show-hide-all-tools" title="'.get_lang('Activate', '').'"><em class="fa fa-eye"></em></button>'; |
||
308 | $toolAllShowHide .= '<button class="btn btn-default hidden invisible-all show-hide-all-tools" title="'.get_lang('Deactivate', '').'"><em class="fa fa-eye-slash"></em></button>'; |
||
309 | } |
||
310 | |||
311 | $toolbar = ''; |
||
312 | $textIntro = ''; |
||
313 | if ($intro_dispCommand) { |
||
314 | $toolbar .= '<div class="toolbar-edit">'; |
||
315 | $toolbar .= '<div class="btn-group pull-right" role="group">'; |
||
316 | if (empty($intro_content)) { |
||
317 | // Displays "Add intro" commands |
||
318 | if (!empty($courseId)) { |
||
319 | $textIntro = '<a class="btn btn-default" title="'.addslashes(get_lang('AddIntro')).'" href="'.api_get_self().'?'.$cidReq.$blogParam.'&intro_cmdAdd=1">'; |
||
320 | $textIntro .= '<em class="fa fa-file-text"></em> '; |
||
321 | $textIntro .= "</a>"; |
||
322 | $toolbar .= $textIntro.$editIconButton.$toolAllShowHide; |
||
323 | } else { |
||
324 | $toolbar .= '<a class="btn btn-default" href="'.api_get_self().'?intro_cmdAdd=1">'.get_lang('AddIntro').'</a>'; |
||
325 | $toolbar .= $editIconButton.$toolAllShowHide; |
||
326 | } |
||
327 | } else { |
||
328 | // Displays "edit intro && delete intro" commands |
||
329 | if (!empty($courseId)) { |
||
330 | $toolbar .= |
||
331 | '<a class="btn btn-default" href="'.api_get_self().'?'.$cidReq.$blogParam.'&intro_cmdEdit=1" title="'.get_lang('Modify').'"> |
||
332 | <em class="fa fa-pencil"></em></a>'; |
||
333 | $toolbar .= $editIconButton.$toolAllShowHide; |
||
334 | $toolbar .= "<a class=\"btn btn-default\" |
||
335 | href=\"".api_get_self()."?".$cidReq.$blogParam."&intro_cmdDel=1\" |
||
336 | onclick=\"if(!confirm('".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES, $charset))."')) return false;\" |
||
337 | ><em class=\"fa fa-trash-o\"></em></a>"; |
||
338 | } else { |
||
339 | $toolbar .= |
||
340 | '<a class="btn btn-default" href="'.api_get_self().'?intro_cmdEdit=1" title="'.get_lang('Modify').'"> |
||
341 | <em class="fa fa-pencil"></em> |
||
342 | </a>"'; |
||
343 | $toolbar .= $editIconButton.$toolAllShowHide; |
||
344 | $toolbar .= "<a class=\"btn btn-default\" |
||
345 | href=\"".api_get_self()."?".$cidReq."&intro_cmdDel=1\" |
||
346 | onclick=\"if(!confirm('".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES, $charset))."')) return false;\" |
||
347 | ><em class=\"fa fa-trash-o\"></em></a>"; |
||
348 | } |
||
349 | // Fix for chrome XSS filter for videos in iframes - BT#7930 |
||
350 | $browser = api_get_navigator(); |
||
351 | if (strpos($introduction_section, '<iframe') !== false && $browser['name'] == 'Chrome') { |
||
352 | header('X-XSS-Protection: 0'); |
||
353 | } |
||
354 | } |
||
355 | $toolbar .= '</div></div>'; |
||
356 | } |
||
357 | |||
358 | $nameSection = get_lang('AddCustomCourseIntro'); |
||
359 | if ($moduleId !== 'course_homepage') { |
||
360 | $nameSection = get_lang('AddCustomToolsIntro'); |
||
361 | } |
||
362 | |||
363 | if (!api_is_anonymous()) { |
||
364 | $intro_content = AnnouncementManager::parseContent(api_get_user_id(), $intro_content, api_get_course_id()); |
||
365 | } |
||
366 | |||
367 | $showSequencesBlock = false; |
||
368 | |||
369 | if (api_get_configuration_value('resource_sequence_show_dependency_in_course_intro' && $tool == TOOL_COURSE_HOMEPAGE)) { |
||
370 | $sequenceResourceRepo = $em->getRepository(SequenceResource::class); |
||
371 | $sequences = $sequenceResourceRepo->getDependents($course_id, SequenceResource::COURSE_TYPE); |
||
372 | $firstSequence = current($sequences); |
||
373 | |||
374 | $showSequencesBlock = !empty($firstSequence['dependents']); |
||
375 | } |
||
376 | |||
377 | $introduction_section .= $showSequencesBlock ? '<div class="col-md-10">' : '<div class="col-md-12">'; |
||
378 | |||
379 | if ($intro_dispDefault) { |
||
380 | if (!empty($intro_content)) { |
||
381 | $introduction_section .= '<div class="page-course">'; |
||
382 | $introduction_section .= $intro_content; |
||
383 | $introduction_section .= '</div>'; |
||
384 | } else { |
||
385 | if (api_is_allowed_to_edit()) { |
||
386 | $introduction_section .= '<div class="help-course">'; |
||
387 | $introduction_section .= $nameSection.' '.$textIntro; |
||
388 | $introduction_section .= '</div>'; |
||
389 | } |
||
390 | } |
||
391 | } |
||
392 | |||
393 | $introduction_section .= $toolbar; |
||
394 | $introduction_section .= '</div>'; |
||
395 | |||
396 | if ($showSequencesBlock) { |
||
397 | $sequenceUrl = http_build_query( |
||
398 | [ |
||
399 | 'a' => 'get_dependents', |
||
400 | 'id' => $course_id, |
||
401 | 'type' => SequenceResource::COURSE_TYPE, |
||
402 | 'sid' => $session_id, |
||
403 | ] |
||
404 | ); |
||
405 | |||
406 | $introduction_section .= '<div class="col-md-2 text-center" id="resource-sequence"> |
||
407 | <span class="fa fa-spinner fa-spin fa-fw" aria-hidden="true"></span> |
||
408 | </div> |
||
409 | <script> |
||
410 | $(function () { |
||
411 | $(\'#resource-sequence\').load(_p.web_ajax + \'sequence.ajax.php?'.$sequenceUrl.'&'.$cidReq.'\') |
||
412 | }); |
||
413 | </script> |
||
414 | '; |
||
415 | } |
||
416 | |||
417 | $introduction_section .= '</div>'; //div.row |
||
418 | |||
419 | $browser = api_get_navigator(); |
||
420 | |||
421 | if (strpos($introduction_section, '<iframe') !== false && $browser['name'] == 'Chrome') { |
||
422 | header("X-XSS-Protection: 0"); |
||
423 | } |
||
424 |