Issues (2128)

main/inc/introductionSection.inc.php (2 issues)

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
$toolIntro is of type CToolIntro, thus it always evaluated to true.
Loading history...
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
$toolIntro is of type CToolIntro, thus it always evaluated to true.
Loading history...
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