1 | <?php |
||
2 | |||
3 | /* For licensing terms, see /license.txt */ |
||
4 | |||
5 | use Chamilo\CoreBundle\Entity\Repository\CourseRepository; |
||
6 | use Chamilo\CoreBundle\Entity\Repository\ItemPropertyRepository; |
||
7 | use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver; |
||
8 | use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder; |
||
9 | use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer; |
||
10 | use Chamilo\CourseBundle\Entity\CDocument; |
||
11 | use Chamilo\CourseBundle\Entity\CItemProperty; |
||
12 | use Chamilo\CourseBundle\Entity\CLp; |
||
13 | use Chamilo\CourseBundle\Entity\CLpCategory; |
||
14 | use Chamilo\CourseBundle\Entity\CLpItem; |
||
15 | use Chamilo\CourseBundle\Entity\CLpItemView; |
||
16 | use Chamilo\CourseBundle\Entity\CTool; |
||
17 | use Chamilo\PluginBundle\Entity\H5pImport\H5pImport; |
||
18 | use Chamilo\PluginBundle\Entity\XApi\ToolLaunch; |
||
19 | use Chamilo\UserBundle\Entity\User; |
||
20 | use ChamiloSession as Session; |
||
21 | use Gedmo\Sortable\Entity\Repository\SortableRepository; |
||
22 | use Symfony\Component\Filesystem\Filesystem; |
||
23 | use Symfony\Component\Finder\Finder; |
||
24 | |||
25 | /** |
||
26 | * Class learnpath |
||
27 | * This class defines the parent attributes and methods for Chamilo learnpaths |
||
28 | * and SCORM learnpaths. It is used by the scorm class. |
||
29 | * |
||
30 | * @todo decouple class |
||
31 | * |
||
32 | * @author Yannick Warnier <[email protected]> |
||
33 | * @author Julio Montoya <[email protected]> Several improvements and fixes |
||
34 | */ |
||
35 | class learnpath |
||
36 | { |
||
37 | public const MAX_LP_ITEM_TITLE_LENGTH = 32; |
||
38 | public const STATUS_CSS_CLASS_NAME = [ |
||
39 | 'not attempted' => 'scorm_not_attempted', |
||
40 | 'incomplete' => 'scorm_not_attempted', |
||
41 | 'failed' => 'scorm_failed', |
||
42 | 'completed' => 'scorm_completed', |
||
43 | 'passed' => 'scorm_completed', |
||
44 | 'succeeded' => 'scorm_completed', |
||
45 | 'browsed' => 'scorm_completed', |
||
46 | ]; |
||
47 | |||
48 | public $attempt = 0; // The number for the current ID view. |
||
49 | public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ... |
||
50 | public $current; // Id of the current item the user is viewing. |
||
51 | public $current_score; // The score of the current item. |
||
52 | public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet). |
||
53 | public $current_time_stop; // The time the user closed this resource. |
||
54 | public $default_status = 'not attempted'; |
||
55 | public $encoding = 'UTF-8'; |
||
56 | public $error = ''; |
||
57 | public $force_commit = false; // For SCORM only- if true will send a scorm LMSCommit() request on each LMSSetValue() |
||
58 | public $index; // The index of the active learnpath_item in $ordered_items array. |
||
59 | /** @var learnpathItem[] */ |
||
60 | public $items = []; |
||
61 | public $last; // item_id of last item viewed in the learning path. |
||
62 | public $last_item_seen = 0; // In case we have already come in this lp, reuse the last item seen if authorized. |
||
63 | public $license; // Which license this course has been given - not used yet on 20060522. |
||
64 | public $lp_id; // DB iid for this learnpath. |
||
65 | public $lp_view_id; // DB ID for lp_view |
||
66 | public $maker; // Which maker has conceived the content (ENI, Articulate, ...). |
||
67 | public $message = ''; |
||
68 | public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded). |
||
69 | public $name; // Learnpath name (they generally have one). |
||
70 | public $ordered_items = []; // List of the learnpath items in the order they are to be read. |
||
71 | public $path = ''; // Path inside the scorm directory (if scorm). |
||
72 | public $theme; // The current theme of the learning path. |
||
73 | public $preview_image; // The current image of the learning path. |
||
74 | public $accumulateScormTime; // Flag to decide whether to accumulate SCORM time or not |
||
75 | public $accumulateWorkTime; // The min time of learnpath |
||
76 | |||
77 | // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1). |
||
78 | public $prevent_reinit = 1; |
||
79 | |||
80 | // Describes the mode of progress bar display. |
||
81 | public $seriousgame_mode = 0; |
||
82 | public $progress_bar_mode = '%'; |
||
83 | |||
84 | // Percentage progress as saved in the db. |
||
85 | public $progress_db = 0; |
||
86 | public $proximity; // Wether the content is distant or local or unknown. |
||
87 | public $refs_list = []; //list of items by ref => db_id. Used only for prerequisites match. |
||
88 | // !!!This array (refs_list) is built differently depending on the nature of the LP. |
||
89 | // If SCORM, uses ref, if Chamilo, uses id to keep a unique value. |
||
90 | public $type; //type of learnpath. Could be 'chamilo', 'scorm', 'scorm2004', 'aicc', ... |
||
91 | // TODO: Check if this type variable is useful here (instead of just in the controller script). |
||
92 | public $user_id; //ID of the user that is viewing/using the course |
||
93 | public $update_queue = []; |
||
94 | public $scorm_debug = 0; |
||
95 | public $arrMenu = []; // Array for the menu items. |
||
96 | public $debug = 0; // Logging level. |
||
97 | public $lp_session_id = 0; |
||
98 | public $lp_view_session_id = 0; // The specific view might be bound to a session. |
||
99 | public $prerequisite = 0; |
||
100 | public $use_max_score = 1; // 1 or 0 |
||
101 | public $subscribeUsers = 0; // Subscribe users or not |
||
102 | public $created_on = ''; |
||
103 | public $modified_on = ''; |
||
104 | public $publicated_on = ''; |
||
105 | public $expired_on = ''; |
||
106 | public $ref = null; |
||
107 | public $course_int_id; |
||
108 | public $course_info = []; |
||
109 | public $categoryId; |
||
110 | |||
111 | /** |
||
112 | * Constructor. |
||
113 | * Needs a database handler, a course code and a learnpath id from the database. |
||
114 | * Also builds the list of items into $this->items. |
||
115 | * |
||
116 | * @param string $course Course code |
||
117 | * @param int $lp_id c_lp.iid |
||
118 | * @param int $user_id |
||
119 | */ |
||
120 | public function __construct($course, $lp_id, $user_id) |
||
121 | { |
||
122 | $debug = $this->debug; |
||
123 | $this->encoding = api_get_system_encoding(); |
||
124 | if (empty($course)) { |
||
125 | $course = api_get_course_id(); |
||
126 | } |
||
127 | $course_info = api_get_course_info($course); |
||
128 | if (!empty($course_info)) { |
||
129 | $this->cc = $course_info['code']; |
||
130 | $this->course_info = $course_info; |
||
131 | $course_id = $course_info['real_id']; |
||
132 | } else { |
||
133 | $this->error = 'Course code does not exist in database.'; |
||
134 | } |
||
135 | |||
136 | $lp_id = (int) $lp_id; |
||
137 | $course_id = (int) $course_id; |
||
138 | $this->set_course_int_id($course_id); |
||
139 | // Check learnpath ID. |
||
140 | if (empty($lp_id) || empty($course_id)) { |
||
141 | $this->error = "Parameter is empty: LpId:'$lp_id', courseId: '$lp_id'"; |
||
142 | } else { |
||
143 | // TODO: Make it flexible to use any course_code (still using env course code here). |
||
144 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
145 | $sql = "SELECT * FROM $lp_table |
||
146 | WHERE iid = $lp_id"; |
||
147 | if ($debug) { |
||
148 | error_log('learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0); |
||
149 | } |
||
150 | $res = Database::query($sql); |
||
151 | if (Database::num_rows($res) > 0) { |
||
152 | $this->lp_id = $lp_id; |
||
153 | $row = Database::fetch_array($res); |
||
154 | $this->type = $row['lp_type']; |
||
155 | $this->name = stripslashes($row['name']); |
||
156 | $this->proximity = $row['content_local']; |
||
157 | $this->theme = $row['theme']; |
||
158 | $this->maker = $row['content_maker']; |
||
159 | $this->prevent_reinit = $row['prevent_reinit']; |
||
160 | $this->seriousgame_mode = $row['seriousgame_mode']; |
||
161 | $this->license = $row['content_license']; |
||
162 | $this->scorm_debug = $row['debug']; |
||
163 | $this->js_lib = $row['js_lib']; |
||
164 | $this->path = $row['path']; |
||
165 | $this->preview_image = $row['preview_image']; |
||
166 | $this->author = $row['author']; |
||
167 | $this->hide_toc_frame = $row['hide_toc_frame']; |
||
168 | $this->lp_session_id = $row['session_id']; |
||
169 | $this->use_max_score = $row['use_max_score']; |
||
170 | $this->subscribeUsers = $row['subscribe_users']; |
||
171 | $this->created_on = $row['created_on']; |
||
172 | $this->modified_on = $row['modified_on']; |
||
173 | $this->ref = $row['ref']; |
||
174 | $this->categoryId = $row['category_id']; |
||
175 | $this->accumulateScormTime = isset($row['accumulate_scorm_time']) ? $row['accumulate_scorm_time'] : 'true'; |
||
176 | $this->accumulateWorkTime = isset($row['accumulate_work_time']) ? $row['accumulate_work_time'] : 0; |
||
177 | |||
178 | if (!empty($row['publicated_on'])) { |
||
179 | $this->publicated_on = $row['publicated_on']; |
||
180 | } |
||
181 | |||
182 | if (!empty($row['expired_on'])) { |
||
183 | $this->expired_on = $row['expired_on']; |
||
184 | } |
||
185 | if ($this->type == 2) { |
||
186 | if ($row['force_commit'] == 1) { |
||
187 | $this->force_commit = true; |
||
188 | } |
||
189 | } |
||
190 | $this->mode = $row['default_view_mod']; |
||
191 | |||
192 | // Check user ID. |
||
193 | if (empty($user_id)) { |
||
194 | $this->error = 'User ID is empty'; |
||
195 | } else { |
||
196 | $userInfo = api_get_user_info($user_id); |
||
197 | if (!empty($userInfo)) { |
||
198 | $this->user_id = $userInfo['user_id']; |
||
199 | } else { |
||
200 | $this->error = 'User ID does not exist in database #'.$user_id; |
||
201 | } |
||
202 | } |
||
203 | |||
204 | // End of variables checking. |
||
205 | $session_id = api_get_session_id(); |
||
206 | // Get the session condition for learning paths of the base + session. |
||
207 | $session = api_get_session_condition($session_id); |
||
208 | // Now get the latest attempt from this user on this LP, if available, otherwise create a new one. |
||
209 | $lp_table = Database::get_course_table(TABLE_LP_VIEW); |
||
210 | |||
211 | // Selecting by view_count descending allows to get the highest view_count first. |
||
212 | $sql = "SELECT * FROM $lp_table |
||
213 | WHERE |
||
214 | c_id = $course_id AND |
||
215 | lp_id = $lp_id AND |
||
216 | user_id = $user_id |
||
217 | $session |
||
218 | ORDER BY view_count DESC"; |
||
219 | $res = Database::query($sql); |
||
220 | if ($debug) { |
||
221 | error_log('learnpath::__construct() '.__LINE__.' - querying lp_view: '.$sql, 0); |
||
222 | } |
||
223 | |||
224 | if (Database::num_rows($res) > 0) { |
||
225 | if ($debug) { |
||
226 | error_log('learnpath::__construct() '.__LINE__.' - Found previous view'); |
||
227 | } |
||
228 | $row = Database::fetch_array($res); |
||
229 | $this->attempt = $row['view_count']; |
||
230 | $this->lp_view_id = $row['id']; |
||
231 | $this->last_item_seen = $row['last_item']; |
||
232 | $this->progress_db = $row['progress']; |
||
233 | $this->lp_view_session_id = $row['session_id']; |
||
234 | } elseif (!api_is_invitee()) { |
||
235 | if ($debug) { |
||
236 | error_log('learnpath::__construct() '.__LINE__.' - NOT Found previous view'); |
||
237 | } |
||
238 | $this->attempt = 1; |
||
239 | $params = [ |
||
240 | 'c_id' => $course_id, |
||
241 | 'lp_id' => $lp_id, |
||
242 | 'user_id' => $user_id, |
||
243 | 'view_count' => 1, |
||
244 | 'session_id' => $session_id, |
||
245 | 'last_item' => 0, |
||
246 | ]; |
||
247 | $this->last_item_seen = 0; |
||
248 | $this->lp_view_session_id = $session_id; |
||
249 | $this->lp_view_id = Database::insert($lp_table, $params); |
||
250 | if (!empty($this->lp_view_id)) { |
||
251 | $sql = "UPDATE $lp_table SET id = iid |
||
252 | WHERE iid = ".$this->lp_view_id; |
||
253 | Database::query($sql); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | // Initialise items. |
||
258 | $lp_item_table = Database::get_course_table(TABLE_LP_ITEM); |
||
259 | $sql = "SELECT * FROM $lp_item_table |
||
260 | WHERE c_id = $course_id AND lp_id = '".$this->lp_id."' |
||
261 | ORDER BY parent_item_id, display_order"; |
||
262 | $res = Database::query($sql); |
||
263 | |||
264 | $lp_item_id_list = []; |
||
265 | while ($row = Database::fetch_array($res)) { |
||
266 | $lp_item_id_list[] = $row['iid']; |
||
267 | switch ($this->type) { |
||
268 | case 3: //aicc |
||
269 | $oItem = new aiccItem('db', $row['iid'], $course_id); |
||
270 | if (is_object($oItem)) { |
||
271 | $my_item_id = $oItem->get_id(); |
||
272 | $oItem->set_lp_view($this->lp_view_id, $course_id); |
||
273 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
274 | // Don't use reference here as the next loop will make the pointed object change. |
||
275 | $this->items[$my_item_id] = $oItem; |
||
276 | $this->refs_list[$oItem->ref] = $my_item_id; |
||
277 | if ($debug) { |
||
278 | error_log( |
||
279 | 'learnpath::__construct() - '. |
||
280 | 'aicc object with id '.$my_item_id. |
||
281 | ' set in items[]', |
||
282 | 0 |
||
283 | ); |
||
284 | } |
||
285 | } |
||
286 | break; |
||
287 | case 2: |
||
288 | $oItem = new scormItem('db', $row['iid'], $course_id); |
||
289 | if (is_object($oItem)) { |
||
290 | $my_item_id = $oItem->get_id(); |
||
291 | $oItem->set_lp_view($this->lp_view_id, $course_id); |
||
292 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
293 | // Don't use reference here as the next loop will make the pointed object change. |
||
294 | $this->items[$my_item_id] = $oItem; |
||
295 | $this->refs_list[$oItem->ref] = $my_item_id; |
||
296 | if ($debug) { |
||
297 | error_log('object with id '.$my_item_id.' set in items[]'); |
||
298 | } |
||
299 | } |
||
300 | break; |
||
301 | case 1: |
||
302 | default: |
||
303 | if ($debug) { |
||
304 | error_log('learnpath::__construct() '.__LINE__.' - calling learnpathItem'); |
||
305 | } |
||
306 | $oItem = new learnpathItem($row['iid'], $user_id, $course_id, $row); |
||
307 | |||
308 | if ($debug) { |
||
309 | error_log('learnpath::__construct() '.__LINE__.' - end calling learnpathItem'); |
||
310 | } |
||
311 | if (is_object($oItem)) { |
||
312 | $my_item_id = $oItem->get_id(); |
||
313 | // Moved down to when we are sure the item_view exists. |
||
314 | //$oItem->set_lp_view($this->lp_view_id); |
||
315 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
316 | // Don't use reference here as the next loop will make the pointed object change. |
||
317 | $this->items[$my_item_id] = $oItem; |
||
318 | $this->refs_list[$my_item_id] = $my_item_id; |
||
319 | if ($debug) { |
||
320 | error_log( |
||
321 | 'learnpath::__construct() '.__LINE__. |
||
322 | ' - object with id '.$my_item_id.' set in items[]' |
||
323 | ); |
||
324 | } |
||
325 | } |
||
326 | break; |
||
327 | } |
||
328 | |||
329 | // Setting the object level with variable $this->items[$i][parent] |
||
330 | foreach ($this->items as $itemLPObject) { |
||
331 | $level = self::get_level_for_item( |
||
332 | $this->items, |
||
333 | $itemLPObject->db_id |
||
334 | ); |
||
335 | $itemLPObject->level = $level; |
||
336 | } |
||
337 | |||
338 | // Setting the view in the item object. |
||
339 | if (is_object($this->items[$row['iid']])) { |
||
340 | $this->items[$row['iid']]->set_lp_view($this->lp_view_id, $course_id); |
||
341 | if ($this->items[$row['iid']]->get_type() == TOOL_HOTPOTATOES) { |
||
342 | $this->items[$row['iid']]->current_start_time = 0; |
||
343 | $this->items[$row['iid']]->current_stop_time = 0; |
||
344 | } |
||
345 | } |
||
346 | } |
||
347 | |||
348 | if (!empty($lp_item_id_list)) { |
||
349 | $lp_item_id_list_to_string = implode("','", $lp_item_id_list); |
||
350 | if (!empty($lp_item_id_list_to_string)) { |
||
351 | // Get last viewing vars. |
||
352 | $itemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
353 | // This query should only return one or zero result. |
||
354 | $sql = "SELECT lp_item_id, status |
||
355 | FROM $itemViewTable |
||
356 | WHERE |
||
357 | c_id = $course_id AND |
||
358 | lp_view_id = ".$this->get_view_id()." AND |
||
359 | lp_item_id IN ('".$lp_item_id_list_to_string."') |
||
360 | ORDER BY view_count DESC "; |
||
361 | $status_list = []; |
||
362 | $res = Database::query($sql); |
||
363 | while ($row = Database::fetch_array($res)) { |
||
364 | $status_list[$row['lp_item_id']] = $row['status']; |
||
365 | } |
||
366 | |||
367 | foreach ($lp_item_id_list as $item_id) { |
||
368 | if (isset($status_list[$item_id])) { |
||
369 | $status = $status_list[$item_id]; |
||
370 | if (is_object($this->items[$item_id])) { |
||
371 | $this->items[$item_id]->set_status($status); |
||
372 | if (empty($status)) { |
||
373 | $this->items[$item_id]->set_status( |
||
374 | $this->default_status |
||
375 | ); |
||
376 | } |
||
377 | } |
||
378 | } else { |
||
379 | if (!api_is_invitee()) { |
||
380 | if (is_object($this->items[$item_id])) { |
||
381 | $this->items[$item_id]->set_status( |
||
382 | $this->default_status |
||
383 | ); |
||
384 | } |
||
385 | |||
386 | if (!empty($this->lp_view_id)) { |
||
387 | // Add that row to the lp_item_view table so that |
||
388 | // we have something to show in the stats page. |
||
389 | $params = [ |
||
390 | 'c_id' => $course_id, |
||
391 | 'lp_item_id' => $item_id, |
||
392 | 'lp_view_id' => $this->lp_view_id, |
||
393 | 'view_count' => 1, |
||
394 | 'status' => 'not attempted', |
||
395 | 'start_time' => time(), |
||
396 | 'total_time' => 0, |
||
397 | 'score' => 0, |
||
398 | ]; |
||
399 | $insertId = Database::insert($itemViewTable, $params); |
||
400 | |||
401 | if ($insertId) { |
||
402 | $sql = "UPDATE $itemViewTable SET id = iid |
||
403 | WHERE iid = $insertId"; |
||
404 | Database::query($sql); |
||
405 | } |
||
406 | |||
407 | $this->items[$item_id]->set_lp_view( |
||
408 | $this->lp_view_id, |
||
409 | $course_id |
||
410 | ); |
||
411 | } |
||
412 | } |
||
413 | } |
||
414 | } |
||
415 | } |
||
416 | } |
||
417 | |||
418 | $this->ordered_items = self::get_flat_ordered_items_list( |
||
419 | $this->get_id(), |
||
420 | 0, |
||
421 | $course_id |
||
422 | ); |
||
423 | $this->max_ordered_items = 0; |
||
424 | foreach ($this->ordered_items as $index => $dummy) { |
||
425 | if ($index > $this->max_ordered_items && !empty($dummy)) { |
||
426 | $this->max_ordered_items = $index; |
||
427 | } |
||
428 | } |
||
429 | // TODO: Define the current item better. |
||
430 | $this->first(); |
||
431 | if ($debug) { |
||
432 | error_log('lp_view_session_id '.$this->lp_view_session_id); |
||
433 | error_log('End of learnpath constructor for learnpath '.$this->get_id()); |
||
434 | } |
||
435 | } else { |
||
436 | $this->error = 'Learnpath ID does not exist in database ('.$sql.')'; |
||
437 | } |
||
438 | } |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * @return string |
||
443 | */ |
||
444 | public function getCourseCode() |
||
445 | { |
||
446 | return $this->cc; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * @return int |
||
451 | */ |
||
452 | public function get_course_int_id() |
||
453 | { |
||
454 | return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id(); |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * @param $course_id |
||
459 | * |
||
460 | * @return int |
||
461 | */ |
||
462 | public function set_course_int_id($course_id) |
||
463 | { |
||
464 | return $this->course_int_id = (int) $course_id; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Function rewritten based on old_add_item() from Yannick Warnier. |
||
469 | * Due the fact that users can decide where the item should come, I had to overlook this function and |
||
470 | * I found it better to rewrite it. Old function is still available. |
||
471 | * Added also the possibility to add a description. |
||
472 | * |
||
473 | * @param int $parent |
||
474 | * @param int $previous |
||
475 | * @param string $type |
||
476 | * @param int $id resource ID (ref) |
||
477 | * @param string $title |
||
478 | * @param string $description |
||
479 | * @param int $prerequisites |
||
480 | * @param int $max_time_allowed |
||
481 | * @param int $userId |
||
482 | * |
||
483 | * @return int |
||
484 | */ |
||
485 | public function add_item( |
||
486 | $parent, |
||
487 | $previous, |
||
488 | $type, |
||
489 | $id, |
||
490 | $title, |
||
491 | $description, |
||
492 | $prerequisites = 0, |
||
493 | $max_time_allowed = 0, |
||
494 | $userId = 0, |
||
495 | $dspOrder = 0 |
||
496 | ) { |
||
497 | $course_id = $this->course_info['real_id']; |
||
498 | if (empty($course_id)) { |
||
499 | // Sometimes Oogie doesn't catch the course info but sets $this->cc |
||
500 | $this->course_info = api_get_course_info($this->cc); |
||
501 | $course_id = $this->course_info['real_id']; |
||
502 | } |
||
503 | $userId = empty($userId) ? api_get_user_id() : $userId; |
||
504 | $sessionId = api_get_session_id(); |
||
505 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
506 | $_course = $this->course_info; |
||
507 | $parent = (int) $parent; |
||
508 | $previous = (int) $previous; |
||
509 | $id = (int) $id; |
||
510 | $max_time_allowed = htmlentities($max_time_allowed); |
||
511 | if (empty($max_time_allowed)) { |
||
512 | $max_time_allowed = 0; |
||
513 | } |
||
514 | $sql = "SELECT COUNT(iid) AS num |
||
515 | FROM $tbl_lp_item |
||
516 | WHERE |
||
517 | c_id = $course_id AND |
||
518 | lp_id = ".$this->get_id()." AND |
||
519 | parent_item_id = $parent "; |
||
520 | |||
521 | $res_count = Database::query($sql); |
||
522 | $row = Database::fetch_array($res_count); |
||
523 | $num = $row['num']; |
||
524 | |||
525 | $tmp_previous = 0; |
||
526 | $display_order = 0; |
||
527 | $next = 0; |
||
528 | if ($num > 0) { |
||
529 | if (empty($previous)) { |
||
530 | $sql = "SELECT iid, next_item_id, display_order |
||
531 | FROM $tbl_lp_item |
||
532 | WHERE |
||
533 | c_id = $course_id AND |
||
534 | lp_id = ".$this->get_id()." AND |
||
535 | parent_item_id = $parent AND |
||
536 | previous_item_id = 0 OR |
||
537 | previous_item_id = $parent"; |
||
538 | $result = Database::query($sql); |
||
539 | $row = Database::fetch_array($result); |
||
540 | if ($row) { |
||
541 | $next = $row['iid']; |
||
542 | } |
||
543 | } else { |
||
544 | $previous = (int) $previous; |
||
545 | $sql = "SELECT iid, previous_item_id, next_item_id, display_order |
||
546 | FROM $tbl_lp_item |
||
547 | WHERE |
||
548 | c_id = $course_id AND |
||
549 | lp_id = ".$this->get_id()." AND |
||
550 | id = $previous"; |
||
551 | $result = Database::query($sql); |
||
552 | $row = Database::fetch_array($result); |
||
553 | if ($row) { |
||
554 | $tmp_previous = $row['iid']; |
||
555 | $next = $row['next_item_id']; |
||
556 | $display_order = $row['display_order']; |
||
557 | } |
||
558 | } |
||
559 | } |
||
560 | |||
561 | $id = (int) $id; |
||
562 | $typeCleaned = Database::escape_string($type); |
||
563 | $max_score = 100; |
||
564 | if ($type === 'quiz' && $id) { |
||
565 | $sql = 'SELECT SUM(ponderation) |
||
566 | FROM '.Database::get_course_table(TABLE_QUIZ_QUESTION).' as quiz_question |
||
567 | INNER JOIN '.Database::get_course_table(TABLE_QUIZ_TEST_QUESTION).' as quiz_rel_question |
||
568 | ON |
||
569 | quiz_question.iid = quiz_rel_question.question_id |
||
570 | WHERE |
||
571 | quiz_rel_question.exercice_id = '.$id." AND |
||
572 | quiz_rel_question.c_id = $course_id "; |
||
573 | $rsQuiz = Database::query($sql); |
||
574 | $max_score = Database::result($rsQuiz, 0, 0); |
||
575 | |||
576 | // Disabling the exercise if we add it inside a LP |
||
577 | $exercise = new Exercise($course_id); |
||
578 | $exercise->read($id); |
||
579 | $exercise->disable(); |
||
580 | $exercise->save(); |
||
581 | } |
||
582 | |||
583 | $newDisplayOrder = $display_order + 1; |
||
584 | if (!empty($dspOrder)) { |
||
585 | $newDisplayOrder = (int) $dspOrder; |
||
586 | } |
||
587 | |||
588 | $params = [ |
||
589 | 'c_id' => $course_id, |
||
590 | 'lp_id' => $this->get_id(), |
||
591 | 'item_type' => $typeCleaned, |
||
592 | 'ref' => '', |
||
593 | 'title' => $title, |
||
594 | 'description' => $description, |
||
595 | 'path' => $id, |
||
596 | 'max_score' => $max_score, |
||
597 | 'parent_item_id' => $parent, |
||
598 | 'previous_item_id' => $previous, |
||
599 | 'next_item_id' => (int) $next, |
||
600 | 'display_order' => $newDisplayOrder, |
||
601 | 'prerequisite' => $prerequisites, |
||
602 | 'max_time_allowed' => $max_time_allowed, |
||
603 | 'min_score' => 0, |
||
604 | 'launch_data' => '', |
||
605 | ]; |
||
606 | |||
607 | if ($prerequisites != 0) { |
||
608 | $params['prerequisite'] = $prerequisites; |
||
609 | } |
||
610 | |||
611 | $new_item_id = Database::insert($tbl_lp_item, $params); |
||
612 | if ($new_item_id) { |
||
613 | $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id"; |
||
614 | Database::query($sql); |
||
615 | |||
616 | if (!empty($next)) { |
||
617 | $sql = "UPDATE $tbl_lp_item |
||
618 | SET previous_item_id = $new_item_id |
||
619 | WHERE c_id = $course_id AND id = $next AND item_type != '".TOOL_LP_FINAL_ITEM."'"; |
||
620 | Database::query($sql); |
||
621 | } |
||
622 | |||
623 | // Update the item that should be before the new item. |
||
624 | if (!empty($tmp_previous)) { |
||
625 | $sql = "UPDATE $tbl_lp_item |
||
626 | SET next_item_id = $new_item_id |
||
627 | WHERE c_id = $course_id AND id = $tmp_previous"; |
||
628 | Database::query($sql); |
||
629 | } |
||
630 | |||
631 | // Update all the items after the new item. |
||
632 | $sql = "UPDATE $tbl_lp_item |
||
633 | SET display_order = display_order + 1 |
||
634 | WHERE |
||
635 | c_id = $course_id AND |
||
636 | lp_id = ".$this->get_id()." AND |
||
637 | iid <> $new_item_id AND |
||
638 | parent_item_id = $parent AND |
||
639 | display_order > $display_order"; |
||
640 | Database::query($sql); |
||
641 | |||
642 | // Update the item that should come after the new item. |
||
643 | $sql = "UPDATE $tbl_lp_item |
||
644 | SET ref = $new_item_id |
||
645 | WHERE c_id = $course_id AND iid = $new_item_id"; |
||
646 | Database::query($sql); |
||
647 | |||
648 | $sql = "UPDATE $tbl_lp_item |
||
649 | SET previous_item_id = ".$this->getLastInFirstLevel()." |
||
650 | WHERE c_id = $course_id AND lp_id = {$this->lp_id} AND item_type = '".TOOL_LP_FINAL_ITEM."'"; |
||
651 | Database::query($sql); |
||
652 | |||
653 | // Upload audio. |
||
654 | if (!empty($_FILES['mp3']['name'])) { |
||
655 | // Create the audio folder if it does not exist yet. |
||
656 | $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||
657 | if (!is_dir($filepath.'audio')) { |
||
658 | mkdir( |
||
659 | $filepath.'audio', |
||
660 | api_get_permissions_for_new_directories() |
||
661 | ); |
||
662 | $audio_id = add_document( |
||
663 | $_course, |
||
664 | '/audio', |
||
665 | 'folder', |
||
666 | 0, |
||
667 | 'audio', |
||
668 | '', |
||
669 | 0, |
||
670 | true, |
||
671 | null, |
||
672 | $sessionId, |
||
673 | $userId |
||
674 | ); |
||
675 | api_item_property_update( |
||
676 | $_course, |
||
677 | TOOL_DOCUMENT, |
||
678 | $audio_id, |
||
679 | 'FolderCreated', |
||
680 | $userId, |
||
681 | null, |
||
682 | null, |
||
683 | null, |
||
684 | null, |
||
685 | $sessionId |
||
686 | ); |
||
687 | api_item_property_update( |
||
688 | $_course, |
||
689 | TOOL_DOCUMENT, |
||
690 | $audio_id, |
||
691 | 'invisible', |
||
692 | $userId, |
||
693 | null, |
||
694 | null, |
||
695 | null, |
||
696 | null, |
||
697 | $sessionId |
||
698 | ); |
||
699 | } |
||
700 | |||
701 | $file_path = handle_uploaded_document( |
||
702 | $_course, |
||
703 | $_FILES['mp3'], |
||
704 | api_get_path(SYS_COURSE_PATH).$_course['path'].'/document', |
||
705 | '/audio', |
||
706 | $userId, |
||
707 | '', |
||
708 | '', |
||
709 | '', |
||
710 | '', |
||
711 | false |
||
712 | ); |
||
713 | |||
714 | // Store the mp3 file in the lp_item table. |
||
715 | $sql = "UPDATE $tbl_lp_item SET |
||
716 | audio = '".Database::escape_string($file_path)."' |
||
717 | WHERE iid = '".intval($new_item_id)."'"; |
||
718 | Database::query($sql); |
||
719 | } |
||
720 | } |
||
721 | |||
722 | return $new_item_id; |
||
723 | } |
||
724 | |||
725 | /** |
||
726 | * Static admin function allowing addition of a learnpath to a course. |
||
727 | * |
||
728 | * @param string $courseCode |
||
729 | * @param string $name |
||
730 | * @param string $description |
||
731 | * @param string $learnpath |
||
732 | * @param string $origin |
||
733 | * @param string $zipname Zip file containing the learnpath or directory containing the learnpath |
||
734 | * @param string $publicated_on |
||
735 | * @param string $expired_on |
||
736 | * @param int $categoryId |
||
737 | * @param int $userId |
||
738 | * |
||
739 | * @return int The new learnpath ID on success, 0 on failure |
||
740 | */ |
||
741 | public static function add_lp( |
||
742 | $courseCode, |
||
743 | $name, |
||
744 | $description = '', |
||
745 | $learnpath = 'guess', |
||
746 | $origin = 'zip', |
||
747 | $zipname = '', |
||
748 | $publicated_on = '', |
||
749 | $expired_on = '', |
||
750 | $categoryId = 0, |
||
751 | $userId = 0 |
||
752 | ) { |
||
753 | global $charset; |
||
754 | |||
755 | if (!empty($courseCode)) { |
||
756 | $courseInfo = api_get_course_info($courseCode); |
||
757 | $course_id = $courseInfo['real_id']; |
||
758 | } else { |
||
759 | $course_id = api_get_course_int_id(); |
||
760 | $courseInfo = api_get_course_info(); |
||
761 | } |
||
762 | |||
763 | $em = Database::getManager(); |
||
764 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
765 | // Check course code exists. |
||
766 | // Check lp_name doesn't exist, otherwise append something. |
||
767 | $i = 0; |
||
768 | $categoryId = (int) $categoryId; |
||
769 | // Session id. |
||
770 | $session_id = api_get_session_id(); |
||
771 | $userId = empty($userId) ? api_get_user_id() : $userId; |
||
772 | |||
773 | if (empty($publicated_on)) { |
||
774 | $publicated_on = null; |
||
775 | } else { |
||
776 | $publicated_on = api_get_utc_datetime($publicated_on, false, true); |
||
777 | } |
||
778 | |||
779 | if (empty($expired_on)) { |
||
780 | $expired_on = null; |
||
781 | } else { |
||
782 | $expired_on = api_get_utc_datetime($expired_on, false, true); |
||
783 | } |
||
784 | |||
785 | $check_name = "SELECT * FROM $tbl_lp |
||
786 | WHERE c_id = $course_id AND name = '".Database::escape_string($name)."'"; |
||
787 | $res_name = Database::query($check_name); |
||
788 | |||
789 | while (Database::num_rows($res_name)) { |
||
790 | // There is already one such name, update the current one a bit. |
||
791 | $i++; |
||
792 | $name = $name.' - '.$i; |
||
793 | $check_name = "SELECT * FROM $tbl_lp |
||
794 | WHERE c_id = $course_id AND name = '".Database::escape_string($name)."' "; |
||
795 | $res_name = Database::query($check_name); |
||
796 | } |
||
797 | // New name does not exist yet; keep it. |
||
798 | // Escape description. |
||
799 | // Kevin: added htmlentities(). |
||
800 | $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset)); |
||
801 | $type = 1; |
||
802 | switch ($learnpath) { |
||
803 | case 'guess': |
||
804 | break; |
||
805 | case 'dokeos': |
||
806 | case 'chamilo': |
||
807 | $type = 1; |
||
808 | break; |
||
809 | case 'aicc': |
||
810 | break; |
||
811 | } |
||
812 | |||
813 | switch ($origin) { |
||
814 | case 'zip': |
||
815 | // Check zip name string. If empty, we are currently creating a new Chamilo learnpath. |
||
816 | break; |
||
817 | case 'manual': |
||
818 | default: |
||
819 | $get_max = "SELECT MAX(display_order) |
||
820 | FROM $tbl_lp WHERE c_id = $course_id"; |
||
821 | $res_max = Database::query($get_max); |
||
822 | if (Database::num_rows($res_max) < 1) { |
||
823 | $dsp = 1; |
||
824 | } else { |
||
825 | $row = Database::fetch_array($res_max); |
||
826 | $dsp = $row[0] + 1; |
||
827 | } |
||
828 | |||
829 | $newLp = (new CLp()) |
||
830 | ->setCId($course_id) |
||
831 | ->setLpType($type) |
||
832 | ->setName($name) |
||
833 | ->setDescription($description) |
||
834 | ->setPath('') |
||
835 | ->setDefaultViewMod('embedded') |
||
836 | ->setDefaultEncoding('UTF-8') |
||
837 | ->setDisplayOrder($dsp) |
||
838 | ->setContentMaker('Chamilo') |
||
839 | ->setContentLocal('local') |
||
840 | ->setJsLib('') |
||
841 | ->setSessionId($session_id) |
||
842 | ->setCreatedOn(api_get_utc_datetime(null, false, true)) |
||
843 | ->setModifiedOn(api_get_utc_datetime(null, false, true)) |
||
844 | ->setPublicatedOn($publicated_on) |
||
845 | ->setExpiredOn($expired_on) |
||
846 | ->setCategoryId($categoryId) |
||
847 | ->setForceCommit(false) |
||
848 | ->setContentLicense('') |
||
849 | ->setDebug(false) |
||
850 | ->setTheme('') |
||
851 | ->setPreviewImage('') |
||
852 | ->setAuthor('') |
||
853 | ->setPrerequisite(0) |
||
854 | ->setHideTocFrame(false) |
||
855 | ->setSeriousgameMode(false) |
||
856 | ->setAutolaunch(0) |
||
857 | ->setMaxAttempts(0) |
||
858 | ->setSubscribeUsers(0) |
||
859 | ->setAccumulateScormTime(1) |
||
860 | ; |
||
861 | |||
862 | $em->persist($newLp); |
||
863 | $em->flush(); |
||
864 | |||
865 | HookLearningPathCreated::create() |
||
866 | ->setEventData(['lp' => $newLp]) |
||
867 | ->notifyCreated() |
||
868 | ; |
||
869 | |||
870 | $newLp->setId($newLp->getIid()); |
||
871 | |||
872 | $em->flush(); |
||
873 | |||
874 | // Insert into item_property. |
||
875 | api_item_property_update( |
||
876 | $courseInfo, |
||
877 | TOOL_LEARNPATH, |
||
878 | $newLp->getIid(), |
||
879 | 'LearnpathAdded', |
||
880 | $userId |
||
881 | ); |
||
882 | api_set_default_visibility( |
||
883 | $newLp->getIid(), |
||
884 | TOOL_LEARNPATH, |
||
885 | 0, |
||
886 | $courseInfo, |
||
887 | $session_id, |
||
888 | $userId |
||
889 | ); |
||
890 | Event::addEvent( |
||
891 | LOG_LP_CREATE, |
||
892 | LOG_LP_ID, |
||
893 | $newLp->getIid().' - '.$name |
||
894 | ); |
||
895 | |||
896 | return $newLp->getIid(); |
||
897 | } |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Auto completes the parents of an item in case it's been completed or passed. |
||
902 | * |
||
903 | * @param int $item Optional ID of the item from which to look for parents |
||
904 | */ |
||
905 | public function autocomplete_parents($item) |
||
906 | { |
||
907 | $debug = $this->debug; |
||
908 | |||
909 | if (empty($item)) { |
||
910 | $item = $this->current; |
||
911 | } |
||
912 | |||
913 | $currentItem = $this->getItem($item); |
||
914 | if ($currentItem) { |
||
915 | $parent_id = $currentItem->get_parent(); |
||
916 | $parent = $this->getItem($parent_id); |
||
917 | if ($parent) { |
||
918 | // if $item points to an object and there is a parent. |
||
919 | if ($debug) { |
||
920 | error_log( |
||
921 | 'Autocompleting parent of item '.$item.' '. |
||
922 | $currentItem->get_title().'" (item '.$parent_id.' "'.$parent->get_title().'") ', |
||
923 | 0 |
||
924 | ); |
||
925 | } |
||
926 | |||
927 | // New experiment including failed and browsed in completed status. |
||
928 | //$current_status = $currentItem->get_status(); |
||
929 | //if ($currentItem->is_done() || $current_status == 'browsed' || $current_status == 'failed') { |
||
930 | // Fixes chapter auto complete |
||
931 | if (true) { |
||
932 | // If the current item is completed or passes or succeeded. |
||
933 | $updateParentStatus = true; |
||
934 | if ($debug) { |
||
935 | error_log('Status of current item is alright'); |
||
936 | } |
||
937 | |||
938 | foreach ($parent->get_children() as $childItemId) { |
||
939 | $childItem = $this->getItem($childItemId); |
||
940 | |||
941 | // If children was not set try to get the info |
||
942 | if (empty($childItem->db_item_view_id)) { |
||
943 | $childItem->set_lp_view($this->lp_view_id, $this->course_int_id); |
||
944 | } |
||
945 | |||
946 | // Check all his brothers (parent's children) for completion status. |
||
947 | if ($childItemId != $item) { |
||
948 | if ($debug) { |
||
949 | error_log( |
||
950 | 'Looking at brother #'.$childItemId.' "'.$childItem->get_title().'", status is '.$childItem->get_status(), |
||
951 | 0 |
||
952 | ); |
||
953 | } |
||
954 | // Trying completing parents of failed and browsed items as well. |
||
955 | if ($childItem->status_is( |
||
956 | [ |
||
957 | 'completed', |
||
958 | 'passed', |
||
959 | 'succeeded', |
||
960 | 'browsed', |
||
961 | 'failed', |
||
962 | ] |
||
963 | ) |
||
964 | ) { |
||
965 | // Keep completion status to true. |
||
966 | continue; |
||
967 | } else { |
||
968 | if ($debug > 2) { |
||
969 | error_log( |
||
970 | 'Found one incomplete child of parent #'.$parent_id.': child #'.$childItemId.' "'.$childItem->get_title().'", is '.$childItem->get_status().' db_item_view_id:#'.$childItem->db_item_view_id, |
||
971 | 0 |
||
972 | ); |
||
973 | } |
||
974 | $updateParentStatus = false; |
||
975 | break; |
||
976 | } |
||
977 | } |
||
978 | } |
||
979 | |||
980 | if ($updateParentStatus) { |
||
981 | // If all the children were completed: |
||
982 | $parent->set_status('completed'); |
||
983 | $parent->save(false, $this->prerequisites_match($parent->get_id())); |
||
984 | // Force the status to "completed" |
||
985 | //$this->update_queue[$parent->get_id()] = $parent->get_status(); |
||
986 | $this->update_queue[$parent->get_id()] = 'completed'; |
||
987 | if ($debug) { |
||
988 | error_log( |
||
989 | 'Added parent #'.$parent->get_id().' "'.$parent->get_title().'" to update queue status: completed '. |
||
990 | print_r($this->update_queue, 1), |
||
991 | 0 |
||
992 | ); |
||
993 | } |
||
994 | // Recursive call. |
||
995 | $this->autocomplete_parents($parent->get_id()); |
||
996 | } |
||
997 | } |
||
998 | } else { |
||
999 | if ($debug) { |
||
1000 | error_log("Parent #$parent_id does not exists"); |
||
1001 | } |
||
1002 | } |
||
1003 | } else { |
||
1004 | if ($debug) { |
||
1005 | error_log("#$item is an item that doesn't have parents"); |
||
1006 | } |
||
1007 | } |
||
1008 | } |
||
1009 | |||
1010 | /** |
||
1011 | * Closes the current resource. |
||
1012 | * |
||
1013 | * Stops the timer |
||
1014 | * Saves into the database if required |
||
1015 | * Clears the current resource data from this object |
||
1016 | * |
||
1017 | * @return bool True on success, false on failure |
||
1018 | */ |
||
1019 | public function close() |
||
1020 | { |
||
1021 | if (empty($this->lp_id)) { |
||
1022 | $this->error = 'Trying to close this learnpath but no ID is set'; |
||
1023 | |||
1024 | return false; |
||
1025 | } |
||
1026 | $this->current_time_stop = time(); |
||
1027 | $this->ordered_items = []; |
||
1028 | $this->index = 0; |
||
1029 | unset($this->lp_id); |
||
1030 | //unset other stuff |
||
1031 | return true; |
||
1032 | } |
||
1033 | |||
1034 | /** |
||
1035 | * Static admin function allowing removal of a learnpath. |
||
1036 | * |
||
1037 | * @param array $courseInfo |
||
1038 | * @param int $id Learnpath ID |
||
1039 | * @param string $delete Whether to delete data or keep it (default: 'keep', others: 'remove') |
||
1040 | * |
||
1041 | * @return bool True on success, false on failure (might change that to return number of elements deleted) |
||
1042 | */ |
||
1043 | public function delete($courseInfo = null, $id = null, $delete = 'keep') |
||
1044 | { |
||
1045 | $course_id = api_get_course_int_id(); |
||
1046 | if (!empty($courseInfo)) { |
||
1047 | $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id; |
||
1048 | } |
||
1049 | |||
1050 | // TODO: Implement a way of getting this to work when the current object is not set. |
||
1051 | // In clear: implement this in the item class as well (abstract class) and use the given ID in queries. |
||
1052 | // If an ID is specifically given and the current LP is not the same, prevent delete. |
||
1053 | if (!empty($id) && ($id != $this->lp_id)) { |
||
1054 | return false; |
||
1055 | } |
||
1056 | |||
1057 | $lp = Database::get_course_table(TABLE_LP_MAIN); |
||
1058 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
1059 | $lp_view = Database::get_course_table(TABLE_LP_VIEW); |
||
1060 | $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
1061 | |||
1062 | // Delete lp item id. |
||
1063 | foreach ($this->items as $lpItemId => $dummy) { |
||
1064 | $sql = "DELETE FROM $lp_item_view |
||
1065 | WHERE c_id = $course_id AND lp_item_id = '".$lpItemId."'"; |
||
1066 | Database::query($sql); |
||
1067 | } |
||
1068 | |||
1069 | // Proposed by Christophe (nickname: clefevre) |
||
1070 | $sql = "DELETE FROM $lp_item |
||
1071 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
1072 | Database::query($sql); |
||
1073 | |||
1074 | $sql = "DELETE FROM $lp_view |
||
1075 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
1076 | Database::query($sql); |
||
1077 | |||
1078 | self::toggle_publish($this->lp_id, 'i'); |
||
1079 | |||
1080 | if ($this->type == 2 || $this->type == 3) { |
||
1081 | // This is a scorm learning path, delete the files as well. |
||
1082 | $sql = "SELECT path FROM $lp |
||
1083 | WHERE iid = ".$this->lp_id; |
||
1084 | $res = Database::query($sql); |
||
1085 | if (Database::num_rows($res) > 0) { |
||
1086 | $row = Database::fetch_array($res); |
||
1087 | $path = $row['path']; |
||
1088 | $sql = "SELECT id FROM $lp |
||
1089 | WHERE |
||
1090 | c_id = $course_id AND |
||
1091 | path = '$path' AND |
||
1092 | iid != ".$this->lp_id; |
||
1093 | $res = Database::query($sql); |
||
1094 | if (Database::num_rows($res) > 0) { |
||
1095 | // Another learning path uses this directory, so don't delete it. |
||
1096 | if ($this->debug > 2) { |
||
1097 | error_log('In learnpath::delete(), found other LP using path '.$path.', keeping directory', 0); |
||
1098 | } |
||
1099 | } else { |
||
1100 | // No other LP uses that directory, delete it. |
||
1101 | $course_rel_dir = api_get_course_path().'/scorm/'; // scorm dir web path starting from /courses |
||
1102 | // The absolute system path for this course. |
||
1103 | $course_scorm_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; |
||
1104 | if ($delete == 'remove' && is_dir($course_scorm_dir.$path) && !empty($course_scorm_dir)) { |
||
1105 | if ($this->debug > 2) { |
||
1106 | error_log('In learnpath::delete(), found SCORM, deleting directory: '.$course_scorm_dir.$path, 0); |
||
1107 | } |
||
1108 | // Proposed by Christophe (clefevre). |
||
1109 | if (strcmp(substr($path, -2), "/.") == 0) { |
||
1110 | $path = substr($path, 0, -1); // Remove "." at the end. |
||
1111 | } |
||
1112 | //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way. |
||
1113 | rmdirr($course_scorm_dir.$path); |
||
1114 | } |
||
1115 | } |
||
1116 | } |
||
1117 | } |
||
1118 | |||
1119 | $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
1120 | $link = 'lp/lp_controller.php?action=view&lp_id='.$this->lp_id; |
||
1121 | // Delete tools |
||
1122 | $sql = "DELETE FROM $tbl_tool |
||
1123 | WHERE c_id = $course_id AND (link LIKE '$link%' AND image='scormbuilder.gif')"; |
||
1124 | Database::query($sql); |
||
1125 | |||
1126 | if (api_get_configuration_value('allow_lp_subscription_to_usergroups')) { |
||
1127 | $table = Database::get_course_table(TABLE_LP_REL_USERGROUP); |
||
1128 | $sql = "DELETE FROM $table |
||
1129 | WHERE |
||
1130 | lp_id = {$this->lp_id} AND |
||
1131 | c_id = $course_id "; |
||
1132 | Database::query($sql); |
||
1133 | } |
||
1134 | |||
1135 | $sql = "DELETE FROM $lp |
||
1136 | WHERE iid = ".$this->lp_id; |
||
1137 | Database::query($sql); |
||
1138 | // Updates the display order of all lps. |
||
1139 | $this->update_display_order(); |
||
1140 | |||
1141 | // Remove prerequisite based on the deleted LP |
||
1142 | $sql = "UPDATE $lp |
||
1143 | SET prerequisite = 0 where c_id = $course_id AND prerequisite = ".$this->lp_id; |
||
1144 | Database::query($sql); |
||
1145 | |||
1146 | api_item_property_update( |
||
1147 | api_get_course_info(), |
||
1148 | TOOL_LEARNPATH, |
||
1149 | $this->lp_id, |
||
1150 | 'delete', |
||
1151 | api_get_user_id() |
||
1152 | ); |
||
1153 | Event::addEvent( |
||
1154 | LOG_LP_DELETE, |
||
1155 | LOG_LP_ID, |
||
1156 | $this->lp_id.' - '.$this->get_name() |
||
1157 | ); |
||
1158 | |||
1159 | $link_info = GradebookUtils::isResourceInCourseGradebook( |
||
1160 | api_get_course_id(), |
||
1161 | 4, |
||
1162 | $id, |
||
1163 | api_get_session_id() |
||
1164 | ); |
||
1165 | |||
1166 | if ($link_info !== false) { |
||
1167 | GradebookUtils::remove_resource_from_course_gradebook($link_info['id']); |
||
1168 | } |
||
1169 | |||
1170 | if (api_get_setting('search_enabled') == 'true') { |
||
1171 | require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php'; |
||
1172 | delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id); |
||
1173 | } |
||
1174 | } |
||
1175 | |||
1176 | /** |
||
1177 | * Removes all the children of one item - dangerous! |
||
1178 | * |
||
1179 | * @param int $id Element ID of which children have to be removed |
||
1180 | * |
||
1181 | * @return int Total number of children removed |
||
1182 | */ |
||
1183 | public function delete_children_items($id) |
||
1184 | { |
||
1185 | $course_id = $this->course_info['real_id']; |
||
1186 | |||
1187 | $num = 0; |
||
1188 | $id = (int) $id; |
||
1189 | if (empty($id) || empty($course_id)) { |
||
1190 | return false; |
||
1191 | } |
||
1192 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
1193 | $sql = "SELECT * FROM $lp_item |
||
1194 | WHERE c_id = $course_id AND parent_item_id = $id"; |
||
1195 | $res = Database::query($sql); |
||
1196 | while ($row = Database::fetch_array($res)) { |
||
1197 | $num += $this->delete_children_items($row['iid']); |
||
1198 | $sql = "DELETE FROM $lp_item |
||
1199 | WHERE c_id = $course_id AND iid = ".$row['iid']; |
||
1200 | Database::query($sql); |
||
1201 | $num++; |
||
1202 | } |
||
1203 | |||
1204 | return $num; |
||
1205 | } |
||
1206 | |||
1207 | /** |
||
1208 | * Removes an item from the current learnpath. |
||
1209 | * |
||
1210 | * @param int $id Elem ID (0 if first) |
||
1211 | * |
||
1212 | * @return int Number of elements moved |
||
1213 | * |
||
1214 | * @todo implement resource removal |
||
1215 | */ |
||
1216 | public function delete_item($id) |
||
1217 | { |
||
1218 | $course_id = api_get_course_int_id(); |
||
1219 | $id = (int) $id; |
||
1220 | // TODO: Implement the resource removal. |
||
1221 | if (empty($id) || empty($course_id)) { |
||
1222 | return false; |
||
1223 | } |
||
1224 | // First select item to get previous, next, and display order. |
||
1225 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
1226 | $sql_sel = "SELECT * FROM $lp_item WHERE iid = $id"; |
||
1227 | $res_sel = Database::query($sql_sel); |
||
1228 | if (Database::num_rows($res_sel) < 1) { |
||
1229 | return false; |
||
1230 | } |
||
1231 | $row = Database::fetch_array($res_sel); |
||
1232 | $previous = $row['previous_item_id']; |
||
1233 | $next = $row['next_item_id']; |
||
1234 | $display = $row['display_order']; |
||
1235 | $parent = $row['parent_item_id']; |
||
1236 | $lp = $row['lp_id']; |
||
1237 | // Delete children items. |
||
1238 | $this->delete_children_items($id); |
||
1239 | // Now delete the item. |
||
1240 | $sql_del = "DELETE FROM $lp_item WHERE iid = $id"; |
||
1241 | Database::query($sql_del); |
||
1242 | // Now update surrounding items. |
||
1243 | $sql_upd = "UPDATE $lp_item SET next_item_id = $next |
||
1244 | WHERE iid = $previous"; |
||
1245 | Database::query($sql_upd); |
||
1246 | $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous |
||
1247 | WHERE iid = $next AND item_type != '".TOOL_LP_FINAL_ITEM."'"; |
||
1248 | Database::query($sql_upd); |
||
1249 | // Now update all following items with new display order. |
||
1250 | $sql_all = "UPDATE $lp_item SET display_order = display_order-1 |
||
1251 | WHERE |
||
1252 | c_id = $course_id AND |
||
1253 | lp_id = $lp AND |
||
1254 | parent_item_id = $parent AND |
||
1255 | display_order > $display"; |
||
1256 | Database::query($sql_all); |
||
1257 | |||
1258 | //Removing prerequisites since the item will not longer exist |
||
1259 | $sql_all = "UPDATE $lp_item SET prerequisite = '' |
||
1260 | WHERE c_id = $course_id AND prerequisite = $id"; |
||
1261 | Database::query($sql_all); |
||
1262 | |||
1263 | $sql = "UPDATE $lp_item |
||
1264 | SET previous_item_id = ".$this->getLastInFirstLevel()." |
||
1265 | WHERE c_id = $course_id AND lp_id = {$this->lp_id} AND item_type = '".TOOL_LP_FINAL_ITEM."'"; |
||
1266 | Database::query($sql); |
||
1267 | |||
1268 | // Remove from search engine if enabled. |
||
1269 | if (api_get_setting('search_enabled') === 'true') { |
||
1270 | $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
||
1271 | $sql = 'SELECT * FROM %s |
||
1272 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
1273 | LIMIT 1'; |
||
1274 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id); |
||
1275 | $res = Database::query($sql); |
||
1276 | if (Database::num_rows($res) > 0) { |
||
1277 | $row2 = Database::fetch_array($res); |
||
1278 | $di = new ChamiloIndexer(); |
||
1279 | $di->remove_document($row2['search_did']); |
||
1280 | } |
||
1281 | $sql = 'DELETE FROM %s |
||
1282 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
1283 | LIMIT 1'; |
||
1284 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id); |
||
1285 | Database::query($sql); |
||
1286 | } |
||
1287 | } |
||
1288 | |||
1289 | /** |
||
1290 | * Updates an item's content in place. |
||
1291 | * |
||
1292 | * @param int $id Element ID |
||
1293 | * @param int $parent Parent item ID |
||
1294 | * @param int $previous Previous item ID |
||
1295 | * @param string $title Item title |
||
1296 | * @param string $description Item description |
||
1297 | * @param string $prerequisites Prerequisites (optional) |
||
1298 | * @param array $audio The array resulting of the $_FILES[mp3] element |
||
1299 | * @param int $max_time_allowed |
||
1300 | * @param string $url |
||
1301 | * |
||
1302 | * @return bool True on success, false on error |
||
1303 | */ |
||
1304 | public function edit_item( |
||
1305 | $id, |
||
1306 | $parent, |
||
1307 | $previous, |
||
1308 | $title, |
||
1309 | $description, |
||
1310 | $prerequisites = '0', |
||
1311 | $audio = [], |
||
1312 | $max_time_allowed = 0, |
||
1313 | $url = '' |
||
1314 | ) { |
||
1315 | $course_id = api_get_course_int_id(); |
||
1316 | $_course = api_get_course_info(); |
||
1317 | $id = (int) $id; |
||
1318 | |||
1319 | if (empty($max_time_allowed)) { |
||
1320 | $max_time_allowed = 0; |
||
1321 | } |
||
1322 | |||
1323 | if (empty($id) || empty($_course)) { |
||
1324 | return false; |
||
1325 | } |
||
1326 | |||
1327 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
1328 | $sql = "SELECT * FROM $tbl_lp_item |
||
1329 | WHERE iid = $id"; |
||
1330 | $res_select = Database::query($sql); |
||
1331 | $row_select = Database::fetch_array($res_select); |
||
1332 | $audio_update_sql = ''; |
||
1333 | if (is_array($audio) && !empty($audio['tmp_name']) && $audio['error'] === 0) { |
||
1334 | // Create the audio folder if it does not exist yet. |
||
1335 | $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||
1336 | if (!is_dir($filepath.'audio')) { |
||
1337 | mkdir($filepath.'audio', api_get_permissions_for_new_directories()); |
||
1338 | $audio_id = add_document( |
||
1339 | $_course, |
||
1340 | '/audio', |
||
1341 | 'folder', |
||
1342 | 0, |
||
1343 | 'audio' |
||
1344 | ); |
||
1345 | api_item_property_update( |
||
1346 | $_course, |
||
1347 | TOOL_DOCUMENT, |
||
1348 | $audio_id, |
||
1349 | 'FolderCreated', |
||
1350 | api_get_user_id(), |
||
1351 | null, |
||
1352 | null, |
||
1353 | null, |
||
1354 | null, |
||
1355 | api_get_session_id() |
||
1356 | ); |
||
1357 | api_item_property_update( |
||
1358 | $_course, |
||
1359 | TOOL_DOCUMENT, |
||
1360 | $audio_id, |
||
1361 | 'invisible', |
||
1362 | api_get_user_id(), |
||
1363 | null, |
||
1364 | null, |
||
1365 | null, |
||
1366 | null, |
||
1367 | api_get_session_id() |
||
1368 | ); |
||
1369 | } |
||
1370 | |||
1371 | // Upload file in documents. |
||
1372 | $pi = pathinfo($audio['name']); |
||
1373 | if ($pi['extension'] === 'mp3') { |
||
1374 | $c_det = api_get_course_info($this->cc); |
||
1375 | $bp = api_get_path(SYS_COURSE_PATH).$c_det['path'].'/document'; |
||
1376 | $path = handle_uploaded_document( |
||
1377 | $c_det, |
||
1378 | $audio, |
||
1379 | $bp, |
||
1380 | '/audio', |
||
1381 | api_get_user_id(), |
||
1382 | 0, |
||
1383 | null, |
||
1384 | 0, |
||
1385 | 'rename', |
||
1386 | false, |
||
1387 | 0 |
||
1388 | ); |
||
1389 | // Update reference in lp_item - audio path is the path from inside de document/audio/ dir. |
||
1390 | $audio_update_sql = ", audio = '".Database::escape_string($path)."' "; |
||
1391 | } |
||
1392 | } |
||
1393 | |||
1394 | $same_parent = $row_select['parent_item_id'] == $parent ? true : false; |
||
1395 | $same_previous = $row_select['previous_item_id'] == $previous ? true : false; |
||
1396 | |||
1397 | // TODO: htmlspecialchars to be checked for encoding related problems. |
||
1398 | if ($same_parent && $same_previous) { |
||
1399 | // Only update title and description. |
||
1400 | $sql = "UPDATE $tbl_lp_item |
||
1401 | SET title = '".Database::escape_string($title)."', |
||
1402 | prerequisite = '".$prerequisites."', |
||
1403 | description = '".Database::escape_string($description)."' |
||
1404 | ".$audio_update_sql.", |
||
1405 | max_time_allowed = '".Database::escape_string($max_time_allowed)."' |
||
1406 | WHERE iid = $id"; |
||
1407 | Database::query($sql); |
||
1408 | } else { |
||
1409 | $old_parent = $row_select['parent_item_id']; |
||
1410 | $old_previous = $row_select['previous_item_id']; |
||
1411 | $old_next = $row_select['next_item_id']; |
||
1412 | $old_order = $row_select['display_order']; |
||
1413 | $old_prerequisite = $row_select['prerequisite']; |
||
1414 | $old_max_time_allowed = $row_select['max_time_allowed']; |
||
1415 | |||
1416 | /* BEGIN -- virtually remove the current item id */ |
||
1417 | /* for the next and previous item it is like the current item doesn't exist anymore */ |
||
1418 | if ($old_previous != 0) { |
||
1419 | // Next |
||
1420 | $sql = "UPDATE $tbl_lp_item |
||
1421 | SET next_item_id = $old_next |
||
1422 | WHERE iid = $old_previous"; |
||
1423 | Database::query($sql); |
||
1424 | } |
||
1425 | |||
1426 | if (!empty($old_next)) { |
||
1427 | // Previous |
||
1428 | $sql = "UPDATE $tbl_lp_item |
||
1429 | SET previous_item_id = $old_previous |
||
1430 | WHERE iid = $old_next"; |
||
1431 | Database::query($sql); |
||
1432 | } |
||
1433 | |||
1434 | // display_order - 1 for every item with a display_order |
||
1435 | // bigger then the display_order of the current item. |
||
1436 | $sql = "UPDATE $tbl_lp_item |
||
1437 | SET display_order = display_order - 1 |
||
1438 | WHERE |
||
1439 | c_id = $course_id AND |
||
1440 | display_order > $old_order AND |
||
1441 | lp_id = ".$this->lp_id." AND |
||
1442 | parent_item_id = $old_parent"; |
||
1443 | Database::query($sql); |
||
1444 | /* END -- virtually remove the current item id */ |
||
1445 | |||
1446 | /* BEGIN -- update the current item id to his new location */ |
||
1447 | if ($previous == 0) { |
||
1448 | // Select the data of the item that should come after the current item. |
||
1449 | $sql = "SELECT id, display_order |
||
1450 | FROM $tbl_lp_item |
||
1451 | WHERE |
||
1452 | c_id = $course_id AND |
||
1453 | lp_id = ".$this->lp_id." AND |
||
1454 | parent_item_id = $parent AND |
||
1455 | previous_item_id = $previous"; |
||
1456 | $res_select_old = Database::query($sql); |
||
1457 | $row_select_old = Database::fetch_array($res_select_old); |
||
1458 | |||
1459 | // If the new parent didn't have children before. |
||
1460 | if (Database::num_rows($res_select_old) == 0) { |
||
1461 | $new_next = 0; |
||
1462 | $new_order = 1; |
||
1463 | } else { |
||
1464 | $new_next = $row_select_old['id']; |
||
1465 | $new_order = $row_select_old['display_order']; |
||
1466 | } |
||
1467 | } else { |
||
1468 | // Select the data of the item that should come before the current item. |
||
1469 | $sql = "SELECT next_item_id, display_order |
||
1470 | FROM $tbl_lp_item |
||
1471 | WHERE iid = $previous"; |
||
1472 | $res_select_old = Database::query($sql); |
||
1473 | $row_select_old = Database::fetch_array($res_select_old); |
||
1474 | $new_next = $row_select_old['next_item_id']; |
||
1475 | $new_order = $row_select_old['display_order'] + 1; |
||
1476 | } |
||
1477 | |||
1478 | // TODO: htmlspecialchars to be checked for encoding related problems. |
||
1479 | // Update the current item with the new data. |
||
1480 | $sql = "UPDATE $tbl_lp_item |
||
1481 | SET |
||
1482 | title = '".Database::escape_string($title)."', |
||
1483 | description = '".Database::escape_string($description)."', |
||
1484 | parent_item_id = $parent, |
||
1485 | previous_item_id = $previous, |
||
1486 | next_item_id = $new_next, |
||
1487 | display_order = $new_order |
||
1488 | $audio_update_sql |
||
1489 | WHERE iid = $id"; |
||
1490 | Database::query($sql); |
||
1491 | |||
1492 | if ($previous != 0) { |
||
1493 | // Update the previous item's next_item_id. |
||
1494 | $sql = "UPDATE $tbl_lp_item |
||
1495 | SET next_item_id = $id |
||
1496 | WHERE iid = $previous"; |
||
1497 | Database::query($sql); |
||
1498 | } |
||
1499 | |||
1500 | if (!empty($new_next)) { |
||
1501 | // Update the next item's previous_item_id. |
||
1502 | $sql = "UPDATE $tbl_lp_item |
||
1503 | SET previous_item_id = $id |
||
1504 | WHERE iid = $new_next"; |
||
1505 | Database::query($sql); |
||
1506 | } |
||
1507 | |||
1508 | if ($old_prerequisite != $prerequisites) { |
||
1509 | $sql = "UPDATE $tbl_lp_item |
||
1510 | SET prerequisite = '$prerequisites' |
||
1511 | WHERE iid = $id"; |
||
1512 | Database::query($sql); |
||
1513 | } |
||
1514 | |||
1515 | if ($old_max_time_allowed != $max_time_allowed) { |
||
1516 | // update max time allowed |
||
1517 | $sql = "UPDATE $tbl_lp_item |
||
1518 | SET max_time_allowed = $max_time_allowed |
||
1519 | WHERE iid = $id"; |
||
1520 | Database::query($sql); |
||
1521 | } |
||
1522 | |||
1523 | // Update all the items with the same or a bigger display_order than the current item. |
||
1524 | $sql = "UPDATE $tbl_lp_item |
||
1525 | SET display_order = display_order + 1 |
||
1526 | WHERE |
||
1527 | c_id = $course_id AND |
||
1528 | lp_id = ".$this->get_id()." AND |
||
1529 | iid <> $id AND |
||
1530 | parent_item_id = $parent AND |
||
1531 | display_order >= $new_order"; |
||
1532 | Database::query($sql); |
||
1533 | } |
||
1534 | |||
1535 | if ($row_select['item_type'] == 'link') { |
||
1536 | $link = new Link(); |
||
1537 | $linkId = $row_select['path']; |
||
1538 | $link->updateLink($linkId, $url); |
||
1539 | } |
||
1540 | } |
||
1541 | |||
1542 | /** |
||
1543 | * Updates an item's prereq in place. |
||
1544 | * |
||
1545 | * @param int $id Element ID |
||
1546 | * @param string $prerequisite_id Prerequisite Element ID |
||
1547 | * @param int $minScore Prerequisite min score |
||
1548 | * @param int $maxScore Prerequisite max score |
||
1549 | * |
||
1550 | * @return bool True on success, false on error |
||
1551 | */ |
||
1552 | public function edit_item_prereq( |
||
1553 | $id, |
||
1554 | $prerequisite_id, |
||
1555 | $minScore = 0, |
||
1556 | $maxScore = 100 |
||
1557 | ) { |
||
1558 | $id = (int) $id; |
||
1559 | |||
1560 | if (empty($id)) { |
||
1561 | return false; |
||
1562 | } |
||
1563 | |||
1564 | $prerequisite_id = (int) $prerequisite_id; |
||
1565 | |||
1566 | if (empty($minScore) || $minScore < 0) { |
||
1567 | $minScore = 0; |
||
1568 | } |
||
1569 | |||
1570 | if (empty($maxScore) || $maxScore < 0) { |
||
1571 | $maxScore = 100; |
||
1572 | } |
||
1573 | |||
1574 | $minScore = (float) $minScore; |
||
1575 | $maxScore = (float) $maxScore; |
||
1576 | |||
1577 | if (empty($prerequisite_id)) { |
||
1578 | $prerequisite_id = 'NULL'; |
||
1579 | $minScore = 0; |
||
1580 | $maxScore = 100; |
||
1581 | } |
||
1582 | |||
1583 | $table = Database::get_course_table(TABLE_LP_ITEM); |
||
1584 | $sql = " UPDATE $table |
||
1585 | SET |
||
1586 | prerequisite = $prerequisite_id , |
||
1587 | prerequisite_min_score = $minScore , |
||
1588 | prerequisite_max_score = $maxScore |
||
1589 | WHERE iid = $id"; |
||
1590 | Database::query($sql); |
||
1591 | |||
1592 | return true; |
||
1593 | } |
||
1594 | |||
1595 | /** |
||
1596 | * Get the specific prefix index terms of this learning path. |
||
1597 | * |
||
1598 | * @param string $prefix |
||
1599 | * |
||
1600 | * @return array Array of terms |
||
1601 | */ |
||
1602 | public function get_common_index_terms_by_prefix($prefix) |
||
1603 | { |
||
1604 | require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php'; |
||
1605 | $terms = get_specific_field_values_list_by_prefix( |
||
1606 | $prefix, |
||
1607 | $this->cc, |
||
1608 | TOOL_LEARNPATH, |
||
1609 | $this->lp_id |
||
1610 | ); |
||
1611 | $prefix_terms = []; |
||
1612 | if (!empty($terms)) { |
||
1613 | foreach ($terms as $term) { |
||
1614 | $prefix_terms[] = $term['value']; |
||
1615 | } |
||
1616 | } |
||
1617 | |||
1618 | return $prefix_terms; |
||
1619 | } |
||
1620 | |||
1621 | /** |
||
1622 | * Gets the number of items currently completed. |
||
1623 | * |
||
1624 | * @param bool $failedStatusException flag to determine the failed status is not considered progressed |
||
1625 | * |
||
1626 | * @return int The number of items currently completed |
||
1627 | */ |
||
1628 | public function get_complete_items_count($failedStatusException = false, $typeListNotToCount = []) |
||
1629 | { |
||
1630 | $i = 0; |
||
1631 | $completedStatusList = [ |
||
1632 | 'completed', |
||
1633 | 'passed', |
||
1634 | 'succeeded', |
||
1635 | 'browsed', |
||
1636 | ]; |
||
1637 | |||
1638 | if (!$failedStatusException) { |
||
1639 | $completedStatusList[] = 'failed'; |
||
1640 | } |
||
1641 | |||
1642 | if ($this->debug) { |
||
1643 | error_log('START - get_complete_items_count'); |
||
1644 | error_log('Counting steps with status in: '.print_r($completedStatusList, 1)); |
||
1645 | } |
||
1646 | |||
1647 | $chapters = self::getChapterTypes(); |
||
1648 | if (!empty($typeListNotToCount)) { |
||
1649 | $typeListNotToCount = array_merge($typeListNotToCount, $chapters); |
||
1650 | } else { |
||
1651 | $typeListNotToCount = $chapters; |
||
1652 | } |
||
1653 | |||
1654 | foreach ($this->items as $id => $dummy) { |
||
1655 | // Trying failed and browsed considered "progressed" as well. |
||
1656 | if ($this->items[$id]->status_is($completedStatusList) && |
||
1657 | !in_array($this->items[$id]->get_type(), $typeListNotToCount) |
||
1658 | ) { |
||
1659 | $i++; |
||
1660 | } |
||
1661 | } |
||
1662 | |||
1663 | if ($this->debug) { |
||
1664 | error_log('END - Count: '.$i); |
||
1665 | } |
||
1666 | |||
1667 | return $i; |
||
1668 | } |
||
1669 | |||
1670 | /** |
||
1671 | * Gets the current item ID. |
||
1672 | * |
||
1673 | * @return int The current learnpath item id |
||
1674 | */ |
||
1675 | public function get_current_item_id() |
||
1676 | { |
||
1677 | $current = 0; |
||
1678 | if (!empty($this->current)) { |
||
1679 | $current = (int) $this->current; |
||
1680 | } |
||
1681 | |||
1682 | return $current; |
||
1683 | } |
||
1684 | |||
1685 | /** |
||
1686 | * Force to get the first learnpath item id. |
||
1687 | * |
||
1688 | * @return int The current learnpath item id |
||
1689 | */ |
||
1690 | public function get_first_item_id() |
||
1691 | { |
||
1692 | $current = 0; |
||
1693 | if (is_array($this->ordered_items)) { |
||
1694 | $current = $this->ordered_items[0]; |
||
1695 | } |
||
1696 | |||
1697 | return $current; |
||
1698 | } |
||
1699 | |||
1700 | /** |
||
1701 | * Gets the total number of items available for viewing in this SCORM. |
||
1702 | * |
||
1703 | * @return int The total number of items |
||
1704 | */ |
||
1705 | public function get_total_items_count() |
||
1706 | { |
||
1707 | return count($this->items); |
||
1708 | } |
||
1709 | |||
1710 | /** |
||
1711 | * Gets the total number of items available for viewing in this SCORM but without chapters. |
||
1712 | * |
||
1713 | * @return int The total no-chapters number of items |
||
1714 | */ |
||
1715 | public function getTotalItemsCountWithoutDirs($typeListNotToCount = []) |
||
1716 | { |
||
1717 | $total = 0; |
||
1718 | $chapters = self::getChapterTypes(); |
||
1719 | if (!empty($typeListNotToCount)) { |
||
1720 | $typeListNotToCount = array_merge($typeListNotToCount, $chapters); |
||
1721 | } else { |
||
1722 | $typeListNotToCount = $chapters; |
||
1723 | } |
||
1724 | foreach ($this->items as $temp2) { |
||
1725 | if (!in_array($temp2->get_type(), $typeListNotToCount)) { |
||
1726 | $total++; |
||
1727 | } |
||
1728 | } |
||
1729 | |||
1730 | return $total; |
||
1731 | } |
||
1732 | |||
1733 | /** |
||
1734 | * Sets the first element URL. |
||
1735 | */ |
||
1736 | public function first() |
||
1737 | { |
||
1738 | if ($this->debug > 0) { |
||
1739 | error_log('In learnpath::first()', 0); |
||
1740 | error_log('$this->last_item_seen '.$this->last_item_seen); |
||
1741 | } |
||
1742 | |||
1743 | // Test if the last_item_seen exists and is not a dir. |
||
1744 | if (count($this->ordered_items) == 0) { |
||
1745 | $this->index = 0; |
||
1746 | } |
||
1747 | |||
1748 | if (!empty($this->last_item_seen) && |
||
1749 | !empty($this->items[$this->last_item_seen]) && |
||
1750 | $this->items[$this->last_item_seen]->get_type() != 'dir' |
||
1751 | //with this change (below) the LP will NOT go to the next item, it will take lp item we left |
||
1752 | //&& !$this->items[$this->last_item_seen]->is_done() |
||
1753 | ) { |
||
1754 | if ($this->debug > 2) { |
||
1755 | error_log( |
||
1756 | 'In learnpath::first() - Last item seen is '.$this->last_item_seen.' of type '. |
||
1757 | $this->items[$this->last_item_seen]->get_type() |
||
1758 | ); |
||
1759 | } |
||
1760 | $index = -1; |
||
1761 | foreach ($this->ordered_items as $myindex => $item_id) { |
||
1762 | if ($item_id == $this->last_item_seen) { |
||
1763 | $index = $myindex; |
||
1764 | break; |
||
1765 | } |
||
1766 | } |
||
1767 | if ($index == -1) { |
||
1768 | // Index hasn't changed, so item not found - panic (this shouldn't happen). |
||
1769 | if ($this->debug > 2) { |
||
1770 | error_log('Last item ('.$this->last_item_seen.') was found in items but not in ordered_items, panic!', 0); |
||
1771 | } |
||
1772 | |||
1773 | return false; |
||
1774 | } else { |
||
1775 | $this->last = $this->last_item_seen; |
||
1776 | $this->current = $this->last_item_seen; |
||
1777 | $this->index = $index; |
||
1778 | } |
||
1779 | } else { |
||
1780 | if ($this->debug > 2) { |
||
1781 | error_log('In learnpath::first() - No last item seen', 0); |
||
1782 | } |
||
1783 | $index = 0; |
||
1784 | // Loop through all ordered items and stop at the first item that is |
||
1785 | // not a directory *and* that has not been completed yet. |
||
1786 | while (!empty($this->ordered_items[$index]) && |
||
1787 | is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') && |
||
1788 | ( |
||
1789 | $this->items[$this->ordered_items[$index]]->get_type() == 'dir' || |
||
1790 | $this->items[$this->ordered_items[$index]]->is_done() === true |
||
1791 | ) && $index < $this->max_ordered_items) { |
||
1792 | $index++; |
||
1793 | } |
||
1794 | |||
1795 | $this->last = $this->current; |
||
1796 | // current is |
||
1797 | $this->current = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null; |
||
1798 | $this->index = $index; |
||
1799 | if ($this->debug > 2) { |
||
1800 | error_log('$index '.$index); |
||
1801 | error_log('In learnpath::first() - No last item seen'); |
||
1802 | error_log('New last = '.$this->last.'('.$this->ordered_items[$index].')'); |
||
1803 | } |
||
1804 | } |
||
1805 | if ($this->debug > 2) { |
||
1806 | error_log('In learnpath::first() - First item is '.$this->get_current_item_id()); |
||
1807 | } |
||
1808 | } |
||
1809 | |||
1810 | /** |
||
1811 | * Gets the js library from the database. |
||
1812 | * |
||
1813 | * @return string The name of the javascript library to be used |
||
1814 | */ |
||
1815 | public function get_js_lib() |
||
1816 | { |
||
1817 | $lib = ''; |
||
1818 | if (!empty($this->js_lib)) { |
||
1819 | $lib = $this->js_lib; |
||
1820 | } |
||
1821 | |||
1822 | return $lib; |
||
1823 | } |
||
1824 | |||
1825 | /** |
||
1826 | * Gets the learnpath database ID. |
||
1827 | * |
||
1828 | * @return int Learnpath ID in the lp table |
||
1829 | */ |
||
1830 | public function get_id() |
||
1831 | { |
||
1832 | if (!empty($this->lp_id)) { |
||
1833 | return (int) $this->lp_id; |
||
1834 | } |
||
1835 | |||
1836 | return 0; |
||
1837 | } |
||
1838 | |||
1839 | /** |
||
1840 | * Gets the last element URL. |
||
1841 | * |
||
1842 | * @return string URL to load into the viewer |
||
1843 | */ |
||
1844 | public function get_last() |
||
1845 | { |
||
1846 | // This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices" |
||
1847 | if (count($this->ordered_items) > 0) { |
||
1848 | $this->index = count($this->ordered_items) - 1; |
||
1849 | |||
1850 | return $this->ordered_items[$this->index]; |
||
1851 | } |
||
1852 | |||
1853 | return false; |
||
1854 | } |
||
1855 | |||
1856 | /** |
||
1857 | * Get the last element in the first level. |
||
1858 | * Unlike learnpath::get_last this function doesn't consider the subsection' elements. |
||
1859 | * |
||
1860 | * @return mixed |
||
1861 | */ |
||
1862 | public function getLastInFirstLevel() |
||
1863 | { |
||
1864 | try { |
||
1865 | $lastId = Database::getManager() |
||
1866 | ->createQuery('SELECT i.iid FROM ChamiloCourseBundle:CLpItem i |
||
1867 | WHERE i.lpId = :lp AND i.parentItemId = 0 AND i.itemType != :type ORDER BY i.displayOrder DESC') |
||
1868 | ->setMaxResults(1) |
||
1869 | ->setParameters(['lp' => $this->lp_id, 'type' => TOOL_LP_FINAL_ITEM]) |
||
1870 | ->getSingleScalarResult(); |
||
1871 | |||
1872 | return $lastId; |
||
1873 | } catch (Exception $exception) { |
||
1874 | return 0; |
||
1875 | } |
||
1876 | } |
||
1877 | |||
1878 | /** |
||
1879 | * Get the learning path name by id. |
||
1880 | * |
||
1881 | * @param int $lpId |
||
1882 | * |
||
1883 | * @return mixed |
||
1884 | */ |
||
1885 | public static function getLpNameById($lpId) |
||
1886 | { |
||
1887 | $em = Database::getManager(); |
||
1888 | |||
1889 | return $em->createQuery('SELECT clp.name FROM ChamiloCourseBundle:CLp clp |
||
1890 | WHERE clp.iid = :iid') |
||
1891 | ->setParameter('iid', $lpId) |
||
1892 | ->getSingleScalarResult(); |
||
1893 | } |
||
1894 | |||
1895 | /** |
||
1896 | * Gets the navigation bar for the learnpath display screen. |
||
1897 | * |
||
1898 | * @param string $barId |
||
1899 | * |
||
1900 | * @return string The HTML string to use as a navigation bar |
||
1901 | */ |
||
1902 | public function get_navigation_bar($barId = '') |
||
1903 | { |
||
1904 | if (empty($barId)) { |
||
1905 | $barId = 'control-top'; |
||
1906 | } |
||
1907 | $lpId = $this->lp_id; |
||
1908 | $mycurrentitemid = $this->get_current_item_id(); |
||
1909 | |||
1910 | $reportingText = get_lang('Reporting'); |
||
1911 | $previousText = get_lang('ScormPrevious'); |
||
1912 | $nextText = get_lang('ScormNext'); |
||
1913 | $fullScreenText = get_lang('ScormExitFullScreen'); |
||
1914 | |||
1915 | $settings = api_get_configuration_value('lp_view_settings'); |
||
1916 | $display = isset($settings['display']) ? $settings['display'] : false; |
||
1917 | $reportingIcon = ' |
||
1918 | <a class="icon-toolbar" |
||
1919 | id="stats_link" |
||
1920 | href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lpId.'" |
||
1921 | onclick="window.parent.API.save_asset(); return true;" |
||
1922 | target="content_name" title="'.$reportingText.'"> |
||
1923 | <span class="fa fa-info"></span><span class="sr-only">'.$reportingText.'</span> |
||
1924 | </a>'; |
||
1925 | |||
1926 | if (!empty($display)) { |
||
1927 | $showReporting = isset($display['show_reporting_icon']) ? $display['show_reporting_icon'] : true; |
||
1928 | if ($showReporting === false) { |
||
1929 | $reportingIcon = ''; |
||
1930 | } |
||
1931 | } |
||
1932 | |||
1933 | $hideArrows = false; |
||
1934 | if (isset($settings['display']) && isset($settings['display']['hide_lp_arrow_navigation'])) { |
||
1935 | $hideArrows = $settings['display']['hide_lp_arrow_navigation']; |
||
1936 | } |
||
1937 | |||
1938 | $previousIcon = ''; |
||
1939 | $nextIcon = ''; |
||
1940 | if ($hideArrows === false) { |
||
1941 | $previousIcon = ' |
||
1942 | <a class="icon-toolbar" id="scorm-previous" href="#" |
||
1943 | onclick="switch_item('.$mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'"> |
||
1944 | <span class="fa fa-chevron-left"></span><span class="sr-only">'.$previousText.'</span> |
||
1945 | </a>'; |
||
1946 | |||
1947 | $nextIcon = ' |
||
1948 | <a class="icon-toolbar" id="scorm-next" href="#" |
||
1949 | onclick="switch_item('.$mycurrentitemid.',\'next\');return false;" title="'.$nextText.'"> |
||
1950 | <span class="fa fa-chevron-right"></span><span class="sr-only">'.$nextText.'</span> |
||
1951 | </a>'; |
||
1952 | } |
||
1953 | |||
1954 | if ($this->mode === 'fullscreen') { |
||
1955 | $navbar = ' |
||
1956 | <span id="'.$barId.'" class="buttons"> |
||
1957 | '.$reportingIcon.' |
||
1958 | '.$previousIcon.' |
||
1959 | '.$nextIcon.' |
||
1960 | <a class="icon-toolbar" id="view-embedded" |
||
1961 | href="lp_controller.php?action=mode&mode=embedded" target="_top" title="'.$fullScreenText.'"> |
||
1962 | <span class="fa fa-columns"></span><span class="sr-only">'.$fullScreenText.'</span> |
||
1963 | </a> |
||
1964 | </span>'; |
||
1965 | } else { |
||
1966 | $navbar = ' |
||
1967 | <span id="'.$barId.'" class="buttons text-right"> |
||
1968 | '.$reportingIcon.' |
||
1969 | '.$previousIcon.' |
||
1970 | '.$nextIcon.' |
||
1971 | </span>'; |
||
1972 | } |
||
1973 | |||
1974 | return $navbar; |
||
1975 | } |
||
1976 | |||
1977 | /** |
||
1978 | * Gets the next resource in queue (url). |
||
1979 | * |
||
1980 | * @return string URL to load into the viewer |
||
1981 | */ |
||
1982 | public function get_next_index() |
||
1983 | { |
||
1984 | // TODO |
||
1985 | $index = $this->index; |
||
1986 | $index++; |
||
1987 | while ( |
||
1988 | !empty($this->ordered_items[$index]) && ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') && |
||
1989 | $index < $this->max_ordered_items |
||
1990 | ) { |
||
1991 | $index++; |
||
1992 | if ($index == $this->max_ordered_items) { |
||
1993 | if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') { |
||
1994 | return $this->index; |
||
1995 | } |
||
1996 | |||
1997 | return $index; |
||
1998 | } |
||
1999 | } |
||
2000 | if (empty($this->ordered_items[$index])) { |
||
2001 | return $this->index; |
||
2002 | } |
||
2003 | |||
2004 | return $index; |
||
2005 | } |
||
2006 | |||
2007 | /** |
||
2008 | * Gets item_id for the next element. |
||
2009 | * |
||
2010 | * @return int Next item (DB) ID |
||
2011 | */ |
||
2012 | public function get_next_item_id() |
||
2013 | { |
||
2014 | $new_index = $this->get_next_index(); |
||
2015 | if (!empty($new_index)) { |
||
2016 | if (isset($this->ordered_items[$new_index])) { |
||
2017 | return $this->ordered_items[$new_index]; |
||
2018 | } |
||
2019 | } |
||
2020 | |||
2021 | return 0; |
||
2022 | } |
||
2023 | |||
2024 | /** |
||
2025 | * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...). |
||
2026 | * |
||
2027 | * Generally, the package provided is in the form of a zip file, so the function |
||
2028 | * has been written to test a zip file. If not a zip, the function will return the |
||
2029 | * default return value: '' |
||
2030 | * |
||
2031 | * @param string $file_path the path to the file |
||
2032 | * @param string $file_name the original name of the file |
||
2033 | * |
||
2034 | * @return string 'scorm','aicc','scorm2004','dokeos', 'error-empty-package' if the package is empty, or '' if the package cannot be recognized |
||
2035 | */ |
||
2036 | public static function getPackageType($file_path, $file_name) |
||
2037 | { |
||
2038 | // Get name of the zip file without the extension. |
||
2039 | $file_info = pathinfo($file_name); |
||
2040 | $extension = $file_info['extension']; // Extension only. |
||
2041 | if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), [ |
||
2042 | 'dll', |
||
2043 | 'exe', |
||
2044 | ])) { |
||
2045 | return 'oogie'; |
||
2046 | } |
||
2047 | if (!empty($_POST['woogie']) && !in_array(strtolower($extension), [ |
||
2048 | 'dll', |
||
2049 | 'exe', |
||
2050 | ])) { |
||
2051 | return 'woogie'; |
||
2052 | } |
||
2053 | |||
2054 | $zipFile = new PclZip($file_path); |
||
2055 | // Check the zip content (real size and file extension). |
||
2056 | $zipContentArray = $zipFile->listContent(); |
||
2057 | $package_type = ''; |
||
2058 | $manifest = ''; |
||
2059 | $aicc_match_crs = 0; |
||
2060 | $aicc_match_au = 0; |
||
2061 | $aicc_match_des = 0; |
||
2062 | $aicc_match_cst = 0; |
||
2063 | $countItems = 0; |
||
2064 | |||
2065 | // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?). |
||
2066 | if (is_array($zipContentArray)) { |
||
2067 | $countItems = count($zipContentArray); |
||
2068 | if ($countItems > 0) { |
||
2069 | foreach ($zipContentArray as $thisContent) { |
||
2070 | if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) { |
||
2071 | // New behaviour: Don't do anything. These files will be removed in scorm::import_package. |
||
2072 | } elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) { |
||
2073 | $manifest = $thisContent['filename']; // Just the relative directory inside scorm/ |
||
2074 | $package_type = 'scorm'; |
||
2075 | break; // Exit the foreach loop. |
||
2076 | } elseif ( |
||
2077 | preg_match('/aicc\//i', $thisContent['filename']) || |
||
2078 | in_array( |
||
2079 | strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)), |
||
2080 | ['crs', 'au', 'des', 'cst'] |
||
2081 | ) |
||
2082 | ) { |
||
2083 | $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)); |
||
2084 | switch ($ext) { |
||
2085 | case 'crs': |
||
2086 | $aicc_match_crs = 1; |
||
2087 | break; |
||
2088 | case 'au': |
||
2089 | $aicc_match_au = 1; |
||
2090 | break; |
||
2091 | case 'des': |
||
2092 | $aicc_match_des = 1; |
||
2093 | break; |
||
2094 | case 'cst': |
||
2095 | $aicc_match_cst = 1; |
||
2096 | break; |
||
2097 | default: |
||
2098 | break; |
||
2099 | } |
||
2100 | //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC. |
||
2101 | } else { |
||
2102 | $package_type = ''; |
||
2103 | } |
||
2104 | } |
||
2105 | } |
||
2106 | } |
||
2107 | |||
2108 | if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) { |
||
2109 | // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)). |
||
2110 | $package_type = 'aicc'; |
||
2111 | } |
||
2112 | |||
2113 | // Try with chamilo course builder |
||
2114 | if (empty($package_type)) { |
||
2115 | // Sometimes users will try to upload an empty zip, or a zip with |
||
2116 | // only a folder. Catch that and make the calling function aware. |
||
2117 | // If the single file was the imsmanifest.xml, then $package_type |
||
2118 | // would be 'scorm' and we wouldn't be here. |
||
2119 | if ($countItems < 2) { |
||
2120 | return 'error-empty-package'; |
||
2121 | } |
||
2122 | $package_type = 'chamilo'; |
||
2123 | } |
||
2124 | |||
2125 | return $package_type; |
||
2126 | } |
||
2127 | |||
2128 | /** |
||
2129 | * Gets the previous resource in queue (url). Also initialises time values for this viewing. |
||
2130 | * |
||
2131 | * @return string URL to load into the viewer |
||
2132 | */ |
||
2133 | public function get_previous_index() |
||
2134 | { |
||
2135 | $index = $this->index; |
||
2136 | if (isset($this->ordered_items[$index - 1])) { |
||
2137 | $index--; |
||
2138 | while (isset($this->ordered_items[$index]) && |
||
2139 | ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') |
||
2140 | ) { |
||
2141 | $index--; |
||
2142 | if ($index < 0) { |
||
2143 | return $this->index; |
||
2144 | } |
||
2145 | } |
||
2146 | } |
||
2147 | |||
2148 | return $index; |
||
2149 | } |
||
2150 | |||
2151 | /** |
||
2152 | * Gets item_id for the next element. |
||
2153 | * |
||
2154 | * @return int Previous item (DB) ID |
||
2155 | */ |
||
2156 | public function get_previous_item_id() |
||
2157 | { |
||
2158 | $index = $this->get_previous_index(); |
||
2159 | |||
2160 | return $this->ordered_items[$index]; |
||
2161 | } |
||
2162 | |||
2163 | /** |
||
2164 | * Returns the HTML necessary to print a mediaplayer block inside a page. |
||
2165 | * |
||
2166 | * @param int $lpItemId |
||
2167 | * @param string $autostart |
||
2168 | * |
||
2169 | * @return string The mediaplayer HTML |
||
2170 | */ |
||
2171 | public function get_mediaplayer($lpItemId, $autostart = 'true') |
||
2172 | { |
||
2173 | $course_id = api_get_course_int_id(); |
||
2174 | $courseInfo = api_get_course_info(); |
||
2175 | $lpItemId = (int) $lpItemId; |
||
2176 | |||
2177 | if (empty($courseInfo) || empty($lpItemId)) { |
||
2178 | return ''; |
||
2179 | } |
||
2180 | $item = isset($this->items[$lpItemId]) ? $this->items[$lpItemId] : null; |
||
2181 | |||
2182 | if (empty($item)) { |
||
2183 | return ''; |
||
2184 | } |
||
2185 | |||
2186 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
2187 | $tbl_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
2188 | $itemViewId = (int) $item->db_item_view_id; |
||
2189 | |||
2190 | // Getting all the information about the item. |
||
2191 | $sql = "SELECT lp_view.status |
||
2192 | FROM $tbl_lp_item as lpi |
||
2193 | INNER JOIN $tbl_lp_item_view as lp_view |
||
2194 | ON (lpi.iid = lp_view.lp_item_id) |
||
2195 | WHERE |
||
2196 | lp_view.iid = $itemViewId AND |
||
2197 | lpi.iid = $lpItemId AND |
||
2198 | lp_view.c_id = $course_id"; |
||
2199 | $result = Database::query($sql); |
||
2200 | $row = Database::fetch_assoc($result); |
||
2201 | $output = ''; |
||
2202 | $audio = $item->audio; |
||
2203 | |||
2204 | if (!empty($audio)) { |
||
2205 | $list = $_SESSION['oLP']->get_toc(); |
||
2206 | |||
2207 | switch ($item->get_type()) { |
||
2208 | case 'quiz': |
||
2209 | $type_quiz = false; |
||
2210 | foreach ($list as $toc) { |
||
2211 | if ($toc['id'] == $_SESSION['oLP']->current) { |
||
2212 | $type_quiz = true; |
||
2213 | } |
||
2214 | } |
||
2215 | |||
2216 | if ($type_quiz) { |
||
2217 | if ($_SESSION['oLP']->prevent_reinit == 1) { |
||
2218 | $autostart_audio = $row['status'] === 'completed' ? 'false' : 'true'; |
||
2219 | } else { |
||
2220 | $autostart_audio = $autostart; |
||
2221 | } |
||
2222 | } |
||
2223 | break; |
||
2224 | case TOOL_READOUT_TEXT: |
||
2225 | $autostart_audio = 'false'; |
||
2226 | break; |
||
2227 | default: |
||
2228 | $autostart_audio = 'true'; |
||
2229 | } |
||
2230 | |||
2231 | $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'.$audio; |
||
2232 | $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document'.$audio.'?'.api_get_cidreq(); |
||
2233 | |||
2234 | $player = Display::getMediaPlayer( |
||
2235 | $file, |
||
2236 | [ |
||
2237 | 'id' => 'lp_audio_media_player', |
||
2238 | 'url' => $url, |
||
2239 | 'autoplay' => $autostart_audio, |
||
2240 | 'width' => '100%', |
||
2241 | ] |
||
2242 | ); |
||
2243 | |||
2244 | // The mp3 player. |
||
2245 | $output = '<div id="container">'; |
||
2246 | $output .= $player; |
||
2247 | $output .= '</div>'; |
||
2248 | } |
||
2249 | |||
2250 | return $output; |
||
2251 | } |
||
2252 | |||
2253 | /** |
||
2254 | * @param int $studentId |
||
2255 | * @param int $prerequisite |
||
2256 | * @param array $courseInfo |
||
2257 | * @param int $sessionId |
||
2258 | * |
||
2259 | * @return bool |
||
2260 | */ |
||
2261 | public static function isBlockedByPrerequisite( |
||
2262 | $studentId, |
||
2263 | $prerequisite, |
||
2264 | $courseInfo, |
||
2265 | $sessionId |
||
2266 | ) { |
||
2267 | if (empty($courseInfo)) { |
||
2268 | return false; |
||
2269 | } |
||
2270 | |||
2271 | $courseId = $courseInfo['real_id']; |
||
2272 | |||
2273 | $allow = api_get_configuration_value('allow_teachers_to_access_blocked_lp_by_prerequisite'); |
||
2274 | if ($allow) { |
||
2275 | if (api_is_allowed_to_edit() || |
||
2276 | api_is_platform_admin(true) || |
||
2277 | api_is_drh() || |
||
2278 | api_is_coach($sessionId, $courseId, false) |
||
2279 | ) { |
||
2280 | return false; |
||
2281 | } |
||
2282 | } |
||
2283 | |||
2284 | $isBlocked = false; |
||
2285 | if (!empty($prerequisite)) { |
||
2286 | $progress = self::getProgress( |
||
2287 | $prerequisite, |
||
2288 | $studentId, |
||
2289 | $courseId, |
||
2290 | $sessionId |
||
2291 | ); |
||
2292 | if ($progress < 100) { |
||
2293 | $isBlocked = true; |
||
2294 | } |
||
2295 | |||
2296 | if (Tracking::minimumTimeAvailable($sessionId, $courseId)) { |
||
2297 | // Block if it does not exceed minimum time |
||
2298 | // Minimum time (in minutes) to pass the learning path |
||
2299 | $accumulateWorkTime = self::getAccumulateWorkTimePrerequisite($prerequisite, $courseId); |
||
2300 | |||
2301 | if ($accumulateWorkTime > 0) { |
||
2302 | // Total time in course (sum of times in learning paths from course) |
||
2303 | $accumulateWorkTimeTotal = self::getAccumulateWorkTimeTotal($courseId); |
||
2304 | |||
2305 | // Connect with the plugin_licences_course_session table |
||
2306 | // which indicates what percentage of the time applies |
||
2307 | // Minimum connection percentage |
||
2308 | $perc = 100; |
||
2309 | // Time from the course |
||
2310 | $tc = $accumulateWorkTimeTotal; |
||
2311 | |||
2312 | // Percentage of the learning paths |
||
2313 | $pl = $accumulateWorkTime / $accumulateWorkTimeTotal; |
||
2314 | // Minimum time for each learning path |
||
2315 | $accumulateWorkTime = ($pl * $tc * $perc / 100); |
||
2316 | |||
2317 | // Spent time (in seconds) so far in the learning path |
||
2318 | $lpTimeList = Tracking::getCalculateTime($studentId, $courseId, $sessionId); |
||
2319 | $lpTime = isset($lpTimeList[TOOL_LEARNPATH][$prerequisite]) ? $lpTimeList[TOOL_LEARNPATH][$prerequisite] : 0; |
||
2320 | |||
2321 | if ($lpTime < ($accumulateWorkTime * 60)) { |
||
2322 | $isBlocked = true; |
||
2323 | } |
||
2324 | } |
||
2325 | } |
||
2326 | } |
||
2327 | |||
2328 | return $isBlocked; |
||
2329 | } |
||
2330 | |||
2331 | /** |
||
2332 | * Checks if the learning path is visible for student after the progress |
||
2333 | * of its prerequisite is completed, considering the time availability and |
||
2334 | * the LP visibility. |
||
2335 | * |
||
2336 | * @param int $lp_id |
||
2337 | * @param int $student_id |
||
2338 | * @param array $courseInfo |
||
2339 | * @param int $sessionId |
||
2340 | * @param bool $checkSubscription Optional. Allow don't check if user is subscribed to the LP. |
||
2341 | * |
||
2342 | * @return bool |
||
2343 | */ |
||
2344 | public static function is_lp_visible_for_student( |
||
2345 | $lp_id, |
||
2346 | $student_id, |
||
2347 | $courseInfo = [], |
||
2348 | $sessionId = 0, |
||
2349 | bool $checkSubscription = true |
||
2350 | ) { |
||
2351 | $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; |
||
2352 | $lp_id = (int) $lp_id; |
||
2353 | $sessionId = (int) $sessionId; |
||
2354 | |||
2355 | if (empty($courseInfo)) { |
||
2356 | return false; |
||
2357 | } |
||
2358 | |||
2359 | if (empty($sessionId)) { |
||
2360 | $sessionId = api_get_session_id(); |
||
2361 | } |
||
2362 | |||
2363 | $courseId = $courseInfo['real_id']; |
||
2364 | |||
2365 | $itemInfo = api_get_item_property_info( |
||
2366 | $courseId, |
||
2367 | TOOL_LEARNPATH, |
||
2368 | $lp_id, |
||
2369 | $sessionId |
||
2370 | ); |
||
2371 | |||
2372 | // If the item was deleted or is invisible. |
||
2373 | if (isset($itemInfo['visibility']) && ($itemInfo['visibility'] == 2 || $itemInfo['visibility'] == 0)) { |
||
2374 | return false; |
||
2375 | } |
||
2376 | |||
2377 | // @todo remove this query and load the row info as a parameter |
||
2378 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
2379 | // Get current prerequisite |
||
2380 | $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on, category_id |
||
2381 | FROM $table |
||
2382 | WHERE iid = $lp_id"; |
||
2383 | $rs = Database::query($sql); |
||
2384 | $now = time(); |
||
2385 | if (Database::num_rows($rs) > 0) { |
||
2386 | $row = Database::fetch_array($rs, 'ASSOC'); |
||
2387 | if (!empty($row['category_id'])) { |
||
2388 | $category = self::getCategory($row['category_id']); |
||
2389 | if (self::categoryIsVisibleForStudent($category, api_get_user_entity($student_id)) === false) { |
||
2390 | return false; |
||
2391 | } |
||
2392 | } |
||
2393 | |||
2394 | $prerequisite = $row['prerequisite']; |
||
2395 | $is_visible = true; |
||
2396 | |||
2397 | $isBlocked = self::isBlockedByPrerequisite( |
||
2398 | $student_id, |
||
2399 | $prerequisite, |
||
2400 | $courseInfo, |
||
2401 | $sessionId |
||
2402 | ); |
||
2403 | |||
2404 | if ($isBlocked) { |
||
2405 | $is_visible = false; |
||
2406 | } |
||
2407 | |||
2408 | // Also check the time availability of the LP |
||
2409 | if ($is_visible) { |
||
2410 | // Adding visibility restrictions |
||
2411 | if (!empty($row['publicated_on'])) { |
||
2412 | if ($now < api_strtotime($row['publicated_on'], 'UTC')) { |
||
2413 | $is_visible = false; |
||
2414 | } |
||
2415 | } |
||
2416 | // Blocking empty start times see BT#2800 |
||
2417 | global $_custom; |
||
2418 | if (isset($_custom['lps_hidden_when_no_start_date']) && |
||
2419 | $_custom['lps_hidden_when_no_start_date'] |
||
2420 | ) { |
||
2421 | if (empty($row['publicated_on'])) { |
||
2422 | $is_visible = false; |
||
2423 | } |
||
2424 | } |
||
2425 | |||
2426 | if (!empty($row['expired_on'])) { |
||
2427 | if ($now > api_strtotime($row['expired_on'], 'UTC')) { |
||
2428 | $is_visible = false; |
||
2429 | } |
||
2430 | } |
||
2431 | } |
||
2432 | |||
2433 | if ($is_visible && $checkSubscription) { |
||
2434 | $is_visible = self::isUserSubscribedToLp( |
||
2435 | $row, |
||
2436 | (int) $student_id, |
||
2437 | $courseInfo, |
||
2438 | (int) $sessionId |
||
2439 | ); |
||
2440 | } |
||
2441 | |||
2442 | return $is_visible; |
||
2443 | } |
||
2444 | |||
2445 | return false; |
||
2446 | } |
||
2447 | |||
2448 | public static function isUserSubscribedToLp( |
||
2449 | array $lpInfo, |
||
2450 | int $studentId, |
||
2451 | array $courseInfo, |
||
2452 | int $sessionId = 0 |
||
2453 | ): bool { |
||
2454 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
2455 | |||
2456 | // Check if the subscription users/group to a LP is ON |
||
2457 | if (isset($lpInfo['subscribe_users']) && $lpInfo['subscribe_users'] == 1 && |
||
2458 | $subscriptionSettings['allow_add_users_to_lp'] === true |
||
2459 | ) { |
||
2460 | // Checking only the user visibility |
||
2461 | $userVisibility = api_get_item_visibility( |
||
2462 | $courseInfo, |
||
2463 | 'learnpath', |
||
2464 | $lpInfo['id'], |
||
2465 | $sessionId, |
||
2466 | $studentId, |
||
2467 | 'LearnpathSubscription' |
||
2468 | ); |
||
2469 | |||
2470 | if (1 == $userVisibility) { |
||
2471 | return true; |
||
2472 | } |
||
2473 | |||
2474 | // Try group |
||
2475 | $userGroups = GroupManager::getAllGroupPerUserSubscription($studentId, $courseInfo['real_id']); |
||
2476 | |||
2477 | if (!empty($userGroups)) { |
||
2478 | foreach ($userGroups as $groupInfo) { |
||
2479 | $userVisibility = api_get_item_visibility( |
||
2480 | $courseInfo, |
||
2481 | 'learnpath', |
||
2482 | $lpInfo['id'], |
||
2483 | $sessionId, |
||
2484 | null, |
||
2485 | 'LearnpathSubscription', |
||
2486 | $groupInfo['iid'] |
||
2487 | ); |
||
2488 | |||
2489 | if (1 == $userVisibility) { |
||
2490 | return true; |
||
2491 | } |
||
2492 | } |
||
2493 | } |
||
2494 | |||
2495 | return false; |
||
2496 | } |
||
2497 | |||
2498 | return true; |
||
2499 | } |
||
2500 | |||
2501 | /** |
||
2502 | * @param int $lpId |
||
2503 | * @param int $userId |
||
2504 | * @param int $courseId |
||
2505 | * @param int $sessionId |
||
2506 | * |
||
2507 | * @return int |
||
2508 | */ |
||
2509 | public static function getProgress($lpId, $userId, $courseId, $sessionId = 0) |
||
2510 | { |
||
2511 | $lpId = (int) $lpId; |
||
2512 | $userId = (int) $userId; |
||
2513 | $courseId = (int) $courseId; |
||
2514 | $sessionId = (int) $sessionId; |
||
2515 | |||
2516 | $sessionCondition = api_get_session_condition($sessionId); |
||
2517 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
2518 | $sql = "SELECT progress FROM $table |
||
2519 | WHERE |
||
2520 | c_id = $courseId AND |
||
2521 | lp_id = $lpId AND |
||
2522 | user_id = $userId $sessionCondition "; |
||
2523 | $res = Database::query($sql); |
||
2524 | |||
2525 | $progress = 0; |
||
2526 | if (Database::num_rows($res) > 0) { |
||
2527 | $row = Database::fetch_array($res); |
||
2528 | $progress = (int) $row['progress']; |
||
2529 | } |
||
2530 | |||
2531 | return $progress; |
||
2532 | } |
||
2533 | |||
2534 | /** |
||
2535 | * @param array $lpList |
||
2536 | * @param int $userId |
||
2537 | * @param int $courseId |
||
2538 | * @param int $sessionId |
||
2539 | * |
||
2540 | * @return array |
||
2541 | */ |
||
2542 | public static function getProgressFromLpList($lpList, $userId, $courseId, $sessionId = 0) |
||
2543 | { |
||
2544 | $lpList = array_map('intval', $lpList); |
||
2545 | if (empty($lpList)) { |
||
2546 | return []; |
||
2547 | } |
||
2548 | |||
2549 | $lpList = implode("','", $lpList); |
||
2550 | |||
2551 | $userId = (int) $userId; |
||
2552 | $courseId = (int) $courseId; |
||
2553 | $sessionId = (int) $sessionId; |
||
2554 | |||
2555 | $sessionCondition = api_get_session_condition($sessionId); |
||
2556 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
2557 | $sql = "SELECT lp_id, progress FROM $table |
||
2558 | WHERE |
||
2559 | c_id = $courseId AND |
||
2560 | lp_id IN ('".$lpList."') AND |
||
2561 | user_id = $userId $sessionCondition "; |
||
2562 | $res = Database::query($sql); |
||
2563 | |||
2564 | if (Database::num_rows($res) > 0) { |
||
2565 | $list = []; |
||
2566 | while ($row = Database::fetch_array($res)) { |
||
2567 | $list[$row['lp_id']] = $row['progress']; |
||
2568 | } |
||
2569 | |||
2570 | return $list; |
||
2571 | } |
||
2572 | |||
2573 | return []; |
||
2574 | } |
||
2575 | |||
2576 | /** |
||
2577 | * Displays a progress bar |
||
2578 | * completed so far. |
||
2579 | * |
||
2580 | * @param int $percentage Progress value to display |
||
2581 | * @param string $text_add Text to display near the progress value |
||
2582 | * |
||
2583 | * @return string HTML string containing the progress bar |
||
2584 | */ |
||
2585 | public static function get_progress_bar($percentage = -1, $text_add = '') |
||
2586 | { |
||
2587 | $text = $percentage.$text_add; |
||
2588 | $output = '<div class="progress"> |
||
2589 | <div id="progress_bar_value" |
||
2590 | class="progress-bar progress-bar-success" role="progressbar" |
||
2591 | aria-valuenow="'.$percentage.'" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';"> |
||
2592 | '.$text.' |
||
2593 | </div> |
||
2594 | </div>'; |
||
2595 | |||
2596 | return $output; |
||
2597 | } |
||
2598 | |||
2599 | /** |
||
2600 | * @param string $mode can be '%' or 'abs' |
||
2601 | * otherwise this value will be used $this->progress_bar_mode |
||
2602 | * |
||
2603 | * @return string |
||
2604 | */ |
||
2605 | public function getProgressBar($mode = null) |
||
2606 | { |
||
2607 | [$percentage, $text_add] = $this->get_progress_bar_text($mode); |
||
2608 | |||
2609 | return self::get_progress_bar($percentage, $text_add); |
||
2610 | } |
||
2611 | |||
2612 | /** |
||
2613 | * Gets the progress bar info to display inside the progress bar. |
||
2614 | * Also used by scorm_api.php. |
||
2615 | * |
||
2616 | * @param string $mode Mode of display (can be '%' or 'abs').abs means |
||
2617 | * we display a number of completed elements per total elements |
||
2618 | * @param int $add Additional steps to fake as completed |
||
2619 | * |
||
2620 | * @return array Percentage or number and symbol (% or /xx) |
||
2621 | */ |
||
2622 | public function get_progress_bar_text($mode = '', $add = 0) |
||
2623 | { |
||
2624 | if (empty($mode)) { |
||
2625 | $mode = $this->progress_bar_mode; |
||
2626 | } |
||
2627 | $text = ''; |
||
2628 | $percentage = 0; |
||
2629 | // If the option to use the score as progress is set for this learning |
||
2630 | // path, then the rules are completely different: we assume only one |
||
2631 | // item exists and the progress of the LP depends on the score |
||
2632 | $scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable'); |
||
2633 | |||
2634 | if ($scoreAsProgressSetting === true) { |
||
2635 | $scoreAsProgress = $this->getUseScoreAsProgress(); |
||
2636 | if ($scoreAsProgress) { |
||
2637 | // Get single item's score |
||
2638 | $itemId = $this->get_current_item_id(); |
||
2639 | $item = $this->getItem($itemId); |
||
2640 | $score = $item->get_score(); |
||
2641 | $maxScore = $item->get_max(); |
||
2642 | if ($mode = '%') { |
||
2643 | if (!empty($maxScore)) { |
||
2644 | $percentage = ((float) $score / (float) $maxScore) * 100; |
||
2645 | } |
||
2646 | $percentage = number_format($percentage, 0); |
||
2647 | $text = '%'; |
||
2648 | } else { |
||
2649 | $percentage = $score; |
||
2650 | $text = '/'.$maxScore; |
||
2651 | } |
||
2652 | |||
2653 | return [$percentage, $text]; |
||
2654 | } |
||
2655 | } |
||
2656 | // otherwise just continue the normal processing of progress |
||
2657 | $total_items = $this->getTotalItemsCountWithoutDirs(); |
||
2658 | $completeItems = $this->get_complete_items_count(); |
||
2659 | if ($add != 0) { |
||
2660 | $completeItems += $add; |
||
2661 | } |
||
2662 | if ($completeItems > $total_items) { |
||
2663 | $completeItems = $total_items; |
||
2664 | } |
||
2665 | |||
2666 | if ($mode == '%') { |
||
2667 | if ($total_items > 0) { |
||
2668 | $percentage = ((float) $completeItems / (float) $total_items) * 100; |
||
2669 | } |
||
2670 | $percentage = number_format($percentage, 0); |
||
2671 | $text = '%'; |
||
2672 | } elseif ($mode === 'abs') { |
||
2673 | $percentage = $completeItems; |
||
2674 | $text = '/'.$total_items; |
||
2675 | } |
||
2676 | |||
2677 | return [ |
||
2678 | $percentage, |
||
2679 | $text, |
||
2680 | ]; |
||
2681 | } |
||
2682 | |||
2683 | /** |
||
2684 | * Gets the progress bar mode. |
||
2685 | * |
||
2686 | * @return string The progress bar mode attribute |
||
2687 | */ |
||
2688 | public function get_progress_bar_mode() |
||
2689 | { |
||
2690 | if (!empty($this->progress_bar_mode)) { |
||
2691 | return $this->progress_bar_mode; |
||
2692 | } |
||
2693 | |||
2694 | return '%'; |
||
2695 | } |
||
2696 | |||
2697 | /** |
||
2698 | * Gets the learnpath theme (remote or local). |
||
2699 | * |
||
2700 | * @return string Learnpath theme |
||
2701 | */ |
||
2702 | public function get_theme() |
||
2703 | { |
||
2704 | if (!empty($this->theme)) { |
||
2705 | return $this->theme; |
||
2706 | } |
||
2707 | |||
2708 | return ''; |
||
2709 | } |
||
2710 | |||
2711 | /** |
||
2712 | * Gets the learnpath session id. |
||
2713 | * |
||
2714 | * @return int |
||
2715 | */ |
||
2716 | public function get_lp_session_id() |
||
2717 | { |
||
2718 | if (!empty($this->lp_session_id)) { |
||
2719 | return (int) $this->lp_session_id; |
||
2720 | } |
||
2721 | |||
2722 | return 0; |
||
2723 | } |
||
2724 | |||
2725 | /** |
||
2726 | * Gets the learnpath image. |
||
2727 | * |
||
2728 | * @return string Web URL of the LP image |
||
2729 | */ |
||
2730 | public function get_preview_image() |
||
2731 | { |
||
2732 | if (!empty($this->preview_image)) { |
||
2733 | return $this->preview_image; |
||
2734 | } |
||
2735 | |||
2736 | return ''; |
||
2737 | } |
||
2738 | |||
2739 | /** |
||
2740 | * @param string $size |
||
2741 | * @param string $path_type |
||
2742 | * |
||
2743 | * @return bool|string |
||
2744 | */ |
||
2745 | public function get_preview_image_path($size = null, $path_type = 'web') |
||
2746 | { |
||
2747 | $preview_image = $this->get_preview_image(); |
||
2748 | if (isset($preview_image) && !empty($preview_image)) { |
||
2749 | $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/'; |
||
2750 | $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/'; |
||
2751 | |||
2752 | if (isset($size)) { |
||
2753 | $info = pathinfo($preview_image); |
||
2754 | $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension']; |
||
2755 | |||
2756 | if (file_exists($image_sys_path.$image_custom_size)) { |
||
2757 | if ($path_type == 'web') { |
||
2758 | return $image_path.$image_custom_size; |
||
2759 | } else { |
||
2760 | return $image_sys_path.$image_custom_size; |
||
2761 | } |
||
2762 | } |
||
2763 | } else { |
||
2764 | if ($path_type == 'web') { |
||
2765 | return $image_path.$preview_image; |
||
2766 | } else { |
||
2767 | return $image_sys_path.$preview_image; |
||
2768 | } |
||
2769 | } |
||
2770 | } |
||
2771 | |||
2772 | return false; |
||
2773 | } |
||
2774 | |||
2775 | /** |
||
2776 | * Gets the learnpath author. |
||
2777 | * |
||
2778 | * @return string LP's author |
||
2779 | */ |
||
2780 | public function get_author() |
||
2781 | { |
||
2782 | if (!empty($this->author)) { |
||
2783 | return $this->author; |
||
2784 | } |
||
2785 | |||
2786 | return ''; |
||
2787 | } |
||
2788 | |||
2789 | /** |
||
2790 | * Gets hide table of contents. |
||
2791 | * |
||
2792 | * @return int |
||
2793 | */ |
||
2794 | public function getHideTableOfContents() |
||
2795 | { |
||
2796 | return (int) $this->hide_toc_frame; |
||
2797 | } |
||
2798 | |||
2799 | /** |
||
2800 | * Generate a new prerequisites string for a given item. If this item was a sco and |
||
2801 | * its prerequisites were strings (instead of IDs), then transform those strings into |
||
2802 | * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table. |
||
2803 | * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the |
||
2804 | * same rule as the scormExport() method. |
||
2805 | * |
||
2806 | * @param int $item_id Item ID |
||
2807 | * |
||
2808 | * @return string Prerequisites string ready for the export as SCORM |
||
2809 | */ |
||
2810 | public function get_scorm_prereq_string($item_id) |
||
2811 | { |
||
2812 | if ($this->debug > 0) { |
||
2813 | error_log('In learnpath::get_scorm_prereq_string()'); |
||
2814 | } |
||
2815 | if (!is_object($this->items[$item_id])) { |
||
2816 | return false; |
||
2817 | } |
||
2818 | /** @var learnpathItem $oItem */ |
||
2819 | $oItem = $this->items[$item_id]; |
||
2820 | $prereq = $oItem->get_prereq_string(); |
||
2821 | |||
2822 | if (empty($prereq)) { |
||
2823 | return ''; |
||
2824 | } |
||
2825 | if (preg_match('/^\d+$/', $prereq) && |
||
2826 | isset($this->items[$prereq]) && |
||
2827 | is_object($this->items[$prereq]) |
||
2828 | ) { |
||
2829 | // If the prerequisite is a simple integer ID and this ID exists as an item ID, |
||
2830 | // then simply return it (with the ITEM_ prefix). |
||
2831 | //return 'ITEM_' . $prereq; |
||
2832 | return $this->items[$prereq]->ref; |
||
2833 | } else { |
||
2834 | if (isset($this->refs_list[$prereq])) { |
||
2835 | // It's a simple string item from which the ID can be found in the refs list, |
||
2836 | // so we can transform it directly to an ID for export. |
||
2837 | return $this->items[$this->refs_list[$prereq]]->ref; |
||
2838 | } elseif (isset($this->refs_list['ITEM_'.$prereq])) { |
||
2839 | return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref; |
||
2840 | } else { |
||
2841 | // The last case, if it's a complex form, then find all the IDs (SCORM strings) |
||
2842 | // and replace them, one by one, by the internal IDs (chamilo db) |
||
2843 | // TODO: Modify the '*' replacement to replace the multiplier in front of it |
||
2844 | // by a space as well. |
||
2845 | $find = [ |
||
2846 | '&', |
||
2847 | '|', |
||
2848 | '~', |
||
2849 | '=', |
||
2850 | '<>', |
||
2851 | '{', |
||
2852 | '}', |
||
2853 | '*', |
||
2854 | '(', |
||
2855 | ')', |
||
2856 | ]; |
||
2857 | $replace = [ |
||
2858 | ' ', |
||
2859 | ' ', |
||
2860 | ' ', |
||
2861 | ' ', |
||
2862 | ' ', |
||
2863 | ' ', |
||
2864 | ' ', |
||
2865 | ' ', |
||
2866 | ' ', |
||
2867 | ' ', |
||
2868 | ]; |
||
2869 | $prereq_mod = str_replace($find, $replace, $prereq); |
||
2870 | $ids = explode(' ', $prereq_mod); |
||
2871 | foreach ($ids as $id) { |
||
2872 | $id = trim($id); |
||
2873 | if (isset($this->refs_list[$id])) { |
||
2874 | $prereq = preg_replace( |
||
2875 | '/[^a-zA-Z_0-9]('.$id.')[^a-zA-Z_0-9]/', |
||
2876 | 'ITEM_'.$this->refs_list[$id], |
||
2877 | $prereq |
||
2878 | ); |
||
2879 | } |
||
2880 | } |
||
2881 | |||
2882 | return $prereq; |
||
2883 | } |
||
2884 | } |
||
2885 | } |
||
2886 | |||
2887 | /** |
||
2888 | * Returns the XML DOM document's node. |
||
2889 | * |
||
2890 | * @param DOMNodeList $children Reference to a list of objects to search for the given ITEM_* |
||
2891 | * @param string $id The identifier to look for |
||
2892 | * |
||
2893 | * @return mixed The reference to the element found with that identifier. False if not found |
||
2894 | */ |
||
2895 | public function get_scorm_xml_node(DOMNodeList &$children, string $id, $nodeName = 'item', $attributeName = 'identifier') |
||
2896 | { |
||
2897 | for ($i = 0; $i < $children->length; $i++) { |
||
2898 | $item_temp = $children->item($i); |
||
2899 | if ($item_temp->nodeName == $nodeName) { |
||
2900 | if ($item_temp instanceof DOMElement && $item_temp->getAttribute($attributeName) == $id) { |
||
2901 | return $item_temp; |
||
2902 | } |
||
2903 | } |
||
2904 | $subchildren = $item_temp->childNodes; |
||
2905 | if ($subchildren && $subchildren->length > 0) { |
||
2906 | $val = $this->get_scorm_xml_node($subchildren, $id, $nodeName, $attributeName); |
||
2907 | if (is_object($val)) { |
||
2908 | return $val; |
||
2909 | } |
||
2910 | } |
||
2911 | } |
||
2912 | |||
2913 | return false; |
||
2914 | } |
||
2915 | |||
2916 | /** |
||
2917 | * Gets the status list for all LP's items. |
||
2918 | * |
||
2919 | * @return array Array of [index] => [item ID => current status] |
||
2920 | */ |
||
2921 | public function get_items_status_list() |
||
2922 | { |
||
2923 | $list = []; |
||
2924 | foreach ($this->ordered_items as $item_id) { |
||
2925 | $list[] = [ |
||
2926 | $item_id => $this->items[$item_id]->get_status(), |
||
2927 | ]; |
||
2928 | } |
||
2929 | |||
2930 | return $list; |
||
2931 | } |
||
2932 | |||
2933 | /** |
||
2934 | * Return the number of interactions for the given learnpath Item View ID. |
||
2935 | * This method can be used as static. |
||
2936 | * |
||
2937 | * @param int $lp_iv_id Item View ID |
||
2938 | * @param int $course_id course id |
||
2939 | * |
||
2940 | * @return int |
||
2941 | */ |
||
2942 | public static function get_interactions_count_from_db($lp_iv_id, $course_id) |
||
2943 | { |
||
2944 | $table = Database::get_course_table(TABLE_LP_IV_INTERACTION); |
||
2945 | $lp_iv_id = (int) $lp_iv_id; |
||
2946 | $course_id = (int) $course_id; |
||
2947 | |||
2948 | $sql = "SELECT count(*) FROM $table |
||
2949 | WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id"; |
||
2950 | $res = Database::query($sql); |
||
2951 | $num = 0; |
||
2952 | if (Database::num_rows($res)) { |
||
2953 | $row = Database::fetch_array($res); |
||
2954 | $num = $row[0]; |
||
2955 | } |
||
2956 | |||
2957 | return $num; |
||
2958 | } |
||
2959 | |||
2960 | /** |
||
2961 | * Return the interactions as an array for the given lp_iv_id. |
||
2962 | * This method can be used as static. |
||
2963 | * |
||
2964 | * @param int $lp_iv_id Learnpath Item View ID |
||
2965 | * |
||
2966 | * @return array |
||
2967 | * |
||
2968 | * @todo Transcode labels instead of switching to HTML (which requires to know the encoding of the LP) |
||
2969 | */ |
||
2970 | public static function get_iv_interactions_array($lp_iv_id, $course_id = 0) |
||
2971 | { |
||
2972 | $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id; |
||
2973 | $list = []; |
||
2974 | $table = Database::get_course_table(TABLE_LP_IV_INTERACTION); |
||
2975 | $lp_iv_id = (int) $lp_iv_id; |
||
2976 | |||
2977 | if (empty($lp_iv_id) || empty($course_id)) { |
||
2978 | return []; |
||
2979 | } |
||
2980 | |||
2981 | $sql = "SELECT * FROM $table |
||
2982 | WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id |
||
2983 | ORDER BY order_id ASC"; |
||
2984 | $res = Database::query($sql); |
||
2985 | $num = Database::num_rows($res); |
||
2986 | if ($num > 0) { |
||
2987 | $list[] = [ |
||
2988 | 'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES), |
||
2989 | 'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES), |
||
2990 | 'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES), |
||
2991 | 'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES), |
||
2992 | 'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES), |
||
2993 | 'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES), |
||
2994 | 'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES), |
||
2995 | 'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES), |
||
2996 | 'student_response_formatted' => '', |
||
2997 | ]; |
||
2998 | while ($row = Database::fetch_array($res)) { |
||
2999 | $studentResponseFormatted = urldecode($row['student_response']); |
||
3000 | $content_student_response = explode('__|', $studentResponseFormatted); |
||
3001 | if (count($content_student_response) > 0) { |
||
3002 | if (count($content_student_response) >= 3) { |
||
3003 | // Pop the element off the end of array. |
||
3004 | array_pop($content_student_response); |
||
3005 | } |
||
3006 | $studentResponseFormatted = implode(',', $content_student_response); |
||
3007 | } |
||
3008 | |||
3009 | $list[] = [ |
||
3010 | 'order_id' => $row['order_id'] + 1, |
||
3011 | 'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that |
||
3012 | 'type' => $row['interaction_type'], |
||
3013 | 'time' => $row['completion_time'], |
||
3014 | 'correct_responses' => '', // Hide correct responses from students. |
||
3015 | 'student_response' => $row['student_response'], |
||
3016 | 'result' => $row['result'], |
||
3017 | 'latency' => $row['latency'], |
||
3018 | 'student_response_formatted' => $studentResponseFormatted, |
||
3019 | ]; |
||
3020 | } |
||
3021 | } |
||
3022 | |||
3023 | return $list; |
||
3024 | } |
||
3025 | |||
3026 | /** |
||
3027 | * Return the number of objectives for the given learnpath Item View ID. |
||
3028 | * This method can be used as static. |
||
3029 | * |
||
3030 | * @param int $lp_iv_id Item View ID |
||
3031 | * @param int $course_id Course ID |
||
3032 | * |
||
3033 | * @return int Number of objectives |
||
3034 | */ |
||
3035 | public static function get_objectives_count_from_db($lp_iv_id, $course_id) |
||
3036 | { |
||
3037 | $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE); |
||
3038 | $course_id = (int) $course_id; |
||
3039 | $lp_iv_id = (int) $lp_iv_id; |
||
3040 | $sql = "SELECT count(*) FROM $table |
||
3041 | WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id"; |
||
3042 | //@todo seems that this always returns 0 |
||
3043 | $res = Database::query($sql); |
||
3044 | $num = 0; |
||
3045 | if (Database::num_rows($res)) { |
||
3046 | $row = Database::fetch_array($res); |
||
3047 | $num = $row[0]; |
||
3048 | } |
||
3049 | |||
3050 | return $num; |
||
3051 | } |
||
3052 | |||
3053 | /** |
||
3054 | * Return the objectives as an array for the given lp_iv_id. |
||
3055 | * This method can be used as static. |
||
3056 | * |
||
3057 | * @param int $lpItemViewId Learnpath Item View ID |
||
3058 | * @param int $course_id |
||
3059 | * |
||
3060 | * @return array |
||
3061 | * |
||
3062 | * @todo Translate labels |
||
3063 | */ |
||
3064 | public static function get_iv_objectives_array($lpItemViewId = 0, $course_id = 0) |
||
3065 | { |
||
3066 | $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id; |
||
3067 | $lpItemViewId = (int) $lpItemViewId; |
||
3068 | |||
3069 | if (empty($course_id) || empty($lpItemViewId)) { |
||
3070 | return []; |
||
3071 | } |
||
3072 | |||
3073 | $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE); |
||
3074 | $sql = "SELECT * FROM $table |
||
3075 | WHERE c_id = $course_id AND lp_iv_id = $lpItemViewId |
||
3076 | ORDER BY order_id ASC"; |
||
3077 | $res = Database::query($sql); |
||
3078 | $num = Database::num_rows($res); |
||
3079 | $list = []; |
||
3080 | if ($num > 0) { |
||
3081 | $list[] = [ |
||
3082 | 'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES), |
||
3083 | 'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES), |
||
3084 | 'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES), |
||
3085 | 'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES), |
||
3086 | 'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES), |
||
3087 | 'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES), |
||
3088 | ]; |
||
3089 | while ($row = Database::fetch_array($res)) { |
||
3090 | $list[] = [ |
||
3091 | 'order_id' => $row['order_id'] + 1, |
||
3092 | 'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F |
||
3093 | 'score_raw' => $row['score_raw'], |
||
3094 | 'score_max' => $row['score_max'], |
||
3095 | 'score_min' => $row['score_min'], |
||
3096 | 'status' => $row['status'], |
||
3097 | ]; |
||
3098 | } |
||
3099 | } |
||
3100 | |||
3101 | return $list; |
||
3102 | } |
||
3103 | |||
3104 | /** |
||
3105 | * Generate and return the table of contents for this learnpath. The (flat) table returned can be |
||
3106 | * used by get_html_toc() to be ready to display. |
||
3107 | * |
||
3108 | * @return array TOC as a table with 4 elements per row: title, link, status and level |
||
3109 | */ |
||
3110 | public function get_toc() |
||
3111 | { |
||
3112 | $toc = []; |
||
3113 | foreach ($this->ordered_items as $item_id) { |
||
3114 | // TODO: Change this link generation and use new function instead. |
||
3115 | $toc[] = [ |
||
3116 | 'id' => $item_id, |
||
3117 | 'title' => $this->items[$item_id]->get_title(), |
||
3118 | 'status' => $this->items[$item_id]->get_status(), |
||
3119 | 'level' => $this->items[$item_id]->get_level(), |
||
3120 | 'type' => $this->items[$item_id]->get_type(), |
||
3121 | 'description' => $this->items[$item_id]->get_description(), |
||
3122 | 'path' => $this->items[$item_id]->get_path(), |
||
3123 | 'parent' => $this->items[$item_id]->get_parent(), |
||
3124 | ]; |
||
3125 | } |
||
3126 | |||
3127 | return $toc; |
||
3128 | } |
||
3129 | |||
3130 | /** |
||
3131 | * Returns the CSS class name associated with a given item status. |
||
3132 | * |
||
3133 | * @param $status string an item status |
||
3134 | * |
||
3135 | * @return string CSS class name |
||
3136 | */ |
||
3137 | public static function getStatusCSSClassName($status) |
||
3138 | { |
||
3139 | if (array_key_exists($status, self::STATUS_CSS_CLASS_NAME)) { |
||
3140 | return self::STATUS_CSS_CLASS_NAME[$status]; |
||
3141 | } |
||
3142 | |||
3143 | return ''; |
||
3144 | } |
||
3145 | |||
3146 | /** |
||
3147 | * Generate the tree of contents for this learnpath as an associative array tree |
||
3148 | * with keys id, title, status, type, description, path, parent_id, children |
||
3149 | * (title and descriptions as secured) |
||
3150 | * and clues for CSS class composition: |
||
3151 | * - booleans is_current, is_parent_of_current, is_chapter |
||
3152 | * - string status_css_class_name. |
||
3153 | * |
||
3154 | * @param $parentId int restrict returned list to children of this parent |
||
3155 | * |
||
3156 | * @return array TOC as a table |
||
3157 | */ |
||
3158 | public function getTOCTree($parentId = 0) |
||
3159 | { |
||
3160 | $toc = []; |
||
3161 | $currentItemId = $this->get_current_item_id(); |
||
3162 | |||
3163 | foreach ($this->ordered_items as $itemId) { |
||
3164 | $item = $this->items[$itemId]; |
||
3165 | if ($item->get_parent() == $parentId) { |
||
3166 | $title = $item->get_title(); |
||
3167 | if (empty($title)) { |
||
3168 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $itemId); |
||
3169 | } |
||
3170 | |||
3171 | $itemData = [ |
||
3172 | 'id' => $itemId, |
||
3173 | 'title' => Security::remove_XSS($title), |
||
3174 | 'status' => $item->get_status(), |
||
3175 | 'level' => $item->get_level(), // FIXME should not be needed |
||
3176 | 'type' => $item->get_type(), |
||
3177 | 'description' => Security::remove_XSS($item->get_description()), |
||
3178 | 'path' => $item->get_path(), |
||
3179 | 'parent_id' => $item->get_parent(), |
||
3180 | 'children' => $this->getTOCTree($itemId), |
||
3181 | 'is_current' => ($itemId == $currentItemId), |
||
3182 | 'is_parent_of_current' => false, |
||
3183 | 'is_chapter' => in_array($item->get_type(), self::getChapterTypes()), |
||
3184 | 'status_css_class_name' => $this->getStatusCSSClassName($item->get_status()), |
||
3185 | 'current_id' => $currentItemId, // FIXME should not be needed, not a property of item |
||
3186 | ]; |
||
3187 | |||
3188 | if (!empty($itemData['children'])) { |
||
3189 | foreach ($itemData['children'] as $child) { |
||
3190 | if ($child['is_current'] || $child['is_parent_of_current']) { |
||
3191 | $itemData['is_parent_of_current'] = true; |
||
3192 | break; |
||
3193 | } |
||
3194 | } |
||
3195 | } |
||
3196 | |||
3197 | $toc[] = $itemData; |
||
3198 | } |
||
3199 | } |
||
3200 | |||
3201 | return $toc; |
||
3202 | } |
||
3203 | |||
3204 | /** |
||
3205 | * Generate and return the table of contents for this learnpath. The JS |
||
3206 | * table returned is used inside of scorm_api.php. |
||
3207 | * |
||
3208 | * @param string $varname |
||
3209 | * |
||
3210 | * @return string A JS array variable construction |
||
3211 | */ |
||
3212 | public function get_items_details_as_js($varname = 'olms.lms_item_types') |
||
3213 | { |
||
3214 | $toc = $varname.' = new Array();'; |
||
3215 | foreach ($this->ordered_items as $item_id) { |
||
3216 | $toc .= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';"; |
||
3217 | } |
||
3218 | |||
3219 | return $toc; |
||
3220 | } |
||
3221 | |||
3222 | /** |
||
3223 | * Gets the learning path type. |
||
3224 | * |
||
3225 | * @param bool $get_name Return the name? If false, return the ID. Default is false. |
||
3226 | * |
||
3227 | * @return mixed Type ID or name, depending on the parameter |
||
3228 | */ |
||
3229 | public function get_type($get_name = false) |
||
3230 | { |
||
3231 | $res = false; |
||
3232 | if (!empty($this->type) && (!$get_name)) { |
||
3233 | $res = $this->type; |
||
3234 | } |
||
3235 | |||
3236 | return $res; |
||
3237 | } |
||
3238 | |||
3239 | /** |
||
3240 | * Gets the learning path type as static method. |
||
3241 | * |
||
3242 | * @param int $lp_id |
||
3243 | * |
||
3244 | * @return mixed Returns the lp_type: 1 = Chamilo lms / 2 = SCORM |
||
3245 | */ |
||
3246 | public static function get_type_static($lp_id = 0) |
||
3247 | { |
||
3248 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
3249 | $lp_id = (int) $lp_id; |
||
3250 | $sql = "SELECT lp_type FROM $tbl_lp |
||
3251 | WHERE iid = $lp_id"; |
||
3252 | $res = Database::query($sql); |
||
3253 | if ($res === false) { |
||
3254 | return null; |
||
3255 | } |
||
3256 | if (Database::num_rows($res) <= 0) { |
||
3257 | return null; |
||
3258 | } |
||
3259 | $row = Database::fetch_array($res); |
||
3260 | |||
3261 | return $row['lp_type']; |
||
3262 | } |
||
3263 | |||
3264 | /** |
||
3265 | * Gets a flat list of item IDs ordered for display (level by level ordered by order_display) |
||
3266 | * This method can be used as abstract and is recursive. |
||
3267 | * |
||
3268 | * @param int $lp Learnpath ID |
||
3269 | * @param int $parent Parent ID of the items to look for |
||
3270 | * @param int $course_id |
||
3271 | * |
||
3272 | * @return array Ordered list of item IDs (empty array on error) |
||
3273 | */ |
||
3274 | public static function get_flat_ordered_items_list($lp = 1, $parent = 0, $course_id = 0) |
||
3275 | { |
||
3276 | if (empty($course_id)) { |
||
3277 | $course_id = api_get_course_int_id(); |
||
3278 | } else { |
||
3279 | $course_id = (int) $course_id; |
||
3280 | } |
||
3281 | $list = []; |
||
3282 | |||
3283 | if (empty($lp)) { |
||
3284 | return $list; |
||
3285 | } |
||
3286 | |||
3287 | $lp = (int) $lp; |
||
3288 | $parent = (int) $parent; |
||
3289 | |||
3290 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
3291 | $sql = "SELECT iid FROM $tbl_lp_item |
||
3292 | WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent |
||
3293 | ORDER BY display_order"; |
||
3294 | |||
3295 | $res = Database::query($sql); |
||
3296 | while ($row = Database::fetch_array($res)) { |
||
3297 | $sublist = self::get_flat_ordered_items_list( |
||
3298 | $lp, |
||
3299 | $row['iid'], |
||
3300 | $course_id |
||
3301 | ); |
||
3302 | $list[] = $row['iid']; |
||
3303 | foreach ($sublist as $item) { |
||
3304 | $list[] = $item; |
||
3305 | } |
||
3306 | } |
||
3307 | |||
3308 | return $list; |
||
3309 | } |
||
3310 | |||
3311 | /** |
||
3312 | * @return array |
||
3313 | */ |
||
3314 | public static function getChapterTypes() |
||
3315 | { |
||
3316 | return [ |
||
3317 | 'dir', |
||
3318 | ]; |
||
3319 | } |
||
3320 | |||
3321 | /** |
||
3322 | * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display. |
||
3323 | * |
||
3324 | * @param $tree |
||
3325 | * |
||
3326 | * @return array HTML TOC ready to display |
||
3327 | */ |
||
3328 | public function getParentToc($tree) |
||
3329 | { |
||
3330 | if (empty($tree)) { |
||
3331 | $tree = $this->get_toc(); |
||
3332 | } |
||
3333 | $dirTypes = self::getChapterTypes(); |
||
3334 | $myCurrentId = $this->get_current_item_id(); |
||
3335 | $listParent = []; |
||
3336 | $listChildren = []; |
||
3337 | $listNotParent = []; |
||
3338 | $list = []; |
||
3339 | foreach ($tree as $subtree) { |
||
3340 | if (in_array($subtree['type'], $dirTypes)) { |
||
3341 | $listChildren = $this->getChildrenToc($tree, $subtree['id']); |
||
3342 | $subtree['children'] = $listChildren; |
||
3343 | if (!empty($subtree['children'])) { |
||
3344 | foreach ($subtree['children'] as $subItem) { |
||
3345 | if ($subItem['id'] == $this->current) { |
||
3346 | $subtree['parent_current'] = 'in'; |
||
3347 | $subtree['current'] = 'on'; |
||
3348 | } |
||
3349 | } |
||
3350 | } |
||
3351 | $listParent[] = $subtree; |
||
3352 | } |
||
3353 | if (!in_array($subtree['type'], $dirTypes) && $subtree['parent'] == null) { |
||
3354 | if (array_key_exists($subtree['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
3355 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$subtree['status']]; |
||
3356 | } |
||
3357 | |||
3358 | $title = Security::remove_XSS($subtree['title']); |
||
3359 | unset($subtree['title']); |
||
3360 | |||
3361 | if (empty($title)) { |
||
3362 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']); |
||
3363 | } |
||
3364 | $classStyle = null; |
||
3365 | if ($subtree['id'] == $this->current) { |
||
3366 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
3367 | } elseif (!in_array($subtree['type'], $dirTypes)) { |
||
3368 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
3369 | } |
||
3370 | $subtree['title'] = $title; |
||
3371 | $subtree['class'] = $classStyle.' '.$cssStatus; |
||
3372 | $subtree['url'] = $this->get_link('http', $subtree['id'], $tree); |
||
3373 | $subtree['current_id'] = $myCurrentId; |
||
3374 | $listNotParent[] = $subtree; |
||
3375 | } |
||
3376 | } |
||
3377 | |||
3378 | $list['are_parents'] = $listParent; |
||
3379 | $list['not_parents'] = $listNotParent; |
||
3380 | |||
3381 | return $list; |
||
3382 | } |
||
3383 | |||
3384 | /** |
||
3385 | * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display. |
||
3386 | * |
||
3387 | * @param array $tree |
||
3388 | * @param int $id |
||
3389 | * @param bool $parent |
||
3390 | * |
||
3391 | * @return array HTML TOC ready to display |
||
3392 | */ |
||
3393 | public function getChildrenToc($tree, $id, $parent = true) |
||
3394 | { |
||
3395 | if (empty($tree)) { |
||
3396 | $tree = $this->get_toc(); |
||
3397 | } |
||
3398 | |||
3399 | $dirTypes = self::getChapterTypes(); |
||
3400 | $currentItemId = $this->get_current_item_id(); |
||
3401 | $list = []; |
||
3402 | |||
3403 | foreach ($tree as $subtree) { |
||
3404 | $subtree['tree'] = null; |
||
3405 | |||
3406 | if (!in_array($subtree['type'], $dirTypes) && $subtree['parent'] == $id) { |
||
3407 | if ($subtree['id'] == $this->current) { |
||
3408 | $subtree['current'] = 'active'; |
||
3409 | } else { |
||
3410 | $subtree['current'] = null; |
||
3411 | } |
||
3412 | if (array_key_exists($subtree['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
3413 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$subtree['status']]; |
||
3414 | } |
||
3415 | |||
3416 | $title = Security::remove_XSS($subtree['title']); |
||
3417 | unset($subtree['title']); |
||
3418 | if (empty($title)) { |
||
3419 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']); |
||
3420 | } |
||
3421 | |||
3422 | $classStyle = null; |
||
3423 | if ($subtree['id'] == $this->current) { |
||
3424 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
3425 | } elseif (!in_array($subtree['type'], $dirTypes)) { |
||
3426 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
3427 | } |
||
3428 | |||
3429 | if (in_array($subtree['type'], $dirTypes)) { |
||
3430 | $subtree['title'] = stripslashes($title); |
||
3431 | } else { |
||
3432 | $subtree['title'] = $title; |
||
3433 | $subtree['class'] = $classStyle.' '.$cssStatus; |
||
3434 | $subtree['url'] = $this->get_link('http', $subtree['id'], $tree); |
||
3435 | $subtree['current_id'] = $currentItemId; |
||
3436 | } |
||
3437 | $list[] = $subtree; |
||
3438 | } |
||
3439 | } |
||
3440 | |||
3441 | return $list; |
||
3442 | } |
||
3443 | |||
3444 | /** |
||
3445 | * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display. |
||
3446 | * |
||
3447 | * @param array $toc_list |
||
3448 | * |
||
3449 | * @return array HTML TOC ready to display |
||
3450 | */ |
||
3451 | public function getListArrayToc($toc_list = []) |
||
3452 | { |
||
3453 | if (empty($toc_list)) { |
||
3454 | $toc_list = $this->get_toc(); |
||
3455 | } |
||
3456 | // Temporary variables. |
||
3457 | $currentItemId = $this->get_current_item_id(); |
||
3458 | $list = []; |
||
3459 | $arrayList = []; |
||
3460 | |||
3461 | foreach ($toc_list as $item) { |
||
3462 | $list['id'] = $item['id']; |
||
3463 | $list['status'] = $item['status']; |
||
3464 | $cssStatus = null; |
||
3465 | |||
3466 | if (array_key_exists($item['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
3467 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$item['status']]; |
||
3468 | } |
||
3469 | |||
3470 | $classStyle = ' '; |
||
3471 | $dirTypes = self::getChapterTypes(); |
||
3472 | |||
3473 | if (in_array($item['type'], $dirTypes)) { |
||
3474 | $classStyle = 'scorm_item_section '; |
||
3475 | } |
||
3476 | if ($item['id'] == $this->current) { |
||
3477 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
3478 | } elseif (!in_array($item['type'], $dirTypes)) { |
||
3479 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
3480 | } |
||
3481 | $title = $item['title']; |
||
3482 | if (empty($title)) { |
||
3483 | $title = self::rl_get_resource_name( |
||
3484 | api_get_course_id(), |
||
3485 | $this->get_id(), |
||
3486 | $item['id'] |
||
3487 | ); |
||
3488 | } |
||
3489 | $title = Security::remove_XSS($item['title']); |
||
3490 | |||
3491 | if (empty($item['description'])) { |
||
3492 | $list['description'] = $title; |
||
3493 | } else { |
||
3494 | $list['description'] = $item['description']; |
||
3495 | } |
||
3496 | |||
3497 | $list['class'] = $classStyle.' '.$cssStatus; |
||
3498 | $list['level'] = $item['level']; |
||
3499 | $list['type'] = $item['type']; |
||
3500 | |||
3501 | if (in_array($item['type'], $dirTypes)) { |
||
3502 | $list['css_level'] = 'level_'.$item['level']; |
||
3503 | } else { |
||
3504 | $list['css_level'] = 'level_'.$item['level'].' scorm_type_'.self::format_scorm_type_item($item['type']); |
||
3505 | } |
||
3506 | |||
3507 | if (in_array($item['type'], $dirTypes)) { |
||
3508 | $list['title'] = stripslashes($title); |
||
3509 | } else { |
||
3510 | $list['title'] = stripslashes($title); |
||
3511 | $list['url'] = $this->get_link('http', $item['id'], $toc_list); |
||
3512 | $list['current_id'] = $currentItemId; |
||
3513 | } |
||
3514 | $arrayList[] = $list; |
||
3515 | } |
||
3516 | |||
3517 | return $arrayList; |
||
3518 | } |
||
3519 | |||
3520 | /** |
||
3521 | * Returns an HTML-formatted string ready to display with teacher buttons |
||
3522 | * in LP view menu. |
||
3523 | * |
||
3524 | * @return string HTML TOC ready to display |
||
3525 | */ |
||
3526 | public function get_teacher_toc_buttons() |
||
3527 | { |
||
3528 | $isAllow = api_is_allowed_to_edit(null, true, false, false); |
||
3529 | $hideIcons = api_get_configuration_value('hide_teacher_icons_lp'); |
||
3530 | $html = ''; |
||
3531 | if ($isAllow && $hideIcons == false) { |
||
3532 | if ($this->get_lp_session_id() == api_get_session_id()) { |
||
3533 | $html .= '<div id="actions_lp" class="actions_lp"><hr>'; |
||
3534 | $html .= '<div class="btn-group">'; |
||
3535 | $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&action=build&lp_id=".$this->lp_id."&isStudentView=false' target='_parent'>". |
||
3536 | Display::returnFontAwesomeIcon('street-view').get_lang('Overview')."</a>"; |
||
3537 | $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&action=add_item&type=step&lp_id=".$this->lp_id."&isStudentView=false' target='_parent'>". |
||
3538 | Display::returnFontAwesomeIcon('pencil').get_lang('Edit')."</a>"; |
||
3539 | $html .= '<a class="btn btn-sm btn-default" href="lp_controller.php?'.api_get_cidreq()."&action=edit&lp_id=".$this->lp_id.'&isStudentView=false">'. |
||
3540 | Display::returnFontAwesomeIcon('cog').get_lang('Settings').'</a>'; |
||
3541 | $html .= '</div>'; |
||
3542 | $html .= '</div>'; |
||
3543 | } |
||
3544 | } |
||
3545 | |||
3546 | return $html; |
||
3547 | } |
||
3548 | |||
3549 | /** |
||
3550 | * Returns an HTML-formatted string ready to display flow buttons |
||
3551 | * in LP view menu. |
||
3552 | * |
||
3553 | * @return string HTML TOC ready to display |
||
3554 | */ |
||
3555 | public function getFlowLpbuttons() |
||
3556 | { |
||
3557 | $allowFlowButtons = api_get_configuration_value('lp_enable_flow'); |
||
3558 | $html = ''; |
||
3559 | if ($allowFlowButtons) { |
||
3560 | $nextLpId = self::getFlowNextLpId($this->lp_id, api_get_course_int_id()); |
||
3561 | $prevLpId = self::getFlowPrevLpId($this->lp_id, api_get_course_int_id()); |
||
3562 | if (!empty($nextLpId) || !empty($prevLpId)) { |
||
3563 | $html .= '<div id="actions_lp" class="actions_lp"><hr>'; |
||
3564 | $html .= '<div class="btn-group">'; |
||
3565 | if ($prevLpId > 0) { |
||
3566 | $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&action=view&lp_id=".$prevLpId."' target='_parent'>". |
||
3567 | Display::returnFontAwesomeIcon('arrow-left').' '.get_lang('Previous')."</a>"; |
||
3568 | } |
||
3569 | if ($nextLpId > 0) { |
||
3570 | $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&action=view&lp_id=".$nextLpId."' target='_parent'>". |
||
3571 | get_lang('Next').' '.Display::returnFontAwesomeIcon('arrow-right')."</a>"; |
||
3572 | } |
||
3573 | $html .= '</div>'; |
||
3574 | $html .= '</div>'; |
||
3575 | } |
||
3576 | } |
||
3577 | |||
3578 | return $html; |
||
3579 | } |
||
3580 | |||
3581 | /** |
||
3582 | * Gets the learnpath maker name - generally the editor's name. |
||
3583 | * |
||
3584 | * @return string Learnpath maker name |
||
3585 | */ |
||
3586 | public function get_maker() |
||
3587 | { |
||
3588 | if (!empty($this->maker)) { |
||
3589 | return $this->maker; |
||
3590 | } |
||
3591 | |||
3592 | return ''; |
||
3593 | } |
||
3594 | |||
3595 | /** |
||
3596 | * Gets the learnpath name/title. |
||
3597 | * |
||
3598 | * @return string Learnpath name/title |
||
3599 | */ |
||
3600 | public function get_name() |
||
3601 | { |
||
3602 | if (!empty($this->name)) { |
||
3603 | return $this->name; |
||
3604 | } |
||
3605 | |||
3606 | return 'N/A'; |
||
3607 | } |
||
3608 | |||
3609 | /** |
||
3610 | * @return string |
||
3611 | */ |
||
3612 | public function getNameNoTags() |
||
3613 | { |
||
3614 | return Security::remove_XSS(strip_tags($this->get_name())); |
||
3615 | } |
||
3616 | |||
3617 | /** |
||
3618 | * Gets a link to the resource from the present location, depending on item ID. |
||
3619 | * |
||
3620 | * @param string $type Type of link expected |
||
3621 | * @param int $item_id Learnpath item ID |
||
3622 | * @param bool $provided_toc |
||
3623 | * |
||
3624 | * @return string $provided_toc Link to the lp_item resource |
||
3625 | */ |
||
3626 | public function get_link($type = 'http', $item_id = 0, $provided_toc = false) |
||
3627 | { |
||
3628 | $course_id = $this->get_course_int_id(); |
||
3629 | $item_id = (int) $item_id; |
||
3630 | |||
3631 | if (empty($item_id)) { |
||
3632 | $item_id = $this->get_current_item_id(); |
||
3633 | |||
3634 | if (empty($item_id)) { |
||
3635 | //still empty, this means there was no item_id given and we are not in an object context or |
||
3636 | //the object property is empty, return empty link |
||
3637 | $this->first(); |
||
3638 | |||
3639 | return ''; |
||
3640 | } |
||
3641 | } |
||
3642 | |||
3643 | $file = ''; |
||
3644 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
3645 | $lp_item_table = Database::get_course_table(TABLE_LP_ITEM); |
||
3646 | $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
3647 | |||
3648 | $sql = "SELECT |
||
3649 | l.lp_type as ltype, |
||
3650 | l.path as lpath, |
||
3651 | li.item_type as litype, |
||
3652 | li.path as lipath, |
||
3653 | li.parameters as liparams |
||
3654 | FROM $lp_table l |
||
3655 | INNER JOIN $lp_item_table li |
||
3656 | ON (li.lp_id = l.iid) |
||
3657 | WHERE |
||
3658 | li.iid = $item_id |
||
3659 | "; |
||
3660 | $res = Database::query($sql); |
||
3661 | if (Database::num_rows($res) > 0) { |
||
3662 | $row = Database::fetch_array($res); |
||
3663 | $lp_type = $row['ltype']; |
||
3664 | $lp_path = $row['lpath']; |
||
3665 | $lp_item_type = $row['litype']; |
||
3666 | $lp_item_path = $row['lipath']; |
||
3667 | $lp_item_params = $row['liparams']; |
||
3668 | |||
3669 | if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) { |
||
3670 | [$lp_item_path, $lp_item_params] = explode('?', $lp_item_path); |
||
3671 | } |
||
3672 | $sys_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path(); |
||
3673 | if ($type === 'http') { |
||
3674 | //web path |
||
3675 | $course_path = api_get_path(WEB_COURSE_PATH).api_get_course_path(); |
||
3676 | } else { |
||
3677 | $course_path = $sys_course_path; //system path |
||
3678 | } |
||
3679 | |||
3680 | // Fixed issue BT#1272 - If the item type is a Chamilo Item (quiz, link, etc), |
||
3681 | // then change the lp type to thread it as a normal Chamilo LP not a SCO. |
||
3682 | if (in_array( |
||
3683 | $lp_item_type, |
||
3684 | ['quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication', 'xapi', 'h5p', 'survey'] |
||
3685 | ) |
||
3686 | ) { |
||
3687 | $lp_type = 1; |
||
3688 | } |
||
3689 | |||
3690 | // Now go through the specific cases to get the end of the path |
||
3691 | // @todo Use constants instead of int values. |
||
3692 | switch ($lp_type) { |
||
3693 | case 1: |
||
3694 | $file = self::rl_get_resource_link_for_learnpath( |
||
3695 | $course_id, |
||
3696 | $this->get_id(), |
||
3697 | $item_id, |
||
3698 | $this->get_view_id(), |
||
3699 | $this->get_lp_session_id() |
||
3700 | ); |
||
3701 | switch ($lp_item_type) { |
||
3702 | case 'document': |
||
3703 | // Shows a button to download the file instead of just downloading the file directly. |
||
3704 | $documentPathInfo = pathinfo($file); |
||
3705 | if (isset($documentPathInfo['extension'])) { |
||
3706 | $parsed = parse_url($documentPathInfo['extension']); |
||
3707 | if (isset($parsed['path'])) { |
||
3708 | $extension = $parsed['path']; |
||
3709 | $extensionsToDownload = [ |
||
3710 | 'zip', |
||
3711 | 'ppt', |
||
3712 | 'pptx', |
||
3713 | 'ods', |
||
3714 | 'xlsx', |
||
3715 | 'xls', |
||
3716 | 'csv', |
||
3717 | 'doc', |
||
3718 | 'docx', |
||
3719 | 'dot', |
||
3720 | ]; |
||
3721 | |||
3722 | if (in_array($extension, $extensionsToDownload)) { |
||
3723 | $file = api_get_path(WEB_CODE_PATH). |
||
3724 | 'lp/embed.php?type=download&source=file&lp_item_id='.$item_id.'&'.api_get_cidreq(); |
||
3725 | } |
||
3726 | } |
||
3727 | } |
||
3728 | break; |
||
3729 | case 'dir': |
||
3730 | $file = 'lp_content.php?type=dir&'.api_get_cidreq(); |
||
3731 | break; |
||
3732 | case 'link': |
||
3733 | if (!empty($file)) { |
||
3734 | if (Link::is_youtube_link($file)) { |
||
3735 | $src = Link::get_youtube_video_id($file); |
||
3736 | $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=youtube&source='.$src; |
||
3737 | } elseif (Link::isVimeoLink($file)) { |
||
3738 | $src = Link::getVimeoLinkId($file); |
||
3739 | $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=vimeo&source='.$src; |
||
3740 | } else { |
||
3741 | // If the current site is HTTPS and the link is |
||
3742 | // HTTP, browsers will refuse opening the link |
||
3743 | $urlId = api_get_current_access_url_id(); |
||
3744 | $url = api_get_access_url($urlId, false); |
||
3745 | $protocol = substr($url['url'], 0, 5); |
||
3746 | if ($protocol === 'https') { |
||
3747 | $linkProtocol = substr($file, 0, 5); |
||
3748 | if ($linkProtocol === 'http:') { |
||
3749 | //this is the special intervention case |
||
3750 | $file = api_get_path( |
||
3751 | WEB_CODE_PATH |
||
3752 | ).'lp/embed.php?type=nonhttps&source='.urlencode($file); |
||
3753 | } |
||
3754 | } |
||
3755 | } |
||
3756 | } |
||
3757 | break; |
||
3758 | case 'quiz': |
||
3759 | // Check how much attempts of a exercise exits in lp |
||
3760 | $lp_item_id = $this->get_current_item_id(); |
||
3761 | $lp_view_id = $this->get_view_id(); |
||
3762 | |||
3763 | $prevent_reinit = null; |
||
3764 | if (isset($this->items[$this->current])) { |
||
3765 | $prevent_reinit = $this->items[$this->current]->get_prevent_reinit(); |
||
3766 | } |
||
3767 | |||
3768 | if (empty($provided_toc)) { |
||
3769 | if ($this->debug > 0) { |
||
3770 | error_log('In learnpath::get_link() Loading get_toc ', 0); |
||
3771 | } |
||
3772 | $list = $this->get_toc(); |
||
3773 | } else { |
||
3774 | if ($this->debug > 0) { |
||
3775 | error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0); |
||
3776 | } |
||
3777 | $list = $provided_toc; |
||
3778 | } |
||
3779 | |||
3780 | $type_quiz = false; |
||
3781 | foreach ($list as $toc) { |
||
3782 | if ($toc['id'] == $lp_item_id && $toc['type'] === 'quiz') { |
||
3783 | $type_quiz = true; |
||
3784 | } |
||
3785 | } |
||
3786 | |||
3787 | if ($type_quiz) { |
||
3788 | $lp_item_id = (int) $lp_item_id; |
||
3789 | $lp_view_id = (int) $lp_view_id; |
||
3790 | $sql = "SELECT count(*) FROM $lp_item_view_table |
||
3791 | WHERE |
||
3792 | c_id = $course_id AND |
||
3793 | lp_item_id='".$lp_item_id."' AND |
||
3794 | lp_view_id ='".$lp_view_id."' AND |
||
3795 | status='completed'"; |
||
3796 | $result = Database::query($sql); |
||
3797 | $row_count = Database::fetch_row($result); |
||
3798 | $count_item_view = (int) $row_count[0]; |
||
3799 | $not_multiple_attempt = 0; |
||
3800 | if ($prevent_reinit === 1 && $count_item_view > 0) { |
||
3801 | $not_multiple_attempt = 1; |
||
3802 | } |
||
3803 | $file .= '¬_multiple_attempt='.$not_multiple_attempt; |
||
3804 | } |
||
3805 | break; |
||
3806 | } |
||
3807 | |||
3808 | $tmp_array = explode('/', $file); |
||
3809 | $document_name = $tmp_array[count($tmp_array) - 1]; |
||
3810 | if (strpos($document_name, '_DELETED_')) { |
||
3811 | $file = 'blank.php?error=document_deleted'; |
||
3812 | } |
||
3813 | break; |
||
3814 | case 2: |
||
3815 | if ($this->debug > 2) { |
||
3816 | error_log('In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0); |
||
3817 | } |
||
3818 | |||
3819 | if ($lp_item_type != 'dir') { |
||
3820 | // Quite complex here: |
||
3821 | // We want to make sure 'http://' (and similar) links can |
||
3822 | // be loaded as is (withouth the Chamilo path in front) but |
||
3823 | // some contents use this form: resource.htm?resource=http://blablabla |
||
3824 | // which means we have to find a protocol at the path's start, otherwise |
||
3825 | // it should not be considered as an external URL. |
||
3826 | // if ($this->prerequisites_match($item_id)) { |
||
3827 | if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) { |
||
3828 | if ($this->debug > 2) { |
||
3829 | error_log('In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0); |
||
3830 | } |
||
3831 | // Distant url, return as is. |
||
3832 | $file = $lp_item_path; |
||
3833 | } else { |
||
3834 | if ($this->debug > 2) { |
||
3835 | error_log('In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0); |
||
3836 | } |
||
3837 | // Prevent getting untranslatable urls. |
||
3838 | $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path); |
||
3839 | $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path); |
||
3840 | // Prepare the path. |
||
3841 | $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path; |
||
3842 | // TODO: Fix this for urls with protocol header. |
||
3843 | $file = str_replace('//', '/', $file); |
||
3844 | $file = str_replace(':/', '://', $file); |
||
3845 | if (substr($lp_path, -1) == '/') { |
||
3846 | $lp_path = substr($lp_path, 0, -1); |
||
3847 | } |
||
3848 | |||
3849 | if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$lp_item_path))) { |
||
3850 | // if file not found. |
||
3851 | $decoded = html_entity_decode($lp_item_path); |
||
3852 | [$decoded] = explode('?', $decoded); |
||
3853 | if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$decoded))) { |
||
3854 | $file = self::rl_get_resource_link_for_learnpath( |
||
3855 | $course_id, |
||
3856 | $this->get_id(), |
||
3857 | $item_id, |
||
3858 | $this->get_view_id() |
||
3859 | ); |
||
3860 | if (empty($file)) { |
||
3861 | $file = 'blank.php?error=document_not_found'; |
||
3862 | } else { |
||
3863 | $tmp_array = explode('/', $file); |
||
3864 | $document_name = $tmp_array[count($tmp_array) - 1]; |
||
3865 | if (strpos($document_name, '_DELETED_')) { |
||
3866 | $file = 'blank.php?error=document_deleted'; |
||
3867 | } else { |
||
3868 | $file = 'blank.php?error=document_not_found'; |
||
3869 | } |
||
3870 | } |
||
3871 | } else { |
||
3872 | $file = $course_path.'/scorm/'.$lp_path.'/'.$decoded; |
||
3873 | } |
||
3874 | } |
||
3875 | } |
||
3876 | |||
3877 | // We want to use parameters if they were defined in the imsmanifest |
||
3878 | if (strpos($file, 'blank.php') === false) { |
||
3879 | $lp_item_params = ltrim($lp_item_params, '?'); |
||
3880 | $file .= (strstr($file, '?') === false ? '?' : '').$lp_item_params; |
||
3881 | } |
||
3882 | } else { |
||
3883 | $file = 'lp_content.php?type=dir&'.api_get_cidreq(); |
||
3884 | } |
||
3885 | break; |
||
3886 | case 3: |
||
3887 | if ($this->debug > 2) { |
||
3888 | error_log('In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0); |
||
3889 | } |
||
3890 | // Formatting AICC HACP append URL. |
||
3891 | $aicc_append = '?aicc_sid='.urlencode(session_id()).'&aicc_url='.urlencode(api_get_path(WEB_CODE_PATH).'lp/aicc_hacp.php').'&'; |
||
3892 | if (!empty($lp_item_params)) { |
||
3893 | $aicc_append .= $lp_item_params.'&'; |
||
3894 | } |
||
3895 | if ($lp_item_type != 'dir') { |
||
3896 | // Quite complex here: |
||
3897 | // We want to make sure 'http://' (and similar) links can |
||
3898 | // be loaded as is (withouth the Chamilo path in front) but |
||
3899 | // some contents use this form: resource.htm?resource=http://blablabla |
||
3900 | // which means we have to find a protocol at the path's start, otherwise |
||
3901 | // it should not be considered as an external URL. |
||
3902 | if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) { |
||
3903 | if ($this->debug > 2) { |
||
3904 | error_log('In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0); |
||
3905 | } |
||
3906 | // Distant url, return as is. |
||
3907 | $file = $lp_item_path; |
||
3908 | // Enabled and modified by Ivan Tcholakov, 16-OCT-2008. |
||
3909 | /* |
||
3910 | if (stristr($file,'<servername>') !== false) { |
||
3911 | $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path); |
||
3912 | } |
||
3913 | */ |
||
3914 | if (stripos($file, '<servername>') !== false) { |
||
3915 | //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path); |
||
3916 | $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path)); |
||
3917 | $file = str_replace('<servername>', $web_course_path.'/scorm/'.$lp_path, $lp_item_path); |
||
3918 | } |
||
3919 | |||
3920 | $file .= $aicc_append; |
||
3921 | } else { |
||
3922 | if ($this->debug > 2) { |
||
3923 | error_log('In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0); |
||
3924 | } |
||
3925 | // Prevent getting untranslatable urls. |
||
3926 | $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path); |
||
3927 | $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path); |
||
3928 | // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name. |
||
3929 | $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path; |
||
3930 | // TODO: Fix this for urls with protocol header. |
||
3931 | $file = str_replace('//', '/', $file); |
||
3932 | $file = str_replace(':/', '://', $file); |
||
3933 | $file .= $aicc_append; |
||
3934 | } |
||
3935 | } else { |
||
3936 | $file = 'lp_content.php?type=dir&'.api_get_cidreq(); |
||
3937 | } |
||
3938 | break; |
||
3939 | case 4: |
||
3940 | break; |
||
3941 | default: |
||
3942 | break; |
||
3943 | } |
||
3944 | // Replace & by & because & will break URL with params |
||
3945 | $file = !empty($file) ? str_replace('&', '&', $file) : ''; |
||
3946 | } |
||
3947 | if ($this->debug > 2) { |
||
3948 | error_log('In learnpath::get_link() - returning "'.$file.'" from get_link', 0); |
||
3949 | } |
||
3950 | |||
3951 | return $file; |
||
3952 | } |
||
3953 | |||
3954 | /** |
||
3955 | * Gets the latest usable view or generate a new one. |
||
3956 | * |
||
3957 | * @param int $attempt_num Optional attempt number. If none given, takes the highest from the lp_view table |
||
3958 | * @param int $userId The user ID, as $this->get_user_id() is not always available |
||
3959 | * |
||
3960 | * @return int DB lp_view id |
||
3961 | */ |
||
3962 | public function get_view($attempt_num = 0, $userId = null) |
||
3963 | { |
||
3964 | $search = ''; |
||
3965 | // Use $attempt_num to enable multi-views management (disabled so far). |
||
3966 | if ($attempt_num != 0 && intval(strval($attempt_num)) == $attempt_num) { |
||
3967 | $search = 'AND view_count = '.$attempt_num; |
||
3968 | } |
||
3969 | // When missing $attempt_num, search for a unique lp_view record for this lp and user. |
||
3970 | $lp_view_table = Database::get_course_table(TABLE_LP_VIEW); |
||
3971 | |||
3972 | $course_id = api_get_course_int_id(); |
||
3973 | $sessionId = api_get_session_id(); |
||
3974 | |||
3975 | // Check user ID. |
||
3976 | if (empty($userId)) { |
||
3977 | if (empty($this->get_user_id())) { |
||
3978 | $this->error = 'User ID is empty in learnpath::get_view()'; |
||
3979 | |||
3980 | return null; |
||
3981 | } else { |
||
3982 | $userId = $this->get_user_id(); |
||
3983 | } |
||
3984 | } |
||
3985 | |||
3986 | $sql = "SELECT iid, view_count FROM $lp_view_table |
||
3987 | WHERE |
||
3988 | c_id = $course_id AND |
||
3989 | lp_id = ".$this->get_id()." AND |
||
3990 | user_id = ".$userId." AND |
||
3991 | session_id = $sessionId |
||
3992 | $search |
||
3993 | ORDER BY view_count DESC"; |
||
3994 | $res = Database::query($sql); |
||
3995 | if (Database::num_rows($res) > 0) { |
||
3996 | $row = Database::fetch_array($res); |
||
3997 | $this->lp_view_id = $row['iid']; |
||
3998 | } elseif (!api_is_invitee()) { |
||
3999 | // There is no database record, create one. |
||
4000 | $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) VALUES |
||
4001 | ($course_id, ".$this->get_id().",".$this->get_user_id().", 1, $sessionId)"; |
||
4002 | Database::query($sql); |
||
4003 | $id = Database::insert_id(); |
||
4004 | $this->lp_view_id = $id; |
||
4005 | |||
4006 | $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $id"; |
||
4007 | Database::query($sql); |
||
4008 | } |
||
4009 | |||
4010 | return $this->lp_view_id; |
||
4011 | } |
||
4012 | |||
4013 | /** |
||
4014 | * Gets the current view id. |
||
4015 | * |
||
4016 | * @return int View ID (from lp_view) |
||
4017 | */ |
||
4018 | public function get_view_id() |
||
4019 | { |
||
4020 | if (!empty($this->lp_view_id)) { |
||
4021 | return (int) $this->lp_view_id; |
||
4022 | } |
||
4023 | |||
4024 | return 0; |
||
4025 | } |
||
4026 | |||
4027 | /** |
||
4028 | * Gets the update queue. |
||
4029 | * |
||
4030 | * @return array Array containing IDs of items to be updated by JavaScript |
||
4031 | */ |
||
4032 | public function get_update_queue() |
||
4033 | { |
||
4034 | return $this->update_queue; |
||
4035 | } |
||
4036 | |||
4037 | /** |
||
4038 | * Gets the user ID. |
||
4039 | * |
||
4040 | * @return int User ID |
||
4041 | */ |
||
4042 | public function get_user_id() |
||
4043 | { |
||
4044 | if (!empty($this->user_id)) { |
||
4045 | return (int) $this->user_id; |
||
4046 | } |
||
4047 | |||
4048 | return false; |
||
4049 | } |
||
4050 | |||
4051 | /** |
||
4052 | * Checks if any of the items has an audio element attached. |
||
4053 | * |
||
4054 | * @return bool True or false |
||
4055 | */ |
||
4056 | public function has_audio() |
||
4057 | { |
||
4058 | $has = false; |
||
4059 | foreach ($this->items as $i => $item) { |
||
4060 | if (!empty($this->items[$i]->audio)) { |
||
4061 | $has = true; |
||
4062 | break; |
||
4063 | } |
||
4064 | } |
||
4065 | |||
4066 | return $has; |
||
4067 | } |
||
4068 | |||
4069 | /** |
||
4070 | * Moves an item up and down at its level. |
||
4071 | * |
||
4072 | * @param int $id Item to move up and down |
||
4073 | * @param string $direction Direction 'up' or 'down' |
||
4074 | * |
||
4075 | * @return bool|int |
||
4076 | */ |
||
4077 | public function move_item($id, $direction) |
||
4078 | { |
||
4079 | $course_id = api_get_course_int_id(); |
||
4080 | if (empty($id) || empty($direction)) { |
||
4081 | return false; |
||
4082 | } |
||
4083 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
4084 | $sql_sel = "SELECT * |
||
4085 | FROM $tbl_lp_item |
||
4086 | WHERE |
||
4087 | iid = $id |
||
4088 | "; |
||
4089 | $res_sel = Database::query($sql_sel); |
||
4090 | // Check if elem exists. |
||
4091 | if (Database::num_rows($res_sel) < 1) { |
||
4092 | return false; |
||
4093 | } |
||
4094 | // Gather data. |
||
4095 | $row = Database::fetch_array($res_sel); |
||
4096 | $previous = $row['previous_item_id']; |
||
4097 | $next = $row['next_item_id']; |
||
4098 | $display = $row['display_order']; |
||
4099 | $parent = $row['parent_item_id']; |
||
4100 | $lp = $row['lp_id']; |
||
4101 | // Update the item (switch with previous/next one). |
||
4102 | switch ($direction) { |
||
4103 | case 'up': |
||
4104 | if ($display > 1) { |
||
4105 | $sql_sel2 = "SELECT * FROM $tbl_lp_item |
||
4106 | WHERE iid = $previous"; |
||
4107 | $res_sel2 = Database::query($sql_sel2); |
||
4108 | if (Database::num_rows($res_sel2) < 1) { |
||
4109 | $previous_previous = 0; |
||
4110 | } |
||
4111 | // Gather data. |
||
4112 | $row2 = Database::fetch_array($res_sel2); |
||
4113 | $previous_previous = $row2['previous_item_id']; |
||
4114 | // Update previous_previous item (switch "next" with current). |
||
4115 | if ($previous_previous != 0) { |
||
4116 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4117 | next_item_id = $id |
||
4118 | WHERE iid = $previous_previous"; |
||
4119 | Database::query($sql_upd2); |
||
4120 | } |
||
4121 | // Update previous item (switch with current). |
||
4122 | if ($previous != 0) { |
||
4123 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4124 | next_item_id = $next, |
||
4125 | previous_item_id = $id, |
||
4126 | display_order = display_order +1 |
||
4127 | WHERE iid = $previous"; |
||
4128 | Database::query($sql_upd2); |
||
4129 | } |
||
4130 | |||
4131 | // Update current item (switch with previous). |
||
4132 | if ($id != 0) { |
||
4133 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4134 | next_item_id = $previous, |
||
4135 | previous_item_id = $previous_previous, |
||
4136 | display_order = display_order-1 |
||
4137 | WHERE c_id = ".$course_id." AND id = $id"; |
||
4138 | Database::query($sql_upd2); |
||
4139 | } |
||
4140 | // Update next item (new previous item). |
||
4141 | if (!empty($next)) { |
||
4142 | $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous |
||
4143 | WHERE iid = $next"; |
||
4144 | Database::query($sql_upd2); |
||
4145 | } |
||
4146 | $display = $display - 1; |
||
4147 | } |
||
4148 | break; |
||
4149 | case 'down': |
||
4150 | if ($next != 0) { |
||
4151 | $sql_sel2 = "SELECT * FROM $tbl_lp_item |
||
4152 | WHERE iid = $next"; |
||
4153 | $res_sel2 = Database::query($sql_sel2); |
||
4154 | if (Database::num_rows($res_sel2) < 1) { |
||
4155 | $next_next = 0; |
||
4156 | } |
||
4157 | // Gather data. |
||
4158 | $row2 = Database::fetch_array($res_sel2); |
||
4159 | $next_next = $row2['next_item_id']; |
||
4160 | // Update previous item (switch with current). |
||
4161 | if ($previous != 0) { |
||
4162 | $sql_upd2 = "UPDATE $tbl_lp_item |
||
4163 | SET next_item_id = $next |
||
4164 | WHERE iid = $previous"; |
||
4165 | Database::query($sql_upd2); |
||
4166 | } |
||
4167 | // Update current item (switch with previous). |
||
4168 | if ($id != 0) { |
||
4169 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4170 | previous_item_id = $next, |
||
4171 | next_item_id = $next_next, |
||
4172 | display_order = display_order + 1 |
||
4173 | WHERE iid = $id"; |
||
4174 | Database::query($sql_upd2); |
||
4175 | } |
||
4176 | |||
4177 | // Update next item (new previous item). |
||
4178 | if ($next != 0) { |
||
4179 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4180 | previous_item_id = $previous, |
||
4181 | next_item_id = $id, |
||
4182 | display_order = display_order-1 |
||
4183 | WHERE iid = $next"; |
||
4184 | Database::query($sql_upd2); |
||
4185 | } |
||
4186 | |||
4187 | // Update next_next item (switch "previous" with current). |
||
4188 | if ($next_next != 0) { |
||
4189 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
4190 | previous_item_id = $id |
||
4191 | WHERE iid = $next_next"; |
||
4192 | Database::query($sql_upd2); |
||
4193 | } |
||
4194 | $display = $display + 1; |
||
4195 | } |
||
4196 | break; |
||
4197 | default: |
||
4198 | return false; |
||
4199 | } |
||
4200 | |||
4201 | return $display; |
||
4202 | } |
||
4203 | |||
4204 | /** |
||
4205 | * Move a LP up (display_order). |
||
4206 | * |
||
4207 | * @param int $lp_id Learnpath ID |
||
4208 | * @param int $categoryId Category ID |
||
4209 | * |
||
4210 | * @return bool |
||
4211 | */ |
||
4212 | public static function move_up($lp_id, $categoryId = 0) |
||
4213 | { |
||
4214 | $courseId = api_get_course_int_id(); |
||
4215 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
4216 | |||
4217 | $categoryCondition = ''; |
||
4218 | if (!empty($categoryId)) { |
||
4219 | $categoryId = (int) $categoryId; |
||
4220 | $categoryCondition = " AND category_id = $categoryId"; |
||
4221 | } |
||
4222 | $sql = "SELECT * FROM $lp_table |
||
4223 | WHERE c_id = $courseId |
||
4224 | $categoryCondition |
||
4225 | ORDER BY display_order"; |
||
4226 | $res = Database::query($sql); |
||
4227 | if ($res === false) { |
||
4228 | return false; |
||
4229 | } |
||
4230 | |||
4231 | $lps = []; |
||
4232 | $lp_order = []; |
||
4233 | $num = Database::num_rows($res); |
||
4234 | // First check the order is correct, globally (might be wrong because |
||
4235 | // of versions < 1.8.4) |
||
4236 | if ($num > 0) { |
||
4237 | $i = 1; |
||
4238 | while ($row = Database::fetch_array($res)) { |
||
4239 | if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it. |
||
4240 | $sql = "UPDATE $lp_table SET display_order = $i |
||
4241 | WHERE iid = ".$row['iid']; |
||
4242 | Database::query($sql); |
||
4243 | } |
||
4244 | $row['display_order'] = $i; |
||
4245 | $lps[$row['iid']] = $row; |
||
4246 | $lp_order[$i] = $row['iid']; |
||
4247 | $i++; |
||
4248 | } |
||
4249 | } |
||
4250 | if ($num > 1) { // If there's only one element, no need to sort. |
||
4251 | $order = $lps[$lp_id]['display_order']; |
||
4252 | if ($order > 1) { // If it's the first element, no need to move up. |
||
4253 | $sql = "UPDATE $lp_table SET display_order = $order |
||
4254 | WHERE iid = ".$lp_order[$order - 1]; |
||
4255 | Database::query($sql); |
||
4256 | $sql = "UPDATE $lp_table SET display_order = ".($order - 1)." |
||
4257 | WHERE iid = $lp_id"; |
||
4258 | Database::query($sql); |
||
4259 | } |
||
4260 | } |
||
4261 | |||
4262 | return true; |
||
4263 | } |
||
4264 | |||
4265 | /** |
||
4266 | * Move a learnpath down (display_order). |
||
4267 | * |
||
4268 | * @param int $lp_id Learnpath ID |
||
4269 | * @param int $categoryId Category ID |
||
4270 | * |
||
4271 | * @return bool |
||
4272 | */ |
||
4273 | public static function move_down($lp_id, $categoryId = 0) |
||
4274 | { |
||
4275 | $courseId = api_get_course_int_id(); |
||
4276 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
4277 | |||
4278 | $categoryCondition = ''; |
||
4279 | if (!empty($categoryId)) { |
||
4280 | $categoryId = (int) $categoryId; |
||
4281 | $categoryCondition = " AND category_id = $categoryId"; |
||
4282 | } |
||
4283 | |||
4284 | $sql = "SELECT * FROM $lp_table |
||
4285 | WHERE c_id = $courseId |
||
4286 | $categoryCondition |
||
4287 | ORDER BY display_order"; |
||
4288 | $res = Database::query($sql); |
||
4289 | if ($res === false) { |
||
4290 | return false; |
||
4291 | } |
||
4292 | $lps = []; |
||
4293 | $lp_order = []; |
||
4294 | $num = Database::num_rows($res); |
||
4295 | $max = 0; |
||
4296 | // First check the order is correct, globally (might be wrong because |
||
4297 | // of versions < 1.8.4). |
||
4298 | if ($num > 0) { |
||
4299 | $i = 1; |
||
4300 | while ($row = Database::fetch_array($res)) { |
||
4301 | $max = $i; |
||
4302 | if ($row['display_order'] != $i) { |
||
4303 | // If we find a gap in the order, we need to fix it. |
||
4304 | $sql = "UPDATE $lp_table SET display_order = $i |
||
4305 | WHERE iid = ".$row['iid']; |
||
4306 | Database::query($sql); |
||
4307 | } |
||
4308 | $row['display_order'] = $i; |
||
4309 | $lps[$row['iid']] = $row; |
||
4310 | $lp_order[$i] = $row['iid']; |
||
4311 | $i++; |
||
4312 | } |
||
4313 | } |
||
4314 | if ($num > 1) { // If there's only one element, no need to sort. |
||
4315 | $order = $lps[$lp_id]['display_order']; |
||
4316 | if ($order < $max) { // If it's the first element, no need to move up. |
||
4317 | $sql = "UPDATE $lp_table SET display_order = $order |
||
4318 | WHERE iid = ".$lp_order[$order + 1]; |
||
4319 | Database::query($sql); |
||
4320 | $sql = "UPDATE $lp_table SET display_order = ".($order + 1)." |
||
4321 | WHERE iid = $lp_id"; |
||
4322 | Database::query($sql); |
||
4323 | } |
||
4324 | } |
||
4325 | |||
4326 | return true; |
||
4327 | } |
||
4328 | |||
4329 | /** |
||
4330 | * Updates learnpath attributes to point to the next element |
||
4331 | * The last part is similar to set_current_item but processing the other way around. |
||
4332 | */ |
||
4333 | public function next() |
||
4334 | { |
||
4335 | if ($this->debug > 0) { |
||
4336 | error_log('In learnpath::next()', 0); |
||
4337 | } |
||
4338 | $this->last = $this->get_current_item_id(); |
||
4339 | $this->items[$this->last]->save( |
||
4340 | false, |
||
4341 | $this->prerequisites_match($this->last) |
||
4342 | ); |
||
4343 | $this->autocomplete_parents($this->last); |
||
4344 | $new_index = $this->get_next_index(); |
||
4345 | if ($this->debug > 2) { |
||
4346 | error_log('New index: '.$new_index, 0); |
||
4347 | } |
||
4348 | $this->index = $new_index; |
||
4349 | if ($this->debug > 2) { |
||
4350 | error_log('Now having orderedlist['.$new_index.'] = '.$this->ordered_items[$new_index], 0); |
||
4351 | } |
||
4352 | $this->current = $this->ordered_items[$new_index]; |
||
4353 | if ($this->debug > 2) { |
||
4354 | error_log('new item id is '.$this->current.'-'.$this->get_current_item_id(), 0); |
||
4355 | } |
||
4356 | } |
||
4357 | |||
4358 | /** |
||
4359 | * Open a resource = initialise all local variables relative to this resource. Depending on the child |
||
4360 | * class, this might be redefined to allow several behaviours depending on the document type. |
||
4361 | * |
||
4362 | * @param int $id Resource ID |
||
4363 | */ |
||
4364 | public function open($id) |
||
4365 | { |
||
4366 | // TODO: |
||
4367 | // set the current resource attribute to this resource |
||
4368 | // switch on element type (redefine in child class?) |
||
4369 | // set status for this item to "opened" |
||
4370 | // start timer |
||
4371 | // initialise score |
||
4372 | $this->index = 0; //or = the last item seen (see $this->last) |
||
4373 | } |
||
4374 | |||
4375 | /** |
||
4376 | * Check that all prerequisites are fulfilled. Returns true and an |
||
4377 | * empty string on success, returns false |
||
4378 | * and the prerequisite string on error. |
||
4379 | * This function is based on the rules for aicc_script language as |
||
4380 | * described in the SCORM 1.2 CAM documentation page 108. |
||
4381 | * |
||
4382 | * @param int $itemId Optional item ID. If none given, uses the current open item. |
||
4383 | * |
||
4384 | * @return bool true if prerequisites are matched, false otherwise - Empty string if true returned, prerequisites |
||
4385 | * string otherwise |
||
4386 | */ |
||
4387 | public function prerequisites_match($itemId = null) |
||
4388 | { |
||
4389 | $allow = api_get_configuration_value('allow_teachers_to_access_blocked_lp_by_prerequisite'); |
||
4390 | if ($allow) { |
||
4391 | if (api_is_allowed_to_edit() || |
||
4392 | api_is_platform_admin(true) || |
||
4393 | api_is_drh() || |
||
4394 | api_is_coach(api_get_session_id(), api_get_course_int_id()) |
||
4395 | ) { |
||
4396 | return true; |
||
4397 | } |
||
4398 | } |
||
4399 | |||
4400 | $debug = $this->debug; |
||
4401 | if ($debug > 0) { |
||
4402 | error_log('In learnpath::prerequisites_match()'); |
||
4403 | } |
||
4404 | |||
4405 | if (empty($itemId)) { |
||
4406 | $itemId = $this->current; |
||
4407 | } |
||
4408 | |||
4409 | $currentItem = $this->getItem($itemId); |
||
4410 | if ($debug > 0) { |
||
4411 | error_log("Checking item id $itemId"); |
||
4412 | } |
||
4413 | |||
4414 | if ($currentItem) { |
||
4415 | if ($this->type == 2) { |
||
4416 | // Getting prereq from scorm |
||
4417 | $prereq_string = $this->get_scorm_prereq_string($itemId); |
||
4418 | } else { |
||
4419 | $prereq_string = $currentItem->get_prereq_string(); |
||
4420 | } |
||
4421 | |||
4422 | if (empty($prereq_string)) { |
||
4423 | if ($debug > 0) { |
||
4424 | error_log('Found prereq_string is empty return true'); |
||
4425 | } |
||
4426 | // checks the dates values as prerequisites |
||
4427 | $result = $this->prerequistesDatesMatch($itemId); |
||
4428 | |||
4429 | return $result; |
||
4430 | } |
||
4431 | |||
4432 | // Clean spaces. |
||
4433 | $prereq_string = str_replace(' ', '', $prereq_string); |
||
4434 | if ($debug > 0) { |
||
4435 | error_log('Found prereq_string: '.$prereq_string, 0); |
||
4436 | } |
||
4437 | |||
4438 | // Now send to the parse_prereq() function that will check this component's prerequisites. |
||
4439 | $result = $currentItem->parse_prereq( |
||
4440 | $prereq_string, |
||
4441 | $this->items, |
||
4442 | $this->refs_list, |
||
4443 | $this->get_user_id() |
||
4444 | ); |
||
4445 | |||
4446 | if ($result === false) { |
||
4447 | $this->set_error_msg($currentItem->prereq_alert); |
||
4448 | } |
||
4449 | } else { |
||
4450 | $result = true; |
||
4451 | if ($debug > 1) { |
||
4452 | error_log('$this->items['.$itemId.'] was not an object'); |
||
4453 | } |
||
4454 | } |
||
4455 | |||
4456 | if ($debug > 1) { |
||
4457 | error_log('Result: '.$result); |
||
4458 | error_log('End of prerequisites_match(). Error message is now '.$this->error); |
||
4459 | } |
||
4460 | |||
4461 | if (true === $result && $itemId) { |
||
4462 | // checks the dates values as prerequisites |
||
4463 | $result = $this->prerequistesDatesMatch($itemId); |
||
4464 | } |
||
4465 | |||
4466 | return $result; |
||
4467 | } |
||
4468 | |||
4469 | public function prerequistesDatesMatch(int $itemId) |
||
4470 | { |
||
4471 | if (true === api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
4472 | $extraFieldValue = new ExtraFieldValue('lp_item'); |
||
4473 | $startDate = $extraFieldValue->get_values_by_handler_and_field_variable( |
||
4474 | $itemId, |
||
4475 | 'start_date' |
||
4476 | ); |
||
4477 | $endDate = $extraFieldValue->get_values_by_handler_and_field_variable( |
||
4478 | $itemId, |
||
4479 | 'end_date' |
||
4480 | ); |
||
4481 | |||
4482 | $now = time(); |
||
4483 | $start = !empty($startDate['value']) ? api_strtotime($startDate['value']) : 0; |
||
4484 | $end = !empty($endDate['value']) ? api_strtotime($endDate['value']) : 0; |
||
4485 | $result = false; |
||
4486 | |||
4487 | if (($start == 0 && $end == 0) || |
||
4488 | (($start > 0 && $end == 0) && $now > $start) || |
||
4489 | (($start == 0 && $end > 0) && $now < $end) || |
||
4490 | (($start > 0 && $end > 0) && ($now > $start && $now < $end)) |
||
4491 | ) { |
||
4492 | $result = true; |
||
4493 | } |
||
4494 | |||
4495 | if (!$result) { |
||
4496 | $errMsg = get_lang('ItemCanNotBeAccessedPrerequisiteDates'); |
||
4497 | if ($start > 0 && $start > $now) { |
||
4498 | $errMsg = get_lang('AccessibleFrom').' '.api_format_date($start, DATE_TIME_FORMAT_LONG); |
||
4499 | } |
||
4500 | if ($end > 0 && $end < $now) { |
||
4501 | $errMsg = get_lang('NoMoreAccessible'); |
||
4502 | } |
||
4503 | $this->set_error_msg($errMsg); |
||
4504 | $currentItem = $this->getItem($itemId); |
||
4505 | $currentItem->prereq_alert = $errMsg; |
||
4506 | } |
||
4507 | |||
4508 | return $result; |
||
4509 | } |
||
4510 | |||
4511 | return true; |
||
4512 | } |
||
4513 | |||
4514 | /** |
||
4515 | * Updates learnpath attributes to point to the previous element |
||
4516 | * The last part is similar to set_current_item but processing the other way around. |
||
4517 | */ |
||
4518 | public function previous() |
||
4519 | { |
||
4520 | $this->last = $this->get_current_item_id(); |
||
4521 | $this->items[$this->last]->save( |
||
4522 | false, |
||
4523 | $this->prerequisites_match($this->last) |
||
4524 | ); |
||
4525 | $this->autocomplete_parents($this->last); |
||
4526 | $new_index = $this->get_previous_index(); |
||
4527 | $this->index = $new_index; |
||
4528 | $this->current = $this->ordered_items[$new_index]; |
||
4529 | } |
||
4530 | |||
4531 | /** |
||
4532 | * Publishes a learnpath. This basically means show or hide the learnpath |
||
4533 | * to normal users. |
||
4534 | * Can be used as abstract. |
||
4535 | * |
||
4536 | * @param int $lp_id Learnpath ID |
||
4537 | * @param int $set_visibility New visibility |
||
4538 | * |
||
4539 | * @return bool |
||
4540 | */ |
||
4541 | public static function toggle_visibility($lp_id, $set_visibility = 1) |
||
4542 | { |
||
4543 | if (empty($lp_id)) { |
||
4544 | return false; |
||
4545 | } |
||
4546 | |||
4547 | $action = 'visible'; |
||
4548 | if ($set_visibility != 1) { |
||
4549 | $action = 'invisible'; |
||
4550 | self::toggle_publish($lp_id, 'i'); |
||
4551 | } |
||
4552 | |||
4553 | return api_item_property_update( |
||
4554 | api_get_course_info(), |
||
4555 | TOOL_LEARNPATH, |
||
4556 | $lp_id, |
||
4557 | $action, |
||
4558 | api_get_user_id() |
||
4559 | ); |
||
4560 | } |
||
4561 | |||
4562 | /** |
||
4563 | * Publishes a learnpath category. |
||
4564 | * This basically means show or hide the learnpath category to normal users. |
||
4565 | * |
||
4566 | * @param int $id |
||
4567 | * @param int $visibility |
||
4568 | * |
||
4569 | * @return bool |
||
4570 | */ |
||
4571 | public static function toggleCategoryVisibility($id, $visibility = 1) |
||
4572 | { |
||
4573 | $action = 'visible'; |
||
4574 | if ($visibility != 1) { |
||
4575 | self::toggleCategoryPublish($id, 0); |
||
4576 | $action = 'invisible'; |
||
4577 | } |
||
4578 | |||
4579 | return api_item_property_update( |
||
4580 | api_get_course_info(), |
||
4581 | TOOL_LEARNPATH_CATEGORY, |
||
4582 | $id, |
||
4583 | $action, |
||
4584 | api_get_user_id() |
||
4585 | ); |
||
4586 | } |
||
4587 | |||
4588 | /** |
||
4589 | * Publishes a learnpath. This basically means show or hide the learnpath |
||
4590 | * on the course homepage |
||
4591 | * Can be used as abstract. |
||
4592 | * |
||
4593 | * @param int $lp_id Learnpath id |
||
4594 | * @param string $set_visibility New visibility (v/i - visible/invisible) |
||
4595 | * |
||
4596 | * @return bool |
||
4597 | */ |
||
4598 | public static function toggle_publish($lp_id, $set_visibility = 'v') |
||
4599 | { |
||
4600 | if (empty($lp_id)) { |
||
4601 | return false; |
||
4602 | } |
||
4603 | $course_id = api_get_course_int_id(); |
||
4604 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
4605 | $lp_id = (int) $lp_id; |
||
4606 | $sql = "SELECT * FROM $tbl_lp |
||
4607 | WHERE iid = $lp_id"; |
||
4608 | $result = Database::query($sql); |
||
4609 | if (Database::num_rows($result)) { |
||
4610 | $row = Database::fetch_array($result); |
||
4611 | $name = Database::escape_string($row['name']); |
||
4612 | if ($set_visibility === 'i') { |
||
4613 | $v = 0; |
||
4614 | } |
||
4615 | if ($set_visibility === 'v') { |
||
4616 | $v = 1; |
||
4617 | } |
||
4618 | |||
4619 | $session_id = api_get_session_id(); |
||
4620 | $session_condition = api_get_session_condition($session_id); |
||
4621 | |||
4622 | $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
4623 | $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id; |
||
4624 | $oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id; |
||
4625 | |||
4626 | $extraLpCondition = ''; |
||
4627 | $extraLink = ''; |
||
4628 | if (!empty($session_id)) { |
||
4629 | $extraLink = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session=0'; |
||
4630 | $extraLpCondition = " OR (link = '$link' AND session_id = $session_id ) "; |
||
4631 | } |
||
4632 | |||
4633 | $sql = "SELECT * FROM $tbl_tool |
||
4634 | WHERE |
||
4635 | c_id = $course_id AND |
||
4636 | (link = '$link' OR link = '$oldLink' $extraLpCondition ) AND |
||
4637 | image = 'scormbuilder.gif' AND |
||
4638 | ( |
||
4639 | link LIKE '$link%' OR |
||
4640 | link LIKE '$oldLink%' |
||
4641 | $extraLpCondition |
||
4642 | ) |
||
4643 | $session_condition |
||
4644 | "; |
||
4645 | |||
4646 | $result = Database::query($sql); |
||
4647 | $num = Database::num_rows($result); |
||
4648 | $resultTool = Database::fetch_array($result, 'ASSOC'); |
||
4649 | |||
4650 | if ($set_visibility === 'i') { |
||
4651 | if ($num > 0) { |
||
4652 | $sql = "DELETE FROM $tbl_tool |
||
4653 | WHERE |
||
4654 | c_id = $course_id AND |
||
4655 | (link = '$link' OR link = '$oldLink') AND |
||
4656 | image='scormbuilder.gif' |
||
4657 | $session_condition"; |
||
4658 | Database::query($sql); |
||
4659 | } |
||
4660 | |||
4661 | // Disables the base course link inside a session. |
||
4662 | if (!empty($session_id) && 0 === (int) $row['session_id']) { |
||
4663 | $sql = "SELECT iid FROM $tbl_tool |
||
4664 | WHERE |
||
4665 | c_id = $course_id AND |
||
4666 | (link = '$extraLink') AND |
||
4667 | image = 'scormbuilder.gif' AND |
||
4668 | session_id = $session_id |
||
4669 | "; |
||
4670 | $resultBaseLp = Database::query($sql); |
||
4671 | if (Database::num_rows($resultBaseLp)) { |
||
4672 | $resultBaseLpRow = Database::fetch_array($resultBaseLp); |
||
4673 | $id = $resultBaseLpRow['iid']; |
||
4674 | /*$sql = "UPDATE $tbl_tool |
||
4675 | SET visibility = 0 |
||
4676 | WHERE iid = $id "; |
||
4677 | Database::query($sql);*/ |
||
4678 | $sql = "DELETE FROM $tbl_tool |
||
4679 | WHERE iid = $id"; |
||
4680 | Database::query($sql); |
||
4681 | } else { |
||
4682 | /*$params = [ |
||
4683 | 'category' => 'authoring', |
||
4684 | 'c_id' => $course_id, |
||
4685 | 'name' => $name, |
||
4686 | 'link' => $link, |
||
4687 | 'image' => 'scormbuilder.gif', |
||
4688 | 'visibility' => '0', |
||
4689 | 'admin' => '0', |
||
4690 | 'address' => 'pastillegris.gif', |
||
4691 | 'added_tool' => '0', |
||
4692 | 'session_id' => $session_id, |
||
4693 | ]; |
||
4694 | $insertId = Database::insert($tbl_tool, $params); |
||
4695 | if ($insertId) { |
||
4696 | $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId"; |
||
4697 | Database::query($sql); |
||
4698 | }*/ |
||
4699 | } |
||
4700 | } |
||
4701 | } |
||
4702 | |||
4703 | if ($set_visibility === 'v') { |
||
4704 | if ($num == 0) { |
||
4705 | $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) |
||
4706 | VALUES ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)"; |
||
4707 | Database::query($sql); |
||
4708 | $insertId = Database::insert_id(); |
||
4709 | if ($insertId) { |
||
4710 | $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId"; |
||
4711 | Database::query($sql); |
||
4712 | } |
||
4713 | } |
||
4714 | if ($num > 0) { |
||
4715 | $id = $resultTool['iid']; |
||
4716 | $sql = "UPDATE $tbl_tool SET |
||
4717 | c_id = $course_id, |
||
4718 | name = '$name', |
||
4719 | link = '$link', |
||
4720 | image = 'scormbuilder.gif', |
||
4721 | visibility = '$v', |
||
4722 | admin = '0', |
||
4723 | address = 'pastillegris.gif', |
||
4724 | added_tool = 0, |
||
4725 | session_id = $session_id |
||
4726 | WHERE |
||
4727 | c_id = ".$course_id." AND |
||
4728 | iid = $id |
||
4729 | "; |
||
4730 | Database::query($sql); |
||
4731 | } |
||
4732 | } |
||
4733 | } |
||
4734 | |||
4735 | return false; |
||
4736 | } |
||
4737 | |||
4738 | /** |
||
4739 | * Publishes a learnpath. |
||
4740 | * Show or hide the learnpath category on the course homepage. |
||
4741 | * |
||
4742 | * @param int $id |
||
4743 | * @param int $setVisibility |
||
4744 | * |
||
4745 | * @return bool |
||
4746 | */ |
||
4747 | public static function toggleCategoryPublish($id, $setVisibility = 1) |
||
4748 | { |
||
4749 | $courseId = api_get_course_int_id(); |
||
4750 | $sessionId = api_get_session_id(); |
||
4751 | $sessionCondition = api_get_session_condition( |
||
4752 | $sessionId, |
||
4753 | true, |
||
4754 | false, |
||
4755 | 't.sessionId' |
||
4756 | ); |
||
4757 | |||
4758 | $em = Database::getManager(); |
||
4759 | $category = self::getCategory($id); |
||
4760 | |||
4761 | if (!$category) { |
||
4762 | return false; |
||
4763 | } |
||
4764 | |||
4765 | if (empty($courseId)) { |
||
4766 | return false; |
||
4767 | } |
||
4768 | |||
4769 | $link = self::getCategoryLinkForTool($id); |
||
4770 | |||
4771 | /** @var CTool $tool */ |
||
4772 | $tool = $em->createQuery(" |
||
4773 | SELECT t FROM ChamiloCourseBundle:CTool t |
||
4774 | WHERE |
||
4775 | t.cId = :course AND |
||
4776 | t.link = :link1 AND |
||
4777 | t.image = 'lp_category.gif' AND |
||
4778 | t.link LIKE :link2 |
||
4779 | $sessionCondition |
||
4780 | ") |
||
4781 | ->setParameters([ |
||
4782 | 'course' => $courseId, |
||
4783 | 'link1' => $link, |
||
4784 | 'link2' => "$link%", |
||
4785 | ]) |
||
4786 | ->getOneOrNullResult(); |
||
4787 | |||
4788 | if ($setVisibility == 0 && $tool) { |
||
4789 | $em->remove($tool); |
||
4790 | $em->flush(); |
||
4791 | |||
4792 | return true; |
||
4793 | } |
||
4794 | |||
4795 | if ($setVisibility == 1 && !$tool) { |
||
4796 | $tool = new CTool(); |
||
4797 | $tool |
||
4798 | ->setCategory('authoring') |
||
4799 | ->setCId($courseId) |
||
4800 | ->setName(strip_tags($category->getName())) |
||
4801 | ->setLink($link) |
||
4802 | ->setImage('lp_category.gif') |
||
4803 | ->setVisibility(1) |
||
4804 | ->setAdmin(0) |
||
4805 | ->setAddress('pastillegris.gif') |
||
4806 | ->setAddedTool(0) |
||
4807 | ->setSessionId($sessionId) |
||
4808 | ->setTarget('_self'); |
||
4809 | |||
4810 | $em->persist($tool); |
||
4811 | $em->flush(); |
||
4812 | |||
4813 | $tool->setId($tool->getIid()); |
||
4814 | |||
4815 | $em->persist($tool); |
||
4816 | $em->flush(); |
||
4817 | |||
4818 | return true; |
||
4819 | } |
||
4820 | |||
4821 | if ($setVisibility == 1 && $tool) { |
||
4822 | $tool |
||
4823 | ->setName(strip_tags($category->getName())) |
||
4824 | ->setVisibility(1); |
||
4825 | |||
4826 | $em->persist($tool); |
||
4827 | $em->flush(); |
||
4828 | |||
4829 | return true; |
||
4830 | } |
||
4831 | |||
4832 | return false; |
||
4833 | } |
||
4834 | |||
4835 | /** |
||
4836 | * Check if the learnpath category is visible for a user. |
||
4837 | * |
||
4838 | * @param int |
||
4839 | * @param int |
||
4840 | * |
||
4841 | * @return bool |
||
4842 | */ |
||
4843 | public static function categoryIsVisibleForStudent( |
||
4844 | CLpCategory $category, |
||
4845 | User $user, |
||
4846 | $courseId = 0, |
||
4847 | $sessionId = 0 |
||
4848 | ) { |
||
4849 | if (empty($category)) { |
||
4850 | return false; |
||
4851 | } |
||
4852 | |||
4853 | $isAllowedToEdit = api_is_allowed_to_edit(null, true); |
||
4854 | |||
4855 | if ($isAllowedToEdit) { |
||
4856 | return true; |
||
4857 | } |
||
4858 | |||
4859 | $courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId; |
||
4860 | $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId; |
||
4861 | |||
4862 | $courseInfo = api_get_course_info_by_id($courseId); |
||
4863 | |||
4864 | $categoryVisibility = api_get_item_visibility( |
||
4865 | $courseInfo, |
||
4866 | TOOL_LEARNPATH_CATEGORY, |
||
4867 | $category->getId(), |
||
4868 | $sessionId |
||
4869 | ); |
||
4870 | |||
4871 | if ($categoryVisibility !== 1 && $categoryVisibility != -1) { |
||
4872 | return false; |
||
4873 | } |
||
4874 | |||
4875 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
4876 | |||
4877 | if ($subscriptionSettings['allow_add_users_to_lp_category'] == false) { |
||
4878 | return true; |
||
4879 | } |
||
4880 | |||
4881 | $noUserSubscribed = false; |
||
4882 | $noGroupSubscribed = true; |
||
4883 | $users = $category->getUsers(); |
||
4884 | if (empty($users) || !$users->count()) { |
||
4885 | $noUserSubscribed = true; |
||
4886 | } elseif ($category->hasUserAdded($user)) { |
||
4887 | return true; |
||
4888 | } |
||
4889 | |||
4890 | $groups = GroupManager::getAllGroupPerUserSubscription($user->getId()); |
||
4891 | $em = Database::getManager(); |
||
4892 | |||
4893 | /** @var ItemPropertyRepository $itemRepo */ |
||
4894 | $itemRepo = $em->getRepository('ChamiloCourseBundle:CItemProperty'); |
||
4895 | |||
4896 | /** @var CourseRepository $courseRepo */ |
||
4897 | $courseRepo = $em->getRepository('ChamiloCoreBundle:Course'); |
||
4898 | $session = null; |
||
4899 | if (!empty($sessionId)) { |
||
4900 | $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId); |
||
4901 | } |
||
4902 | |||
4903 | $course = $courseRepo->find($courseId); |
||
4904 | |||
4905 | if ($courseId != 0) { |
||
4906 | // Subscribed groups to a LP |
||
4907 | $subscribedGroupsInLp = $itemRepo->getGroupsSubscribedToItem( |
||
4908 | TOOL_LEARNPATH_CATEGORY, |
||
4909 | $category->getId(), |
||
4910 | $course, |
||
4911 | $session |
||
4912 | ); |
||
4913 | } |
||
4914 | |||
4915 | if (!empty($subscribedGroupsInLp)) { |
||
4916 | $noGroupSubscribed = false; |
||
4917 | if (!empty($groups)) { |
||
4918 | $groups = array_column($groups, 'iid'); |
||
4919 | /** @var CItemProperty $item */ |
||
4920 | foreach ($subscribedGroupsInLp as $item) { |
||
4921 | if ($item->getGroup() && |
||
4922 | in_array($item->getGroup()->getId(), $groups) |
||
4923 | ) { |
||
4924 | return true; |
||
4925 | } |
||
4926 | } |
||
4927 | } |
||
4928 | } |
||
4929 | $response = $noGroupSubscribed && $noUserSubscribed; |
||
4930 | |||
4931 | return $response; |
||
4932 | } |
||
4933 | |||
4934 | /** |
||
4935 | * Check if a learnpath category is published as course tool. |
||
4936 | * |
||
4937 | * @param int $courseId |
||
4938 | * |
||
4939 | * @return bool |
||
4940 | */ |
||
4941 | public static function categoryIsPublished(CLpCategory $category, $courseId) |
||
4942 | { |
||
4943 | $link = self::getCategoryLinkForTool($category->getId()); |
||
4944 | $em = Database::getManager(); |
||
4945 | |||
4946 | $tools = $em |
||
4947 | ->createQuery(" |
||
4948 | SELECT t FROM ChamiloCourseBundle:CTool t |
||
4949 | WHERE t.cId = :course AND |
||
4950 | t.name = :name AND |
||
4951 | t.image = 'lp_category.gif' AND |
||
4952 | t.link LIKE :link |
||
4953 | ") |
||
4954 | ->setParameters([ |
||
4955 | 'course' => $courseId, |
||
4956 | 'name' => strip_tags($category->getName()), |
||
4957 | 'link' => "$link%", |
||
4958 | ]) |
||
4959 | ->getResult(); |
||
4960 | |||
4961 | /** @var CTool $tool */ |
||
4962 | $tool = current($tools); |
||
4963 | |||
4964 | return $tool ? $tool->getVisibility() : false; |
||
4965 | } |
||
4966 | |||
4967 | /** |
||
4968 | * Restart the whole learnpath. Return the URL of the first element. |
||
4969 | * Make sure the results are saved with anoter method. This method should probably be redefined in children classes. |
||
4970 | * To use a similar method statically, use the create_new_attempt() method. |
||
4971 | * |
||
4972 | * @return bool |
||
4973 | */ |
||
4974 | public function restart() |
||
4975 | { |
||
4976 | if ($this->debug > 0) { |
||
4977 | error_log('In learnpath::restart()', 0); |
||
4978 | } |
||
4979 | // TODO |
||
4980 | // Call autosave method to save the current progress. |
||
4981 | //$this->index = 0; |
||
4982 | if (api_is_invitee()) { |
||
4983 | return false; |
||
4984 | } |
||
4985 | $session_id = api_get_session_id(); |
||
4986 | $course_id = api_get_course_int_id(); |
||
4987 | $lp_view_table = Database::get_course_table(TABLE_LP_VIEW); |
||
4988 | $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) |
||
4989 | VALUES ($course_id, ".$this->lp_id.",".$this->get_user_id().",".($this->attempt + 1).", $session_id)"; |
||
4990 | if ($this->debug > 2) { |
||
4991 | error_log('Inserting new lp_view for restart: '.$sql, 0); |
||
4992 | } |
||
4993 | Database::query($sql); |
||
4994 | $view_id = Database::insert_id(); |
||
4995 | |||
4996 | if ($view_id) { |
||
4997 | $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $view_id"; |
||
4998 | Database::query($sql); |
||
4999 | $this->lp_view_id = $view_id; |
||
5000 | $this->attempt = $this->attempt + 1; |
||
5001 | } else { |
||
5002 | $this->error = 'Could not insert into item_view table...'; |
||
5003 | |||
5004 | return false; |
||
5005 | } |
||
5006 | $this->autocomplete_parents($this->current); |
||
5007 | foreach ($this->items as $index => $dummy) { |
||
5008 | $this->items[$index]->restart(); |
||
5009 | $this->items[$index]->set_lp_view($this->lp_view_id); |
||
5010 | } |
||
5011 | $this->first(); |
||
5012 | |||
5013 | return true; |
||
5014 | } |
||
5015 | |||
5016 | /** |
||
5017 | * Saves the current item. |
||
5018 | * |
||
5019 | * @return bool |
||
5020 | */ |
||
5021 | public function save_current() |
||
5022 | { |
||
5023 | $debug = $this->debug; |
||
5024 | // TODO: Do a better check on the index pointing to the right item (it is supposed to be working |
||
5025 | // on $ordered_items[] but not sure it's always safe to use with $items[]). |
||
5026 | if ($debug) { |
||
5027 | error_log('save_current() saving item '.$this->current, 0); |
||
5028 | error_log(''.print_r($this->items, true), 0); |
||
5029 | } |
||
5030 | if (isset($this->items[$this->current]) && |
||
5031 | is_object($this->items[$this->current]) |
||
5032 | ) { |
||
5033 | if ($debug) { |
||
5034 | error_log('Before save last_scorm_session_time: '.$this->items[$this->current]->getLastScormSessionTime()); |
||
5035 | } |
||
5036 | |||
5037 | $res = $this->items[$this->current]->save( |
||
5038 | false, |
||
5039 | $this->prerequisites_match($this->current) |
||
5040 | ); |
||
5041 | $this->autocomplete_parents($this->current); |
||
5042 | $status = $this->items[$this->current]->get_status(); |
||
5043 | $this->update_queue[$this->current] = $status; |
||
5044 | |||
5045 | if ($debug) { |
||
5046 | error_log('After save last_scorm_session_time: '.$this->items[$this->current]->getLastScormSessionTime()); |
||
5047 | } |
||
5048 | |||
5049 | return $res; |
||
5050 | } |
||
5051 | |||
5052 | return false; |
||
5053 | } |
||
5054 | |||
5055 | /** |
||
5056 | * Saves the given item. |
||
5057 | * |
||
5058 | * @param int $item_id Optional (will take from $_REQUEST if null) |
||
5059 | * @param bool $from_outside Save from url params (true) or from current attributes (false). Default true |
||
5060 | * |
||
5061 | * @return bool |
||
5062 | */ |
||
5063 | public function save_item($item_id = null, $from_outside = true) |
||
5064 | { |
||
5065 | $debug = $this->debug; |
||
5066 | if ($debug) { |
||
5067 | error_log('In learnpath::save_item('.$item_id.','.intval($from_outside).')', 0); |
||
5068 | } |
||
5069 | // TODO: Do a better check on the index pointing to the right item (it is supposed to be working |
||
5070 | // on $ordered_items[] but not sure it's always safe to use with $items[]). |
||
5071 | if (empty($item_id)) { |
||
5072 | $item_id = (int) $_REQUEST['id']; |
||
5073 | } |
||
5074 | |||
5075 | if (empty($item_id)) { |
||
5076 | $item_id = $this->get_current_item_id(); |
||
5077 | } |
||
5078 | if (isset($this->items[$item_id]) && |
||
5079 | is_object($this->items[$item_id]) |
||
5080 | ) { |
||
5081 | // Saving the item. |
||
5082 | $res = $this->items[$item_id]->save( |
||
5083 | $from_outside, |
||
5084 | $this->prerequisites_match($item_id) |
||
5085 | ); |
||
5086 | |||
5087 | if ($debug) { |
||
5088 | error_log('update_queue before:'); |
||
5089 | error_log(print_r($this->update_queue, 1)); |
||
5090 | } |
||
5091 | $this->autocomplete_parents($item_id); |
||
5092 | |||
5093 | $status = $this->items[$item_id]->get_status(); |
||
5094 | $this->update_queue[$item_id] = $status; |
||
5095 | |||
5096 | if ($debug) { |
||
5097 | error_log('get_status(): '.$status); |
||
5098 | error_log('update_queue after:'); |
||
5099 | error_log(print_r($this->update_queue, 1)); |
||
5100 | } |
||
5101 | |||
5102 | return $res; |
||
5103 | } |
||
5104 | |||
5105 | return false; |
||
5106 | } |
||
5107 | |||
5108 | /** |
||
5109 | * Update the last progress only in case. |
||
5110 | */ |
||
5111 | public function updateLpProgress() |
||
5112 | { |
||
5113 | $debug = $this->debug; |
||
5114 | if ($debug) { |
||
5115 | error_log('In learnpath::updateLpProgress()', 0); |
||
5116 | } |
||
5117 | $sessionCondition = api_get_session_condition( |
||
5118 | api_get_session_id(), |
||
5119 | true, |
||
5120 | false |
||
5121 | ); |
||
5122 | $courseId = api_get_course_int_id(); |
||
5123 | $userId = $this->get_user_id(); |
||
5124 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
5125 | |||
5126 | [$progress] = $this->get_progress_bar_text('%'); |
||
5127 | if ($progress >= 0 && $progress <= 100) { |
||
5128 | // Check database. |
||
5129 | $progress = (int) $progress; |
||
5130 | $sql = "UPDATE $table SET |
||
5131 | progress = $progress |
||
5132 | WHERE |
||
5133 | c_id = $courseId AND |
||
5134 | lp_id = ".$this->get_id()." AND |
||
5135 | progress < $progress AND |
||
5136 | user_id = ".$userId." ".$sessionCondition; |
||
5137 | // Ignore errors as some tables might not have the progress field just yet. |
||
5138 | Database::query($sql); |
||
5139 | if ($debug) { |
||
5140 | error_log($sql); |
||
5141 | } |
||
5142 | $this->progress_db = $progress; |
||
5143 | |||
5144 | if (100 == $progress) { |
||
5145 | HookLearningPathEnd::create() |
||
5146 | ->setEventData(['lp_view_id' => $this->lp_view_id]) |
||
5147 | ->hookLearningPathEnd(); |
||
5148 | } |
||
5149 | } |
||
5150 | } |
||
5151 | |||
5152 | /** |
||
5153 | * Saves the last item seen's ID only in case. |
||
5154 | */ |
||
5155 | public function save_last($score = null) |
||
5156 | { |
||
5157 | $course_id = api_get_course_int_id(); |
||
5158 | $debug = $this->debug; |
||
5159 | if ($debug) { |
||
5160 | error_log('In learnpath::save_last()', 0); |
||
5161 | } |
||
5162 | $session_condition = api_get_session_condition( |
||
5163 | api_get_session_id(), |
||
5164 | true, |
||
5165 | false |
||
5166 | ); |
||
5167 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
5168 | |||
5169 | $userId = $this->get_user_id(); |
||
5170 | if (empty($userId)) { |
||
5171 | $userId = api_get_user_id(); |
||
5172 | if ($debug) { |
||
5173 | error_log('$this->get_user_id() was empty, used api_get_user_id() instead in '.__FILE__.' line '.__LINE__); |
||
5174 | } |
||
5175 | } |
||
5176 | if (isset($this->current) && !api_is_invitee()) { |
||
5177 | if ($debug) { |
||
5178 | error_log('Saving current item ('.$this->current.') for later review', 0); |
||
5179 | } |
||
5180 | $sql = "UPDATE $table SET |
||
5181 | last_item = ".$this->get_current_item_id()." |
||
5182 | WHERE |
||
5183 | c_id = $course_id AND |
||
5184 | lp_id = ".$this->get_id()." AND |
||
5185 | user_id = ".$userId." ".$session_condition; |
||
5186 | if ($debug) { |
||
5187 | error_log('Saving last item seen : '.$sql, 0); |
||
5188 | } |
||
5189 | Database::query($sql); |
||
5190 | } |
||
5191 | |||
5192 | if (!api_is_invitee()) { |
||
5193 | // Save progress. |
||
5194 | [$progress] = $this->get_progress_bar_text('%'); |
||
5195 | $scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable'); |
||
5196 | $scoreAsProgress = $this->getUseScoreAsProgress(); |
||
5197 | if ($scoreAsProgress && $scoreAsProgressSetting && (null === $score || empty($score) || -1 == $score)) { |
||
5198 | if ($debug) { |
||
5199 | error_log("Return false: Dont save score: $score"); |
||
5200 | error_log("progress: $progress"); |
||
5201 | } |
||
5202 | |||
5203 | return false; |
||
5204 | } |
||
5205 | |||
5206 | if ($scoreAsProgress && $scoreAsProgressSetting) { |
||
5207 | $storedProgress = self::getProgress( |
||
5208 | $this->get_id(), |
||
5209 | $userId, |
||
5210 | $course_id, |
||
5211 | $this->get_lp_session_id() |
||
5212 | ); |
||
5213 | |||
5214 | // Check if the stored progress is higher than the new value |
||
5215 | if ($storedProgress >= $progress) { |
||
5216 | if ($debug) { |
||
5217 | error_log("Return false: New progress value is lower than stored value - Current value: $storedProgress - New value: $progress [lp ".$this->get_id()." - user ".$userId."]"); |
||
5218 | } |
||
5219 | |||
5220 | return false; |
||
5221 | } |
||
5222 | } |
||
5223 | |||
5224 | if ($progress >= 0 && $progress <= 100) { |
||
5225 | // Check database. |
||
5226 | $progress = (int) $progress; |
||
5227 | $sql = "UPDATE $table SET |
||
5228 | progress = $progress |
||
5229 | WHERE |
||
5230 | c_id = $course_id AND |
||
5231 | lp_id = ".$this->get_id()." AND |
||
5232 | user_id = ".$userId." ".$session_condition; |
||
5233 | // Ignore errors as some tables might not have the progress field just yet. |
||
5234 | Database::query($sql); |
||
5235 | if ($debug) { |
||
5236 | error_log($sql); |
||
5237 | } |
||
5238 | $this->progress_db = $progress; |
||
5239 | |||
5240 | if (100 == $progress) { |
||
5241 | HookLearningPathEnd::create() |
||
5242 | ->setEventData(['lp_view_id' => $this->lp_view_id]) |
||
5243 | ->hookLearningPathEnd(); |
||
5244 | } |
||
5245 | } |
||
5246 | } |
||
5247 | } |
||
5248 | |||
5249 | /** |
||
5250 | * Sets the current item ID (checks if valid and authorized first). |
||
5251 | * |
||
5252 | * @param int $item_id New item ID. If not given or not authorized, defaults to current |
||
5253 | */ |
||
5254 | public function set_current_item($item_id = null) |
||
5255 | { |
||
5256 | $debug = $this->debug; |
||
5257 | if ($debug) { |
||
5258 | error_log('In learnpath::set_current_item('.$item_id.')', 0); |
||
5259 | } |
||
5260 | if (empty($item_id)) { |
||
5261 | if ($debug) { |
||
5262 | error_log('No new current item given, ignore...', 0); |
||
5263 | } |
||
5264 | // Do nothing. |
||
5265 | } else { |
||
5266 | if ($debug) { |
||
5267 | error_log('New current item given is '.$item_id.'...', 0); |
||
5268 | } |
||
5269 | if (is_numeric($item_id)) { |
||
5270 | $item_id = (int) $item_id; |
||
5271 | // TODO: Check in database here. |
||
5272 | $this->last = $this->current; |
||
5273 | $this->current = $item_id; |
||
5274 | // TODO: Update $this->index as well. |
||
5275 | foreach ($this->ordered_items as $index => $item) { |
||
5276 | if ($item == $this->current) { |
||
5277 | $this->index = $index; |
||
5278 | break; |
||
5279 | } |
||
5280 | } |
||
5281 | if ($debug) { |
||
5282 | error_log('set_current_item('.$item_id.') done. Index is now : '.$this->index); |
||
5283 | } |
||
5284 | } else { |
||
5285 | if ($debug) { |
||
5286 | error_log('set_current_item('.$item_id.') failed. Not a numeric value: '); |
||
5287 | } |
||
5288 | } |
||
5289 | } |
||
5290 | } |
||
5291 | |||
5292 | /** |
||
5293 | * Sets the encoding. |
||
5294 | * |
||
5295 | * @param string $enc New encoding |
||
5296 | * |
||
5297 | * @return bool |
||
5298 | * |
||
5299 | * @todo (as of Chamilo 1.8.8): Check in the future whether this method is needed. |
||
5300 | */ |
||
5301 | public function set_encoding($enc = 'UTF-8') |
||
5302 | { |
||
5303 | $enc = api_refine_encoding_id($enc); |
||
5304 | if (empty($enc)) { |
||
5305 | $enc = api_get_system_encoding(); |
||
5306 | } |
||
5307 | if (api_is_encoding_supported($enc)) { |
||
5308 | $lp = $this->get_id(); |
||
5309 | if ($lp != 0) { |
||
5310 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
5311 | $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' |
||
5312 | WHERE iid = ".$lp; |
||
5313 | $res = Database::query($sql); |
||
5314 | |||
5315 | return $res; |
||
5316 | } |
||
5317 | } |
||
5318 | |||
5319 | return false; |
||
5320 | } |
||
5321 | |||
5322 | /** |
||
5323 | * Sets the JS lib setting in the database directly. |
||
5324 | * This is the JavaScript library file this lp needs to load on startup. |
||
5325 | * |
||
5326 | * @param string $lib Proximity setting |
||
5327 | * |
||
5328 | * @return bool True on update success. False otherwise. |
||
5329 | */ |
||
5330 | public function set_jslib($lib = '') |
||
5331 | { |
||
5332 | $lp = $this->get_id(); |
||
5333 | |||
5334 | if ($lp != 0) { |
||
5335 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
5336 | $lib = Database::escape_string($lib); |
||
5337 | $sql = "UPDATE $tbl_lp SET js_lib = '$lib' |
||
5338 | WHERE iid = $lp"; |
||
5339 | $res = Database::query($sql); |
||
5340 | |||
5341 | return $res; |
||
5342 | } |
||
5343 | |||
5344 | return false; |
||
5345 | } |
||
5346 | |||
5347 | /** |
||
5348 | * Sets the name of the LP maker (publisher) (and save). |
||
5349 | * |
||
5350 | * @param string $name Optional string giving the new content_maker of this learnpath |
||
5351 | * |
||
5352 | * @return bool True |
||
5353 | */ |
||
5354 | public function set_maker($name = '') |
||
5355 | { |
||
5356 | if (empty($name)) { |
||
5357 | return false; |
||
5358 | } |
||
5359 | $this->maker = $name; |
||
5360 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5361 | $lp_id = $this->get_id(); |
||
5362 | $sql = "UPDATE $table SET |
||
5363 | content_maker = '".Database::escape_string($this->maker)."' |
||
5364 | WHERE iid = $lp_id"; |
||
5365 | Database::query($sql); |
||
5366 | |||
5367 | return true; |
||
5368 | } |
||
5369 | |||
5370 | /** |
||
5371 | * Sets the name of the current learnpath (and save). |
||
5372 | * |
||
5373 | * @param string $name Optional string giving the new name of this learnpath |
||
5374 | * |
||
5375 | * @return bool True/False |
||
5376 | */ |
||
5377 | public function set_name($name = null) |
||
5378 | { |
||
5379 | if (empty($name)) { |
||
5380 | return false; |
||
5381 | } |
||
5382 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
5383 | $name = Database::escape_string($name); |
||
5384 | |||
5385 | $this->name = $name; |
||
5386 | |||
5387 | $lp_id = $this->get_id(); |
||
5388 | $course_id = $this->course_info['real_id']; |
||
5389 | $sql = "UPDATE $lp_table SET |
||
5390 | name = '$name' |
||
5391 | WHERE iid = $lp_id"; |
||
5392 | $result = Database::query($sql); |
||
5393 | // If the lp is visible on the homepage, change his name there. |
||
5394 | if (Database::affected_rows($result)) { |
||
5395 | $session_id = api_get_session_id(); |
||
5396 | $session_condition = api_get_session_condition($session_id); |
||
5397 | $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
5398 | $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id; |
||
5399 | $sql = "UPDATE $tbl_tool SET name = '$name' |
||
5400 | WHERE |
||
5401 | c_id = $course_id AND |
||
5402 | (link='$link' AND image='scormbuilder.gif' $session_condition)"; |
||
5403 | Database::query($sql); |
||
5404 | |||
5405 | return true; |
||
5406 | } |
||
5407 | |||
5408 | return false; |
||
5409 | } |
||
5410 | |||
5411 | /** |
||
5412 | * Set index specified prefix terms for all items in this path. |
||
5413 | * |
||
5414 | * @param string $terms_string Comma-separated list of terms |
||
5415 | * @param string $prefix Xapian term prefix |
||
5416 | * |
||
5417 | * @return bool False on error, true otherwise |
||
5418 | */ |
||
5419 | public function set_terms_by_prefix($terms_string, $prefix) |
||
5420 | { |
||
5421 | $course_id = api_get_course_int_id(); |
||
5422 | if (api_get_setting('search_enabled') !== 'true') { |
||
5423 | return false; |
||
5424 | } |
||
5425 | |||
5426 | if (!extension_loaded('xapian')) { |
||
5427 | return false; |
||
5428 | } |
||
5429 | |||
5430 | $terms_string = trim($terms_string); |
||
5431 | $terms = explode(',', $terms_string); |
||
5432 | array_walk($terms, 'trim_value'); |
||
5433 | $stored_terms = $this->get_common_index_terms_by_prefix($prefix); |
||
5434 | |||
5435 | // Don't do anything if no change, verify only at DB, not the search engine. |
||
5436 | if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0)) { |
||
5437 | return false; |
||
5438 | } |
||
5439 | |||
5440 | require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API. |
||
5441 | require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php'; |
||
5442 | |||
5443 | $items_table = Database::get_course_table(TABLE_LP_ITEM); |
||
5444 | // TODO: Make query secure agains XSS : use member attr instead of post var. |
||
5445 | $lp_id = (int) $_POST['lp_id']; |
||
5446 | $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id"; |
||
5447 | $result = Database::query($sql); |
||
5448 | $di = new ChamiloIndexer(); |
||
5449 | |||
5450 | while ($lp_item = Database::fetch_array($result)) { |
||
5451 | // Get search_did. |
||
5452 | $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
||
5453 | $sql = 'SELECT * FROM %s |
||
5454 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
5455 | LIMIT 1'; |
||
5456 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']); |
||
5457 | |||
5458 | //echo $sql; echo '<br>'; |
||
5459 | $res = Database::query($sql); |
||
5460 | if (Database::num_rows($res) > 0) { |
||
5461 | $se_ref = Database::fetch_array($res); |
||
5462 | // Compare terms. |
||
5463 | $doc = $di->get_document($se_ref['search_did']); |
||
5464 | $xapian_terms = xapian_get_doc_terms($doc, $prefix); |
||
5465 | $xterms = []; |
||
5466 | foreach ($xapian_terms as $xapian_term) { |
||
5467 | $xterms[] = substr($xapian_term['name'], 1); |
||
5468 | } |
||
5469 | |||
5470 | $dterms = $terms; |
||
5471 | $missing_terms = array_diff($dterms, $xterms); |
||
5472 | $deprecated_terms = array_diff($xterms, $dterms); |
||
5473 | |||
5474 | // Save it to search engine. |
||
5475 | foreach ($missing_terms as $term) { |
||
5476 | $doc->add_term($prefix.$term, 1); |
||
5477 | } |
||
5478 | foreach ($deprecated_terms as $term) { |
||
5479 | $doc->remove_term($prefix.$term); |
||
5480 | } |
||
5481 | $di->getDb()->replace_document((int) $se_ref['search_did'], $doc); |
||
5482 | $di->getDb()->flush(); |
||
5483 | } |
||
5484 | } |
||
5485 | |||
5486 | return true; |
||
5487 | } |
||
5488 | |||
5489 | /** |
||
5490 | * Sets the theme of the LP (local/remote) (and save). |
||
5491 | * |
||
5492 | * @param string $name Optional string giving the new theme of this learnpath |
||
5493 | * |
||
5494 | * @return bool Returns true if theme name is not empty |
||
5495 | */ |
||
5496 | public function set_theme($name = '') |
||
5497 | { |
||
5498 | $this->theme = $name; |
||
5499 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5500 | $lp_id = $this->get_id(); |
||
5501 | $sql = "UPDATE $table |
||
5502 | SET theme = '".Database::escape_string($this->theme)."' |
||
5503 | WHERE iid = $lp_id"; |
||
5504 | Database::query($sql); |
||
5505 | |||
5506 | return true; |
||
5507 | } |
||
5508 | |||
5509 | /** |
||
5510 | * Sets the image of an LP (and save). |
||
5511 | * |
||
5512 | * @param string $name Optional string giving the new image of this learnpath |
||
5513 | * |
||
5514 | * @return bool Returns true if theme name is not empty |
||
5515 | */ |
||
5516 | public function set_preview_image($name = '') |
||
5517 | { |
||
5518 | $this->preview_image = $name; |
||
5519 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5520 | $lp_id = $this->get_id(); |
||
5521 | $sql = "UPDATE $table SET |
||
5522 | preview_image = '".Database::escape_string($this->preview_image)."' |
||
5523 | WHERE iid = $lp_id"; |
||
5524 | Database::query($sql); |
||
5525 | |||
5526 | return true; |
||
5527 | } |
||
5528 | |||
5529 | /** |
||
5530 | * Sets the author of a LP (and save). |
||
5531 | * |
||
5532 | * @param string $name Optional string giving the new author of this learnpath |
||
5533 | * |
||
5534 | * @return bool Returns true if author's name is not empty |
||
5535 | */ |
||
5536 | public function set_author($name = '') |
||
5537 | { |
||
5538 | $this->author = $name; |
||
5539 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5540 | $lp_id = $this->get_id(); |
||
5541 | $sql = "UPDATE $table SET author = '".Database::escape_string($name)."' |
||
5542 | WHERE iid = $lp_id"; |
||
5543 | Database::query($sql); |
||
5544 | |||
5545 | return true; |
||
5546 | } |
||
5547 | |||
5548 | /** |
||
5549 | * Sets the hide_toc_frame parameter of a LP (and save). |
||
5550 | * |
||
5551 | * @param int $hide 1 if frame is hidden 0 then else |
||
5552 | * |
||
5553 | * @return bool Returns true if author's name is not empty |
||
5554 | */ |
||
5555 | public function set_hide_toc_frame($hide) |
||
5556 | { |
||
5557 | if (intval($hide) == $hide) { |
||
5558 | $this->hide_toc_frame = $hide; |
||
5559 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5560 | $lp_id = $this->get_id(); |
||
5561 | $sql = "UPDATE $table SET |
||
5562 | hide_toc_frame = '".(int) $this->hide_toc_frame."' |
||
5563 | WHERE iid = $lp_id"; |
||
5564 | Database::query($sql); |
||
5565 | |||
5566 | return true; |
||
5567 | } |
||
5568 | |||
5569 | return false; |
||
5570 | } |
||
5571 | |||
5572 | /** |
||
5573 | * Sets the prerequisite of a LP (and save). |
||
5574 | * |
||
5575 | * @param int $prerequisite integer giving the new prerequisite of this learnpath |
||
5576 | * |
||
5577 | * @return bool returns true if prerequisite is not empty |
||
5578 | */ |
||
5579 | public function set_prerequisite($prerequisite) |
||
5580 | { |
||
5581 | $this->prerequisite = (int) $prerequisite; |
||
5582 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5583 | $lp_id = $this->get_id(); |
||
5584 | $sql = "UPDATE $table SET prerequisite = '".$this->prerequisite."' |
||
5585 | WHERE iid = $lp_id"; |
||
5586 | Database::query($sql); |
||
5587 | |||
5588 | return true; |
||
5589 | } |
||
5590 | |||
5591 | /** |
||
5592 | * Sets the location/proximity of the LP (local/remote) (and save). |
||
5593 | * |
||
5594 | * @param string $name Optional string giving the new location of this learnpath |
||
5595 | * |
||
5596 | * @return bool True on success / False on error |
||
5597 | */ |
||
5598 | public function set_proximity($name = '') |
||
5599 | { |
||
5600 | if (empty($name)) { |
||
5601 | return false; |
||
5602 | } |
||
5603 | |||
5604 | $this->proximity = $name; |
||
5605 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5606 | $lp_id = $this->get_id(); |
||
5607 | $sql = "UPDATE $table SET |
||
5608 | content_local = '".Database::escape_string($name)."' |
||
5609 | WHERE iid = $lp_id"; |
||
5610 | Database::query($sql); |
||
5611 | |||
5612 | return true; |
||
5613 | } |
||
5614 | |||
5615 | /** |
||
5616 | * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item. |
||
5617 | * |
||
5618 | * @param int $id DB ID of the item |
||
5619 | */ |
||
5620 | public function set_previous_item($id) |
||
5621 | { |
||
5622 | if ($this->debug > 0) { |
||
5623 | error_log('In learnpath::set_previous_item()', 0); |
||
5624 | } |
||
5625 | $this->last = $id; |
||
5626 | } |
||
5627 | |||
5628 | /** |
||
5629 | * Sets use_max_score. |
||
5630 | * |
||
5631 | * @param int $use_max_score Optional string giving the new location of this learnpath |
||
5632 | * |
||
5633 | * @return bool True on success / False on error |
||
5634 | */ |
||
5635 | public function set_use_max_score($use_max_score = 1) |
||
5636 | { |
||
5637 | $use_max_score = (int) $use_max_score; |
||
5638 | $this->use_max_score = $use_max_score; |
||
5639 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5640 | $lp_id = $this->get_id(); |
||
5641 | $sql = "UPDATE $table SET |
||
5642 | use_max_score = '".$this->use_max_score."' |
||
5643 | WHERE iid = $lp_id"; |
||
5644 | Database::query($sql); |
||
5645 | |||
5646 | return true; |
||
5647 | } |
||
5648 | |||
5649 | /** |
||
5650 | * Sets and saves the expired_on date. |
||
5651 | * |
||
5652 | * @param string $expired_on Optional string giving the new author of this learnpath |
||
5653 | * |
||
5654 | * @throws \Doctrine\ORM\OptimisticLockException |
||
5655 | * |
||
5656 | * @return bool Returns true if author's name is not empty |
||
5657 | */ |
||
5658 | public function set_expired_on($expired_on) |
||
5659 | { |
||
5660 | $em = Database::getManager(); |
||
5661 | /** @var CLp $lp */ |
||
5662 | $lp = $em |
||
5663 | ->getRepository('ChamiloCourseBundle:CLp') |
||
5664 | ->findOneBy( |
||
5665 | [ |
||
5666 | 'iid' => $this->get_id(), |
||
5667 | ] |
||
5668 | ); |
||
5669 | |||
5670 | if (!$lp) { |
||
5671 | return false; |
||
5672 | } |
||
5673 | |||
5674 | $this->expired_on = !empty($expired_on) ? api_get_utc_datetime($expired_on, false, true) : null; |
||
5675 | |||
5676 | $lp->setExpiredOn($this->expired_on); |
||
5677 | $em->persist($lp); |
||
5678 | $em->flush(); |
||
5679 | |||
5680 | return true; |
||
5681 | } |
||
5682 | |||
5683 | /** |
||
5684 | * Sets and saves the publicated_on date. |
||
5685 | * |
||
5686 | * @param string $publicated_on Optional string giving the new author of this learnpath |
||
5687 | * |
||
5688 | * @throws \Doctrine\ORM\OptimisticLockException |
||
5689 | * |
||
5690 | * @return bool Returns true if author's name is not empty |
||
5691 | */ |
||
5692 | public function set_publicated_on($publicated_on) |
||
5693 | { |
||
5694 | $em = Database::getManager(); |
||
5695 | /** @var CLp $lp */ |
||
5696 | $lp = $em |
||
5697 | ->getRepository('ChamiloCourseBundle:CLp') |
||
5698 | ->findOneBy( |
||
5699 | [ |
||
5700 | 'iid' => $this->get_id(), |
||
5701 | ] |
||
5702 | ); |
||
5703 | |||
5704 | if (!$lp) { |
||
5705 | return false; |
||
5706 | } |
||
5707 | |||
5708 | $this->publicated_on = !empty($publicated_on) ? api_get_utc_datetime($publicated_on, false, true) : null; |
||
5709 | $lp->setPublicatedOn($this->publicated_on); |
||
5710 | $em->persist($lp); |
||
5711 | $em->flush(); |
||
5712 | |||
5713 | return true; |
||
5714 | } |
||
5715 | |||
5716 | /** |
||
5717 | * Sets and saves the expired_on date. |
||
5718 | * |
||
5719 | * @return bool Returns true if author's name is not empty |
||
5720 | */ |
||
5721 | public function set_modified_on() |
||
5722 | { |
||
5723 | $this->modified_on = api_get_utc_datetime(); |
||
5724 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5725 | $lp_id = $this->get_id(); |
||
5726 | $sql = "UPDATE $table SET modified_on = '".$this->modified_on."' |
||
5727 | WHERE iid = $lp_id"; |
||
5728 | Database::query($sql); |
||
5729 | |||
5730 | return true; |
||
5731 | } |
||
5732 | |||
5733 | /** |
||
5734 | * Sets the object's error message. |
||
5735 | * |
||
5736 | * @param string $error Error message. If empty, reinits the error string |
||
5737 | */ |
||
5738 | public function set_error_msg($error = '') |
||
5739 | { |
||
5740 | if ($this->debug > 0) { |
||
5741 | error_log('In learnpath::set_error_msg()', 0); |
||
5742 | } |
||
5743 | if (empty($error)) { |
||
5744 | $this->error = ''; |
||
5745 | } else { |
||
5746 | $this->error .= $error; |
||
5747 | } |
||
5748 | } |
||
5749 | |||
5750 | /** |
||
5751 | * Launches the current item if not 'sco' |
||
5752 | * (starts timer and make sure there is a record ready in the DB). |
||
5753 | * |
||
5754 | * @param bool $allow_new_attempt Whether to allow a new attempt or not |
||
5755 | * |
||
5756 | * @return bool |
||
5757 | */ |
||
5758 | public function start_current_item($allow_new_attempt = false) |
||
5759 | { |
||
5760 | $debug = $this->debug; |
||
5761 | if ($debug) { |
||
5762 | error_log('In learnpath::start_current_item()'); |
||
5763 | error_log('current: '.$this->current); |
||
5764 | } |
||
5765 | if ($this->current != 0 && isset($this->items[$this->current]) && is_object($this->items[$this->current])) { |
||
5766 | $type = $this->get_type(); |
||
5767 | $item_type = $this->items[$this->current]->get_type(); |
||
5768 | if (($type == 2 && $item_type != 'sco') || |
||
5769 | ($type == 3 && $item_type != 'au') || |
||
5770 | ( |
||
5771 | $type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES && |
||
5772 | WhispeakAuthPlugin::isAllowedToSaveLpItem($this->current) |
||
5773 | ) |
||
5774 | ) { |
||
5775 | if ($debug) { |
||
5776 | error_log('item type: '.$item_type); |
||
5777 | error_log('lp type: '.$type); |
||
5778 | } |
||
5779 | $this->items[$this->current]->open($allow_new_attempt); |
||
5780 | $this->autocomplete_parents($this->current); |
||
5781 | $prereq_check = $this->prerequisites_match($this->current); |
||
5782 | if ($debug) { |
||
5783 | error_log('start_current_item will save item with prereq: '.$prereq_check); |
||
5784 | } |
||
5785 | |||
5786 | $saveStatus = learnpathItem::isLpItemAutoComplete($this->current); |
||
5787 | if ($saveStatus) { |
||
5788 | $this->items[$this->current]->save(false, $prereq_check); |
||
5789 | } |
||
5790 | } |
||
5791 | // If sco, then it is supposed to have been updated by some other call. |
||
5792 | if ($item_type === 'sco') { |
||
5793 | $this->items[$this->current]->restart(); |
||
5794 | } |
||
5795 | } |
||
5796 | if ($debug) { |
||
5797 | error_log('lp_view_session_id: '.$this->lp_view_session_id); |
||
5798 | error_log('api_get_session_id: '.api_get_session_id()); |
||
5799 | error_log('End of learnpath::start_current_item()'); |
||
5800 | } |
||
5801 | |||
5802 | return true; |
||
5803 | } |
||
5804 | |||
5805 | /** |
||
5806 | * Stops the processing and counters for the old item (as held in $this->last). |
||
5807 | * |
||
5808 | * @return bool True/False |
||
5809 | */ |
||
5810 | public function stop_previous_item() |
||
5811 | { |
||
5812 | $debug = $this->debug; |
||
5813 | if ($debug) { |
||
5814 | error_log('In learnpath::stop_previous_item()'); |
||
5815 | } |
||
5816 | |||
5817 | if ($this->last != 0 && $this->last != $this->current && |
||
5818 | isset($this->items[$this->last]) && is_object($this->items[$this->last]) |
||
5819 | ) { |
||
5820 | if ($debug) { |
||
5821 | error_log('In learnpath::stop_previous_item() - '.$this->last.' is object'); |
||
5822 | } |
||
5823 | switch ($this->get_type()) { |
||
5824 | case '3': |
||
5825 | if ($this->items[$this->last]->get_type() != 'au') { |
||
5826 | if ($debug) { |
||
5827 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 3 is <> au'); |
||
5828 | } |
||
5829 | $this->items[$this->last]->close(); |
||
5830 | } else { |
||
5831 | if ($debug) { |
||
5832 | error_log('In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals'); |
||
5833 | } |
||
5834 | } |
||
5835 | break; |
||
5836 | case '2': |
||
5837 | if ($this->items[$this->last]->get_type() != 'sco') { |
||
5838 | if ($debug) { |
||
5839 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 2 is <> sco'); |
||
5840 | } |
||
5841 | $this->items[$this->last]->close(); |
||
5842 | } else { |
||
5843 | if ($debug) { |
||
5844 | error_log('In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals'); |
||
5845 | } |
||
5846 | } |
||
5847 | break; |
||
5848 | case '1': |
||
5849 | default: |
||
5850 | if ($debug) { |
||
5851 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 1 is asset'); |
||
5852 | } |
||
5853 | $this->items[$this->last]->close(); |
||
5854 | break; |
||
5855 | } |
||
5856 | } else { |
||
5857 | if ($debug) { |
||
5858 | error_log('In learnpath::stop_previous_item() - No previous element found, ignoring...'); |
||
5859 | } |
||
5860 | |||
5861 | return false; |
||
5862 | } |
||
5863 | |||
5864 | return true; |
||
5865 | } |
||
5866 | |||
5867 | /** |
||
5868 | * Updates the default view mode from fullscreen to embedded and inversely. |
||
5869 | * |
||
5870 | * @return string The current default view mode ('fullscreen' or 'embedded') |
||
5871 | */ |
||
5872 | public function update_default_view_mode() |
||
5873 | { |
||
5874 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5875 | $sql = "SELECT * FROM $table |
||
5876 | WHERE iid = ".$this->get_id(); |
||
5877 | $res = Database::query($sql); |
||
5878 | if (Database::num_rows($res) > 0) { |
||
5879 | $row = Database::fetch_array($res); |
||
5880 | $default_view_mode = $row['default_view_mod']; |
||
5881 | $view_mode = $default_view_mode; |
||
5882 | switch ($default_view_mode) { |
||
5883 | case 'fullscreen': // default with popup |
||
5884 | $view_mode = 'embedded'; |
||
5885 | break; |
||
5886 | case 'embedded': // default view with left menu |
||
5887 | $view_mode = 'embedframe'; |
||
5888 | break; |
||
5889 | case 'embedframe': //folded menu |
||
5890 | $view_mode = 'impress'; |
||
5891 | break; |
||
5892 | case 'impress': |
||
5893 | $view_mode = 'fullscreen'; |
||
5894 | break; |
||
5895 | } |
||
5896 | $sql = "UPDATE $table SET default_view_mod = '$view_mode' |
||
5897 | WHERE iid = ".$this->get_id(); |
||
5898 | Database::query($sql); |
||
5899 | $this->mode = $view_mode; |
||
5900 | |||
5901 | return $view_mode; |
||
5902 | } |
||
5903 | |||
5904 | return -1; |
||
5905 | } |
||
5906 | |||
5907 | /** |
||
5908 | * Updates the default behaviour about auto-commiting SCORM updates. |
||
5909 | * |
||
5910 | * @return bool True if auto-commit has been set to 'on', false otherwise |
||
5911 | */ |
||
5912 | public function update_default_scorm_commit() |
||
5913 | { |
||
5914 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
5915 | $sql = "SELECT * FROM $lp_table |
||
5916 | WHERE iid = ".$this->get_id(); |
||
5917 | $res = Database::query($sql); |
||
5918 | if (Database::num_rows($res) > 0) { |
||
5919 | $row = Database::fetch_array($res); |
||
5920 | $force = $row['force_commit']; |
||
5921 | if ($force == 1) { |
||
5922 | $force = 0; |
||
5923 | $force_return = false; |
||
5924 | } elseif ($force == 0) { |
||
5925 | $force = 1; |
||
5926 | $force_return = true; |
||
5927 | } |
||
5928 | $sql = "UPDATE $lp_table SET force_commit = $force |
||
5929 | WHERE iid = ".$this->get_id(); |
||
5930 | Database::query($sql); |
||
5931 | $this->force_commit = $force_return; |
||
5932 | |||
5933 | return $force_return; |
||
5934 | } |
||
5935 | |||
5936 | return -1; |
||
5937 | } |
||
5938 | |||
5939 | /** |
||
5940 | * Updates the order of learning paths (goes through all of them by order and fills the gaps). |
||
5941 | * |
||
5942 | * @return bool True on success, false on failure |
||
5943 | */ |
||
5944 | public function update_display_order() |
||
5945 | { |
||
5946 | $course_id = api_get_course_int_id(); |
||
5947 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
5948 | $sql = "SELECT * FROM $table |
||
5949 | WHERE c_id = $course_id |
||
5950 | ORDER BY display_order"; |
||
5951 | $res = Database::query($sql); |
||
5952 | if ($res === false) { |
||
5953 | return false; |
||
5954 | } |
||
5955 | |||
5956 | $num = Database::num_rows($res); |
||
5957 | // First check the order is correct, globally (might be wrong because |
||
5958 | // of versions < 1.8.4). |
||
5959 | if ($num > 0) { |
||
5960 | $i = 1; |
||
5961 | while ($row = Database::fetch_array($res)) { |
||
5962 | if ($row['display_order'] != $i) { |
||
5963 | // If we find a gap in the order, we need to fix it. |
||
5964 | $sql = "UPDATE $table SET display_order = $i |
||
5965 | WHERE iid = ".$row['iid']; |
||
5966 | Database::query($sql); |
||
5967 | } |
||
5968 | $i++; |
||
5969 | } |
||
5970 | } |
||
5971 | |||
5972 | return true; |
||
5973 | } |
||
5974 | |||
5975 | /** |
||
5976 | * Updates the "prevent_reinit" value that enables control on reinitialising items on second view. |
||
5977 | * |
||
5978 | * @return bool True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case) |
||
5979 | */ |
||
5980 | public function update_reinit() |
||
5981 | { |
||
5982 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
5983 | $sql = "SELECT * FROM $lp_table |
||
5984 | WHERE iid = ".$this->get_id(); |
||
5985 | $res = Database::query($sql); |
||
5986 | if (Database::num_rows($res) > 0) { |
||
5987 | $row = Database::fetch_array($res); |
||
5988 | $force = $row['prevent_reinit']; |
||
5989 | if ($force == 1) { |
||
5990 | $force = 0; |
||
5991 | } elseif ($force == 0) { |
||
5992 | $force = 1; |
||
5993 | } |
||
5994 | $sql = "UPDATE $lp_table SET prevent_reinit = $force |
||
5995 | WHERE iid = ".$this->get_id(); |
||
5996 | Database::query($sql); |
||
5997 | $this->prevent_reinit = $force; |
||
5998 | |||
5999 | return $force; |
||
6000 | } |
||
6001 | |||
6002 | return -1; |
||
6003 | } |
||
6004 | |||
6005 | /** |
||
6006 | * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag. |
||
6007 | * |
||
6008 | * @return string 'single', 'multi' or 'seriousgame' |
||
6009 | * |
||
6010 | * @author ndiechburg <[email protected]> |
||
6011 | */ |
||
6012 | public function get_attempt_mode() |
||
6013 | { |
||
6014 | //Set default value for seriousgame_mode |
||
6015 | if (!isset($this->seriousgame_mode)) { |
||
6016 | $this->seriousgame_mode = 0; |
||
6017 | } |
||
6018 | // Set default value for prevent_reinit |
||
6019 | if (!isset($this->prevent_reinit)) { |
||
6020 | $this->prevent_reinit = 1; |
||
6021 | } |
||
6022 | if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) { |
||
6023 | return 'seriousgame'; |
||
6024 | } |
||
6025 | if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) { |
||
6026 | return 'single'; |
||
6027 | } |
||
6028 | if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) { |
||
6029 | return 'multiple'; |
||
6030 | } |
||
6031 | |||
6032 | return 'single'; |
||
6033 | } |
||
6034 | |||
6035 | /** |
||
6036 | * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags. |
||
6037 | * |
||
6038 | * @param string 'seriousgame', 'single' or 'multiple' |
||
6039 | * |
||
6040 | * @return bool |
||
6041 | * |
||
6042 | * @author ndiechburg <[email protected]> |
||
6043 | */ |
||
6044 | public function set_attempt_mode($mode) |
||
6045 | { |
||
6046 | switch ($mode) { |
||
6047 | case 'seriousgame': |
||
6048 | $sg_mode = 1; |
||
6049 | $prevent_reinit = 1; |
||
6050 | break; |
||
6051 | case 'single': |
||
6052 | $sg_mode = 0; |
||
6053 | $prevent_reinit = 1; |
||
6054 | break; |
||
6055 | case 'multiple': |
||
6056 | $sg_mode = 0; |
||
6057 | $prevent_reinit = 0; |
||
6058 | break; |
||
6059 | default: |
||
6060 | $sg_mode = 0; |
||
6061 | $prevent_reinit = 0; |
||
6062 | break; |
||
6063 | } |
||
6064 | $this->prevent_reinit = $prevent_reinit; |
||
6065 | $this->seriousgame_mode = $sg_mode; |
||
6066 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
6067 | $sql = "UPDATE $table SET |
||
6068 | prevent_reinit = $prevent_reinit , |
||
6069 | seriousgame_mode = $sg_mode |
||
6070 | WHERE iid = ".$this->get_id(); |
||
6071 | $res = Database::query($sql); |
||
6072 | if ($res) { |
||
6073 | return true; |
||
6074 | } else { |
||
6075 | return false; |
||
6076 | } |
||
6077 | } |
||
6078 | |||
6079 | /** |
||
6080 | * Switch between multiple attempt, single attempt or serious_game mode (only for scorm). |
||
6081 | * |
||
6082 | * @author ndiechburg <[email protected]> |
||
6083 | */ |
||
6084 | public function switch_attempt_mode() |
||
6085 | { |
||
6086 | $mode = $this->get_attempt_mode(); |
||
6087 | switch ($mode) { |
||
6088 | case 'single': |
||
6089 | $next_mode = 'multiple'; |
||
6090 | break; |
||
6091 | case 'multiple': |
||
6092 | $next_mode = 'seriousgame'; |
||
6093 | break; |
||
6094 | case 'seriousgame': |
||
6095 | default: |
||
6096 | $next_mode = 'single'; |
||
6097 | break; |
||
6098 | } |
||
6099 | $this->set_attempt_mode($next_mode); |
||
6100 | } |
||
6101 | |||
6102 | /** |
||
6103 | * Switch the lp in ktm mode. This is a special scorm mode with unique attempt |
||
6104 | * but possibility to do again a completed item. |
||
6105 | * |
||
6106 | * @return bool true if seriousgame_mode has been set to 1, false otherwise |
||
6107 | * |
||
6108 | * @author ndiechburg <[email protected]> |
||
6109 | */ |
||
6110 | public function set_seriousgame_mode() |
||
6111 | { |
||
6112 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
6113 | $sql = "SELECT * FROM $lp_table |
||
6114 | WHERE iid = ".$this->get_id(); |
||
6115 | $res = Database::query($sql); |
||
6116 | if (Database::num_rows($res) > 0) { |
||
6117 | $row = Database::fetch_array($res); |
||
6118 | $force = $row['seriousgame_mode']; |
||
6119 | if ($force == 1) { |
||
6120 | $force = 0; |
||
6121 | } elseif ($force == 0) { |
||
6122 | $force = 1; |
||
6123 | } |
||
6124 | $sql = "UPDATE $lp_table SET seriousgame_mode = $force |
||
6125 | WHERE iid = ".$this->get_id(); |
||
6126 | Database::query($sql); |
||
6127 | $this->seriousgame_mode = $force; |
||
6128 | |||
6129 | return $force; |
||
6130 | } |
||
6131 | |||
6132 | return -1; |
||
6133 | } |
||
6134 | |||
6135 | /** |
||
6136 | * Updates the "scorm_debug" value that shows or hide the debug window. |
||
6137 | * |
||
6138 | * @return bool True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case) |
||
6139 | */ |
||
6140 | public function update_scorm_debug() |
||
6141 | { |
||
6142 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
6143 | $sql = "SELECT * FROM $lp_table |
||
6144 | WHERE iid = ".$this->get_id(); |
||
6145 | $res = Database::query($sql); |
||
6146 | if (Database::num_rows($res) > 0) { |
||
6147 | $row = Database::fetch_array($res); |
||
6148 | $force = $row['debug']; |
||
6149 | if ($force == 1) { |
||
6150 | $force = 0; |
||
6151 | } elseif ($force == 0) { |
||
6152 | $force = 1; |
||
6153 | } |
||
6154 | $sql = "UPDATE $lp_table SET debug = $force |
||
6155 | WHERE iid = ".$this->get_id(); |
||
6156 | Database::query($sql); |
||
6157 | $this->scorm_debug = $force; |
||
6158 | |||
6159 | return $force; |
||
6160 | } |
||
6161 | |||
6162 | return -1; |
||
6163 | } |
||
6164 | |||
6165 | /** |
||
6166 | * Function that makes a call to the function sort_tree_array and create_tree_array. |
||
6167 | * |
||
6168 | * @author Kevin Van Den Haute |
||
6169 | * |
||
6170 | * @param array |
||
6171 | */ |
||
6172 | public function tree_array($array) |
||
6173 | { |
||
6174 | $array = $this->sort_tree_array($array); |
||
6175 | $this->create_tree_array($array); |
||
6176 | } |
||
6177 | |||
6178 | /** |
||
6179 | * Creates an array with the elements of the learning path tree in it. |
||
6180 | * |
||
6181 | * @author Kevin Van Den Haute |
||
6182 | * |
||
6183 | * @param array $array |
||
6184 | * @param int $parent |
||
6185 | * @param int $depth |
||
6186 | * @param array $tmp |
||
6187 | */ |
||
6188 | public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = []) |
||
6189 | { |
||
6190 | if (is_array($array)) { |
||
6191 | for ($i = 0; $i < count($array); $i++) { |
||
0 ignored issues
–
show
|
|||
6192 | if ($array[$i]['parent_item_id'] == $parent) { |
||
6193 | if (!in_array($array[$i]['parent_item_id'], $tmp)) { |
||
6194 | $tmp[] = $array[$i]['parent_item_id']; |
||
6195 | $depth++; |
||
6196 | } |
||
6197 | $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']); |
||
6198 | $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null; |
||
6199 | $path = isset($array[$i]['path']) ? $array[$i]['path'] : null; |
||
6200 | |||
6201 | $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null; |
||
6202 | $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null; |
||
6203 | $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : ''; |
||
6204 | $this->arrMenu[] = [ |
||
6205 | 'id' => $array[$i]['id'], |
||
6206 | 'ref' => $ref, |
||
6207 | 'item_type' => $array[$i]['item_type'], |
||
6208 | 'title' => $array[$i]['title'], |
||
6209 | 'title_raw' => $array[$i]['title_raw'], |
||
6210 | 'path' => $path, |
||
6211 | 'description' => $array[$i]['description'], |
||
6212 | 'parent_item_id' => $array[$i]['parent_item_id'], |
||
6213 | 'previous_item_id' => $array[$i]['previous_item_id'], |
||
6214 | 'next_item_id' => $array[$i]['next_item_id'], |
||
6215 | 'min_score' => $array[$i]['min_score'], |
||
6216 | 'max_score' => $array[$i]['max_score'], |
||
6217 | 'mastery_score' => $array[$i]['mastery_score'], |
||
6218 | 'display_order' => $array[$i]['display_order'], |
||
6219 | 'prerequisite' => $preq, |
||
6220 | 'depth' => $depth, |
||
6221 | 'audio' => $audio, |
||
6222 | 'prerequisite_min_score' => $prerequisiteMinScore, |
||
6223 | 'prerequisite_max_score' => $prerequisiteMaxScore, |
||
6224 | ]; |
||
6225 | $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp); |
||
6226 | } |
||
6227 | } |
||
6228 | } |
||
6229 | } |
||
6230 | |||
6231 | /** |
||
6232 | * Sorts a multi dimensional array by parent id and display order. |
||
6233 | * |
||
6234 | * @author Kevin Van Den Haute |
||
6235 | * |
||
6236 | * @param array $array (array with al the learning path items in it) |
||
6237 | * |
||
6238 | * @return array |
||
6239 | */ |
||
6240 | public function sort_tree_array($array) |
||
6241 | { |
||
6242 | foreach ($array as $key => $row) { |
||
6243 | $parent[$key] = $row['parent_item_id']; |
||
6244 | $position[$key] = $row['display_order']; |
||
6245 | } |
||
6246 | |||
6247 | if (count($array) > 0) { |
||
6248 | array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array); |
||
6249 | } |
||
6250 | |||
6251 | return $array; |
||
6252 | } |
||
6253 | |||
6254 | /** |
||
6255 | * Function that creates a html list of learning path items so that we can add audio files to them. |
||
6256 | * |
||
6257 | * @author Kevin Van Den Haute |
||
6258 | * |
||
6259 | * @return string |
||
6260 | */ |
||
6261 | public function overview() |
||
6262 | { |
||
6263 | $return = ''; |
||
6264 | $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null; |
||
6265 | |||
6266 | // we need to start a form when we want to update all the mp3 files |
||
6267 | if ($update_audio == 'true') { |
||
6268 | $return .= '<form action="'.api_get_self().'?'.api_get_cidreq().'&updateaudio='.Security::remove_XSS($_GET['updateaudio']).'&action='.Security::remove_XSS($_GET['action']).'&lp_id='.$_SESSION['oLP']->lp_id.'" method="post" enctype="multipart/form-data" name="updatemp3" id="updatemp3">'; |
||
6269 | } |
||
6270 | $return .= '<div id="message"></div>'; |
||
6271 | if (count($this->items) == 0) { |
||
6272 | $return .= Display::return_message(get_lang('YouShouldAddItemsBeforeAttachAudio'), 'normal'); |
||
6273 | } else { |
||
6274 | $return_audio = '<table class="table table-hover table-striped data_table">'; |
||
6275 | $return_audio .= '<tr>'; |
||
6276 | $return_audio .= '<th width="40%">'.get_lang('Title').'</th>'; |
||
6277 | $return_audio .= '<th>'.get_lang('Audio').'</th>'; |
||
6278 | $return_audio .= '</tr>'; |
||
6279 | |||
6280 | if ($update_audio != 'true') { |
||
6281 | $return .= '<div class="col-md-12">'; |
||
6282 | $return .= self::return_new_tree($update_audio); |
||
6283 | $return .= '</div>'; |
||
6284 | $return .= Display::div( |
||
6285 | Display::url(get_lang('Save'), '#', ['id' => 'listSubmit', 'class' => 'btn btn-primary']), |
||
6286 | ['style' => 'float:left; margin-top:15px;width:100%'] |
||
6287 | ); |
||
6288 | } else { |
||
6289 | $return_audio .= self::return_new_tree($update_audio); |
||
6290 | $return .= $return_audio.'</table>'; |
||
6291 | } |
||
6292 | |||
6293 | // We need to close the form when we are updating the mp3 files. |
||
6294 | if ($update_audio == 'true') { |
||
6295 | $return .= '<div class="footer-audio">'; |
||
6296 | $return .= Display::button( |
||
6297 | 'save_audio', |
||
6298 | '<em class="fa fa-file-audio-o"></em> '.get_lang('SaveAudioAndOrganization'), |
||
6299 | ['class' => 'btn btn-primary', 'type' => 'submit'] |
||
6300 | ); |
||
6301 | $return .= '</div>'; |
||
6302 | } |
||
6303 | } |
||
6304 | |||
6305 | // We need to close the form when we are updating the mp3 files. |
||
6306 | if ($update_audio == 'true' && isset($this->arrMenu) && count($this->arrMenu) != 0) { |
||
6307 | $return .= '</form>'; |
||
6308 | } |
||
6309 | |||
6310 | return $return; |
||
6311 | } |
||
6312 | |||
6313 | /** |
||
6314 | * @param string $update_audio |
||
6315 | * |
||
6316 | * @return array |
||
6317 | */ |
||
6318 | public function processBuildMenuElements($update_audio = 'false') |
||
6319 | { |
||
6320 | $is_allowed_to_edit = api_is_allowed_to_edit(null, true); |
||
6321 | $arrLP = $this->getItemsForForm(); |
||
6322 | |||
6323 | $this->tree_array($arrLP); |
||
6324 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
6325 | unset($this->arrMenu); |
||
6326 | $default_data = null; |
||
6327 | $default_content = null; |
||
6328 | $elements = []; |
||
6329 | $return_audio = null; |
||
6330 | $iconPath = api_get_path(SYS_CODE_PATH).'img/'; |
||
6331 | $mainUrl = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq(); |
||
6332 | $countItems = count($arrLP); |
||
6333 | |||
6334 | $upIcon = Display::return_icon( |
||
6335 | 'up.png', |
||
6336 | get_lang('Up'), |
||
6337 | [], |
||
6338 | ICON_SIZE_TINY |
||
6339 | ); |
||
6340 | |||
6341 | $disableUpIcon = Display::return_icon( |
||
6342 | 'up_na.png', |
||
6343 | get_lang('Up'), |
||
6344 | [], |
||
6345 | ICON_SIZE_TINY |
||
6346 | ); |
||
6347 | |||
6348 | $downIcon = Display::return_icon( |
||
6349 | 'down.png', |
||
6350 | get_lang('Down'), |
||
6351 | [], |
||
6352 | ICON_SIZE_TINY |
||
6353 | ); |
||
6354 | |||
6355 | $disableDownIcon = Display::return_icon( |
||
6356 | 'down_na.png', |
||
6357 | get_lang('Down'), |
||
6358 | [], |
||
6359 | ICON_SIZE_TINY |
||
6360 | ); |
||
6361 | |||
6362 | $show = api_get_configuration_value('show_full_lp_item_title_in_edition'); |
||
6363 | |||
6364 | $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true'; |
||
6365 | $plugin = null; |
||
6366 | if ($pluginCalendar) { |
||
6367 | $plugin = LearningCalendarPlugin::create(); |
||
6368 | } |
||
6369 | |||
6370 | for ($i = 0; $i < $countItems; $i++) { |
||
6371 | $parent_id = $arrLP[$i]['parent_item_id']; |
||
6372 | $title = $arrLP[$i]['title']; |
||
6373 | $title_cut = $arrLP[$i]['title_raw']; |
||
6374 | if ($show === false) { |
||
6375 | $title_cut = cut($arrLP[$i]['title'], self::MAX_LP_ITEM_TITLE_LENGTH); |
||
6376 | } |
||
6377 | // Link for the documents |
||
6378 | if ($arrLP[$i]['item_type'] === 'document' || $arrLP[$i]['item_type'] == TOOL_READOUT_TEXT) { |
||
6379 | $url = $mainUrl.'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
6380 | $title_cut = Display::url( |
||
6381 | $title_cut, |
||
6382 | $url, |
||
6383 | [ |
||
6384 | 'class' => 'ajax moved', |
||
6385 | 'data-title' => $title, |
||
6386 | 'title' => $title, |
||
6387 | ] |
||
6388 | ); |
||
6389 | } |
||
6390 | |||
6391 | // Detect if type is FINAL_ITEM to set path_id to SESSION |
||
6392 | if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) { |
||
6393 | Session::write('pathItem', $arrLP[$i]['path']); |
||
6394 | } |
||
6395 | |||
6396 | $oddClass = 'row_even'; |
||
6397 | if (($i % 2) == 0) { |
||
6398 | $oddClass = 'row_odd'; |
||
6399 | } |
||
6400 | $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'].'" class="'.$oddClass.'">'; |
||
6401 | $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']); |
||
6402 | |||
6403 | if (file_exists($iconPath.'lp_'.$icon_name.'.png')) { |
||
6404 | $icon = Display::return_icon('lp_'.$icon_name.'.png'); |
||
6405 | } else { |
||
6406 | if (file_exists($iconPath.'lp_'.$icon_name.'.gif')) { |
||
6407 | $icon = Display::return_icon('lp_'.$icon_name.'.gif'); |
||
6408 | } else { |
||
6409 | if ($arrLP[$i]['item_type'] === TOOL_LP_FINAL_ITEM) { |
||
6410 | $icon = Display::return_icon('flag_checkered.png'); |
||
6411 | } elseif (TOOL_XAPI === $arrLP[$i]['item_type']) { |
||
6412 | $icon = Display::return_icon('import_scorm.png'); |
||
6413 | } elseif (TOOL_H5P) { |
||
6414 | $icon = Display::return_icon('plugin_h5p_import.png'); |
||
6415 | } elseif (TOOL_SURVEY === $arrLP[$i]['item_type']) { |
||
6416 | $icon = Display::return_icon('survey.gif'); |
||
6417 | } else { |
||
6418 | $icon = Display::return_icon('folder_document.gif'); |
||
6419 | } |
||
6420 | } |
||
6421 | } |
||
6422 | |||
6423 | // The audio column. |
||
6424 | $return_audio .= '<td align="left" style="padding-left:10px;">'; |
||
6425 | $audio = ''; |
||
6426 | if (!$update_audio || $update_audio != 'true') { |
||
6427 | if (empty($arrLP[$i]['audio'])) { |
||
6428 | $audio .= ''; |
||
6429 | } |
||
6430 | } else { |
||
6431 | $types = self::getChapterTypes(); |
||
6432 | if (!in_array($arrLP[$i]['item_type'], $types)) { |
||
6433 | $audio .= '<input type="file" name="mp3file'.$arrLP[$i]['id'].'" id="mp3file" />'; |
||
6434 | if (!empty($arrLP[$i]['audio'])) { |
||
6435 | $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br /> |
||
6436 | <input type="checkbox" name="removemp3'.$arrLP[$i]['id'].'" id="checkbox'.$arrLP[$i]['id'].'" />'.get_lang('RemoveAudio'); |
||
6437 | } |
||
6438 | } |
||
6439 | } |
||
6440 | |||
6441 | $return_audio .= Display::span($icon.' '.$title). |
||
6442 | Display::tag( |
||
6443 | 'td', |
||
6444 | $audio, |
||
6445 | ['style' => ''] |
||
6446 | ); |
||
6447 | $return_audio .= '</td>'; |
||
6448 | $move_icon = ''; |
||
6449 | $move_item_icon = ''; |
||
6450 | $edit_icon = ''; |
||
6451 | $delete_icon = ''; |
||
6452 | $audio_icon = ''; |
||
6453 | $prerequisities_icon = ''; |
||
6454 | $forumIcon = ''; |
||
6455 | $previewIcon = ''; |
||
6456 | $pluginCalendarIcon = ''; |
||
6457 | $orderIcons = ''; |
||
6458 | $pluginUrl = api_get_path(WEB_PLUGIN_PATH).'learning_calendar/start.php?'; |
||
6459 | |||
6460 | if ($is_allowed_to_edit) { |
||
6461 | if (!$update_audio || $update_audio != 'true') { |
||
6462 | if ($arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) { |
||
6463 | $move_icon .= '<a class="moved" href="#">'; |
||
6464 | $move_icon .= Display::return_icon( |
||
6465 | 'move_everywhere.png', |
||
6466 | get_lang('Move'), |
||
6467 | [], |
||
6468 | ICON_SIZE_TINY |
||
6469 | ); |
||
6470 | $move_icon .= '</a>'; |
||
6471 | } |
||
6472 | } |
||
6473 | |||
6474 | // No edit for this item types |
||
6475 | if (!in_array($arrLP[$i]['item_type'], ['sco', 'asset', 'final_item'])) { |
||
6476 | if ($arrLP[$i]['item_type'] != 'dir') { |
||
6477 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">'; |
||
6478 | $edit_icon .= Display::return_icon( |
||
6479 | 'edit.png', |
||
6480 | get_lang('LearnpathEditModule'), |
||
6481 | [], |
||
6482 | ICON_SIZE_TINY |
||
6483 | ); |
||
6484 | $edit_icon .= '</a>'; |
||
6485 | |||
6486 | if (!in_array($arrLP[$i]['item_type'], ['forum', 'thread'])) { |
||
6487 | $forumThread = null; |
||
6488 | if (isset($this->items[$arrLP[$i]['id']])) { |
||
6489 | $forumThread = $this->items[$arrLP[$i]['id']]->getForumThread( |
||
6490 | $this->course_int_id, |
||
6491 | $this->lp_session_id |
||
6492 | ); |
||
6493 | } |
||
6494 | if ($forumThread) { |
||
6495 | $forumIconUrl = $mainUrl.'&'.http_build_query([ |
||
6496 | 'action' => 'dissociate_forum', |
||
6497 | 'id' => $arrLP[$i]['id'], |
||
6498 | 'lp_id' => $this->lp_id, |
||
6499 | ]); |
||
6500 | $forumIcon = Display::url( |
||
6501 | Display::return_icon( |
||
6502 | 'forum.png', |
||
6503 | get_lang('DissociateForumToLPItem'), |
||
6504 | [], |
||
6505 | ICON_SIZE_TINY |
||
6506 | ), |
||
6507 | $forumIconUrl, |
||
6508 | ['class' => 'btn btn-default lp-btn-dissociate-forum'] |
||
6509 | ); |
||
6510 | } else { |
||
6511 | $forumIconUrl = $mainUrl.'&'.http_build_query([ |
||
6512 | 'action' => 'create_forum', |
||
6513 | 'id' => $arrLP[$i]['id'], |
||
6514 | 'lp_id' => $this->lp_id, |
||
6515 | ]); |
||
6516 | $forumIcon = Display::url( |
||
6517 | Display::return_icon( |
||
6518 | 'forum.png', |
||
6519 | get_lang('AssociateForumToLPItem'), |
||
6520 | [], |
||
6521 | ICON_SIZE_TINY |
||
6522 | ), |
||
6523 | $forumIconUrl, |
||
6524 | ['class' => 'btn btn-default lp-btn-associate-forum'] |
||
6525 | ); |
||
6526 | } |
||
6527 | } |
||
6528 | } else { |
||
6529 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">'; |
||
6530 | $edit_icon .= Display::return_icon( |
||
6531 | 'edit.png', |
||
6532 | get_lang('LearnpathEditModule'), |
||
6533 | [], |
||
6534 | ICON_SIZE_TINY |
||
6535 | ); |
||
6536 | $edit_icon .= '</a>'; |
||
6537 | } |
||
6538 | } else { |
||
6539 | if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) { |
||
6540 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" class="btn btn-default">'; |
||
6541 | $edit_icon .= Display::return_icon( |
||
6542 | 'edit.png', |
||
6543 | get_lang('Edit'), |
||
6544 | [], |
||
6545 | ICON_SIZE_TINY |
||
6546 | ); |
||
6547 | $edit_icon .= '</a>'; |
||
6548 | } |
||
6549 | } |
||
6550 | |||
6551 | if ($pluginCalendar) { |
||
6552 | $pluginLink = $pluginUrl. |
||
6553 | '&action=toggle_visibility&lp_item_id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
6554 | $iconCalendar = Display::return_icon('agenda_na.png', get_lang('OneDay'), [], ICON_SIZE_TINY); |
||
6555 | $itemInfo = $plugin->getItemVisibility($arrLP[$i]['id']); |
||
6556 | if ($itemInfo && $itemInfo['value'] == 1) { |
||
6557 | $iconCalendar = Display::return_icon('agenda.png', get_lang('OneDay'), [], ICON_SIZE_TINY); |
||
6558 | } |
||
6559 | $pluginCalendarIcon = Display::url( |
||
6560 | $iconCalendar, |
||
6561 | $pluginLink, |
||
6562 | ['class' => 'btn btn-default'] |
||
6563 | ); |
||
6564 | } |
||
6565 | |||
6566 | if ($arrLP[$i]['item_type'] != 'final_item') { |
||
6567 | $orderIcons = Display::url( |
||
6568 | $upIcon, |
||
6569 | 'javascript:void(0)', |
||
6570 | ['class' => 'btn btn-default order_items', 'data-dir' => 'up', 'data-id' => $arrLP[$i]['id']] |
||
6571 | ); |
||
6572 | $orderIcons .= Display::url( |
||
6573 | $downIcon, |
||
6574 | 'javascript:void(0)', |
||
6575 | ['class' => 'btn btn-default order_items', 'data-dir' => 'down', 'data-id' => $arrLP[$i]['id']] |
||
6576 | ); |
||
6577 | } |
||
6578 | |||
6579 | $delete_icon .= ' <a |
||
6580 | href="'.$mainUrl.'&action=delete_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" |
||
6581 | onclick="return confirmation(\''.addslashes($title).'\');" |
||
6582 | class="btn btn-default">'; |
||
6583 | $delete_icon .= Display::return_icon( |
||
6584 | 'delete.png', |
||
6585 | get_lang('LearnpathDeleteModule'), |
||
6586 | [], |
||
6587 | ICON_SIZE_TINY |
||
6588 | ); |
||
6589 | $delete_icon .= '</a>'; |
||
6590 | |||
6591 | $url = $mainUrl.'&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
6592 | $previewImage = Display::return_icon( |
||
6593 | 'preview_view.png', |
||
6594 | get_lang('Preview'), |
||
6595 | [], |
||
6596 | ICON_SIZE_TINY |
||
6597 | ); |
||
6598 | |||
6599 | switch ($arrLP[$i]['item_type']) { |
||
6600 | case TOOL_DOCUMENT: |
||
6601 | case TOOL_LP_FINAL_ITEM: |
||
6602 | case TOOL_READOUT_TEXT: |
||
6603 | $urlPreviewLink = $mainUrl.'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
6604 | $previewIcon = Display::url( |
||
6605 | $previewImage, |
||
6606 | $urlPreviewLink, |
||
6607 | [ |
||
6608 | 'target' => '_blank', |
||
6609 | 'class' => 'btn btn-default', |
||
6610 | 'data-title' => $arrLP[$i]['title'], |
||
6611 | 'title' => $arrLP[$i]['title'], |
||
6612 | ] |
||
6613 | ); |
||
6614 | break; |
||
6615 | case TOOL_THREAD: |
||
6616 | case TOOL_FORUM: |
||
6617 | case TOOL_QUIZ: |
||
6618 | case TOOL_STUDENTPUBLICATION: |
||
6619 | case TOOL_LP_FINAL_ITEM: |
||
6620 | case TOOL_LINK: |
||
6621 | $class = 'btn btn-default'; |
||
6622 | $target = '_blank'; |
||
6623 | $link = self::rl_get_resource_link_for_learnpath( |
||
6624 | $this->course_int_id, |
||
6625 | $this->lp_id, |
||
6626 | $arrLP[$i]['id'], |
||
6627 | 0 |
||
6628 | ); |
||
6629 | $previewIcon = Display::url( |
||
6630 | $previewImage, |
||
6631 | $link, |
||
6632 | [ |
||
6633 | 'class' => $class, |
||
6634 | 'data-title' => $arrLP[$i]['title'], |
||
6635 | 'title' => $arrLP[$i]['title'], |
||
6636 | 'target' => $target, |
||
6637 | ] |
||
6638 | ); |
||
6639 | break; |
||
6640 | default: |
||
6641 | $previewIcon = Display::url( |
||
6642 | $previewImage, |
||
6643 | $url.'&action=view_item', |
||
6644 | ['class' => 'btn btn-default', 'target' => '_blank'] |
||
6645 | ); |
||
6646 | break; |
||
6647 | } |
||
6648 | |||
6649 | if ($arrLP[$i]['item_type'] != 'dir') { |
||
6650 | $prerequisities_icon = Display::url( |
||
6651 | Display::return_icon( |
||
6652 | 'accept.png', |
||
6653 | get_lang('LearnpathPrerequisites'), |
||
6654 | [], |
||
6655 | ICON_SIZE_TINY |
||
6656 | ), |
||
6657 | $url.'&action=edit_item_prereq', |
||
6658 | ['class' => 'btn btn-default'] |
||
6659 | ); |
||
6660 | if ($arrLP[$i]['item_type'] != 'final_item') { |
||
6661 | $move_item_icon = Display::url( |
||
6662 | Display::return_icon( |
||
6663 | 'move.png', |
||
6664 | get_lang('Move'), |
||
6665 | [], |
||
6666 | ICON_SIZE_TINY |
||
6667 | ), |
||
6668 | $url.'&action=move_item', |
||
6669 | ['class' => 'btn btn-default'] |
||
6670 | ); |
||
6671 | } |
||
6672 | $audio_icon = Display::url( |
||
6673 | Display::return_icon( |
||
6674 | 'audio.png', |
||
6675 | get_lang('UplUpload'), |
||
6676 | [], |
||
6677 | ICON_SIZE_TINY |
||
6678 | ), |
||
6679 | $url.'&action=add_audio', |
||
6680 | ['class' => 'btn btn-default'] |
||
6681 | ); |
||
6682 | } |
||
6683 | } |
||
6684 | if ($update_audio != 'true') { |
||
6685 | $row = $move_icon.' '.$icon. |
||
6686 | Display::span($title_cut). |
||
6687 | Display::tag( |
||
6688 | 'div', |
||
6689 | "<div class=\"btn-group btn-group-xs\"> |
||
6690 | $previewIcon |
||
6691 | $audio |
||
6692 | $edit_icon |
||
6693 | $pluginCalendarIcon |
||
6694 | $forumIcon |
||
6695 | $prerequisities_icon |
||
6696 | $move_item_icon |
||
6697 | $audio_icon |
||
6698 | $orderIcons |
||
6699 | $delete_icon |
||
6700 | </div>", |
||
6701 | ['class' => 'btn-toolbar button_actions'] |
||
6702 | ); |
||
6703 | } else { |
||
6704 | $row = |
||
6705 | Display::span($title.$icon). |
||
6706 | Display::span($audio, ['class' => 'button_actions']); |
||
6707 | } |
||
6708 | |||
6709 | $default_data[$arrLP[$i]['id']] = $row; |
||
6710 | $default_content[$arrLP[$i]['id']] = $arrLP[$i]; |
||
6711 | |||
6712 | if (empty($parent_id)) { |
||
6713 | $elements[$arrLP[$i]['id']]['data'] = $row; |
||
6714 | $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type']; |
||
6715 | } else { |
||
6716 | $parent_arrays = []; |
||
6717 | if ($arrLP[$i]['depth'] > 1) { |
||
6718 | // Getting list of parents |
||
6719 | for ($j = 0; $j < $arrLP[$i]['depth']; $j++) { |
||
6720 | foreach ($arrLP as $item) { |
||
6721 | if ($item['id'] == $parent_id) { |
||
6722 | if ($item['parent_item_id'] == 0) { |
||
6723 | $parent_id = $item['id']; |
||
6724 | break; |
||
6725 | } else { |
||
6726 | $parent_id = $item['parent_item_id']; |
||
6727 | if (empty($parent_arrays)) { |
||
6728 | $parent_arrays[] = intval($item['id']); |
||
6729 | } |
||
6730 | $parent_arrays[] = $parent_id; |
||
6731 | break; |
||
6732 | } |
||
6733 | } |
||
6734 | } |
||
6735 | } |
||
6736 | } |
||
6737 | |||
6738 | if (!empty($parent_arrays)) { |
||
6739 | $parent_arrays = array_reverse($parent_arrays); |
||
6740 | $val = '$elements'; |
||
6741 | $x = 0; |
||
6742 | foreach ($parent_arrays as $item) { |
||
6743 | if ($x != count($parent_arrays) - 1) { |
||
6744 | $val .= '["'.$item.'"]["children"]'; |
||
6745 | } else { |
||
6746 | $val .= '["'.$item.'"]["children"]'; |
||
6747 | } |
||
6748 | $x++; |
||
6749 | } |
||
6750 | $val .= ""; |
||
6751 | $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; "; |
||
6752 | eval($code_str); |
||
6753 | } else { |
||
6754 | $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row; |
||
6755 | $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type']; |
||
6756 | } |
||
6757 | } |
||
6758 | } |
||
6759 | |||
6760 | return [ |
||
6761 | 'elements' => $elements, |
||
6762 | 'default_data' => $default_data, |
||
6763 | 'default_content' => $default_content, |
||
6764 | 'return_audio' => $return_audio, |
||
6765 | ]; |
||
6766 | } |
||
6767 | |||
6768 | /** |
||
6769 | * @param string $updateAudio true/false strings |
||
6770 | * |
||
6771 | * @return string |
||
6772 | */ |
||
6773 | public function returnLpItemList($updateAudio) |
||
6774 | { |
||
6775 | $result = $this->processBuildMenuElements($updateAudio); |
||
6776 | |||
6777 | $html = self::print_recursive( |
||
6778 | $result['elements'], |
||
6779 | $result['default_data'], |
||
6780 | $result['default_content'] |
||
6781 | ); |
||
6782 | |||
6783 | if (!empty($html)) { |
||
6784 | $html .= Display::return_message(get_lang('DragAndDropAnElementHere')); |
||
6785 | } |
||
6786 | |||
6787 | return $html; |
||
6788 | } |
||
6789 | |||
6790 | /** |
||
6791 | * @param string $update_audio |
||
6792 | * @param bool $drop_element_here |
||
6793 | * |
||
6794 | * @return string |
||
6795 | */ |
||
6796 | public function return_new_tree($update_audio = 'false', $drop_element_here = false) |
||
6797 | { |
||
6798 | $result = $this->processBuildMenuElements($update_audio); |
||
6799 | |||
6800 | $list = '<ul id="lp_item_list">'; |
||
6801 | $tree = $this->print_recursive( |
||
6802 | $result['elements'], |
||
6803 | $result['default_data'], |
||
6804 | $result['default_content'] |
||
6805 | ); |
||
6806 | |||
6807 | if (!empty($tree)) { |
||
6808 | $list .= $tree; |
||
6809 | } else { |
||
6810 | if ($drop_element_here) { |
||
6811 | $list .= Display::return_message(get_lang('DragAndDropAnElementHere')); |
||
6812 | } |
||
6813 | } |
||
6814 | $list .= '</ul>'; |
||
6815 | |||
6816 | $return = Display::panelCollapse( |
||
6817 | $this->getNameNoTags(), |
||
6818 | $list, |
||
6819 | 'scorm-list', |
||
6820 | null, |
||
6821 | 'scorm-list-accordion', |
||
6822 | 'scorm-list-collapse' |
||
6823 | ); |
||
6824 | |||
6825 | if ($update_audio === 'true') { |
||
6826 | $return = $result['return_audio']; |
||
6827 | } |
||
6828 | |||
6829 | return $return; |
||
6830 | } |
||
6831 | |||
6832 | /** |
||
6833 | * @param array $elements |
||
6834 | * @param array $default_data |
||
6835 | * @param array $default_content |
||
6836 | * |
||
6837 | * @return string |
||
6838 | */ |
||
6839 | public function print_recursive($elements, $default_data, $default_content) |
||
6840 | { |
||
6841 | $return = ''; |
||
6842 | foreach ($elements as $key => $item) { |
||
6843 | if (TOOL_LP_FINAL_ITEM === $item['type']) { |
||
6844 | $key = 'final_item'; |
||
6845 | } |
||
6846 | if (isset($item['load_data']) || empty($item['data'])) { |
||
6847 | $item['data'] = $default_data[$item['load_data']]; |
||
6848 | $item['type'] = $default_content[$item['load_data']]['item_type']; |
||
6849 | } |
||
6850 | $sub_list = ''; |
||
6851 | if (isset($item['type']) && $item['type'] === 'dir') { |
||
6852 | // empty value |
||
6853 | $sub_list = Display::tag('li', '', ['class' => 'sub_item empty']); |
||
6854 | } |
||
6855 | if (empty($item['children'])) { |
||
6856 | $sub_list = Display::tag('ul', $sub_list, ['id' => 'UL_'.$key, 'class' => 'record li_container']); |
||
6857 | $active = null; |
||
6858 | if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) { |
||
6859 | $active = 'active'; |
||
6860 | } |
||
6861 | $return .= Display::tag( |
||
6862 | 'li', |
||
6863 | Display::div($item['data'], ['class' => "item_data $active"]).$sub_list, |
||
6864 | ['id' => $key, 'class' => 'record li_container'] |
||
6865 | ); |
||
6866 | } else { |
||
6867 | // Sections |
||
6868 | $data = ''; |
||
6869 | if (isset($item['children'])) { |
||
6870 | $data = self::print_recursive($item['children'], $default_data, $default_content); |
||
6871 | } |
||
6872 | $sub_list = Display::tag('ul', $sub_list.$data, ['id' => 'UL_'.$key, 'class' => 'record li_container']); |
||
6873 | $return .= Display::tag( |
||
6874 | 'li', |
||
6875 | Display::div($item['data'], ['class' => 'item_data']).$sub_list, |
||
6876 | ['id' => $key, 'class' => 'record li_container'] |
||
6877 | ); |
||
6878 | } |
||
6879 | } |
||
6880 | |||
6881 | return $return; |
||
6882 | } |
||
6883 | |||
6884 | /** |
||
6885 | * This function builds the action menu. |
||
6886 | * |
||
6887 | * @param bool $returnString Optional |
||
6888 | * @param bool $showRequirementButtons Optional. Allow show the requirements button |
||
6889 | * @param bool $isConfigPage Optional. If is the config page, show the edit button |
||
6890 | * @param bool $allowExpand Optional. Allow show the expand/contract button |
||
6891 | * @param string $action |
||
6892 | * @param array $extraField |
||
6893 | * |
||
6894 | * @return string |
||
6895 | */ |
||
6896 | public function build_action_menu( |
||
6897 | $returnString = false, |
||
6898 | $showRequirementButtons = true, |
||
6899 | $isConfigPage = false, |
||
6900 | $allowExpand = true, |
||
6901 | $action = '', |
||
6902 | $extraField = [] |
||
6903 | ) { |
||
6904 | $actionsRight = ''; |
||
6905 | $lpId = $this->lp_id; |
||
6906 | if (!isset($extraField['backTo']) && empty($extraField['backTo'])) { |
||
6907 | $back = Display::url( |
||
6908 | Display::return_icon( |
||
6909 | 'back.png', |
||
6910 | get_lang('ReturnToLearningPaths'), |
||
6911 | '', |
||
6912 | ICON_SIZE_MEDIUM |
||
6913 | ), |
||
6914 | 'lp_controller.php?'.api_get_cidreq() |
||
6915 | ); |
||
6916 | } else { |
||
6917 | $back = Display::url( |
||
6918 | Display::return_icon( |
||
6919 | 'back.png', |
||
6920 | get_lang('Back'), |
||
6921 | '', |
||
6922 | ICON_SIZE_MEDIUM |
||
6923 | ), |
||
6924 | $extraField['backTo'] |
||
6925 | ); |
||
6926 | } |
||
6927 | |||
6928 | $actionsLeft = $back; |
||
6929 | $actionsLeft .= Display::url( |
||
6930 | Display::return_icon( |
||
6931 | 'preview_view.png', |
||
6932 | get_lang('Preview'), |
||
6933 | '', |
||
6934 | ICON_SIZE_MEDIUM |
||
6935 | ), |
||
6936 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
6937 | 'action' => 'view', |
||
6938 | 'lp_id' => $lpId, |
||
6939 | 'isStudentView' => 'true', |
||
6940 | ]) |
||
6941 | ); |
||
6942 | |||
6943 | $actionsLeft .= Display::url( |
||
6944 | Display::return_icon( |
||
6945 | 'upload_audio.png', |
||
6946 | get_lang('UpdateAllAudioFragments'), |
||
6947 | '', |
||
6948 | ICON_SIZE_MEDIUM |
||
6949 | ), |
||
6950 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
6951 | 'action' => 'admin_view', |
||
6952 | 'lp_id' => $lpId, |
||
6953 | 'updateaudio' => 'true', |
||
6954 | ]) |
||
6955 | ); |
||
6956 | |||
6957 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
6958 | $request = api_request_uri(); |
||
6959 | if (strpos($request, 'edit') === false) { |
||
6960 | $actionsLeft .= Display::url( |
||
6961 | Display::return_icon( |
||
6962 | 'settings.png', |
||
6963 | get_lang('CourseSettings'), |
||
6964 | '', |
||
6965 | ICON_SIZE_MEDIUM |
||
6966 | ), |
||
6967 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
6968 | 'action' => 'edit', |
||
6969 | 'lp_id' => $lpId, |
||
6970 | ]) |
||
6971 | ); |
||
6972 | } |
||
6973 | |||
6974 | if ((strpos($request, 'build') === false && |
||
6975 | strpos($request, 'add_item') === false) || |
||
6976 | in_array($action, ['add_audio']) |
||
6977 | ) { |
||
6978 | $actionsLeft .= Display::url( |
||
6979 | Display::return_icon( |
||
6980 | 'edit.png', |
||
6981 | get_lang('Edit'), |
||
6982 | '', |
||
6983 | ICON_SIZE_MEDIUM |
||
6984 | ), |
||
6985 | 'lp_controller.php?'.http_build_query([ |
||
6986 | 'action' => 'build', |
||
6987 | 'lp_id' => $lpId, |
||
6988 | ]).'&'.api_get_cidreq() |
||
6989 | ); |
||
6990 | } |
||
6991 | |||
6992 | if (strpos(api_get_self(), 'lp_subscribe_users.php') === false) { |
||
6993 | if ($this->subscribeUsers == 1 && |
||
6994 | $subscriptionSettings['allow_add_users_to_lp']) { |
||
6995 | $actionsLeft .= Display::url( |
||
6996 | Display::return_icon( |
||
6997 | 'user.png', |
||
6998 | get_lang('SubscribeUsersToLp'), |
||
6999 | '', |
||
7000 | ICON_SIZE_MEDIUM |
||
7001 | ), |
||
7002 | api_get_path(WEB_CODE_PATH)."lp/lp_subscribe_users.php?lp_id=".$lpId."&".api_get_cidreq() |
||
7003 | ); |
||
7004 | } |
||
7005 | } |
||
7006 | |||
7007 | if ($allowExpand) { |
||
7008 | $actionsLeft .= Display::url( |
||
7009 | Display::return_icon( |
||
7010 | 'expand.png', |
||
7011 | get_lang('Expand'), |
||
7012 | ['id' => 'expand'], |
||
7013 | ICON_SIZE_MEDIUM |
||
7014 | ). |
||
7015 | Display::return_icon( |
||
7016 | 'contract.png', |
||
7017 | get_lang('Collapse'), |
||
7018 | ['id' => 'contract', 'class' => 'hide'], |
||
7019 | ICON_SIZE_MEDIUM |
||
7020 | ), |
||
7021 | '#', |
||
7022 | ['role' => 'button', 'id' => 'hide_bar_template'] |
||
7023 | ); |
||
7024 | } |
||
7025 | |||
7026 | if ($showRequirementButtons) { |
||
7027 | $buttons = [ |
||
7028 | [ |
||
7029 | 'title' => get_lang('SetPrerequisiteForEachItem'), |
||
7030 | 'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
7031 | 'action' => 'set_previous_step_as_prerequisite', |
||
7032 | 'lp_id' => $lpId, |
||
7033 | ]), |
||
7034 | ], |
||
7035 | [ |
||
7036 | 'title' => get_lang('ClearAllPrerequisites'), |
||
7037 | 'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
7038 | 'action' => 'clear_prerequisites', |
||
7039 | 'lp_id' => $lpId, |
||
7040 | ]), |
||
7041 | ], |
||
7042 | ]; |
||
7043 | $actionsRight = Display::groupButtonWithDropDown( |
||
7044 | get_lang('PrerequisitesOptions'), |
||
7045 | $buttons, |
||
7046 | true |
||
7047 | ); |
||
7048 | } |
||
7049 | |||
7050 | if (api_is_platform_admin() && isset($extraField['authorlp'])) { |
||
7051 | $actionsLeft .= Display::url( |
||
7052 | Display::return_icon( |
||
7053 | 'add-groups.png', |
||
7054 | get_lang('Author'), |
||
7055 | '', |
||
7056 | ICON_SIZE_MEDIUM |
||
7057 | ), |
||
7058 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
7059 | 'action' => 'author_view', |
||
7060 | 'lp_id' => $lpId, |
||
7061 | ]) |
||
7062 | ); |
||
7063 | } |
||
7064 | |||
7065 | $toolbar = Display::toolbarAction( |
||
7066 | 'actions-lp-controller', |
||
7067 | [$actionsLeft, $actionsRight] |
||
7068 | ); |
||
7069 | |||
7070 | if ($returnString) { |
||
7071 | return $toolbar; |
||
7072 | } |
||
7073 | |||
7074 | echo $toolbar; |
||
7075 | } |
||
7076 | |||
7077 | /** |
||
7078 | * Creates the default learning path folder. |
||
7079 | * |
||
7080 | * @param array $course |
||
7081 | * @param int $creatorId |
||
7082 | * |
||
7083 | * @return bool |
||
7084 | */ |
||
7085 | public static function generate_learning_path_folder($course, $creatorId = 0) |
||
7086 | { |
||
7087 | // Creating learning_path folder |
||
7088 | $dir = '/learning_path'; |
||
7089 | $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document'; |
||
7090 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
7091 | |||
7092 | $folder = false; |
||
7093 | if (!is_dir($filepath.'/'.$dir)) { |
||
7094 | $folderData = create_unexisting_directory( |
||
7095 | $course, |
||
7096 | $creatorId, |
||
7097 | 0, |
||
7098 | null, |
||
7099 | 0, |
||
7100 | $filepath, |
||
7101 | $dir, |
||
7102 | get_lang('LearningPaths'), |
||
7103 | 0 |
||
7104 | ); |
||
7105 | if (!empty($folderData)) { |
||
7106 | $folder = true; |
||
7107 | } |
||
7108 | } else { |
||
7109 | $folder = true; |
||
7110 | } |
||
7111 | |||
7112 | return $folder; |
||
7113 | } |
||
7114 | |||
7115 | /** |
||
7116 | * @param array $course |
||
7117 | * @param string $lp_name |
||
7118 | * @param int $creatorId |
||
7119 | * |
||
7120 | * @return array |
||
7121 | */ |
||
7122 | public function generate_lp_folder($course, $lp_name = '', $creatorId = 0) |
||
7123 | { |
||
7124 | $filepath = ''; |
||
7125 | $dir = '/learning_path/'; |
||
7126 | |||
7127 | if (empty($lp_name)) { |
||
7128 | $lp_name = $this->name; |
||
7129 | } |
||
7130 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
7131 | $folder = self::generate_learning_path_folder($course, $creatorId); |
||
7132 | |||
7133 | // Limits title size |
||
7134 | $title = api_substr(api_replace_dangerous_char($lp_name), 0, 80); |
||
7135 | $dir = $dir.$title; |
||
7136 | |||
7137 | // Creating LP folder |
||
7138 | $documentId = null; |
||
7139 | if ($folder) { |
||
7140 | $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document'; |
||
7141 | if (!is_dir($filepath.'/'.$dir)) { |
||
7142 | $folderData = create_unexisting_directory( |
||
7143 | $course, |
||
7144 | $creatorId, |
||
7145 | 0, |
||
7146 | 0, |
||
7147 | 0, |
||
7148 | $filepath, |
||
7149 | $dir, |
||
7150 | $lp_name |
||
7151 | ); |
||
7152 | if (!empty($folderData)) { |
||
7153 | $folder = true; |
||
7154 | } |
||
7155 | |||
7156 | $documentId = $folderData['id']; |
||
7157 | } else { |
||
7158 | $folder = true; |
||
7159 | } |
||
7160 | $dir = $dir.'/'; |
||
7161 | if ($folder) { |
||
7162 | $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document'.$dir; |
||
7163 | } |
||
7164 | } |
||
7165 | |||
7166 | if (empty($documentId)) { |
||
7167 | $dir = api_remove_trailing_slash($dir); |
||
7168 | $documentId = DocumentManager::get_document_id($course, $dir, 0); |
||
7169 | } |
||
7170 | |||
7171 | $array = [ |
||
7172 | 'dir' => $dir, |
||
7173 | 'filepath' => $filepath, |
||
7174 | 'folder' => $folder, |
||
7175 | 'id' => $documentId, |
||
7176 | ]; |
||
7177 | |||
7178 | return $array; |
||
7179 | } |
||
7180 | |||
7181 | /** |
||
7182 | * Create a new document //still needs some finetuning. |
||
7183 | * |
||
7184 | * @param array $courseInfo |
||
7185 | * @param string $content |
||
7186 | * @param string $title |
||
7187 | * @param string $extension |
||
7188 | * @param int $parentId |
||
7189 | * @param int $creatorId creator id |
||
7190 | * |
||
7191 | * @return int |
||
7192 | */ |
||
7193 | public function create_document( |
||
7194 | $courseInfo, |
||
7195 | $content = '', |
||
7196 | $title = '', |
||
7197 | $extension = 'html', |
||
7198 | $parentId = 0, |
||
7199 | $creatorId = 0 |
||
7200 | ) { |
||
7201 | if (!empty($courseInfo)) { |
||
7202 | $course_id = $courseInfo['real_id']; |
||
7203 | } else { |
||
7204 | $course_id = api_get_course_int_id(); |
||
7205 | } |
||
7206 | |||
7207 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
7208 | $sessionId = api_get_session_id(); |
||
7209 | |||
7210 | // Generates folder |
||
7211 | $result = $this->generate_lp_folder($courseInfo); |
||
7212 | $dir = $result['dir']; |
||
7213 | |||
7214 | if (empty($parentId) || $parentId == '/') { |
||
7215 | $postDir = isset($_POST['dir']) ? $_POST['dir'] : $dir; |
||
7216 | $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting. |
||
7217 | |||
7218 | if ($parentId === '/') { |
||
7219 | $dir = '/'; |
||
7220 | } |
||
7221 | |||
7222 | // Please, do not modify this dirname formatting. |
||
7223 | if (strstr($dir, '..')) { |
||
7224 | $dir = '/'; |
||
7225 | } |
||
7226 | |||
7227 | if (!empty($dir[0]) && $dir[0] == '.') { |
||
7228 | $dir = substr($dir, 1); |
||
7229 | } |
||
7230 | if (!empty($dir[0]) && $dir[0] != '/') { |
||
7231 | $dir = '/'.$dir; |
||
7232 | } |
||
7233 | if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') { |
||
7234 | $dir .= '/'; |
||
7235 | } |
||
7236 | } else { |
||
7237 | $parentInfo = DocumentManager::get_document_data_by_id( |
||
7238 | $parentId, |
||
7239 | $courseInfo['code'] |
||
7240 | ); |
||
7241 | if (!empty($parentInfo)) { |
||
7242 | $dir = $parentInfo['path'].'/'; |
||
7243 | } |
||
7244 | } |
||
7245 | |||
7246 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
7247 | if (!is_dir($filepath)) { |
||
7248 | $dir = '/'; |
||
7249 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
7250 | } |
||
7251 | |||
7252 | // stripslashes() before calling api_replace_dangerous_char() because $_POST['title'] |
||
7253 | // is already escaped twice when it gets here. |
||
7254 | $originalTitle = !empty($title) ? $title : $_POST['title']; |
||
7255 | if (!empty($title)) { |
||
7256 | $title = api_replace_dangerous_char(stripslashes($title)); |
||
7257 | } else { |
||
7258 | $title = api_replace_dangerous_char(stripslashes($_POST['title'])); |
||
7259 | } |
||
7260 | |||
7261 | $title = disable_dangerous_file($title); |
||
7262 | $filename = $title; |
||
7263 | $content = !empty($content) ? $content : $_POST['content_lp']; |
||
7264 | $tmp_filename = $filename; |
||
7265 | |||
7266 | $i = 0; |
||
7267 | while (file_exists($filepath.$tmp_filename.'.'.$extension)) { |
||
7268 | $tmp_filename = $filename.'_'.++$i; |
||
7269 | } |
||
7270 | |||
7271 | $filename = $tmp_filename.'.'.$extension; |
||
7272 | if ($extension == 'html') { |
||
7273 | $content = stripslashes($content); |
||
7274 | $content = str_replace( |
||
7275 | api_get_path(WEB_COURSE_PATH), |
||
7276 | api_get_path(REL_PATH).'courses/', |
||
7277 | $content |
||
7278 | ); |
||
7279 | |||
7280 | // Change the path of mp3 to absolute. |
||
7281 | // The first regexp deals with :// urls. |
||
7282 | $content = preg_replace( |
||
7283 | "|(flashvars=\"file=)([^:/]+)/|", |
||
7284 | "$1".api_get_path( |
||
7285 | REL_COURSE_PATH |
||
7286 | ).$courseInfo['path'].'/document/', |
||
7287 | $content |
||
7288 | ); |
||
7289 | // The second regexp deals with audio/ urls. |
||
7290 | $content = preg_replace( |
||
7291 | "|(flashvars=\"file=)([^/]+)/|", |
||
7292 | "$1".api_get_path( |
||
7293 | REL_COURSE_PATH |
||
7294 | ).$courseInfo['path'].'/document/$2/', |
||
7295 | $content |
||
7296 | ); |
||
7297 | // For flv player: To prevent edition problem with firefox, |
||
7298 | // we have to use a strange tip (don't blame me please). |
||
7299 | $content = str_replace( |
||
7300 | '</body>', |
||
7301 | '<style type="text/css">body{}</style></body>', |
||
7302 | $content |
||
7303 | ); |
||
7304 | } |
||
7305 | |||
7306 | if (!file_exists($filepath.$filename)) { |
||
7307 | if ($fp = @fopen($filepath.$filename, 'w')) { |
||
7308 | fputs($fp, $content); |
||
7309 | fclose($fp); |
||
7310 | |||
7311 | $file_size = filesize($filepath.$filename); |
||
7312 | $save_file_path = $dir.$filename; |
||
7313 | |||
7314 | $document_id = add_document( |
||
7315 | $courseInfo, |
||
7316 | $save_file_path, |
||
7317 | 'file', |
||
7318 | $file_size, |
||
7319 | $tmp_filename, |
||
7320 | '', |
||
7321 | 0, //readonly |
||
7322 | true, |
||
7323 | null, |
||
7324 | $sessionId, |
||
7325 | $creatorId |
||
7326 | ); |
||
7327 | |||
7328 | if ($document_id) { |
||
7329 | api_item_property_update( |
||
7330 | $courseInfo, |
||
7331 | TOOL_DOCUMENT, |
||
7332 | $document_id, |
||
7333 | 'DocumentAdded', |
||
7334 | $creatorId, |
||
7335 | null, |
||
7336 | null, |
||
7337 | null, |
||
7338 | null, |
||
7339 | $sessionId |
||
7340 | ); |
||
7341 | |||
7342 | $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : ''; |
||
7343 | $new_title = $originalTitle; |
||
7344 | |||
7345 | if ($new_comment || $new_title) { |
||
7346 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
7347 | $ct = ''; |
||
7348 | if ($new_comment) { |
||
7349 | $ct .= ", comment='".Database::escape_string($new_comment)."'"; |
||
7350 | } |
||
7351 | if ($new_title) { |
||
7352 | $ct .= ", title='".Database::escape_string($new_title)."' "; |
||
7353 | } |
||
7354 | |||
7355 | $sql = "UPDATE ".$tbl_doc." SET ".substr($ct, 1)." |
||
7356 | WHERE c_id = ".$course_id." AND id = ".$document_id; |
||
7357 | Database::query($sql); |
||
7358 | } |
||
7359 | } |
||
7360 | |||
7361 | return $document_id; |
||
7362 | } |
||
7363 | } |
||
7364 | } |
||
7365 | |||
7366 | /** |
||
7367 | * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'. |
||
7368 | * |
||
7369 | * @param array $_course array |
||
7370 | */ |
||
7371 | public function edit_document($_course) |
||
7372 | { |
||
7373 | $course_id = api_get_course_int_id(); |
||
7374 | $urlAppend = api_get_configuration_value('url_append'); |
||
7375 | // Please, do not modify this dirname formatting. |
||
7376 | $postDir = isset($_POST['dir']) ? $_POST['dir'] : ''; |
||
7377 | $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; |
||
7378 | |||
7379 | if (strstr($dir, '..')) { |
||
7380 | $dir = '/'; |
||
7381 | } |
||
7382 | |||
7383 | if (isset($dir[0]) && $dir[0] == '.') { |
||
7384 | $dir = substr($dir, 1); |
||
7385 | } |
||
7386 | |||
7387 | if (isset($dir[0]) && $dir[0] != '/') { |
||
7388 | $dir = '/'.$dir; |
||
7389 | } |
||
7390 | |||
7391 | if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') { |
||
7392 | $dir .= '/'; |
||
7393 | } |
||
7394 | |||
7395 | $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$dir; |
||
7396 | if (!is_dir($filepath)) { |
||
7397 | $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||
7398 | } |
||
7399 | |||
7400 | $table_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
7401 | |||
7402 | if (isset($_POST['path']) && !empty($_POST['path'])) { |
||
7403 | $document_id = (int) $_POST['path']; |
||
7404 | $documentInfo = DocumentManager::get_document_data_by_id($document_id, api_get_course_id(), false, null, true); |
||
7405 | if (empty($documentInfo)) { |
||
7406 | // Try with iid |
||
7407 | $table = Database::get_course_table(TABLE_DOCUMENT); |
||
7408 | $sql = "SELECT id, path FROM $table |
||
7409 | WHERE c_id = $course_id AND iid = $document_id AND path NOT LIKE '%_DELETED_%' "; |
||
7410 | $res_doc = Database::query($sql); |
||
7411 | $row = Database::fetch_array($res_doc); |
||
7412 | if ($row) { |
||
7413 | $document_id = $row['id']; |
||
7414 | $documentPath = $row['path']; |
||
7415 | } |
||
7416 | } else { |
||
7417 | $documentPath = $documentInfo['path']; |
||
7418 | } |
||
7419 | |||
7420 | $content = stripslashes($_POST['content_lp']); |
||
7421 | $file = $filepath.$documentPath; |
||
7422 | |||
7423 | if (!file_exists($file)) { |
||
7424 | return false; |
||
7425 | } |
||
7426 | |||
7427 | if ($fp = @fopen($file, 'w')) { |
||
7428 | $content = str_replace( |
||
7429 | api_get_path(WEB_COURSE_PATH), |
||
7430 | $urlAppend.api_get_path(REL_COURSE_PATH), |
||
7431 | $content |
||
7432 | ); |
||
7433 | // Change the path of mp3 to absolute. |
||
7434 | // The first regexp deals with :// urls. |
||
7435 | $content = preg_replace( |
||
7436 | "|(flashvars=\"file=)([^:/]+)/|", |
||
7437 | "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/', |
||
7438 | $content |
||
7439 | ); |
||
7440 | // The second regexp deals with audio/ urls. |
||
7441 | $content = preg_replace( |
||
7442 | "|(flashvars=\"file=)([^:/]+)/|", |
||
7443 | "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/$2/', |
||
7444 | $content |
||
7445 | ); |
||
7446 | fputs($fp, $content); |
||
7447 | fclose($fp); |
||
7448 | |||
7449 | $sql = "UPDATE $table_doc SET |
||
7450 | title='".Database::escape_string($_POST['title'])."' |
||
7451 | WHERE c_id = $course_id AND id = ".$document_id; |
||
7452 | Database::query($sql); |
||
7453 | } |
||
7454 | } |
||
7455 | } |
||
7456 | |||
7457 | /** |
||
7458 | * Displays the selected item, with a panel for manipulating the item. |
||
7459 | * |
||
7460 | * @param int $item_id |
||
7461 | * @param string $msg |
||
7462 | * @param bool $show_actions |
||
7463 | * |
||
7464 | * @return string |
||
7465 | */ |
||
7466 | public function display_item($item_id, $msg = null, $show_actions = true) |
||
7467 | { |
||
7468 | $course_id = api_get_course_int_id(); |
||
7469 | $return = ''; |
||
7470 | if (is_numeric($item_id)) { |
||
7471 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
7472 | $sql = "SELECT lp.* FROM $tbl_lp_item as lp |
||
7473 | WHERE lp.iid = ".intval($item_id); |
||
7474 | $result = Database::query($sql); |
||
7475 | while ($row = Database::fetch_array($result, 'ASSOC')) { |
||
7476 | $_SESSION['parent_item_id'] = $row['item_type'] == 'dir' ? $item_id : 0; |
||
7477 | |||
7478 | // Prevents wrong parent selection for document, see Bug#1251. |
||
7479 | if ($row['item_type'] != 'dir') { |
||
7480 | $_SESSION['parent_item_id'] = $row['parent_item_id']; |
||
7481 | } |
||
7482 | |||
7483 | if ($show_actions) { |
||
7484 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7485 | } |
||
7486 | $return .= '<div style="padding:10px;">'; |
||
7487 | |||
7488 | if ($msg != '') { |
||
7489 | $return .= $msg; |
||
7490 | } |
||
7491 | |||
7492 | $return .= '<h3>'.$row['title'].'</h3>'; |
||
7493 | |||
7494 | switch ($row['item_type']) { |
||
7495 | case TOOL_THREAD: |
||
7496 | $link = $this->rl_get_resource_link_for_learnpath( |
||
7497 | $course_id, |
||
7498 | $row['lp_id'], |
||
7499 | $item_id, |
||
7500 | 0 |
||
7501 | ); |
||
7502 | $return .= Display::url( |
||
7503 | get_lang('GoToThread'), |
||
7504 | $link, |
||
7505 | ['class' => 'btn btn-primary'] |
||
7506 | ); |
||
7507 | break; |
||
7508 | case TOOL_FORUM: |
||
7509 | $return .= Display::url( |
||
7510 | get_lang('GoToForum'), |
||
7511 | api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$row['path'], |
||
7512 | ['class' => 'btn btn-primary'] |
||
7513 | ); |
||
7514 | break; |
||
7515 | case TOOL_QUIZ: |
||
7516 | if (!empty($row['path'])) { |
||
7517 | $exercise = new Exercise(); |
||
7518 | $exercise->read($row['path']); |
||
7519 | $return .= $exercise->description.'<br />'; |
||
7520 | $return .= Display::url( |
||
7521 | get_lang('GoToExercise'), |
||
7522 | api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&exerciseId='.$exercise->id, |
||
7523 | ['class' => 'btn btn-primary'] |
||
7524 | ); |
||
7525 | } |
||
7526 | break; |
||
7527 | case TOOL_LP_FINAL_ITEM: |
||
7528 | $return .= $this->getSavedFinalItem(); |
||
7529 | break; |
||
7530 | case TOOL_DOCUMENT: |
||
7531 | case TOOL_READOUT_TEXT: |
||
7532 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
7533 | $sql_doc = "SELECT path FROM $tbl_doc |
||
7534 | WHERE c_id = $course_id AND iid = ".intval($row['path']); |
||
7535 | $result = Database::query($sql_doc); |
||
7536 | $path_file = Database::result($result, 0, 0); |
||
7537 | $path_parts = pathinfo($path_file); |
||
7538 | // TODO: Correct the following naive comparisons. |
||
7539 | if (in_array($path_parts['extension'], [ |
||
7540 | 'html', |
||
7541 | 'txt', |
||
7542 | 'png', |
||
7543 | 'jpg', |
||
7544 | 'JPG', |
||
7545 | 'jpeg', |
||
7546 | 'JPEG', |
||
7547 | 'gif', |
||
7548 | 'swf', |
||
7549 | 'pdf', |
||
7550 | 'htm', |
||
7551 | ])) { |
||
7552 | $return .= $this->display_document($row['path'], true, true); |
||
7553 | } |
||
7554 | break; |
||
7555 | case TOOL_HOTPOTATOES: |
||
7556 | $return .= $this->display_document($row['path'], false, true); |
||
7557 | break; |
||
7558 | } |
||
7559 | $return .= '</div>'; |
||
7560 | } |
||
7561 | } |
||
7562 | |||
7563 | return $return; |
||
7564 | } |
||
7565 | |||
7566 | /** |
||
7567 | * Shows the needed forms for editing a specific item. |
||
7568 | * |
||
7569 | * @param int $item_id |
||
7570 | * |
||
7571 | * @throws Exception |
||
7572 | * @throws HTML_QuickForm_Error |
||
7573 | * |
||
7574 | * @return string |
||
7575 | */ |
||
7576 | public function display_edit_item($item_id, $excludeExtraFields = []) |
||
7577 | { |
||
7578 | $course_id = api_get_course_int_id(); |
||
7579 | $return = ''; |
||
7580 | $item_id = (int) $item_id; |
||
7581 | |||
7582 | if (empty($item_id)) { |
||
7583 | return ''; |
||
7584 | } |
||
7585 | |||
7586 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
7587 | $sql = "SELECT * FROM $tbl_lp_item |
||
7588 | WHERE iid = ".$item_id; |
||
7589 | $res = Database::query($sql); |
||
7590 | $row = Database::fetch_array($res); |
||
7591 | switch ($row['item_type']) { |
||
7592 | case 'dir': |
||
7593 | case 'asset': |
||
7594 | case 'sco': |
||
7595 | if (isset($_GET['view']) && $_GET['view'] == 'build') { |
||
7596 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7597 | $return .= $this->display_item_form( |
||
7598 | $row['item_type'], |
||
7599 | get_lang('EditCurrentChapter').' :', |
||
7600 | 'edit', |
||
7601 | $item_id, |
||
7602 | $row |
||
7603 | ); |
||
7604 | } else { |
||
7605 | $return .= $this->display_item_form( |
||
7606 | $row['item_type'], |
||
7607 | get_lang('EditCurrentChapter').' :', |
||
7608 | 'edit_item', |
||
7609 | $item_id, |
||
7610 | $row |
||
7611 | ); |
||
7612 | } |
||
7613 | break; |
||
7614 | case TOOL_DOCUMENT: |
||
7615 | case TOOL_READOUT_TEXT: |
||
7616 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
7617 | $sql = "SELECT lp.*, doc.path as dir |
||
7618 | FROM $tbl_lp_item as lp |
||
7619 | LEFT JOIN $tbl_doc as doc |
||
7620 | ON (doc.iid = lp.path AND lp.c_id = doc.c_id) |
||
7621 | WHERE |
||
7622 | doc.c_id = $course_id AND |
||
7623 | lp.iid = ".$item_id; |
||
7624 | $res_step = Database::query($sql); |
||
7625 | $row_step = Database::fetch_array($res_step, 'ASSOC'); |
||
7626 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7627 | |||
7628 | if ($row['item_type'] === TOOL_DOCUMENT) { |
||
7629 | $return .= $this->display_document_form( |
||
7630 | 'edit', |
||
7631 | $item_id, |
||
7632 | $row_step, |
||
7633 | null, |
||
7634 | $excludeExtraFields |
||
7635 | ); |
||
7636 | } |
||
7637 | |||
7638 | if ($row['item_type'] === TOOL_READOUT_TEXT) { |
||
7639 | $return .= $this->displayFrmReadOutText('edit', $item_id, $row_step); |
||
7640 | } |
||
7641 | break; |
||
7642 | case TOOL_LINK: |
||
7643 | $linkId = (int) $row['path']; |
||
7644 | if (!empty($linkId)) { |
||
7645 | $table = Database::get_course_table(TABLE_LINK); |
||
7646 | $sql = 'SELECT url FROM '.$table.' |
||
7647 | WHERE c_id = '.$course_id.' AND iid = '.$linkId; |
||
7648 | $res_link = Database::query($sql); |
||
7649 | $row_link = Database::fetch_array($res_link); |
||
7650 | if (empty($row_link)) { |
||
7651 | // Try with id |
||
7652 | $sql = 'SELECT url FROM '.$table.' |
||
7653 | WHERE c_id = '.$course_id.' AND id = '.$linkId; |
||
7654 | $res_link = Database::query($sql); |
||
7655 | $row_link = Database::fetch_array($res_link); |
||
7656 | } |
||
7657 | |||
7658 | if (is_array($row_link)) { |
||
7659 | $row['url'] = $row_link['url']; |
||
7660 | } |
||
7661 | } |
||
7662 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7663 | $return .= $this->display_link_form('edit', $item_id, $row, null, $excludeExtraFields); |
||
7664 | break; |
||
7665 | case TOOL_LP_FINAL_ITEM: |
||
7666 | Session::write('finalItem', true); |
||
7667 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
7668 | $sql = "SELECT lp.*, doc.path as dir |
||
7669 | FROM $tbl_lp_item as lp |
||
7670 | LEFT JOIN $tbl_doc as doc |
||
7671 | ON (doc.iid = lp.path AND lp.c_id = doc.c_id) |
||
7672 | WHERE |
||
7673 | doc.c_id = $course_id AND |
||
7674 | lp.iid = ".$item_id; |
||
7675 | $res_step = Database::query($sql); |
||
7676 | $row_step = Database::fetch_array($res_step, 'ASSOC'); |
||
7677 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7678 | $return .= $this->display_document_form( |
||
7679 | 'edit', |
||
7680 | $item_id, |
||
7681 | $row_step, |
||
7682 | null, |
||
7683 | $excludeExtraFields |
||
7684 | ); |
||
7685 | break; |
||
7686 | case TOOL_QUIZ: |
||
7687 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7688 | $return .= $this->display_quiz_form('edit', $item_id, $row, $excludeExtraFields); |
||
7689 | break; |
||
7690 | case TOOL_HOTPOTATOES: |
||
7691 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7692 | $return .= $this->display_hotpotatoes_form('edit', $item_id, $row); |
||
7693 | break; |
||
7694 | case TOOL_STUDENTPUBLICATION: |
||
7695 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7696 | $return .= $this->display_student_publication_form('edit', $item_id, $row, null, $excludeExtraFields); |
||
7697 | break; |
||
7698 | case TOOL_FORUM: |
||
7699 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7700 | $return .= $this->display_forum_form('edit', $item_id, $row, $excludeExtraFields); |
||
7701 | break; |
||
7702 | case TOOL_THREAD: |
||
7703 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7704 | $return .= $this->display_thread_form('edit', $item_id, $row); |
||
7705 | break; |
||
7706 | case TOOL_SURVEY: |
||
7707 | $return .= $this->display_manipulate($item_id, $row['item_type']); |
||
7708 | $return .= $this->displaySurveyForm('edit', $item_id, $row); |
||
7709 | break; |
||
7710 | } |
||
7711 | |||
7712 | return $return; |
||
7713 | } |
||
7714 | |||
7715 | /** |
||
7716 | * Function that displays a list with al the resources that |
||
7717 | * could be added to the learning path. |
||
7718 | * |
||
7719 | * @throws Exception |
||
7720 | * @throws HTML_QuickForm_Error |
||
7721 | * |
||
7722 | * @return bool |
||
7723 | */ |
||
7724 | public function display_resources() |
||
7725 | { |
||
7726 | $course_code = api_get_course_id(); |
||
7727 | |||
7728 | // Get all the docs. |
||
7729 | $documents = $this->get_documents(true); |
||
7730 | |||
7731 | // Get all the exercises. |
||
7732 | $exercises = $this->get_exercises(); |
||
7733 | |||
7734 | // Get all the links. |
||
7735 | $links = $this->get_links(); |
||
7736 | |||
7737 | // Get all the student publications. |
||
7738 | $works = $this->get_student_publications(); |
||
7739 | |||
7740 | // Get all the forums. |
||
7741 | $forums = $this->get_forums(null, $course_code); |
||
7742 | |||
7743 | $dir = $this->display_item_form('dir', get_lang('EnterDataNewChapter'), 'add_item'); |
||
7744 | |||
7745 | // Get the final item form (see BT#11048) . |
||
7746 | $finish = $this->getFinalItemForm(); |
||
7747 | |||
7748 | $headers = [ |
||
7749 | Display::return_icon('folder_document.png', get_lang('Documents'), [], ICON_SIZE_BIG), |
||
7750 | Display::return_icon('quiz.png', get_lang('Quiz'), [], ICON_SIZE_BIG), |
||
7751 | Display::return_icon('links.png', get_lang('Links'), [], ICON_SIZE_BIG), |
||
7752 | Display::return_icon('works.png', get_lang('Works'), [], ICON_SIZE_BIG), |
||
7753 | Display::return_icon('forum.png', get_lang('Forums'), [], ICON_SIZE_BIG), |
||
7754 | Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), [], ICON_SIZE_BIG), |
||
7755 | ]; |
||
7756 | |||
7757 | $items = [ |
||
7758 | $documents, |
||
7759 | $exercises, |
||
7760 | $links, |
||
7761 | $works, |
||
7762 | $forums, |
||
7763 | $dir, |
||
7764 | ]; |
||
7765 | |||
7766 | $allowSurveyTool = api_get_configuration_value('allow_survey_tool_in_lp'); |
||
7767 | if ($allowSurveyTool) { |
||
7768 | // Get all the surveys |
||
7769 | $surveys = $this->getSurveys(); |
||
7770 | $items[] = $surveys; |
||
7771 | $headers[] = Display::return_icon('survey.png', get_lang('Surveys'), [], ICON_SIZE_BIG); |
||
7772 | } |
||
7773 | |||
7774 | $xApiPlugin = XApiPlugin::create(); |
||
7775 | if ($xApiPlugin->isEnabled()) { |
||
7776 | $headers[] = Display::return_icon( |
||
7777 | 'import_scorm.png', |
||
7778 | get_lang($xApiPlugin->get_lang('ToolTinCan')), |
||
7779 | [], |
||
7780 | ICON_SIZE_BIG |
||
7781 | ); |
||
7782 | $items[] = $xApiPlugin->getLpResourceBlock($this->lp_id); |
||
7783 | } |
||
7784 | |||
7785 | $h5pImportPlugin = H5pImportPlugin::create(); |
||
7786 | if ($h5pImportPlugin->isEnabled()) { |
||
7787 | $headers[] = Display::return_icon( |
||
7788 | 'plugin_h5p_import_upload.png', |
||
7789 | $h5pImportPlugin->get_lang('plugin_title'), |
||
7790 | [], |
||
7791 | ICON_SIZE_BIG |
||
7792 | ); |
||
7793 | $items[] = $h5pImportPlugin->getLpResourceBlock($this->lp_id); |
||
7794 | } |
||
7795 | |||
7796 | $headers[] = Display::return_icon('flag_checkered.png', get_lang('Certificate'), [], ICON_SIZE_BIG); |
||
7797 | $items[] = $finish; |
||
7798 | |||
7799 | echo Display::return_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'), 'normal'); |
||
7800 | |||
7801 | $selected = isset($_REQUEST['lp_build_selected']) ? (int) $_REQUEST['lp_build_selected'] : 0; |
||
7802 | |||
7803 | echo Display::tabs( |
||
7804 | $headers, |
||
7805 | $items, |
||
7806 | 'resource_tab', |
||
7807 | [], |
||
7808 | [], |
||
7809 | $selected |
||
7810 | ); |
||
7811 | |||
7812 | return true; |
||
7813 | } |
||
7814 | |||
7815 | /** |
||
7816 | * Returns the extension of a document. |
||
7817 | * |
||
7818 | * @param string $filename |
||
7819 | * |
||
7820 | * @return string Extension (part after the last dot) |
||
7821 | */ |
||
7822 | public function get_extension($filename) |
||
7823 | { |
||
7824 | $explode = explode('.', $filename); |
||
7825 | |||
7826 | return $explode[count($explode) - 1]; |
||
7827 | } |
||
7828 | |||
7829 | /** |
||
7830 | * Displays a document by id. |
||
7831 | * |
||
7832 | * @param int $id |
||
7833 | * @param bool $show_title |
||
7834 | * @param bool $iframe |
||
7835 | * @param bool $edit_link |
||
7836 | * |
||
7837 | * @return string |
||
7838 | */ |
||
7839 | public function display_document($id, $show_title = false, $iframe = true, $edit_link = false) |
||
7840 | { |
||
7841 | $_course = api_get_course_info(); |
||
7842 | $course_id = api_get_course_int_id(); |
||
7843 | $id = (int) $id; |
||
7844 | $return = ''; |
||
7845 | $table = Database::get_course_table(TABLE_DOCUMENT); |
||
7846 | $sql_doc = "SELECT * FROM $table |
||
7847 | WHERE c_id = $course_id AND iid = $id"; |
||
7848 | $res_doc = Database::query($sql_doc); |
||
7849 | $row_doc = Database::fetch_array($res_doc); |
||
7850 | |||
7851 | // TODO: Add a path filter. |
||
7852 | if ($iframe) { |
||
7853 | $return .= '<iframe id="learnpath_preview_frame" frameborder="0" height="400" width="100%" scrolling="auto" src="'.api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.str_replace('%2F', '/', urlencode($row_doc['path'])).'?'.api_get_cidreq().'"></iframe>'; |
||
7854 | } else { |
||
7855 | $return .= file_get_contents(api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'.$row_doc['path']); |
||
7856 | } |
||
7857 | |||
7858 | return $return; |
||
7859 | } |
||
7860 | |||
7861 | /** |
||
7862 | * Return HTML form to add/edit a quiz. |
||
7863 | * |
||
7864 | * @param string $action Action (add/edit) |
||
7865 | * @param int $id Item ID if already exists |
||
7866 | * @param mixed $extra_info Extra information (quiz ID if integer) |
||
7867 | * |
||
7868 | * @throws Exception |
||
7869 | * |
||
7870 | * @return string HTML form |
||
7871 | */ |
||
7872 | public function display_quiz_form( |
||
7873 | $action = 'add', |
||
7874 | $id = 0, |
||
7875 | $extra_info = '', |
||
7876 | $excludeExtraFields = [] |
||
7877 | ) { |
||
7878 | $course_id = api_get_course_int_id(); |
||
7879 | $id = (int) $id; |
||
7880 | $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST); |
||
7881 | |||
7882 | if ($id != 0 && is_array($extra_info)) { |
||
7883 | $item_title = $extra_info['title']; |
||
7884 | $item_description = $extra_info['description']; |
||
7885 | } elseif (is_numeric($extra_info)) { |
||
7886 | $sql = "SELECT title, description |
||
7887 | FROM $tbl_quiz |
||
7888 | WHERE iid = $extra_info"; |
||
7889 | |||
7890 | $result = Database::query($sql); |
||
7891 | $row = Database::fetch_array($result); |
||
7892 | $item_title = $row['title']; |
||
7893 | $item_description = $row['description']; |
||
7894 | } else { |
||
7895 | $item_title = ''; |
||
7896 | $item_description = ''; |
||
7897 | } |
||
7898 | $item_title = Security::remove_XSS($item_title); |
||
7899 | $item_description = Security::remove_XSS($item_description); |
||
7900 | |||
7901 | $parent = 0; |
||
7902 | if ($id != 0 && is_array($extra_info)) { |
||
7903 | $parent = $extra_info['parent_item_id']; |
||
7904 | } |
||
7905 | |||
7906 | $arrLP = $this->getItemsForForm(); |
||
7907 | $this->tree_array($arrLP); |
||
7908 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
7909 | unset($this->arrMenu); |
||
7910 | |||
7911 | $form = new FormValidator( |
||
7912 | 'quiz_form', |
||
7913 | 'POST', |
||
7914 | $this->getCurrentBuildingModeURL() |
||
7915 | ); |
||
7916 | $defaults = []; |
||
7917 | |||
7918 | if ($action === 'add') { |
||
7919 | $legend = get_lang('CreateTheExercise'); |
||
7920 | } elseif ($action === 'move') { |
||
7921 | $legend = get_lang('MoveTheCurrentExercise'); |
||
7922 | } else { |
||
7923 | $legend = get_lang('EditCurrentExecice'); |
||
7924 | } |
||
7925 | |||
7926 | if (isset($_GET['edit']) && $_GET['edit'] == 'true') { |
||
7927 | $legend .= Display::return_message(get_lang('Warning').' ! '.get_lang('WarningEditingDocument')); |
||
7928 | } |
||
7929 | |||
7930 | $form->addHeader($legend); |
||
7931 | |||
7932 | if ($action != 'move') { |
||
7933 | $this->setItemTitle($form); |
||
7934 | $defaults['title'] = $item_title; |
||
7935 | } |
||
7936 | |||
7937 | // Select for Parent item, root or chapter |
||
7938 | $selectParent = $form->addSelect( |
||
7939 | 'parent', |
||
7940 | get_lang('Parent'), |
||
7941 | [], |
||
7942 | ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);'] |
||
7943 | ); |
||
7944 | $selectParent->addOption($this->name, 0); |
||
7945 | |||
7946 | $arrHide = [ |
||
7947 | $id, |
||
7948 | ]; |
||
7949 | for ($i = 0; $i < count($arrLP); $i++) { |
||
7950 | if ($action != 'add') { |
||
7951 | if ( |
||
7952 | ($arrLP[$i]['item_type'] == 'dir') && |
||
7953 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
7954 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
7955 | ) { |
||
7956 | $selectParent->addOption( |
||
7957 | $arrLP[$i]['title'], |
||
7958 | $arrLP[$i]['id'], |
||
7959 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
7960 | ); |
||
7961 | |||
7962 | if ($parent == $arrLP[$i]['id']) { |
||
7963 | $selectParent->setSelected($arrLP[$i]['id']); |
||
7964 | } |
||
7965 | } else { |
||
7966 | $arrHide[] = $arrLP[$i]['id']; |
||
7967 | } |
||
7968 | } else { |
||
7969 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
7970 | $selectParent->addOption( |
||
7971 | $arrLP[$i]['title'], |
||
7972 | $arrLP[$i]['id'], |
||
7973 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
7974 | ); |
||
7975 | |||
7976 | if ($parent == $arrLP[$i]['id']) { |
||
7977 | $selectParent->setSelected($arrLP[$i]['id']); |
||
7978 | } |
||
7979 | } |
||
7980 | } |
||
7981 | } |
||
7982 | |||
7983 | if (is_array($arrLP)) { |
||
7984 | reset($arrLP); |
||
7985 | } |
||
7986 | |||
7987 | $selectPrevious = $form->addSelect( |
||
7988 | 'previous', |
||
7989 | get_lang('Position'), |
||
7990 | [], |
||
7991 | ['id' => 'previous'] |
||
7992 | ); |
||
7993 | $selectPrevious->addOption(get_lang('FirstPosition'), 0); |
||
7994 | |||
7995 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
7996 | if ($arrLP[$i]['parent_item_id'] == $parent && |
||
7997 | $arrLP[$i]['id'] != $id |
||
7998 | ) { |
||
7999 | $selectPrevious->addOption( |
||
8000 | get_lang('After').' "'.$arrLP[$i]['title'].'"', |
||
8001 | $arrLP[$i]['id'] |
||
8002 | ); |
||
8003 | |||
8004 | if (is_array($extra_info)) { |
||
8005 | if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) { |
||
8006 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8007 | } |
||
8008 | } elseif ($action == 'add') { |
||
8009 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8010 | } |
||
8011 | } |
||
8012 | } |
||
8013 | |||
8014 | if ($action != 'move') { |
||
8015 | $arrHide = []; |
||
8016 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8017 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
8018 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8019 | } |
||
8020 | } |
||
8021 | } |
||
8022 | |||
8023 | if ('edit' === $action) { |
||
8024 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
8025 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
8026 | } |
||
8027 | $extraField = new ExtraField('lp_item'); |
||
8028 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
8029 | } |
||
8030 | |||
8031 | if ($action === 'add') { |
||
8032 | $form->addButtonSave(get_lang('AddExercise'), 'submit_button'); |
||
8033 | } else { |
||
8034 | $form->addButtonSave(get_lang('EditCurrentExecice'), 'submit_button'); |
||
8035 | } |
||
8036 | |||
8037 | if ($action === 'move') { |
||
8038 | $form->addHidden('title', $item_title); |
||
8039 | $form->addHidden('description', $item_description); |
||
8040 | } |
||
8041 | |||
8042 | if (is_numeric($extra_info)) { |
||
8043 | $form->addHidden('path', $extra_info); |
||
8044 | } elseif (is_array($extra_info)) { |
||
8045 | $form->addHidden('path', $extra_info['path']); |
||
8046 | } |
||
8047 | |||
8048 | $form->addHidden('type', TOOL_QUIZ); |
||
8049 | $form->addHidden('post_time', time()); |
||
8050 | $this->setAuthorLpItem($form); |
||
8051 | $form->setDefaults($defaults); |
||
8052 | |||
8053 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
8054 | } |
||
8055 | |||
8056 | /** |
||
8057 | * Addition of Hotpotatoes tests. |
||
8058 | * |
||
8059 | * @param string $action |
||
8060 | * @param int $id Internal ID of the item |
||
8061 | * @param string $extra_info |
||
8062 | * |
||
8063 | * @return string HTML structure to display the hotpotatoes addition formular |
||
8064 | */ |
||
8065 | public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '') |
||
8066 | { |
||
8067 | $course_id = api_get_course_int_id(); |
||
8068 | $uploadPath = DIR_HOTPOTATOES; |
||
8069 | |||
8070 | if ($id != 0 && is_array($extra_info)) { |
||
8071 | $item_title = stripslashes($extra_info['title']); |
||
8072 | $item_description = stripslashes($extra_info['description']); |
||
8073 | } elseif (is_numeric($extra_info)) { |
||
8074 | $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT); |
||
8075 | |||
8076 | $sql = "SELECT * FROM $TBL_DOCUMENT |
||
8077 | WHERE |
||
8078 | c_id = $course_id AND |
||
8079 | path LIKE '".$uploadPath."/%/%htm%' AND |
||
8080 | iid = ".(int) $extra_info." |
||
8081 | ORDER BY iid ASC"; |
||
8082 | |||
8083 | $res_hot = Database::query($sql); |
||
8084 | $row = Database::fetch_array($res_hot); |
||
8085 | |||
8086 | $item_title = $row['title']; |
||
8087 | $item_description = $row['description']; |
||
8088 | |||
8089 | if (!empty($row['comment'])) { |
||
8090 | $item_title = $row['comment']; |
||
8091 | } |
||
8092 | } else { |
||
8093 | $item_title = ''; |
||
8094 | $item_description = ''; |
||
8095 | } |
||
8096 | |||
8097 | $parent = 0; |
||
8098 | if ($id != 0 && is_array($extra_info)) { |
||
8099 | $parent = $extra_info['parent_item_id']; |
||
8100 | } |
||
8101 | |||
8102 | $arrLP = $this->getItemsForForm(); |
||
8103 | $legend = '<legend>'; |
||
8104 | if ($action == 'add') { |
||
8105 | $legend .= get_lang('CreateTheExercise'); |
||
8106 | } elseif ($action == 'move') { |
||
8107 | $legend .= get_lang('MoveTheCurrentExercise'); |
||
8108 | } else { |
||
8109 | $legend .= get_lang('EditCurrentExecice'); |
||
8110 | } |
||
8111 | if (isset($_GET['edit']) && $_GET['edit'] == 'true') { |
||
8112 | $legend .= Display::return_message( |
||
8113 | get_lang('Warning').' ! '.get_lang('WarningEditingDocument') |
||
8114 | ); |
||
8115 | } |
||
8116 | $legend .= '</legend>'; |
||
8117 | |||
8118 | $return = '<form method="POST">'; |
||
8119 | $return .= $legend; |
||
8120 | $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">'; |
||
8121 | $return .= '<tr>'; |
||
8122 | $return .= '<td class="label"><label for="idParent">'.get_lang('Parent').' :</label></td>'; |
||
8123 | $return .= '<td class="input">'; |
||
8124 | $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">'; |
||
8125 | $return .= '<option class="top" value="0">'.$this->name.'</option>'; |
||
8126 | $arrHide = [$id]; |
||
8127 | |||
8128 | if (count($arrLP) > 0) { |
||
8129 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8130 | if ($action != 'add') { |
||
8131 | if ($arrLP[$i]['item_type'] == 'dir' && |
||
8132 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
8133 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
8134 | ) { |
||
8135 | $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>'; |
||
8136 | } else { |
||
8137 | $arrHide[] = $arrLP[$i]['id']; |
||
8138 | } |
||
8139 | } else { |
||
8140 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
8141 | $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>'; |
||
8142 | } |
||
8143 | } |
||
8144 | } |
||
8145 | reset($arrLP); |
||
8146 | } |
||
8147 | |||
8148 | $return .= '</select>'; |
||
8149 | $return .= '</td>'; |
||
8150 | $return .= '</tr>'; |
||
8151 | $return .= '<tr>'; |
||
8152 | $return .= '<td class="label"><label for="previous">'.get_lang('Position').' :</label></td>'; |
||
8153 | $return .= '<td class="input">'; |
||
8154 | $return .= '<select id="previous" name="previous" size="1">'; |
||
8155 | $return .= '<option class="top" value="0">'.get_lang('FirstPosition').'</option>'; |
||
8156 | |||
8157 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8158 | if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) { |
||
8159 | if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) { |
||
8160 | $selected = 'selected="selected" '; |
||
8161 | } elseif ($action == 'add') { |
||
8162 | $selected = 'selected="selected" '; |
||
8163 | } else { |
||
8164 | $selected = ''; |
||
8165 | } |
||
8166 | |||
8167 | $return .= '<option '.$selected.'value="'.$arrLP[$i]['id'].'">'. |
||
8168 | get_lang('After').' "'.$arrLP[$i]['title'].'"</option>'; |
||
8169 | } |
||
8170 | } |
||
8171 | |||
8172 | $return .= '</select>'; |
||
8173 | $return .= '</td>'; |
||
8174 | $return .= '</tr>'; |
||
8175 | |||
8176 | if ($action != 'move') { |
||
8177 | $return .= '<tr>'; |
||
8178 | $return .= '<td class="label"><label for="idTitle">'.get_lang('Title').' :</label></td>'; |
||
8179 | $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="'.$item_title.'" /></td>'; |
||
8180 | $return .= '</tr>'; |
||
8181 | $id_prerequisite = 0; |
||
8182 | if (is_array($arrLP) && count($arrLP) > 0) { |
||
8183 | foreach ($arrLP as $key => $value) { |
||
8184 | if ($value['id'] == $id) { |
||
8185 | $id_prerequisite = $value['prerequisite']; |
||
8186 | break; |
||
8187 | } |
||
8188 | } |
||
8189 | |||
8190 | $arrHide = []; |
||
8191 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8192 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
8193 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8194 | } |
||
8195 | } |
||
8196 | } |
||
8197 | } |
||
8198 | |||
8199 | $return .= '<tr>'; |
||
8200 | $return .= '<td> </td><td><button class="save" name="submit_button" action="edit" type="submit">'. |
||
8201 | get_lang('SaveHotpotatoes').'</button></td>'; |
||
8202 | $return .= '</tr>'; |
||
8203 | $return .= '</table>'; |
||
8204 | |||
8205 | if ($action == 'move') { |
||
8206 | $return .= '<input name="title" type="hidden" value="'.$item_title.'" />'; |
||
8207 | $return .= '<input name="description" type="hidden" value="'.$item_description.'" />'; |
||
8208 | } |
||
8209 | |||
8210 | if (is_numeric($extra_info)) { |
||
8211 | $return .= '<input name="path" type="hidden" value="'.$extra_info.'" />'; |
||
8212 | } elseif (is_array($extra_info)) { |
||
8213 | $return .= '<input name="path" type="hidden" value="'.$extra_info['path'].'" />'; |
||
8214 | } |
||
8215 | $return .= '<input name="type" type="hidden" value="'.TOOL_HOTPOTATOES.'" />'; |
||
8216 | $return .= '<input name="post_time" type="hidden" value="'.time().'" />'; |
||
8217 | $return .= '</form>'; |
||
8218 | |||
8219 | return $return; |
||
8220 | } |
||
8221 | |||
8222 | public function displaySurveyForm( |
||
8223 | $action = 'add', |
||
8224 | $id = 0, |
||
8225 | $extraInfo = '', |
||
8226 | $excludeExtraFields = [] |
||
8227 | ) { |
||
8228 | $courseId = api_get_course_int_id(); |
||
8229 | $tblSurvey = Database::get_course_table(TABLE_SURVEY); |
||
8230 | |||
8231 | $itemTitle = ''; |
||
8232 | $itemDescription = ''; |
||
8233 | |||
8234 | if ($id != 0 && is_array($extraInfo)) { |
||
8235 | $itemTitle = stripslashes($extraInfo['title']); |
||
8236 | } elseif (is_numeric($extraInfo)) { |
||
8237 | $sql = "SELECT title, intro as comment |
||
8238 | FROM $tblSurvey |
||
8239 | WHERE c_id = $courseId AND survey_id = ".(int) $extraInfo; |
||
8240 | |||
8241 | $result = Database::query($sql); |
||
8242 | $row = Database::fetch_array($result); |
||
8243 | |||
8244 | $itemTitle = strip_tags($row['title']); |
||
8245 | $itemDescription = $row['comment']; |
||
8246 | } |
||
8247 | $parent = 0; |
||
8248 | if ($id != 0 && is_array($extraInfo)) { |
||
8249 | $parent = $extraInfo['parent_item_id']; |
||
8250 | } |
||
8251 | $arrLP = $this->getItemsForForm(); |
||
8252 | $this->tree_array($arrLP); |
||
8253 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
8254 | unset($this->arrMenu); |
||
8255 | |||
8256 | if ($action == 'add') { |
||
8257 | $legend = get_lang('CreateSurvey'); |
||
8258 | } elseif ($action == 'move') { |
||
8259 | $legend = get_lang('MoveTheCurrentSurvey'); |
||
8260 | } else { |
||
8261 | $legend = get_lang('ModifySurveyInformation'); |
||
8262 | } |
||
8263 | |||
8264 | $form = new FormValidator( |
||
8265 | 'survey_form', |
||
8266 | 'POST', |
||
8267 | $this->getCurrentBuildingModeURL() |
||
8268 | ); |
||
8269 | $defaults = []; |
||
8270 | |||
8271 | $form->addHeader($legend); |
||
8272 | |||
8273 | if ($action != 'move') { |
||
8274 | $this->setItemTitle($form); |
||
8275 | $defaults['title'] = $itemTitle; |
||
8276 | } |
||
8277 | |||
8278 | $selectParent = $form->addSelect( |
||
8279 | 'parent', |
||
8280 | get_lang('Parent'), |
||
8281 | [], |
||
8282 | ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form'] |
||
8283 | ); |
||
8284 | $selectParent->addOption($this->name, 0); |
||
8285 | $arrHide = [ |
||
8286 | $id, |
||
8287 | ]; |
||
8288 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8289 | if ($action != 'add') { |
||
8290 | if ($arrLP[$i]['item_type'] == 'dir' && |
||
8291 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
8292 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
8293 | ) { |
||
8294 | $selectParent->addOption( |
||
8295 | $arrLP[$i]['title'], |
||
8296 | $arrLP[$i]['id'], |
||
8297 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8298 | ); |
||
8299 | |||
8300 | if ($parent == $arrLP[$i]['id']) { |
||
8301 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8302 | } |
||
8303 | } else { |
||
8304 | $arrHide[] = $arrLP[$i]['id']; |
||
8305 | } |
||
8306 | } else { |
||
8307 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
8308 | $selectParent->addOption( |
||
8309 | $arrLP[$i]['title'], |
||
8310 | $arrLP[$i]['id'], |
||
8311 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8312 | ); |
||
8313 | |||
8314 | if ($parent == $arrLP[$i]['id']) { |
||
8315 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8316 | } |
||
8317 | } |
||
8318 | } |
||
8319 | } |
||
8320 | |||
8321 | if (is_array($arrLP)) { |
||
8322 | reset($arrLP); |
||
8323 | } |
||
8324 | |||
8325 | $selectPrevious = $form->addSelect( |
||
8326 | 'previous', |
||
8327 | get_lang('Position'), |
||
8328 | [], |
||
8329 | ['id' => 'previous', 'class' => 'learnpath_item_form'] |
||
8330 | ); |
||
8331 | $selectPrevious->addOption(get_lang('FirstPosition'), 0); |
||
8332 | |||
8333 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8334 | if ($arrLP[$i]['parent_item_id'] == $parent && |
||
8335 | $arrLP[$i]['id'] != $id |
||
8336 | ) { |
||
8337 | $selectPrevious->addOption( |
||
8338 | get_lang('After').' "'.$arrLP[$i]['title'].'"', |
||
8339 | $arrLP[$i]['id'] |
||
8340 | ); |
||
8341 | |||
8342 | if (isset($extra_info['previous_item_id']) && |
||
8343 | $extra_info['previous_item_id'] == $arrLP[$i]['id'] |
||
8344 | ) { |
||
8345 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8346 | } elseif ($action == 'add') { |
||
8347 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8348 | } |
||
8349 | } |
||
8350 | } |
||
8351 | |||
8352 | if ($action != 'move') { |
||
8353 | $idPrerequisite = 0; |
||
8354 | if (is_array($arrLP)) { |
||
8355 | foreach ($arrLP as $key => $value) { |
||
8356 | if ($value['id'] == $id) { |
||
8357 | $idPrerequisite = $value['prerequisite']; |
||
8358 | break; |
||
8359 | } |
||
8360 | } |
||
8361 | } |
||
8362 | |||
8363 | $arrHide = []; |
||
8364 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8365 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
8366 | if (isset($extra_info['previous_item_id']) && |
||
8367 | $extra_info['previous_item_id'] == $arrLP[$i]['id'] |
||
8368 | ) { |
||
8369 | $sSelectedPosition = $arrLP[$i]['id']; |
||
8370 | } elseif ($action == 'add') { |
||
8371 | $sSelectedPosition = 0; |
||
8372 | } |
||
8373 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8374 | } |
||
8375 | } |
||
8376 | } |
||
8377 | |||
8378 | if ('edit' === $action) { |
||
8379 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
8380 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
8381 | } |
||
8382 | $extraField = new ExtraField('lp_item'); |
||
8383 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
8384 | } |
||
8385 | |||
8386 | if ($action == 'add') { |
||
8387 | $form->addButtonSave(get_lang('AddSurveyToCourse'), 'submit_button'); |
||
8388 | } else { |
||
8389 | $form->addButtonSave(get_lang('EditCurrentSurvey'), 'submit_button'); |
||
8390 | } |
||
8391 | |||
8392 | if ($action == 'move') { |
||
8393 | $form->addHidden('title', $itemTitle); |
||
8394 | $form->addHidden('description', $itemDescription); |
||
8395 | } |
||
8396 | |||
8397 | if (is_numeric($extraInfo)) { |
||
8398 | $form->addHidden('path', $extraInfo); |
||
8399 | } elseif (is_array($extraInfo)) { |
||
8400 | $form->addHidden('path', $extraInfo['path']); |
||
8401 | } |
||
8402 | $form->addHidden('type', TOOL_SURVEY); |
||
8403 | $form->addHidden('post_time', time()); |
||
8404 | $this->setAuthorLpItem($form); |
||
8405 | $form->setDefaults($defaults); |
||
8406 | |||
8407 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
8408 | } |
||
8409 | |||
8410 | /** |
||
8411 | * Return the form to display the forum edit/add option. |
||
8412 | * |
||
8413 | * @param string $action |
||
8414 | * @param int $id ID of the lp_item if already exists |
||
8415 | * @param string $extra_info |
||
8416 | * |
||
8417 | * @throws Exception |
||
8418 | * |
||
8419 | * @return string HTML form |
||
8420 | */ |
||
8421 | public function display_forum_form( |
||
8422 | $action = 'add', |
||
8423 | $id = 0, |
||
8424 | $extra_info = '', |
||
8425 | $excludeExtraFields = [] |
||
8426 | ) { |
||
8427 | $course_id = api_get_course_int_id(); |
||
8428 | $tbl_forum = Database::get_course_table(TABLE_FORUM); |
||
8429 | |||
8430 | $item_title = ''; |
||
8431 | $item_description = ''; |
||
8432 | |||
8433 | if ($id != 0 && is_array($extra_info)) { |
||
8434 | $item_title = stripslashes($extra_info['title']); |
||
8435 | } elseif (is_numeric($extra_info)) { |
||
8436 | $sql = "SELECT forum_title as title, forum_comment as comment |
||
8437 | FROM $tbl_forum |
||
8438 | WHERE c_id = $course_id AND forum_id = ".$extra_info; |
||
8439 | |||
8440 | $result = Database::query($sql); |
||
8441 | $row = Database::fetch_array($result); |
||
8442 | |||
8443 | $item_title = $row['title']; |
||
8444 | $item_description = $row['comment']; |
||
8445 | } |
||
8446 | $parent = 0; |
||
8447 | if ($id != 0 && is_array($extra_info)) { |
||
8448 | $parent = $extra_info['parent_item_id']; |
||
8449 | } |
||
8450 | $arrLP = $this->getItemsForForm(); |
||
8451 | $this->tree_array($arrLP); |
||
8452 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
8453 | unset($this->arrMenu); |
||
8454 | |||
8455 | if ($action == 'add') { |
||
8456 | $legend = get_lang('CreateTheForum'); |
||
8457 | } elseif ($action == 'move') { |
||
8458 | $legend = get_lang('MoveTheCurrentForum'); |
||
8459 | } else { |
||
8460 | $legend = get_lang('EditCurrentForum'); |
||
8461 | } |
||
8462 | |||
8463 | $form = new FormValidator( |
||
8464 | 'forum_form', |
||
8465 | 'POST', |
||
8466 | $this->getCurrentBuildingModeURL() |
||
8467 | ); |
||
8468 | $defaults = []; |
||
8469 | |||
8470 | $form->addHeader($legend); |
||
8471 | |||
8472 | if ($action != 'move') { |
||
8473 | $this->setItemTitle($form); |
||
8474 | $defaults['title'] = $item_title; |
||
8475 | } |
||
8476 | |||
8477 | $selectParent = $form->addSelect( |
||
8478 | 'parent', |
||
8479 | get_lang('Parent'), |
||
8480 | [], |
||
8481 | ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form'] |
||
8482 | ); |
||
8483 | $selectParent->addOption($this->name, 0); |
||
8484 | $arrHide = [ |
||
8485 | $id, |
||
8486 | ]; |
||
8487 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8488 | if ($action != 'add') { |
||
8489 | if ($arrLP[$i]['item_type'] == 'dir' && |
||
8490 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
8491 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
8492 | ) { |
||
8493 | $selectParent->addOption( |
||
8494 | $arrLP[$i]['title'], |
||
8495 | $arrLP[$i]['id'], |
||
8496 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8497 | ); |
||
8498 | |||
8499 | if ($parent == $arrLP[$i]['id']) { |
||
8500 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8501 | } |
||
8502 | } else { |
||
8503 | $arrHide[] = $arrLP[$i]['id']; |
||
8504 | } |
||
8505 | } else { |
||
8506 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
8507 | $selectParent->addOption( |
||
8508 | $arrLP[$i]['title'], |
||
8509 | $arrLP[$i]['id'], |
||
8510 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8511 | ); |
||
8512 | |||
8513 | if ($parent == $arrLP[$i]['id']) { |
||
8514 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8515 | } |
||
8516 | } |
||
8517 | } |
||
8518 | } |
||
8519 | |||
8520 | if (is_array($arrLP)) { |
||
8521 | reset($arrLP); |
||
8522 | } |
||
8523 | |||
8524 | $selectPrevious = $form->addSelect( |
||
8525 | 'previous', |
||
8526 | get_lang('Position'), |
||
8527 | [], |
||
8528 | ['id' => 'previous', 'class' => 'learnpath_item_form'] |
||
8529 | ); |
||
8530 | $selectPrevious->addOption(get_lang('FirstPosition'), 0); |
||
8531 | |||
8532 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8533 | if ($arrLP[$i]['parent_item_id'] == $parent && |
||
8534 | $arrLP[$i]['id'] != $id |
||
8535 | ) { |
||
8536 | $selectPrevious->addOption( |
||
8537 | get_lang('After').' "'.$arrLP[$i]['title'].'"', |
||
8538 | $arrLP[$i]['id'] |
||
8539 | ); |
||
8540 | |||
8541 | if (isset($extra_info['previous_item_id']) && |
||
8542 | $extra_info['previous_item_id'] == $arrLP[$i]['id'] |
||
8543 | ) { |
||
8544 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8545 | } elseif ($action == 'add') { |
||
8546 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8547 | } |
||
8548 | } |
||
8549 | } |
||
8550 | |||
8551 | if ($action != 'move') { |
||
8552 | $id_prerequisite = 0; |
||
8553 | if (is_array($arrLP)) { |
||
8554 | foreach ($arrLP as $key => $value) { |
||
8555 | if ($value['id'] == $id) { |
||
8556 | $id_prerequisite = $value['prerequisite']; |
||
8557 | break; |
||
8558 | } |
||
8559 | } |
||
8560 | } |
||
8561 | |||
8562 | $arrHide = []; |
||
8563 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8564 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
8565 | if (isset($extra_info['previous_item_id']) && |
||
8566 | $extra_info['previous_item_id'] == $arrLP[$i]['id'] |
||
8567 | ) { |
||
8568 | $s_selected_position = $arrLP[$i]['id']; |
||
8569 | } elseif ($action == 'add') { |
||
8570 | $s_selected_position = 0; |
||
8571 | } |
||
8572 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8573 | } |
||
8574 | } |
||
8575 | } |
||
8576 | |||
8577 | if ('edit' === $action) { |
||
8578 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
8579 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
8580 | } |
||
8581 | $extraField = new ExtraField('lp_item'); |
||
8582 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
8583 | } |
||
8584 | |||
8585 | if ($action == 'add') { |
||
8586 | $form->addButtonSave(get_lang('AddForumToCourse'), 'submit_button'); |
||
8587 | } else { |
||
8588 | $form->addButtonSave(get_lang('EditCurrentForum'), 'submit_button'); |
||
8589 | } |
||
8590 | |||
8591 | if ($action == 'move') { |
||
8592 | $form->addHidden('title', $item_title); |
||
8593 | $form->addHidden('description', $item_description); |
||
8594 | } |
||
8595 | |||
8596 | if (is_numeric($extra_info)) { |
||
8597 | $form->addHidden('path', $extra_info); |
||
8598 | } elseif (is_array($extra_info)) { |
||
8599 | $form->addHidden('path', $extra_info['path']); |
||
8600 | } |
||
8601 | $form->addHidden('type', TOOL_FORUM); |
||
8602 | $form->addHidden('post_time', time()); |
||
8603 | $this->setAuthorLpItem($form); |
||
8604 | $form->setDefaults($defaults); |
||
8605 | |||
8606 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
8607 | } |
||
8608 | |||
8609 | /** |
||
8610 | * Return HTML form to add/edit forum threads. |
||
8611 | * |
||
8612 | * @param string $action |
||
8613 | * @param int $id Item ID if already exists in learning path |
||
8614 | * @param string $extra_info |
||
8615 | * |
||
8616 | * @throws Exception |
||
8617 | * |
||
8618 | * @return string HTML form |
||
8619 | */ |
||
8620 | public function display_thread_form($action = 'add', $id = 0, $extra_info = '') |
||
8621 | { |
||
8622 | $course_id = api_get_course_int_id(); |
||
8623 | if (empty($course_id)) { |
||
8624 | return null; |
||
8625 | } |
||
8626 | $tbl_forum = Database::get_course_table(TABLE_FORUM_THREAD); |
||
8627 | |||
8628 | $item_title = ''; |
||
8629 | $item_description = ''; |
||
8630 | if ($id != 0 && is_array($extra_info)) { |
||
8631 | $item_title = stripslashes($extra_info['title']); |
||
8632 | } elseif (is_numeric($extra_info)) { |
||
8633 | $sql = "SELECT thread_title as title FROM $tbl_forum |
||
8634 | WHERE c_id = $course_id AND thread_id = ".$extra_info; |
||
8635 | |||
8636 | $result = Database::query($sql); |
||
8637 | $row = Database::fetch_array($result); |
||
8638 | |||
8639 | $item_title = $row['title']; |
||
8640 | $item_description = ''; |
||
8641 | } |
||
8642 | |||
8643 | $parent = 0; |
||
8644 | if ($id != 0 && is_array($extra_info)) { |
||
8645 | $parent = $extra_info['parent_item_id']; |
||
8646 | } |
||
8647 | |||
8648 | $arrLP = $this->getItemsForForm(); |
||
8649 | $this->tree_array($arrLP); |
||
8650 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
8651 | unset($this->arrMenu); |
||
8652 | |||
8653 | $form = new FormValidator( |
||
8654 | 'thread_form', |
||
8655 | 'POST', |
||
8656 | $this->getCurrentBuildingModeURL() |
||
8657 | ); |
||
8658 | $defaults = []; |
||
8659 | |||
8660 | if ($action == 'add') { |
||
8661 | $legend = get_lang('CreateTheForum'); |
||
8662 | } elseif ($action == 'move') { |
||
8663 | $legend = get_lang('MoveTheCurrentForum'); |
||
8664 | } else { |
||
8665 | $legend = get_lang('EditCurrentForum'); |
||
8666 | } |
||
8667 | |||
8668 | $form->addHeader($legend); |
||
8669 | $selectParent = $form->addSelect( |
||
8670 | 'parent', |
||
8671 | get_lang('Parent'), |
||
8672 | [], |
||
8673 | ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);'] |
||
8674 | ); |
||
8675 | $selectParent->addOption($this->name, 0); |
||
8676 | |||
8677 | $arrHide = [ |
||
8678 | $id, |
||
8679 | ]; |
||
8680 | |||
8681 | for ($i = 0; $i < count($arrLP); $i++) { |
||
8682 | if ($action != 'add') { |
||
8683 | if ( |
||
8684 | ($arrLP[$i]['item_type'] == 'dir') && |
||
8685 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
8686 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
8687 | ) { |
||
8688 | $selectParent->addOption( |
||
8689 | $arrLP[$i]['title'], |
||
8690 | $arrLP[$i]['id'], |
||
8691 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8692 | ); |
||
8693 | |||
8694 | if ($parent == $arrLP[$i]['id']) { |
||
8695 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8696 | } |
||
8697 | } else { |
||
8698 | $arrHide[] = $arrLP[$i]['id']; |
||
8699 | } |
||
8700 | } else { |
||
8701 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
8702 | $selectParent->addOption( |
||
8703 | $arrLP[$i]['title'], |
||
8704 | $arrLP[$i]['id'], |
||
8705 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
8706 | ); |
||
8707 | |||
8708 | if ($parent == $arrLP[$i]['id']) { |
||
8709 | $selectParent->setSelected($arrLP[$i]['id']); |
||
8710 | } |
||
8711 | } |
||
8712 | } |
||
8713 | } |
||
8714 | |||
8715 | if ($arrLP != null) { |
||
8716 | reset($arrLP); |
||
8717 | } |
||
8718 | |||
8719 | $selectPrevious = $form->addSelect( |
||
8720 | 'previous', |
||
8721 | get_lang('Position'), |
||
8722 | [], |
||
8723 | ['id' => 'previous'] |
||
8724 | ); |
||
8725 | $selectPrevious->addOption(get_lang('FirstPosition'), 0); |
||
8726 | |||
8727 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8728 | if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) { |
||
8729 | $selectPrevious->addOption( |
||
8730 | get_lang('After').' "'.$arrLP[$i]['title'].'"', |
||
8731 | $arrLP[$i]['id'] |
||
8732 | ); |
||
8733 | |||
8734 | if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) { |
||
8735 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8736 | } elseif ($action == 'add') { |
||
8737 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
8738 | } |
||
8739 | } |
||
8740 | } |
||
8741 | |||
8742 | if ($action != 'move') { |
||
8743 | $this->setItemTitle($form); |
||
8744 | $defaults['title'] = $item_title; |
||
8745 | |||
8746 | $id_prerequisite = 0; |
||
8747 | if ($arrLP != null) { |
||
8748 | foreach ($arrLP as $key => $value) { |
||
8749 | if ($value['id'] == $id) { |
||
8750 | $id_prerequisite = $value['prerequisite']; |
||
8751 | break; |
||
8752 | } |
||
8753 | } |
||
8754 | } |
||
8755 | |||
8756 | $arrHide = []; |
||
8757 | $s_selected_position = 0; |
||
8758 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8759 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
8760 | if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) { |
||
8761 | $s_selected_position = $arrLP[$i]['id']; |
||
8762 | } elseif ($action == 'add') { |
||
8763 | $s_selected_position = 0; |
||
8764 | } |
||
8765 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8766 | } |
||
8767 | } |
||
8768 | |||
8769 | $selectPrerequisites = $form->addSelect( |
||
8770 | 'prerequisites', |
||
8771 | get_lang('LearnpathPrerequisites'), |
||
8772 | [], |
||
8773 | ['id' => 'prerequisites'] |
||
8774 | ); |
||
8775 | $selectPrerequisites->addOption(get_lang('NoPrerequisites'), 0); |
||
8776 | |||
8777 | foreach ($arrHide as $key => $value) { |
||
8778 | $selectPrerequisites->addOption($value['value'], $key); |
||
8779 | |||
8780 | if ($key == $s_selected_position && $action == 'add') { |
||
8781 | $selectPrerequisites->setSelected($key); |
||
8782 | } elseif ($key == $id_prerequisite && $action == 'edit') { |
||
8783 | $selectPrerequisites->setSelected($key); |
||
8784 | } |
||
8785 | } |
||
8786 | } |
||
8787 | |||
8788 | if ('edit' === $action) { |
||
8789 | $excludeExtraFields = []; |
||
8790 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
8791 | $excludeExtraFields = ['start_date', 'end_date']; |
||
8792 | } |
||
8793 | $extraField = new ExtraField('lp_item'); |
||
8794 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
8795 | } |
||
8796 | |||
8797 | $form->addButtonSave(get_lang('Ok'), 'submit_button'); |
||
8798 | |||
8799 | if ($action == 'move') { |
||
8800 | $form->addHidden('title', $item_title); |
||
8801 | $form->addHidden('description', $item_description); |
||
8802 | } |
||
8803 | |||
8804 | if (is_numeric($extra_info)) { |
||
8805 | $form->addHidden('path', $extra_info); |
||
8806 | } elseif (is_array($extra_info)) { |
||
8807 | $form->addHidden('path', $extra_info['path']); |
||
8808 | } |
||
8809 | |||
8810 | $form->addHidden('type', TOOL_THREAD); |
||
8811 | $form->addHidden('post_time', time()); |
||
8812 | $this->setAuthorLpItem($form); |
||
8813 | $form->setDefaults($defaults); |
||
8814 | |||
8815 | return $form->returnForm(); |
||
8816 | } |
||
8817 | |||
8818 | /** |
||
8819 | * Return the HTML form to display an item (generally a dir item). |
||
8820 | * |
||
8821 | * @param string $item_type |
||
8822 | * @param string $title |
||
8823 | * @param string $action |
||
8824 | * @param int $id |
||
8825 | * @param string $extra_info |
||
8826 | * |
||
8827 | * @throws Exception |
||
8828 | * @throws HTML_QuickForm_Error |
||
8829 | * |
||
8830 | * @return string HTML form |
||
8831 | */ |
||
8832 | public function display_item_form( |
||
8833 | $item_type, |
||
8834 | $title = '', |
||
8835 | $action = 'add_item', |
||
8836 | $id = 0, |
||
8837 | $extra_info = 'new' |
||
8838 | ) { |
||
8839 | $_course = api_get_course_info(); |
||
8840 | |||
8841 | global $charset; |
||
8842 | |||
8843 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
8844 | $item_title = ''; |
||
8845 | $item_description = ''; |
||
8846 | $item_path_fck = ''; |
||
8847 | |||
8848 | $parent = 0; |
||
8849 | $previousId = null; |
||
8850 | if ($id != 0 && is_array($extra_info)) { |
||
8851 | $item_title = $extra_info['title']; |
||
8852 | $item_description = $extra_info['description']; |
||
8853 | $item_path = api_get_path(WEB_COURSE_PATH).$_course['path'].'/scorm/'.$this->path.'/'.stripslashes($extra_info['path']); |
||
8854 | $item_path_fck = '/scorm/'.$this->path.'/'.stripslashes($extra_info['path']); |
||
8855 | $parent = $extra_info['parent_item_id']; |
||
8856 | $previousId = $extra_info['previous_item_id']; |
||
8857 | } |
||
8858 | |||
8859 | if ($extra_info instanceof learnpathItem) { |
||
8860 | $item_title = $extra_info->get_title(); |
||
8861 | $item_description = $extra_info->get_description(); |
||
8862 | $path = $extra_info->get_path(); |
||
8863 | $item_path = api_get_path(WEB_COURSE_PATH).$_course['path'].'/scorm/'.$this->path.'/'.stripslashes($path); |
||
8864 | $item_path_fck = '/scorm/'.$this->path.'/'.stripslashes($path); |
||
8865 | $parent = $extra_info->get_parent(); |
||
8866 | $previousId = $extra_info->previous; |
||
8867 | } |
||
8868 | |||
8869 | $id = (int) $id; |
||
8870 | $sql = "SELECT * FROM $tbl_lp_item |
||
8871 | WHERE |
||
8872 | lp_id = ".$this->lp_id." AND |
||
8873 | iid != $id"; |
||
8874 | |||
8875 | if ($item_type === 'dir') { |
||
8876 | $sql .= " AND parent_item_id = 0"; |
||
8877 | } |
||
8878 | |||
8879 | $result = Database::query($sql); |
||
8880 | $arrLP = []; |
||
8881 | while ($row = Database::fetch_array($result)) { |
||
8882 | $arrLP[] = [ |
||
8883 | 'id' => $row['iid'], |
||
8884 | 'item_type' => $row['item_type'], |
||
8885 | 'title' => $this->cleanItemTitle($row['title']), |
||
8886 | 'title_raw' => $row['title'], |
||
8887 | 'path' => $row['path'], |
||
8888 | 'description' => $row['description'], |
||
8889 | 'parent_item_id' => $row['parent_item_id'], |
||
8890 | 'previous_item_id' => $row['previous_item_id'], |
||
8891 | 'next_item_id' => $row['next_item_id'], |
||
8892 | 'max_score' => $row['max_score'], |
||
8893 | 'min_score' => $row['min_score'], |
||
8894 | 'mastery_score' => $row['mastery_score'], |
||
8895 | 'prerequisite' => $row['prerequisite'], |
||
8896 | 'display_order' => $row['display_order'], |
||
8897 | ]; |
||
8898 | } |
||
8899 | |||
8900 | $this->tree_array($arrLP); |
||
8901 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
8902 | unset($this->arrMenu); |
||
8903 | |||
8904 | $url = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id; |
||
8905 | |||
8906 | $form = new FormValidator('form_'.$item_type, 'POST', $url); |
||
8907 | $defaults['title'] = api_html_entity_decode( |
||
8908 | $item_title, |
||
8909 | ENT_QUOTES, |
||
8910 | $charset |
||
8911 | ); |
||
8912 | $defaults['description'] = $item_description; |
||
8913 | |||
8914 | $form->addHeader($title); |
||
8915 | $arrHide[0]['value'] = Security::remove_XSS($this->name); |
||
8916 | $arrHide[0]['padding'] = 20; |
||
8917 | $charset = api_get_system_encoding(); |
||
8918 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8919 | if ($action != 'add') { |
||
8920 | if ($arrLP[$i]['item_type'] === 'dir' && !in_array($arrLP[$i]['id'], $arrHide) && |
||
8921 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
8922 | ) { |
||
8923 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8924 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
8925 | if ($parent == $arrLP[$i]['id']) { |
||
8926 | $s_selected_parent = $arrHide[$arrLP[$i]['id']]; |
||
8927 | } |
||
8928 | } |
||
8929 | } else { |
||
8930 | if ($arrLP[$i]['item_type'] === 'dir') { |
||
8931 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
8932 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
8933 | if ($parent == $arrLP[$i]['id']) { |
||
8934 | $s_selected_parent = $arrHide[$arrLP[$i]['id']]; |
||
8935 | } |
||
8936 | } |
||
8937 | } |
||
8938 | } |
||
8939 | |||
8940 | if ($action !== 'move') { |
||
8941 | $this->setItemTitle($form); |
||
8942 | } else { |
||
8943 | $form->addElement('hidden', 'title'); |
||
8944 | } |
||
8945 | |||
8946 | $parentSelect = $form->addElement( |
||
8947 | 'select', |
||
8948 | 'parent', |
||
8949 | get_lang('Parent'), |
||
8950 | '', |
||
8951 | [ |
||
8952 | 'id' => 'idParent', |
||
8953 | 'onchange' => 'javascript: load_cbo(this.value);', |
||
8954 | ] |
||
8955 | ); |
||
8956 | |||
8957 | foreach ($arrHide as $key => $value) { |
||
8958 | $parentSelect->addOption( |
||
8959 | $value['value'], |
||
8960 | $key, |
||
8961 | 'style="padding-left:'.$value['padding'].'px;"' |
||
8962 | ); |
||
8963 | $lastPosition = $key; |
||
8964 | } |
||
8965 | |||
8966 | if (!empty($s_selected_parent)) { |
||
8967 | $parentSelect->setSelected($s_selected_parent); |
||
8968 | } |
||
8969 | |||
8970 | if (is_array($arrLP)) { |
||
8971 | reset($arrLP); |
||
8972 | } |
||
8973 | |||
8974 | $arrHide = []; |
||
8975 | // POSITION |
||
8976 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
8977 | if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id && |
||
8978 | $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) { |
||
8979 | //this is the same! |
||
8980 | if (isset($previousId) && $previousId == $arrLP[$i]['id']) { |
||
8981 | $s_selected_position = $arrLP[$i]['id']; |
||
8982 | } elseif ($action === 'add') { |
||
8983 | $s_selected_position = $arrLP[$i]['id']; |
||
8984 | } |
||
8985 | |||
8986 | $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"'; |
||
8987 | } |
||
8988 | } |
||
8989 | |||
8990 | $position = $form->addElement( |
||
8991 | 'select', |
||
8992 | 'previous', |
||
8993 | get_lang('Position'), |
||
8994 | '', |
||
8995 | ['id' => 'previous'] |
||
8996 | ); |
||
8997 | $padding = isset($value['padding']) ? $value['padding'] : 0; |
||
8998 | $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:'.$padding.'px;"'); |
||
8999 | |||
9000 | $lastPosition = null; |
||
9001 | foreach ($arrHide as $key => $value) { |
||
9002 | $position->addOption($value['value'], $key, 'style="padding-left:'.$padding.'px;"'); |
||
9003 | $lastPosition = $key; |
||
9004 | } |
||
9005 | |||
9006 | if (!empty($s_selected_position)) { |
||
9007 | $position->setSelected($s_selected_position); |
||
9008 | } |
||
9009 | |||
9010 | // When new chapter add at the end |
||
9011 | if ($action === 'add_item') { |
||
9012 | $position->setSelected($lastPosition); |
||
9013 | } |
||
9014 | |||
9015 | if (is_array($arrLP)) { |
||
9016 | reset($arrLP); |
||
9017 | } |
||
9018 | |||
9019 | $form->addButtonSave(get_lang('SaveSection'), 'submit_button'); |
||
9020 | |||
9021 | //fix in order to use the tab |
||
9022 | if ($item_type === 'dir') { |
||
9023 | $form->addElement('hidden', 'type', 'dir'); |
||
9024 | } |
||
9025 | |||
9026 | $extension = null; |
||
9027 | if (!empty($item_path)) { |
||
9028 | $extension = pathinfo($item_path, PATHINFO_EXTENSION); |
||
9029 | } |
||
9030 | |||
9031 | //assets can't be modified |
||
9032 | //$item_type == 'asset' || |
||
9033 | if (($item_type === 'sco') && ($extension === 'html' || $extension === 'htm')) { |
||
9034 | if ($item_type === 'sco') { |
||
9035 | $form->addElement( |
||
9036 | 'html', |
||
9037 | '<script>alert("'.get_lang('WarningWhenEditingScorm').'")</script>' |
||
9038 | ); |
||
9039 | } |
||
9040 | $renderer = $form->defaultRenderer(); |
||
9041 | $renderer->setElementTemplate( |
||
9042 | '<br /> {label}<br />{element}', |
||
9043 | 'content_lp' |
||
9044 | ); |
||
9045 | |||
9046 | $relative_prefix = ''; |
||
9047 | $editor_config = [ |
||
9048 | 'ToolbarSet' => 'LearningPathDocuments', |
||
9049 | 'Width' => '100%', |
||
9050 | 'Height' => '500', |
||
9051 | 'FullPage' => true, |
||
9052 | 'CreateDocumentDir' => $relative_prefix, |
||
9053 | 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/scorm/', |
||
9054 | 'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().$item_path_fck, |
||
9055 | ]; |
||
9056 | |||
9057 | $form->addElement('html_editor', 'content_lp', '', null, $editor_config); |
||
9058 | $content_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck; |
||
9059 | $defaults['content_lp'] = file_get_contents($content_path); |
||
9060 | } |
||
9061 | |||
9062 | if (!empty($id)) { |
||
9063 | $form->addHidden('id', $id); |
||
9064 | } |
||
9065 | |||
9066 | $form->addElement('hidden', 'type', $item_type); |
||
9067 | $form->addElement('hidden', 'post_time', time()); |
||
9068 | $form->setDefaults($defaults); |
||
9069 | |||
9070 | return $form->returnForm(); |
||
9071 | } |
||
9072 | |||
9073 | /** |
||
9074 | * @return string |
||
9075 | */ |
||
9076 | public function getCurrentBuildingModeURL() |
||
9077 | { |
||
9078 | $pathItem = isset($_GET['path_item']) ? (int) $_GET['path_item'] : ''; |
||
9079 | $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : ''; |
||
9080 | $id = isset($_GET['id']) ? (int) $_GET['id'] : ''; |
||
9081 | $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : ''; |
||
9082 | |||
9083 | $currentUrl = api_get_self().'?'.api_get_cidreq(). |
||
9084 | '&action='.$action.'&lp_id='.$this->lp_id.'&path_item='.$pathItem.'&view='.$view.'&id='.$id; |
||
9085 | |||
9086 | return $currentUrl; |
||
9087 | } |
||
9088 | |||
9089 | /** |
||
9090 | * Returns the form to update or create a document. |
||
9091 | * |
||
9092 | * @param string $action (add/edit) |
||
9093 | * @param int $id ID of the lp_item (if already exists) |
||
9094 | * @param mixed $extra_info Integer if document ID, string if info ('new') |
||
9095 | * @param learnpathItem $item |
||
9096 | * |
||
9097 | * @return string HTML form |
||
9098 | */ |
||
9099 | public function display_document_form( |
||
9100 | $action = 'add', |
||
9101 | $id = 0, |
||
9102 | $extra_info = 'new', |
||
9103 | $item = null, |
||
9104 | $excludeExtraFields = [] |
||
9105 | ) { |
||
9106 | $course_id = api_get_course_int_id(); |
||
9107 | $_course = api_get_course_info(); |
||
9108 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
9109 | |||
9110 | $no_display_edit_textarea = false; |
||
9111 | //If action==edit document |
||
9112 | //We don't display the document form if it's not an editable document (html or txt file) |
||
9113 | if ($action === 'edit') { |
||
9114 | if (is_array($extra_info)) { |
||
9115 | $path_parts = pathinfo($extra_info['dir']); |
||
9116 | if ($path_parts['extension'] !== 'txt' && $path_parts['extension'] !== 'html') { |
||
9117 | $no_display_edit_textarea = true; |
||
9118 | } |
||
9119 | } |
||
9120 | } |
||
9121 | $no_display_add = false; |
||
9122 | // If action==add an existing document |
||
9123 | // We don't display the document form if it's not an editable document (html or txt file). |
||
9124 | if ($action === 'add') { |
||
9125 | if (is_numeric($extra_info)) { |
||
9126 | $extra_info = (int) $extra_info; |
||
9127 | $sql_doc = "SELECT path FROM $tbl_doc |
||
9128 | WHERE c_id = $course_id AND iid = ".$extra_info; |
||
9129 | $result = Database::query($sql_doc); |
||
9130 | $path_file = Database::result($result, 0, 0); |
||
9131 | $path_parts = pathinfo($path_file); |
||
9132 | if ($path_parts['extension'] != 'txt' && $path_parts['extension'] != 'html') { |
||
9133 | $no_display_add = true; |
||
9134 | } |
||
9135 | } |
||
9136 | } |
||
9137 | |||
9138 | $item_title = ''; |
||
9139 | $item_description = ''; |
||
9140 | if ($id != 0 && is_array($extra_info)) { |
||
9141 | $item_title = stripslashes($extra_info['title']); |
||
9142 | $item_description = stripslashes($extra_info['description']); |
||
9143 | if (empty($item_title)) { |
||
9144 | $path_parts = pathinfo($extra_info['path']); |
||
9145 | $item_title = stripslashes($path_parts['filename']); |
||
9146 | } |
||
9147 | } elseif (is_numeric($extra_info)) { |
||
9148 | $sql = "SELECT path, title FROM $tbl_doc |
||
9149 | WHERE |
||
9150 | c_id = ".$course_id." AND |
||
9151 | iid = ".intval($extra_info); |
||
9152 | $result = Database::query($sql); |
||
9153 | $row = Database::fetch_array($result); |
||
9154 | $item_title = $row['title']; |
||
9155 | $item_title = str_replace('_', ' ', $item_title); |
||
9156 | if (empty($item_title)) { |
||
9157 | $path_parts = pathinfo($row['path']); |
||
9158 | $item_title = stripslashes($path_parts['filename']); |
||
9159 | } |
||
9160 | } |
||
9161 | |||
9162 | $return = '<legend>'; |
||
9163 | $parent = 0; |
||
9164 | if ($id != 0 && is_array($extra_info)) { |
||
9165 | $parent = $extra_info['parent_item_id']; |
||
9166 | } |
||
9167 | |||
9168 | $selectedPosition = 0; |
||
9169 | if (is_array($extra_info) && isset($extra_info['previous_item_id'])) { |
||
9170 | $selectedPosition = $extra_info['previous_item_id']; |
||
9171 | } |
||
9172 | |||
9173 | if ($item instanceof learnpathItem) { |
||
9174 | $item_title = stripslashes($item->get_title()); |
||
9175 | $item_description = stripslashes($item->get_description()); |
||
9176 | $selectedPosition = $item->previous; |
||
9177 | $parent = $item->parent; |
||
9178 | } |
||
9179 | |||
9180 | $arrLP = $this->getItemsForForm(); |
||
9181 | $this->tree_array($arrLP); |
||
9182 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
9183 | unset($this->arrMenu); |
||
9184 | |||
9185 | if ($action === 'add') { |
||
9186 | $return .= get_lang('CreateTheDocument'); |
||
9187 | } elseif ($action === 'move') { |
||
9188 | $return .= get_lang('MoveTheCurrentDocument'); |
||
9189 | } else { |
||
9190 | $return .= get_lang('EditTheCurrentDocument'); |
||
9191 | } |
||
9192 | $return .= '</legend>'; |
||
9193 | |||
9194 | if (isset($_GET['edit']) && $_GET['edit'] === 'true') { |
||
9195 | $return .= Display::return_message( |
||
9196 | '<strong>'.get_lang('Warning').' !</strong><br />'.get_lang('WarningEditingDocument'), |
||
9197 | false |
||
9198 | ); |
||
9199 | } |
||
9200 | $form = new FormValidator( |
||
9201 | 'form', |
||
9202 | 'POST', |
||
9203 | $this->getCurrentBuildingModeURL(), |
||
9204 | '', |
||
9205 | ['enctype' => 'multipart/form-data'] |
||
9206 | ); |
||
9207 | $defaults['title'] = Security::remove_XSS($item_title); |
||
9208 | if (empty($item_title)) { |
||
9209 | $defaults['title'] = Security::remove_XSS($item_title); |
||
9210 | } |
||
9211 | $defaults['description'] = $item_description; |
||
9212 | $form->addElement('html', $return); |
||
9213 | |||
9214 | if ($action !== 'move') { |
||
9215 | $data = $this->generate_lp_folder($_course); |
||
9216 | if ($action !== 'edit') { |
||
9217 | $folders = DocumentManager::get_all_document_folders( |
||
9218 | $_course, |
||
9219 | 0, |
||
9220 | true |
||
9221 | ); |
||
9222 | DocumentManager::build_directory_selector( |
||
9223 | $folders, |
||
9224 | '', |
||
9225 | [], |
||
9226 | true, |
||
9227 | $form, |
||
9228 | 'directory_parent_id' |
||
9229 | ); |
||
9230 | } |
||
9231 | |||
9232 | if (isset($data['id'])) { |
||
9233 | $defaults['directory_parent_id'] = $data['id']; |
||
9234 | } |
||
9235 | $this->setItemTitle($form); |
||
9236 | } |
||
9237 | |||
9238 | $arrHide[0]['value'] = $this->name; |
||
9239 | $arrHide[0]['padding'] = 20; |
||
9240 | |||
9241 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
9242 | if ($action !== 'add') { |
||
9243 | if ($arrLP[$i]['item_type'] === 'dir' && |
||
9244 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
9245 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
9246 | ) { |
||
9247 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9248 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
9249 | } |
||
9250 | } else { |
||
9251 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
9252 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9253 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
9254 | } |
||
9255 | } |
||
9256 | } |
||
9257 | |||
9258 | $parentSelect = $form->addSelect( |
||
9259 | 'parent', |
||
9260 | get_lang('Parent'), |
||
9261 | [], |
||
9262 | [ |
||
9263 | 'id' => 'idParent', |
||
9264 | 'onchange' => 'javascript: load_cbo(this.value);', |
||
9265 | ] |
||
9266 | ); |
||
9267 | |||
9268 | $my_count = 0; |
||
9269 | foreach ($arrHide as $key => $value) { |
||
9270 | if ($my_count != 0) { |
||
9271 | // The LP name is also the first section and is not in the same charset like the other sections. |
||
9272 | $value['value'] = Security::remove_XSS($value['value']); |
||
9273 | $parentSelect->addOption( |
||
9274 | $value['value'], |
||
9275 | $key, |
||
9276 | 'style="padding-left:'.$value['padding'].'px;"' |
||
9277 | ); |
||
9278 | } else { |
||
9279 | $value['value'] = Security::remove_XSS($value['value']); |
||
9280 | $parentSelect->addOption( |
||
9281 | $value['value'], |
||
9282 | $key, |
||
9283 | 'style="padding-left:'.$value['padding'].'px;"' |
||
9284 | ); |
||
9285 | } |
||
9286 | $my_count++; |
||
9287 | } |
||
9288 | |||
9289 | if (!empty($id)) { |
||
9290 | $parentSelect->setSelected($parent); |
||
9291 | } else { |
||
9292 | $parent_item_id = Session::read('parent_item_id', 0); |
||
9293 | $parentSelect->setSelected($parent_item_id); |
||
9294 | } |
||
9295 | |||
9296 | if (is_array($arrLP)) { |
||
9297 | reset($arrLP); |
||
9298 | } |
||
9299 | |||
9300 | $arrHide = []; |
||
9301 | // POSITION |
||
9302 | for ($i = 0; $i < count($arrLP); $i++) { |
||
9303 | if (($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) || |
||
9304 | $arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM |
||
9305 | ) { |
||
9306 | $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"'; |
||
9307 | } |
||
9308 | } |
||
9309 | |||
9310 | $position = $form->addSelect( |
||
9311 | 'previous', |
||
9312 | get_lang('Position'), |
||
9313 | [], |
||
9314 | ['id' => 'previous'] |
||
9315 | ); |
||
9316 | |||
9317 | $position->addOption(get_lang('FirstPosition'), 0); |
||
9318 | foreach ($arrHide as $key => $value) { |
||
9319 | $padding = isset($value['padding']) ? $value['padding'] : 20; |
||
9320 | $position->addOption( |
||
9321 | $value['value'], |
||
9322 | $key, |
||
9323 | 'style="padding-left:'.$padding.'px;"' |
||
9324 | ); |
||
9325 | } |
||
9326 | $position->setSelected($selectedPosition); |
||
9327 | |||
9328 | if (is_array($arrLP)) { |
||
9329 | reset($arrLP); |
||
9330 | } |
||
9331 | |||
9332 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
9333 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
9334 | } |
||
9335 | $extraField = new ExtraField('lp_item'); |
||
9336 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
9337 | |||
9338 | if ($action !== 'move') { |
||
9339 | $arrHide = []; |
||
9340 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
9341 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] !== 'dir' && |
||
9342 | $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM |
||
9343 | ) { |
||
9344 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9345 | } |
||
9346 | } |
||
9347 | |||
9348 | if (!$no_display_add) { |
||
9349 | $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null; |
||
9350 | $edit = isset($_GET['edit']) ? $_GET['edit'] : null; |
||
9351 | if ($extra_info === 'new' || $item_type == TOOL_DOCUMENT || |
||
9352 | $item_type == TOOL_LP_FINAL_ITEM || $edit === 'true' |
||
9353 | ) { |
||
9354 | if (isset($_POST['content'])) { |
||
9355 | $content = stripslashes($_POST['content']); |
||
9356 | } elseif (is_array($extra_info)) { |
||
9357 | //If it's an html document or a text file |
||
9358 | if (!$no_display_edit_textarea) { |
||
9359 | $content = $this->display_document( |
||
9360 | $extra_info['path'], |
||
9361 | false, |
||
9362 | false |
||
9363 | ); |
||
9364 | } |
||
9365 | } elseif (is_numeric($extra_info)) { |
||
9366 | $content = $this->display_document( |
||
9367 | $extra_info, |
||
9368 | false, |
||
9369 | false |
||
9370 | ); |
||
9371 | } else { |
||
9372 | $content = ''; |
||
9373 | } |
||
9374 | |||
9375 | if (!$no_display_edit_textarea) { |
||
9376 | // We need to calculate here some specific settings for the online editor. |
||
9377 | // The calculated settings work for documents in the Documents tool |
||
9378 | // (on the root or in subfolders). |
||
9379 | // For documents in native scorm packages it is unclear whether the |
||
9380 | // online editor should be activated or not. |
||
9381 | |||
9382 | // A new document, it is in the root of the repository. |
||
9383 | if (is_array($extra_info) && $extra_info != 'new') { |
||
9384 | // The document already exists. Whe have to determine its relative path towards the repository root. |
||
9385 | $relative_path = explode('/', $extra_info['dir']); |
||
9386 | $cnt = count($relative_path) - 2; |
||
9387 | if ($cnt < 0) { |
||
9388 | $cnt = 0; |
||
9389 | } |
||
9390 | $relative_prefix = str_repeat('../', $cnt); |
||
9391 | $relative_path = array_slice($relative_path, 1, $cnt); |
||
9392 | $relative_path = implode('/', $relative_path); |
||
9393 | if (strlen($relative_path) > 0) { |
||
9394 | $relative_path = $relative_path.'/'; |
||
9395 | } |
||
9396 | } else { |
||
9397 | $result = $this->generate_lp_folder($_course); |
||
9398 | $relative_path = api_substr($result['dir'], 1, strlen($result['dir'])); |
||
9399 | $relative_prefix = '../../'; |
||
9400 | } |
||
9401 | |||
9402 | $editor_config = [ |
||
9403 | 'ToolbarSet' => 'LearningPathDocuments', |
||
9404 | 'Width' => '100%', |
||
9405 | 'Height' => '500', |
||
9406 | 'FullPage' => true, |
||
9407 | 'CreateDocumentDir' => $relative_prefix, |
||
9408 | 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/', |
||
9409 | 'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path, |
||
9410 | ]; |
||
9411 | |||
9412 | if ($_GET['action'] === 'add_item') { |
||
9413 | $class = 'add'; |
||
9414 | $text = get_lang('LPCreateDocument'); |
||
9415 | } else { |
||
9416 | if ($_GET['action'] === 'edit_item') { |
||
9417 | $class = 'save'; |
||
9418 | $text = get_lang('SaveDocument'); |
||
9419 | } |
||
9420 | } |
||
9421 | |||
9422 | $form->addButtonSave($text, 'submit_button'); |
||
9423 | $renderer = $form->defaultRenderer(); |
||
9424 | $renderer->setElementTemplate(' {label}{element}', 'content_lp'); |
||
9425 | $form->addElement('html', '<div class="editor-lp">'); |
||
9426 | $form->addHtmlEditor('content_lp', null, null, true, $editor_config, true); |
||
9427 | $form->addElement('html', '</div>'); |
||
9428 | $defaults['content_lp'] = $content; |
||
9429 | } |
||
9430 | } elseif (is_numeric($extra_info)) { |
||
9431 | $form->addButtonSave(get_lang('SaveDocument'), 'submit_button'); |
||
9432 | |||
9433 | $return = $this->display_document($extra_info, true, true, true); |
||
9434 | $form->addElement('html', $return); |
||
9435 | } |
||
9436 | } |
||
9437 | } |
||
9438 | if (isset($extra_info['item_type']) && |
||
9439 | $extra_info['item_type'] == TOOL_LP_FINAL_ITEM |
||
9440 | ) { |
||
9441 | $parentSelect->freeze(); |
||
9442 | $position->freeze(); |
||
9443 | } |
||
9444 | |||
9445 | if ($action === 'move') { |
||
9446 | $form->addElement('hidden', 'title', $item_title); |
||
9447 | $form->addElement('hidden', 'description', $item_description); |
||
9448 | } |
||
9449 | if (is_numeric($extra_info)) { |
||
9450 | $form->addButtonSave(get_lang('SaveDocument'), 'submit_button'); |
||
9451 | $form->addElement('hidden', 'path', $extra_info); |
||
9452 | } elseif (is_array($extra_info)) { |
||
9453 | $form->addButtonSave(get_lang('SaveDocument'), 'submit_button'); |
||
9454 | $form->addElement('hidden', 'path', $extra_info['path']); |
||
9455 | } |
||
9456 | |||
9457 | if ($item instanceof learnpathItem) { |
||
9458 | $form->addButtonSave(get_lang('SaveDocument'), 'submit_button'); |
||
9459 | $form->addElement('hidden', 'path', $item->path); |
||
9460 | } |
||
9461 | $form->addElement('hidden', 'type', TOOL_DOCUMENT); |
||
9462 | $form->addElement('hidden', 'post_time', time()); |
||
9463 | $this->setAuthorLpItem($form); |
||
9464 | $form->setDefaults($defaults); |
||
9465 | |||
9466 | return $form->returnForm(); |
||
9467 | } |
||
9468 | |||
9469 | /** |
||
9470 | * Returns the form to update or create a read-out text. |
||
9471 | * |
||
9472 | * @param string $action "add" or "edit" |
||
9473 | * @param int $id ID of the lp_item (if already exists) |
||
9474 | * @param mixed $extra_info Integer if document ID, string if info ('new') |
||
9475 | * |
||
9476 | * @throws Exception |
||
9477 | * @throws HTML_QuickForm_Error |
||
9478 | * |
||
9479 | * @return string HTML form |
||
9480 | */ |
||
9481 | public function displayFrmReadOutText($action = 'add', $id = 0, $extra_info = 'new') |
||
9482 | { |
||
9483 | $course_id = api_get_course_int_id(); |
||
9484 | $_course = api_get_course_info(); |
||
9485 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
9486 | |||
9487 | $no_display_edit_textarea = false; |
||
9488 | //If action==edit document |
||
9489 | //We don't display the document form if it's not an editable document (html or txt file) |
||
9490 | if ($action == 'edit') { |
||
9491 | if (is_array($extra_info)) { |
||
9492 | $path_parts = pathinfo($extra_info['dir']); |
||
9493 | if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") { |
||
9494 | $no_display_edit_textarea = true; |
||
9495 | } |
||
9496 | } |
||
9497 | } |
||
9498 | $no_display_add = false; |
||
9499 | |||
9500 | $item_title = ''; |
||
9501 | $item_description = ''; |
||
9502 | if ($id != 0 && is_array($extra_info)) { |
||
9503 | $item_title = stripslashes($extra_info['title']); |
||
9504 | $item_description = stripslashes($extra_info['description']); |
||
9505 | $item_terms = stripslashes($extra_info['terms']); |
||
9506 | if (empty($item_title)) { |
||
9507 | $path_parts = pathinfo($extra_info['path']); |
||
9508 | $item_title = stripslashes($path_parts['filename']); |
||
9509 | } |
||
9510 | } elseif (is_numeric($extra_info)) { |
||
9511 | $sql = "SELECT path, title FROM $tbl_doc WHERE c_id = ".$course_id." AND iid = ".intval($extra_info); |
||
9512 | $result = Database::query($sql); |
||
9513 | $row = Database::fetch_array($result); |
||
9514 | $item_title = $row['title']; |
||
9515 | $item_title = str_replace('_', ' ', $item_title); |
||
9516 | if (empty($item_title)) { |
||
9517 | $path_parts = pathinfo($row['path']); |
||
9518 | $item_title = stripslashes($path_parts['filename']); |
||
9519 | } |
||
9520 | } |
||
9521 | |||
9522 | $parent = 0; |
||
9523 | if ($id != 0 && is_array($extra_info)) { |
||
9524 | $parent = $extra_info['parent_item_id']; |
||
9525 | } |
||
9526 | |||
9527 | $arrLP = $this->getItemsForForm(); |
||
9528 | $this->tree_array($arrLP); |
||
9529 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
9530 | unset($this->arrMenu); |
||
9531 | |||
9532 | if ($action === 'add') { |
||
9533 | $formHeader = get_lang('CreateTheDocument'); |
||
9534 | } else { |
||
9535 | $formHeader = get_lang('EditTheCurrentDocument'); |
||
9536 | } |
||
9537 | |||
9538 | if ('edit' === $action) { |
||
9539 | $urlAudioIcon = Display::url( |
||
9540 | Display::return_icon('audio.png', get_lang('CreateReadOutText'), [], ICON_SIZE_TINY), |
||
9541 | api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq().'&lp_id='.$this->lp_id.'&' |
||
9542 | .http_build_query(['view' => 'build', 'id' => $id, 'action' => 'add_audio']) |
||
9543 | ); |
||
9544 | } else { |
||
9545 | $urlAudioIcon = Display::return_icon('audio.png', get_lang('CreateReadOutText'), [], ICON_SIZE_TINY); |
||
9546 | } |
||
9547 | |||
9548 | $form = new FormValidator( |
||
9549 | 'frm_add_reading', |
||
9550 | 'POST', |
||
9551 | $this->getCurrentBuildingModeURL(), |
||
9552 | '', |
||
9553 | ['enctype' => 'multipart/form-data'] |
||
9554 | ); |
||
9555 | $form->addHeader($formHeader); |
||
9556 | $form->addHtml( |
||
9557 | Display::return_message( |
||
9558 | sprintf(get_lang('FrmReadOutTextIntro'), $urlAudioIcon), |
||
9559 | 'normal', |
||
9560 | false |
||
9561 | ) |
||
9562 | ); |
||
9563 | $defaults['title'] = !empty($item_title) ? Security::remove_XSS($item_title) : ''; |
||
9564 | $defaults['description'] = $item_description; |
||
9565 | |||
9566 | $data = $this->generate_lp_folder($_course); |
||
9567 | |||
9568 | if ($action != 'edit') { |
||
9569 | $folders = DocumentManager::get_all_document_folders($_course, 0, true); |
||
9570 | DocumentManager::build_directory_selector( |
||
9571 | $folders, |
||
9572 | '', |
||
9573 | [], |
||
9574 | true, |
||
9575 | $form, |
||
9576 | 'directory_parent_id' |
||
9577 | ); |
||
9578 | } |
||
9579 | |||
9580 | if (isset($data['id'])) { |
||
9581 | $defaults['directory_parent_id'] = $data['id']; |
||
9582 | } |
||
9583 | $this->setItemTitle($form); |
||
9584 | |||
9585 | $arrHide[0]['value'] = $this->name; |
||
9586 | $arrHide[0]['padding'] = 20; |
||
9587 | |||
9588 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
9589 | if ($action != 'add') { |
||
9590 | if ($arrLP[$i]['item_type'] == 'dir' && |
||
9591 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
9592 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
9593 | ) { |
||
9594 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9595 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
9596 | } |
||
9597 | } else { |
||
9598 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
9599 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9600 | $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20; |
||
9601 | } |
||
9602 | } |
||
9603 | } |
||
9604 | |||
9605 | $parent_select = $form->addSelect( |
||
9606 | 'parent', |
||
9607 | get_lang('Parent'), |
||
9608 | [], |
||
9609 | ['onchange' => "javascript: load_cbo(this.value, 'frm_add_reading_previous');"] |
||
9610 | ); |
||
9611 | |||
9612 | $my_count = 0; |
||
9613 | foreach ($arrHide as $key => $value) { |
||
9614 | if ($my_count != 0) { |
||
9615 | // The LP name is also the first section and is not in the same charset like the other sections. |
||
9616 | $value['value'] = Security::remove_XSS($value['value']); |
||
9617 | $parent_select->addOption( |
||
9618 | $value['value'], |
||
9619 | $key, |
||
9620 | 'style="padding-left:'.$value['padding'].'px;"' |
||
9621 | ); |
||
9622 | } else { |
||
9623 | $value['value'] = Security::remove_XSS($value['value']); |
||
9624 | $parent_select->addOption( |
||
9625 | $value['value'], |
||
9626 | $key, |
||
9627 | 'style="padding-left:'.$value['padding'].'px;"' |
||
9628 | ); |
||
9629 | } |
||
9630 | $my_count++; |
||
9631 | } |
||
9632 | |||
9633 | if (!empty($id)) { |
||
9634 | $parent_select->setSelected($parent); |
||
9635 | } else { |
||
9636 | $parent_item_id = Session::read('parent_item_id', 0); |
||
9637 | $parent_select->setSelected($parent_item_id); |
||
9638 | } |
||
9639 | |||
9640 | if (is_array($arrLP)) { |
||
9641 | reset($arrLP); |
||
9642 | } |
||
9643 | |||
9644 | $arrHide = []; |
||
9645 | $s_selected_position = null; |
||
9646 | |||
9647 | // POSITION |
||
9648 | $lastPosition = null; |
||
9649 | |||
9650 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
9651 | if (($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) && |
||
9652 | $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM |
||
9653 | ) { |
||
9654 | if ((isset($extra_info['previous_item_id']) && |
||
9655 | $extra_info['previous_item_id'] == $arrLP[$i]['id']) || $action == 'add' |
||
9656 | ) { |
||
9657 | $s_selected_position = $arrLP[$i]['id']; |
||
9658 | } |
||
9659 | $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"'; |
||
9660 | } |
||
9661 | $lastPosition = $arrLP[$i]['id']; |
||
9662 | } |
||
9663 | |||
9664 | if (empty($s_selected_position)) { |
||
9665 | $s_selected_position = $lastPosition; |
||
9666 | } |
||
9667 | |||
9668 | $position = $form->addSelect( |
||
9669 | 'previous', |
||
9670 | get_lang('Position'), |
||
9671 | [] |
||
9672 | ); |
||
9673 | $position->addOption(get_lang('FirstPosition'), 0); |
||
9674 | |||
9675 | foreach ($arrHide as $key => $value) { |
||
9676 | $padding = isset($value['padding']) ? $value['padding'] : 20; |
||
9677 | $position->addOption( |
||
9678 | $value['value'], |
||
9679 | $key, |
||
9680 | 'style="padding-left:'.$padding.'px;"' |
||
9681 | ); |
||
9682 | } |
||
9683 | $position->setSelected($s_selected_position); |
||
9684 | |||
9685 | if (is_array($arrLP)) { |
||
9686 | reset($arrLP); |
||
9687 | } |
||
9688 | |||
9689 | if ('edit' === $action) { |
||
9690 | $excludeExtraFields = []; |
||
9691 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
9692 | $excludeExtraFields = ['start_date', 'end_date']; |
||
9693 | } |
||
9694 | $extraField = new ExtraField('lp_item'); |
||
9695 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
9696 | } |
||
9697 | |||
9698 | $arrHide = []; |
||
9699 | |||
9700 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
9701 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir' && |
||
9702 | $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM |
||
9703 | ) { |
||
9704 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
9705 | } |
||
9706 | } |
||
9707 | |||
9708 | if (!$no_display_add) { |
||
9709 | $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null; |
||
9710 | $edit = isset($_GET['edit']) ? $_GET['edit'] : null; |
||
9711 | |||
9712 | if ($extra_info == 'new' || $item_type == TOOL_READOUT_TEXT || $edit == 'true') { |
||
9713 | if (!$no_display_edit_textarea) { |
||
9714 | $content = ''; |
||
9715 | |||
9716 | if (isset($_POST['content'])) { |
||
9717 | $content = stripslashes($_POST['content']); |
||
9718 | } elseif (is_array($extra_info)) { |
||
9719 | $content = $this->display_document($extra_info['path'], false, false); |
||
9720 | } elseif (is_numeric($extra_info)) { |
||
9721 | $content = $this->display_document($extra_info, false, false); |
||
9722 | } |
||
9723 | |||
9724 | // A new document, it is in the root of the repository. |
||
9725 | if (is_array($extra_info) && $extra_info != 'new') { |
||
9726 | } else { |
||
9727 | $this->generate_lp_folder($_course); |
||
9728 | } |
||
9729 | |||
9730 | if ($_GET['action'] == 'add_item') { |
||
9731 | $text = get_lang('LPCreateDocument'); |
||
9732 | } else { |
||
9733 | $text = get_lang('SaveDocument'); |
||
9734 | } |
||
9735 | |||
9736 | $form->addTextarea('content_lp', get_lang('Content'), ['rows' => 20]); |
||
9737 | $form |
||
9738 | ->defaultRenderer() |
||
9739 | ->setElementTemplate($form->getDefaultElementTemplate(), 'content_lp'); |
||
9740 | $form->addButtonSave($text, 'submit_button'); |
||
9741 | $defaults['content_lp'] = $content; |
||
9742 | } |
||
9743 | } elseif (is_numeric($extra_info)) { |
||
9744 | $form->addButtonSave(get_lang('SaveDocument'), 'submit_button'); |
||
9745 | |||
9746 | $return = $this->display_document($extra_info, true, true, true); |
||
9747 | $form->addElement('html', $return); |
||
9748 | } |
||
9749 | } |
||
9750 | |||
9751 | if (is_numeric($extra_info)) { |
||
9752 | $form->addElement('hidden', 'path', $extra_info); |
||
9753 | } elseif (is_array($extra_info)) { |
||
9754 | $form->addElement('hidden', 'path', $extra_info['path']); |
||
9755 | } |
||
9756 | |||
9757 | $form->addElement('hidden', 'type', TOOL_READOUT_TEXT); |
||
9758 | $form->addElement('hidden', 'post_time', time()); |
||
9759 | $this->setAuthorLpItem($form); |
||
9760 | $form->setDefaults($defaults); |
||
9761 | |||
9762 | return $form->returnForm(); |
||
9763 | } |
||
9764 | |||
9765 | /** |
||
9766 | * @param array $courseInfo |
||
9767 | * @param string $content |
||
9768 | * @param string $title |
||
9769 | * @param int $parentId |
||
9770 | * |
||
9771 | * @throws \Doctrine\ORM\ORMException |
||
9772 | * @throws \Doctrine\ORM\OptimisticLockException |
||
9773 | * @throws \Doctrine\ORM\TransactionRequiredException |
||
9774 | * |
||
9775 | * @return int |
||
9776 | */ |
||
9777 | public function createReadOutText($courseInfo, $content = '', $title = '', $parentId = 0) |
||
9778 | { |
||
9779 | $creatorId = api_get_user_id(); |
||
9780 | $sessionId = api_get_session_id(); |
||
9781 | |||
9782 | // Generates folder |
||
9783 | $result = $this->generate_lp_folder($courseInfo); |
||
9784 | $dir = $result['dir']; |
||
9785 | |||
9786 | if (empty($parentId) || $parentId == '/') { |
||
9787 | $postDir = isset($_POST['dir']) ? $_POST['dir'] : $dir; |
||
9788 | $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting. |
||
9789 | |||
9790 | if ($parentId === '/') { |
||
9791 | $dir = '/'; |
||
9792 | } |
||
9793 | |||
9794 | // Please, do not modify this dirname formatting. |
||
9795 | if (strstr($dir, '..')) { |
||
9796 | $dir = '/'; |
||
9797 | } |
||
9798 | |||
9799 | if (!empty($dir[0]) && $dir[0] == '.') { |
||
9800 | $dir = substr($dir, 1); |
||
9801 | } |
||
9802 | if (!empty($dir[0]) && $dir[0] != '/') { |
||
9803 | $dir = '/'.$dir; |
||
9804 | } |
||
9805 | if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') { |
||
9806 | $dir .= '/'; |
||
9807 | } |
||
9808 | } else { |
||
9809 | $parentInfo = DocumentManager::get_document_data_by_id( |
||
9810 | $parentId, |
||
9811 | $courseInfo['code'] |
||
9812 | ); |
||
9813 | if (!empty($parentInfo)) { |
||
9814 | $dir = $parentInfo['path'].'/'; |
||
9815 | } |
||
9816 | } |
||
9817 | |||
9818 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
9819 | |||
9820 | if (!is_dir($filepath)) { |
||
9821 | $dir = '/'; |
||
9822 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
9823 | } |
||
9824 | |||
9825 | $originalTitle = !empty($title) ? $title : $_POST['title']; |
||
9826 | |||
9827 | if (!empty($title)) { |
||
9828 | $title = api_replace_dangerous_char(stripslashes($title)); |
||
9829 | } else { |
||
9830 | $title = api_replace_dangerous_char(stripslashes($_POST['title'])); |
||
9831 | } |
||
9832 | |||
9833 | $title = disable_dangerous_file($title); |
||
9834 | $filename = $title; |
||
9835 | $content = !empty($content) ? $content : $_POST['content_lp']; |
||
9836 | $tmpFileName = $filename; |
||
9837 | |||
9838 | $i = 0; |
||
9839 | while (file_exists($filepath.$tmpFileName.'.html')) { |
||
9840 | $tmpFileName = $filename.'_'.++$i; |
||
9841 | } |
||
9842 | |||
9843 | $filename = $tmpFileName.'.html'; |
||
9844 | $content = stripslashes($content); |
||
9845 | |||
9846 | if (file_exists($filepath.$filename)) { |
||
9847 | return 0; |
||
9848 | } |
||
9849 | |||
9850 | $putContent = file_put_contents($filepath.$filename, $content); |
||
9851 | |||
9852 | if ($putContent === false) { |
||
9853 | return 0; |
||
9854 | } |
||
9855 | |||
9856 | $fileSize = filesize($filepath.$filename); |
||
9857 | $saveFilePath = $dir.$filename; |
||
9858 | |||
9859 | $documentId = add_document( |
||
9860 | $courseInfo, |
||
9861 | $saveFilePath, |
||
9862 | 'file', |
||
9863 | $fileSize, |
||
9864 | $tmpFileName, |
||
9865 | '', |
||
9866 | 0, //readonly |
||
9867 | true, |
||
9868 | null, |
||
9869 | $sessionId, |
||
9870 | $creatorId |
||
9871 | ); |
||
9872 | |||
9873 | if (!$documentId) { |
||
9874 | return 0; |
||
9875 | } |
||
9876 | |||
9877 | api_item_property_update( |
||
9878 | $courseInfo, |
||
9879 | TOOL_DOCUMENT, |
||
9880 | $documentId, |
||
9881 | 'DocumentAdded', |
||
9882 | $creatorId, |
||
9883 | null, |
||
9884 | null, |
||
9885 | null, |
||
9886 | null, |
||
9887 | $sessionId |
||
9888 | ); |
||
9889 | |||
9890 | $newComment = isset($_POST['comment']) ? trim($_POST['comment']) : ''; |
||
9891 | $newTitle = $originalTitle; |
||
9892 | |||
9893 | if ($newComment || $newTitle) { |
||
9894 | $em = Database::getManager(); |
||
9895 | |||
9896 | /** @var CDocument $doc */ |
||
9897 | $doc = $em->find('ChamiloCourseBundle:CDocument', $documentId); |
||
9898 | |||
9899 | if ($newComment) { |
||
9900 | $doc->setComment($newComment); |
||
9901 | } |
||
9902 | |||
9903 | if ($newTitle) { |
||
9904 | $doc->setTitle($newTitle); |
||
9905 | } |
||
9906 | |||
9907 | $em->persist($doc); |
||
9908 | $em->flush(); |
||
9909 | } |
||
9910 | |||
9911 | return $documentId; |
||
9912 | } |
||
9913 | |||
9914 | /** |
||
9915 | * Return HTML form to add/edit a link item. |
||
9916 | * |
||
9917 | * @param string $action (add/edit) |
||
9918 | * @param int $id Item ID if exists |
||
9919 | * @param mixed $extra_info |
||
9920 | * |
||
9921 | * @throws Exception |
||
9922 | * @throws HTML_QuickForm_Error |
||
9923 | * |
||
9924 | * @return string HTML form |
||
9925 | */ |
||
9926 | public function display_link_form( |
||
9927 | $action = 'add', |
||
9928 | $id = 0, |
||
9929 | $extra_info = '', |
||
9930 | $item = null, |
||
9931 | $excludeExtraFields = [] |
||
9932 | ) { |
||
9933 | $course_id = api_get_course_int_id(); |
||
9934 | $tbl_link = Database::get_course_table(TABLE_LINK); |
||
9935 | |||
9936 | $item_title = ''; |
||
9937 | $item_description = ''; |
||
9938 | $item_url = ''; |
||
9939 | |||
9940 | $previousId = 0; |
||
9941 | if ($id != 0 && is_array($extra_info)) { |
||
9942 | $item_title = stripslashes($extra_info['title']); |
||
9943 | $item_description = stripslashes($extra_info['description']); |
||
9944 | $item_url = stripslashes($extra_info['url']); |
||
9945 | $previousId = $extra_info['previous_item_id']; |
||
9946 | } elseif (is_numeric($extra_info)) { |
||
9947 | $extra_info = (int) $extra_info; |
||
9948 | $sql = "SELECT title, description, url |
||
9949 | FROM $tbl_link |
||
9950 | WHERE c_id = $course_id AND iid = $extra_info"; |
||
9951 | $result = Database::query($sql); |
||
9952 | $row = Database::fetch_array($result); |
||
9953 | $item_title = $row['title']; |
||
9954 | $item_description = $row['description']; |
||
9955 | $item_url = $row['url']; |
||
9956 | } |
||
9957 | |||
9958 | if ($item instanceof learnpathItem) { |
||
9959 | $previousId = $extra_info->previous; |
||
9960 | } |
||
9961 | |||
9962 | $form = new FormValidator( |
||
9963 | 'edit_link', |
||
9964 | 'POST', |
||
9965 | $this->getCurrentBuildingModeURL() |
||
9966 | ); |
||
9967 | $defaults = []; |
||
9968 | $parent = 0; |
||
9969 | if ($id != 0 && is_array($extra_info)) { |
||
9970 | $parent = $extra_info['parent_item_id']; |
||
9971 | } |
||
9972 | |||
9973 | $arrLP = $this->getItemsForForm(); |
||
9974 | |||
9975 | $this->tree_array($arrLP); |
||
9976 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
9977 | unset($this->arrMenu); |
||
9978 | |||
9979 | if ($action === 'add') { |
||
9980 | $legend = get_lang('CreateTheLink'); |
||
9981 | } elseif ($action === 'move') { |
||
9982 | $legend = get_lang('MoveCurrentLink'); |
||
9983 | } else { |
||
9984 | $legend = get_lang('EditCurrentLink'); |
||
9985 | } |
||
9986 | |||
9987 | $form->addHeader($legend); |
||
9988 | |||
9989 | if ($action !== 'move') { |
||
9990 | $this->setItemTitle($form); |
||
9991 | $defaults['title'] = $item_title; |
||
9992 | } |
||
9993 | |||
9994 | $selectParent = $form->addSelect( |
||
9995 | 'parent', |
||
9996 | get_lang('Parent'), |
||
9997 | [], |
||
9998 | ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form'] |
||
9999 | ); |
||
10000 | $selectParent->addOption($this->name, 0); |
||
10001 | $arrHide = [ |
||
10002 | $id, |
||
10003 | ]; |
||
10004 | |||
10005 | $parent_item_id = Session::read('parent_item_id', 0); |
||
10006 | |||
10007 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
10008 | if ($action != 'add') { |
||
10009 | if ( |
||
10010 | ($arrLP[$i]['item_type'] == 'dir') && |
||
10011 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
10012 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
10013 | ) { |
||
10014 | $selectParent->addOption( |
||
10015 | $arrLP[$i]['title'], |
||
10016 | $arrLP[$i]['id'], |
||
10017 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px;'] |
||
10018 | ); |
||
10019 | |||
10020 | if ($parent == $arrLP[$i]['id']) { |
||
10021 | $selectParent->setSelected($arrLP[$i]['id']); |
||
10022 | } |
||
10023 | } else { |
||
10024 | $arrHide[] = $arrLP[$i]['id']; |
||
10025 | } |
||
10026 | } else { |
||
10027 | if ($arrLP[$i]['item_type'] == 'dir') { |
||
10028 | $selectParent->addOption( |
||
10029 | $arrLP[$i]['title'], |
||
10030 | $arrLP[$i]['id'], |
||
10031 | ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px'] |
||
10032 | ); |
||
10033 | |||
10034 | if ($parent_item_id == $arrLP[$i]['id']) { |
||
10035 | $selectParent->setSelected($arrLP[$i]['id']); |
||
10036 | } |
||
10037 | } |
||
10038 | } |
||
10039 | } |
||
10040 | |||
10041 | if (is_array($arrLP)) { |
||
10042 | reset($arrLP); |
||
10043 | } |
||
10044 | |||
10045 | $selectPrevious = $form->addSelect( |
||
10046 | 'previous', |
||
10047 | get_lang('Position'), |
||
10048 | [], |
||
10049 | ['id' => 'previous', 'class' => 'learnpath_item_form'] |
||
10050 | ); |
||
10051 | $selectPrevious->addOption(get_lang('FirstPosition'), 0); |
||
10052 | |||
10053 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
10054 | if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) { |
||
10055 | $selectPrevious->addOption( |
||
10056 | $arrLP[$i]['title'], |
||
10057 | $arrLP[$i]['id'] |
||
10058 | ); |
||
10059 | |||
10060 | if ($previousId == $arrLP[$i]['id']) { |
||
10061 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
10062 | } elseif ($action === 'add') { |
||
10063 | $selectPrevious->setSelected($arrLP[$i]['id']); |
||
10064 | } |
||
10065 | } |
||
10066 | } |
||
10067 | |||
10068 | if ($action !== 'move') { |
||
10069 | $urlAttributes = ['class' => 'learnpath_item_form']; |
||
10070 | |||
10071 | if (is_numeric($extra_info)) { |
||
10072 | $urlAttributes['disabled'] = 'disabled'; |
||
10073 | } |
||
10074 | |||
10075 | $form->addElement('url', 'url', get_lang('Url'), $urlAttributes); |
||
10076 | $defaults['url'] = $item_url; |
||
10077 | $arrHide = []; |
||
10078 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
10079 | if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') { |
||
10080 | $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title']; |
||
10081 | } |
||
10082 | } |
||
10083 | } |
||
10084 | |||
10085 | if ('edit' === $action) { |
||
10086 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
10087 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
10088 | } |
||
10089 | $extraField = new ExtraField('lp_item'); |
||
10090 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
10091 | } |
||
10092 | |||
10093 | if ($action === 'add') { |
||
10094 | $form->addButtonSave(get_lang('AddLinkToCourse'), 'submit_button'); |
||
10095 | } else { |
||
10096 | $form->addButtonSave(get_lang('EditCurrentLink'), 'submit_button'); |
||
10097 | } |
||
10098 | |||
10099 | if ($action === 'move') { |
||
10100 | $form->addHidden('title', $item_title); |
||
10101 | $form->addHidden('description', $item_description); |
||
10102 | } |
||
10103 | |||
10104 | if (is_numeric($extra_info)) { |
||
10105 | $form->addHidden('path', $extra_info); |
||
10106 | } elseif (is_array($extra_info)) { |
||
10107 | $form->addHidden('path', $extra_info['path']); |
||
10108 | } |
||
10109 | $form->addHidden('type', TOOL_LINK); |
||
10110 | $form->addHidden('post_time', time()); |
||
10111 | $this->setAuthorLpItem($form); |
||
10112 | $form->setDefaults($defaults); |
||
10113 | |||
10114 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
10115 | } |
||
10116 | |||
10117 | /** |
||
10118 | * Return HTML form to add/edit a student publication (work). |
||
10119 | * |
||
10120 | * @param string $action |
||
10121 | * @param int $id Item ID if already exists |
||
10122 | * @param string $extra_info |
||
10123 | * |
||
10124 | * @throws Exception |
||
10125 | * |
||
10126 | * @return string HTML form |
||
10127 | */ |
||
10128 | public function display_student_publication_form( |
||
10129 | $action = 'add', |
||
10130 | $id = 0, |
||
10131 | $extra_info = '', |
||
10132 | $item = null, |
||
10133 | $excludeExtraFields = [] |
||
10134 | ) { |
||
10135 | $course_id = api_get_course_int_id(); |
||
10136 | $tbl_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION); |
||
10137 | |||
10138 | $item_title = get_lang('Student_publication'); |
||
10139 | $previousId = 0; |
||
10140 | if ($id != 0 && is_array($extra_info)) { |
||
10141 | $item_title = stripslashes($extra_info['title']); |
||
10142 | $item_description = stripslashes($extra_info['description']); |
||
10143 | $previousId = $extra_info['previous_item_id']; |
||
10144 | } elseif (is_numeric($extra_info)) { |
||
10145 | $extra_info = (int) $extra_info; |
||
10146 | $sql = "SELECT title, description |
||
10147 | FROM $tbl_publication |
||
10148 | WHERE c_id = $course_id AND id = ".$extra_info; |
||
10149 | |||
10150 | $result = Database::query($sql); |
||
10151 | $row = Database::fetch_array($result); |
||
10152 | if ($row) { |
||
10153 | $item_title = $row['title']; |
||
10154 | } |
||
10155 | } |
||
10156 | |||
10157 | if ($item instanceof learnpathItem) { |
||
10158 | $item_title = $item->get_title(); |
||
10159 | $item_description = $item->get_description(); |
||
10160 | $previousId = $item->previous; |
||
10161 | } |
||
10162 | |||
10163 | $parent = 0; |
||
10164 | if ($id != 0 && is_array($extra_info)) { |
||
10165 | $parent = $extra_info['parent_item_id']; |
||
10166 | } |
||
10167 | |||
10168 | $arrLP = $this->getItemsForForm(); |
||
10169 | |||
10170 | $this->tree_array($arrLP); |
||
10171 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
10172 | unset($this->arrMenu); |
||
10173 | |||
10174 | $form = new FormValidator('frm_student_publication', 'post', '#'); |
||
10175 | |||
10176 | if ($action === 'add') { |
||
10177 | $form->addHeader(get_lang('Student_publication')); |
||
10178 | } elseif ($action === 'move') { |
||
10179 | $form->addHeader(get_lang('MoveCurrentStudentPublication')); |
||
10180 | } else { |
||
10181 | $form->addHeader(get_lang('EditCurrentStudentPublication')); |
||
10182 | } |
||
10183 | |||
10184 | if ($action !== 'move') { |
||
10185 | $this->setItemTitle($form); |
||
10186 | } |
||
10187 | |||
10188 | $parentSelect = $form->addSelect( |
||
10189 | 'parent', |
||
10190 | get_lang('Parent'), |
||
10191 | ['0' => $this->name], |
||
10192 | [ |
||
10193 | 'onchange' => 'javascript: load_cbo(this.value);', |
||
10194 | 'class' => 'learnpath_item_form', |
||
10195 | 'id' => 'idParent', |
||
10196 | ] |
||
10197 | ); |
||
10198 | |||
10199 | $arrHide = [$id]; |
||
10200 | for ($i = 0; $i < count($arrLP); $i++) { |
||
10201 | if ($action != 'add') { |
||
10202 | if ( |
||
10203 | ($arrLP[$i]['item_type'] == 'dir') && |
||
10204 | !in_array($arrLP[$i]['id'], $arrHide) && |
||
10205 | !in_array($arrLP[$i]['parent_item_id'], $arrHide) |
||
10206 | ) { |
||
10207 | $parentSelect->addOption( |
||
10208 | $arrLP[$i]['title'], |
||
10209 | $arrLP[$i]['id'], |
||
10210 | ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;'] |
||
10211 | ); |
||
10212 | |||
10213 | if ($parent == $arrLP[$i]['id']) { |
||
10214 | $parentSelect->setSelected($arrLP[$i]['id']); |
||
10215 | } |
||
10216 | } else { |
||
10217 | $arrHide[] = $arrLP[$i]['id']; |
||
10218 | } |
||
10219 | } else { |
||
10220 | if ($arrLP[$i]['item_type'] === 'dir') { |
||
10221 | $parentSelect->addOption( |
||
10222 | $arrLP[$i]['title'], |
||
10223 | $arrLP[$i]['id'], |
||
10224 | ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;'] |
||
10225 | ); |
||
10226 | |||
10227 | if ($parent == $arrLP[$i]['id']) { |
||
10228 | $parentSelect->setSelected($arrLP[$i]['id']); |
||
10229 | } |
||
10230 | } |
||
10231 | } |
||
10232 | } |
||
10233 | |||
10234 | if (is_array($arrLP)) { |
||
10235 | reset($arrLP); |
||
10236 | } |
||
10237 | |||
10238 | $previousSelect = $form->addSelect( |
||
10239 | 'previous', |
||
10240 | get_lang('Position'), |
||
10241 | ['0' => get_lang('FirstPosition')], |
||
10242 | ['id' => 'previous', 'class' => 'learnpath_item_form'] |
||
10243 | ); |
||
10244 | |||
10245 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
10246 | if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) { |
||
10247 | $previousSelect->addOption( |
||
10248 | get_lang('After').' "'.$arrLP[$i]['title'].'"', |
||
10249 | $arrLP[$i]['id'] |
||
10250 | ); |
||
10251 | |||
10252 | if ($previousId == $arrLP[$i]['id']) { |
||
10253 | $previousSelect->setSelected($arrLP[$i]['id']); |
||
10254 | } elseif ($action === 'add') { |
||
10255 | $previousSelect->setSelected($arrLP[$i]['id']); |
||
10256 | } |
||
10257 | } |
||
10258 | } |
||
10259 | |||
10260 | if ('edit' === $action) { |
||
10261 | if (true !== api_get_configuration_value('lp_item_prerequisite_dates')) { |
||
10262 | $excludeExtraFields = array_merge($excludeExtraFields, ['start_date', 'end_date']); |
||
10263 | } |
||
10264 | $extraField = new ExtraField('lp_item'); |
||
10265 | $extraField->addElements($form, $id, $excludeExtraFields); |
||
10266 | } |
||
10267 | |||
10268 | if ($action === 'add') { |
||
10269 | $form->addButtonCreate(get_lang('AddAssignmentToCourse'), 'submit_button'); |
||
10270 | } else { |
||
10271 | $form->addButtonCreate(get_lang('EditCurrentStudentPublication'), 'submit_button'); |
||
10272 | } |
||
10273 | |||
10274 | if ($action === 'move') { |
||
10275 | $form->addHidden('title', $item_title); |
||
10276 | $form->addHidden('description', $item_description); |
||
10277 | } |
||
10278 | |||
10279 | if (is_numeric($extra_info)) { |
||
10280 | $form->addHidden('path', $extra_info); |
||
10281 | } elseif (is_array($extra_info)) { |
||
10282 | $form->addHidden('path', $extra_info['path']); |
||
10283 | } |
||
10284 | |||
10285 | $form->addHidden('type', TOOL_STUDENTPUBLICATION); |
||
10286 | $form->addHidden('post_time', time()); |
||
10287 | $this->setAuthorLpItem($form); |
||
10288 | $form->setDefaults(['title' => $item_title, 'start_date' => null]); |
||
10289 | |||
10290 | $return = '<div class="sectioncomment">'; |
||
10291 | $return .= $form->returnForm(); |
||
10292 | $return .= '</div>'; |
||
10293 | |||
10294 | return $return; |
||
10295 | } |
||
10296 | |||
10297 | /** |
||
10298 | * Displays the menu for manipulating a step. |
||
10299 | * |
||
10300 | * @param int $item_id |
||
10301 | * @param string $item_type |
||
10302 | * |
||
10303 | * @return string |
||
10304 | */ |
||
10305 | public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT) |
||
10306 | { |
||
10307 | $_course = api_get_course_info(); |
||
10308 | $course_code = api_get_course_id(); |
||
10309 | $item_id = (int) $item_id; |
||
10310 | |||
10311 | $return = '<div class="actions">'; |
||
10312 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
10313 | $sql = "SELECT * FROM $tbl_lp_item |
||
10314 | WHERE iid = ".$item_id; |
||
10315 | $result = Database::query($sql); |
||
10316 | $row = Database::fetch_assoc($result); |
||
10317 | |||
10318 | $audio_player = null; |
||
10319 | // We display an audio player if needed. |
||
10320 | if (!empty($row['audio'])) { |
||
10321 | $audio = learnpathItem::fixAudio($row['audio']); |
||
10322 | $webAudioPath = '../..'.api_get_path(REL_COURSE_PATH).$_course['path'].'/document'.$audio; |
||
10323 | $audio_player .= '<div class="lp_mediaplayer" id="container"> |
||
10324 | <audio src="'.$webAudioPath.'" controls> |
||
10325 | </div><br />'; |
||
10326 | } |
||
10327 | |||
10328 | $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$item_id.'&lp_id='.$this->lp_id; |
||
10329 | |||
10330 | if ($item_type != TOOL_LP_FINAL_ITEM) { |
||
10331 | $return .= Display::url( |
||
10332 | Display::return_icon( |
||
10333 | 'edit.png', |
||
10334 | get_lang('Edit'), |
||
10335 | [], |
||
10336 | ICON_SIZE_SMALL |
||
10337 | ), |
||
10338 | $url.'&action=edit_item&path_item='.$row['path'] |
||
10339 | ); |
||
10340 | |||
10341 | $return .= Display::url( |
||
10342 | Display::return_icon( |
||
10343 | 'move.png', |
||
10344 | get_lang('Move'), |
||
10345 | [], |
||
10346 | ICON_SIZE_SMALL |
||
10347 | ), |
||
10348 | $url.'&action=move_item' |
||
10349 | ); |
||
10350 | } |
||
10351 | |||
10352 | // Commented for now as prerequisites cannot be added to chapters. |
||
10353 | if ($item_type != 'dir') { |
||
10354 | $return .= Display::url( |
||
10355 | Display::return_icon( |
||
10356 | 'accept.png', |
||
10357 | get_lang('LearnpathPrerequisites'), |
||
10358 | [], |
||
10359 | ICON_SIZE_SMALL |
||
10360 | ), |
||
10361 | $url.'&action=edit_item_prereq' |
||
10362 | ); |
||
10363 | } |
||
10364 | $return .= Display::url( |
||
10365 | Display::return_icon( |
||
10366 | 'delete.png', |
||
10367 | get_lang('Delete'), |
||
10368 | [], |
||
10369 | ICON_SIZE_SMALL |
||
10370 | ), |
||
10371 | $url.'&action=delete_item' |
||
10372 | ); |
||
10373 | |||
10374 | if (in_array($item_type, [TOOL_DOCUMENT, TOOL_LP_FINAL_ITEM, TOOL_HOTPOTATOES])) { |
||
10375 | $documentData = DocumentManager::get_document_data_by_id($row['path'], $course_code); |
||
10376 | if (empty($documentData)) { |
||
10377 | // Try with iid |
||
10378 | $table = Database::get_course_table(TABLE_DOCUMENT); |
||
10379 | $sql = "SELECT path FROM $table |
||
10380 | WHERE |
||
10381 | c_id = ".api_get_course_int_id()." AND |
||
10382 | iid = ".$row['path']." AND |
||
10383 | path NOT LIKE '%_DELETED_%'"; |
||
10384 | $result = Database::query($sql); |
||
10385 | $documentData = Database::fetch_array($result); |
||
10386 | if ($documentData) { |
||
10387 | $documentData['absolute_path_from_document'] = '/document'.$documentData['path']; |
||
10388 | } |
||
10389 | } |
||
10390 | if (isset($documentData['absolute_path_from_document'])) { |
||
10391 | $return .= get_lang('File').': '.$documentData['absolute_path_from_document']; |
||
10392 | } |
||
10393 | } |
||
10394 | |||
10395 | $return .= '</div>'; |
||
10396 | |||
10397 | if (!empty($audio_player)) { |
||
10398 | $return .= $audio_player; |
||
10399 | } |
||
10400 | |||
10401 | return $return; |
||
10402 | } |
||
10403 | |||
10404 | /** |
||
10405 | * Creates the javascript needed for filling up the checkboxes without page reload. |
||
10406 | * |
||
10407 | * @return string |
||
10408 | */ |
||
10409 | public function get_js_dropdown_array() |
||
10410 | { |
||
10411 | $course_id = api_get_course_int_id(); |
||
10412 | $return = 'var child_name = new Array();'."\n"; |
||
10413 | $return .= 'var child_value = new Array();'."\n\n"; |
||
10414 | $return .= 'child_name[0] = new Array();'."\n"; |
||
10415 | $return .= 'child_value[0] = new Array();'."\n\n"; |
||
10416 | |||
10417 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
10418 | $i = 0; |
||
10419 | $list = $this->getItemsForForm(true); |
||
10420 | |||
10421 | foreach ($list as $row_zero) { |
||
10422 | if ($row_zero['item_type'] !== TOOL_LP_FINAL_ITEM) { |
||
10423 | if ($row_zero['item_type'] == TOOL_QUIZ) { |
||
10424 | $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']); |
||
10425 | } |
||
10426 | $js_var = json_encode(get_lang('After').' '.$row_zero['title']); |
||
10427 | $return .= 'child_name[0]['.$i.'] = '.$js_var.' ;'."\n"; |
||
10428 | $return .= 'child_value[0]['.$i++.'] = "'.$row_zero['iid'].'";'."\n"; |
||
10429 | } |
||
10430 | } |
||
10431 | |||
10432 | $return .= "\n"; |
||
10433 | $sql = "SELECT * FROM $tbl_lp_item |
||
10434 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
10435 | $res = Database::query($sql); |
||
10436 | while ($row = Database::fetch_array($res)) { |
||
10437 | $sql_parent = "SELECT * FROM ".$tbl_lp_item." |
||
10438 | WHERE |
||
10439 | c_id = ".$course_id." AND |
||
10440 | parent_item_id = ".$row['iid']." |
||
10441 | ORDER BY display_order ASC"; |
||
10442 | $res_parent = Database::query($sql_parent); |
||
10443 | $i = 0; |
||
10444 | $return .= 'child_name['.$row['iid'].'] = new Array();'."\n"; |
||
10445 | $return .= 'child_value['.$row['iid'].'] = new Array();'."\n\n"; |
||
10446 | |||
10447 | while ($row_parent = Database::fetch_array($res_parent)) { |
||
10448 | $js_var = json_encode(get_lang('After').' '.$this->cleanItemTitle($row_parent['title'])); |
||
10449 | $return .= 'child_name['.$row['iid'].']['.$i.'] = '.$js_var.' ;'."\n"; |
||
10450 | $return .= 'child_value['.$row['iid'].']['.$i++.'] = "'.$row_parent['iid'].'";'."\n"; |
||
10451 | } |
||
10452 | $return .= "\n"; |
||
10453 | } |
||
10454 | |||
10455 | $return .= " |
||
10456 | function load_cbo(id) { |
||
10457 | if (!id) { |
||
10458 | return false; |
||
10459 | } |
||
10460 | |||
10461 | var cbo = document.getElementById('previous'); |
||
10462 | for (var i = cbo.length - 1; i > 0; i--) { |
||
10463 | cbo.options[i] = null; |
||
10464 | } |
||
10465 | |||
10466 | var k=0; |
||
10467 | for(var i = 1; i <= child_name[id].length; i++){ |
||
10468 | var option = new Option(child_name[id][i - 1], child_value[id][i - 1]); |
||
10469 | option.style.paddingLeft = '40px'; |
||
10470 | cbo.options[i] = option; |
||
10471 | k = i; |
||
10472 | } |
||
10473 | |||
10474 | cbo.options[k].selected = true; |
||
10475 | $('#previous').selectpicker('refresh'); |
||
10476 | }"; |
||
10477 | |||
10478 | return $return; |
||
10479 | } |
||
10480 | |||
10481 | /** |
||
10482 | * Display the form to allow moving an item. |
||
10483 | * |
||
10484 | * @param learnpathItem $item Item ID |
||
10485 | * |
||
10486 | * @throws Exception |
||
10487 | * @throws HTML_QuickForm_Error |
||
10488 | * |
||
10489 | * @return string HTML form |
||
10490 | */ |
||
10491 | public function display_move_item($item) |
||
10492 | { |
||
10493 | $return = ''; |
||
10494 | if ($item) { |
||
10495 | $item_id = $item->getIid(); |
||
10496 | $type = $item->get_type(); |
||
10497 | $path = (int) $item->get_path(); |
||
10498 | |||
10499 | switch ($type) { |
||
10500 | case 'dir': |
||
10501 | case 'asset': |
||
10502 | $return .= $this->display_manipulate($item_id, $type); |
||
10503 | $return .= $this->display_item_form( |
||
10504 | $type, |
||
10505 | get_lang('MoveCurrentChapter'), |
||
10506 | 'move', |
||
10507 | $item_id, |
||
10508 | $item |
||
10509 | ); |
||
10510 | break; |
||
10511 | case TOOL_DOCUMENT: |
||
10512 | $return .= $this->display_manipulate($item_id, $type); |
||
10513 | $return .= $this->display_document_form('move', $item_id, null, $item); |
||
10514 | break; |
||
10515 | case TOOL_LINK: |
||
10516 | $return .= $this->display_manipulate($item_id, $type); |
||
10517 | $return .= $this->display_link_form('move', $item_id, $path, $item); |
||
10518 | break; |
||
10519 | case TOOL_HOTPOTATOES: |
||
10520 | $return .= $this->display_manipulate($item_id, $type); |
||
10521 | $return .= $this->display_link_form('move', $item_id, $item); |
||
10522 | break; |
||
10523 | case TOOL_QUIZ: |
||
10524 | $return .= $this->display_manipulate($item_id, $type); |
||
10525 | $return .= $this->display_quiz_form('move', $item_id, $item); |
||
10526 | break; |
||
10527 | case TOOL_STUDENTPUBLICATION: |
||
10528 | $return .= $this->display_manipulate($item_id, $type); |
||
10529 | $return .= $this->display_student_publication_form('move', $item_id, $path, $item); |
||
10530 | break; |
||
10531 | case TOOL_FORUM: |
||
10532 | $return .= $this->display_manipulate($item_id, $type); |
||
10533 | $return .= $this->display_forum_form('move', $item_id, $path); |
||
10534 | break; |
||
10535 | case TOOL_THREAD: |
||
10536 | $return .= $this->display_manipulate($item_id, $type); |
||
10537 | $return .= $this->display_forum_form('move', $item_id, $path); |
||
10538 | break; |
||
10539 | } |
||
10540 | } |
||
10541 | |||
10542 | return $return; |
||
10543 | } |
||
10544 | |||
10545 | /** |
||
10546 | * Return HTML form to allow prerequisites selection. |
||
10547 | * |
||
10548 | * @todo use FormValidator |
||
10549 | * |
||
10550 | * @param int Item ID |
||
10551 | * |
||
10552 | * @return string HTML form |
||
10553 | */ |
||
10554 | public function display_item_prerequisites_form($item_id = 0) |
||
10555 | { |
||
10556 | $course_id = api_get_course_int_id(); |
||
10557 | $item_id = (int) $item_id; |
||
10558 | |||
10559 | if (empty($item_id)) { |
||
10560 | return ''; |
||
10561 | } |
||
10562 | |||
10563 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
10564 | |||
10565 | /* Current prerequisite */ |
||
10566 | $sql = "SELECT * FROM $tbl_lp_item |
||
10567 | WHERE iid = $item_id"; |
||
10568 | $result = Database::query($sql); |
||
10569 | $row = Database::fetch_array($result); |
||
10570 | $prerequisiteId = $row['prerequisite']; |
||
10571 | |||
10572 | $return = '<legend>'; |
||
10573 | $return .= get_lang('AddEditPrerequisites'); |
||
10574 | $return .= '</legend>'; |
||
10575 | $return .= '<form method="POST">'; |
||
10576 | $return .= '<div class="table-responsive">'; |
||
10577 | $return .= '<table class="table table-hover">'; |
||
10578 | $return .= '<thead>'; |
||
10579 | $return .= '<tr>'; |
||
10580 | $return .= '<th>'.get_lang('LearnpathPrerequisites').'</th>'; |
||
10581 | $return .= '<th width="140">'.get_lang('Minimum').'</th>'; |
||
10582 | $return .= '<th width="140">'.get_lang('Maximum').'</th>'; |
||
10583 | $return .= '</tr>'; |
||
10584 | $return .= '</thead>'; |
||
10585 | |||
10586 | // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146 |
||
10587 | $return .= '<tbody>'; |
||
10588 | $return .= '<tr>'; |
||
10589 | $return .= '<td colspan="3">'; |
||
10590 | $return .= '<div class="radio learnpath"><label for="idNone">'; |
||
10591 | $return .= '<input checked="checked" id="idNone" name="prerequisites" type="radio" />'; |
||
10592 | $return .= get_lang('None').'</label>'; |
||
10593 | $return .= '</div>'; |
||
10594 | $return .= '</tr>'; |
||
10595 | |||
10596 | $sql = "SELECT * FROM $tbl_lp_item |
||
10597 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
10598 | $result = Database::query($sql); |
||
10599 | |||
10600 | $selectedMinScore = []; |
||
10601 | $selectedMaxScore = []; |
||
10602 | $masteryScore = []; |
||
10603 | while ($row = Database::fetch_array($result)) { |
||
10604 | if ($row['iid'] == $item_id) { |
||
10605 | $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score']; |
||
10606 | $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score']; |
||
10607 | } |
||
10608 | $masteryScore[$row['iid']] = $row['mastery_score']; |
||
10609 | } |
||
10610 | |||
10611 | $arrLP = $this->getItemsForForm(); |
||
10612 | $this->tree_array($arrLP); |
||
10613 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
10614 | unset($this->arrMenu); |
||
10615 | |||
10616 | for ($i = 0; $i < count($arrLP); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
|
|||
10617 | $item = $arrLP[$i]; |
||
10618 | |||
10619 | if ($item['id'] == $item_id) { |
||
10620 | break; |
||
10621 | } |
||
10622 | |||
10623 | $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score']; |
||
10624 | $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']] : 0; |
||
10625 | $masteryScoreAsMinValue = isset($masteryScore[$item['id']]) ? $masteryScore[$item['id']] : 0; |
||
10626 | |||
10627 | $return .= '<tr>'; |
||
10628 | $return .= '<td '.(($item['item_type'] != TOOL_QUIZ && $item['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '').'>'; |
||
10629 | $return .= '<div style="margin-left:'.($item['depth'] * 20).'px;" class="radio learnpath">'; |
||
10630 | $return .= '<label for="id'.$item['id'].'">'; |
||
10631 | |||
10632 | $checked = ''; |
||
10633 | if (null !== $prerequisiteId) { |
||
10634 | $checked = in_array($prerequisiteId, [$item['id'], $item['ref']]) ? ' checked="checked" ' : ''; |
||
10635 | } |
||
10636 | |||
10637 | $disabled = $item['item_type'] === 'dir' ? ' disabled="disabled" ' : ''; |
||
10638 | |||
10639 | $return .= '<input '.$checked.' '.$disabled.' id="id'.$item['id'].'" name="prerequisites" type="radio" value="'.$item['id'].'" />'; |
||
10640 | |||
10641 | $icon_name = str_replace(' ', '', $item['item_type']); |
||
10642 | if (file_exists('../img/lp_'.$icon_name.'.png')) { |
||
10643 | $return .= Display::return_icon('lp_'.$icon_name.'.png'); |
||
10644 | } else { |
||
10645 | if (file_exists('../img/lp_'.$icon_name.'.png')) { |
||
10646 | $return .= Display::return_icon('lp_'.$icon_name.'.png'); |
||
10647 | } else { |
||
10648 | $return .= Display::return_icon('folder_document.png'); |
||
10649 | } |
||
10650 | } |
||
10651 | |||
10652 | $return .= $item['title'].'</label>'; |
||
10653 | $return .= '</div>'; |
||
10654 | $return .= '</td>'; |
||
10655 | |||
10656 | if ($item['item_type'] == TOOL_QUIZ) { |
||
10657 | // lets update max_score Quiz information depending of the Quiz Advanced properties |
||
10658 | $lpItemObj = new LpItem($course_id, $item['id']); |
||
10659 | $exercise = new Exercise($course_id); |
||
10660 | $exercise->read($lpItemObj->path); |
||
10661 | $lpItemObj->max_score = $exercise->get_max_score(); |
||
10662 | $lpItemObj->update(); |
||
10663 | $item['max_score'] = $lpItemObj->max_score; |
||
10664 | |||
10665 | //if (empty($selectedMinScoreValue) && !empty($masteryScoreAsMinValue)) { |
||
10666 | if (!isset($selectedMinScore[$item['id']]) && !empty($masteryScoreAsMinValue)) { |
||
10667 | // Backwards compatibility with 1.9.x use mastery_score as min value |
||
10668 | $selectedMinScoreValue = $masteryScoreAsMinValue; |
||
10669 | } |
||
10670 | |||
10671 | $return .= '<td>'; |
||
10672 | $return .= '<input |
||
10673 | class="form-control" |
||
10674 | size="4" maxlength="3" |
||
10675 | name="min_'.$item['id'].'" |
||
10676 | type="number" |
||
10677 | min="0" |
||
10678 | step="any" |
||
10679 | max="'.$item['max_score'].'" |
||
10680 | value="'.$selectedMinScoreValue.'" |
||
10681 | />'; |
||
10682 | $return .= '</td>'; |
||
10683 | $return .= '<td>'; |
||
10684 | $return .= '<input |
||
10685 | class="form-control" |
||
10686 | size="4" |
||
10687 | maxlength="3" |
||
10688 | name="max_'.$item['id'].'" |
||
10689 | type="number" |
||
10690 | min="0" |
||
10691 | step="any" |
||
10692 | max="'.$item['max_score'].'" |
||
10693 | value="'.$selectedMaxScoreValue.'" |
||
10694 | />'; |
||
10695 | $return .= '</td>'; |
||
10696 | } |
||
10697 | |||
10698 | if ($item['item_type'] == TOOL_HOTPOTATOES) { |
||
10699 | $return .= '<td>'; |
||
10700 | $return .= '<input |
||
10701 | size="4" |
||
10702 | maxlength="3" |
||
10703 | name="min_'.$item['id'].'" |
||
10704 | type="number" |
||
10705 | min="0" |
||
10706 | step="any" |
||
10707 | max="'.$item['max_score'].'" |
||
10708 | value="'.$selectedMinScoreValue.'" |
||
10709 | />'; |
||
10710 | $return .= '</td>'; |
||
10711 | $return .= '<td>'; |
||
10712 | $return .= '<input |
||
10713 | size="4" |
||
10714 | maxlength="3" |
||
10715 | name="max_'.$item['id'].'" |
||
10716 | type="number" |
||
10717 | min="0" |
||
10718 | step="any" |
||
10719 | max="'.$item['max_score'].'" |
||
10720 | value="'.$selectedMaxScoreValue.'" |
||
10721 | />'; |
||
10722 | $return .= '</td>'; |
||
10723 | } |
||
10724 | $return .= '</tr>'; |
||
10725 | } |
||
10726 | $return .= '<tr>'; |
||
10727 | $return .= '</tr>'; |
||
10728 | $return .= '</tbody>'; |
||
10729 | $return .= '</table>'; |
||
10730 | $return .= '</div>'; |
||
10731 | $return .= '<div class="form-group">'; |
||
10732 | $return .= '<button class="btn btn-primary" name="submit_button" type="submit">'. |
||
10733 | get_lang('ModifyPrerequisites').'</button>'; |
||
10734 | $return .= '</form>'; |
||
10735 | |||
10736 | return $return; |
||
10737 | } |
||
10738 | |||
10739 | /** |
||
10740 | * Return HTML list to allow prerequisites selection for lp. |
||
10741 | * |
||
10742 | * @return string HTML form |
||
10743 | */ |
||
10744 | public function display_lp_prerequisites_list() |
||
10745 | { |
||
10746 | $course_id = api_get_course_int_id(); |
||
10747 | $lp_id = $this->lp_id; |
||
10748 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
10749 | |||
10750 | // get current prerequisite |
||
10751 | $sql = "SELECT * FROM $tbl_lp WHERE iid = $lp_id "; |
||
10752 | $result = Database::query($sql); |
||
10753 | $row = Database::fetch_array($result); |
||
10754 | $prerequisiteId = $row['prerequisite']; |
||
10755 | $session_id = api_get_session_id(); |
||
10756 | $session_condition = api_get_session_condition($session_id, true, true); |
||
10757 | $sql = "SELECT * FROM $tbl_lp |
||
10758 | WHERE c_id = $course_id $session_condition |
||
10759 | ORDER BY display_order "; |
||
10760 | $rs = Database::query($sql); |
||
10761 | $return = ''; |
||
10762 | $return .= '<select name="prerequisites" class="form-control">'; |
||
10763 | $return .= '<option value="0">'.get_lang('None').'</option>'; |
||
10764 | if (Database::num_rows($rs) > 0) { |
||
10765 | while ($row = Database::fetch_array($rs)) { |
||
10766 | if ($row['id'] == $lp_id) { |
||
10767 | continue; |
||
10768 | } |
||
10769 | $return .= '<option value="'.$row['id'].'" '.(($row['id'] == $prerequisiteId) ? ' selected ' : '').'>'.$row['name'].'</option>'; |
||
10770 | } |
||
10771 | } |
||
10772 | $return .= '</select>'; |
||
10773 | |||
10774 | return $return; |
||
10775 | } |
||
10776 | |||
10777 | /** |
||
10778 | * Creates a list with all the documents in it. |
||
10779 | * |
||
10780 | * @param bool $showInvisibleFiles |
||
10781 | * |
||
10782 | * @throws Exception |
||
10783 | * @throws HTML_QuickForm_Error |
||
10784 | * |
||
10785 | * @return string |
||
10786 | */ |
||
10787 | public function get_documents($showInvisibleFiles = false) |
||
10788 | { |
||
10789 | $course_info = api_get_course_info(); |
||
10790 | $sessionId = api_get_session_id(); |
||
10791 | $documentTree = DocumentManager::get_document_preview( |
||
10792 | $course_info, |
||
10793 | $this->lp_id, |
||
10794 | null, |
||
10795 | $sessionId, |
||
10796 | true, |
||
10797 | null, |
||
10798 | null, |
||
10799 | $showInvisibleFiles, |
||
10800 | true |
||
10801 | ); |
||
10802 | |||
10803 | $headers = [ |
||
10804 | get_lang('Files'), |
||
10805 | get_lang('CreateTheDocument'), |
||
10806 | get_lang('CreateReadOutText'), |
||
10807 | get_lang('Upload'), |
||
10808 | ]; |
||
10809 | |||
10810 | $form = new FormValidator( |
||
10811 | 'form_upload', |
||
10812 | 'POST', |
||
10813 | $this->getCurrentBuildingModeURL(), |
||
10814 | '', |
||
10815 | ['enctype' => 'multipart/form-data'] |
||
10816 | ); |
||
10817 | |||
10818 | $folders = DocumentManager::get_all_document_folders( |
||
10819 | api_get_course_info(), |
||
10820 | 0, |
||
10821 | true |
||
10822 | ); |
||
10823 | |||
10824 | $lpPathInfo = $this->generate_lp_folder(api_get_course_info()); |
||
10825 | |||
10826 | DocumentManager::build_directory_selector( |
||
10827 | $folders, |
||
10828 | $lpPathInfo['id'], |
||
10829 | [], |
||
10830 | true, |
||
10831 | $form, |
||
10832 | 'directory_parent_id' |
||
10833 | ); |
||
10834 | |||
10835 | $group = [ |
||
10836 | $form->createElement( |
||
10837 | 'radio', |
||
10838 | 'if_exists', |
||
10839 | get_lang('UplWhatIfFileExists'), |
||
10840 | get_lang('UplDoNothing'), |
||
10841 | 'nothing' |
||
10842 | ), |
||
10843 | $form->createElement( |
||
10844 | 'radio', |
||
10845 | 'if_exists', |
||
10846 | null, |
||
10847 | get_lang('UplOverwriteLong'), |
||
10848 | 'overwrite' |
||
10849 | ), |
||
10850 | $form->createElement( |
||
10851 | 'radio', |
||
10852 | 'if_exists', |
||
10853 | null, |
||
10854 | get_lang('UplRenameLong'), |
||
10855 | 'rename' |
||
10856 | ), |
||
10857 | ]; |
||
10858 | $form->addGroup($group, null, get_lang('UplWhatIfFileExists')); |
||
10859 | |||
10860 | $fileExistsOption = api_get_setting('document_if_file_exists_option'); |
||
10861 | $defaultFileExistsOption = 'rename'; |
||
10862 | if (!empty($fileExistsOption)) { |
||
10863 | $defaultFileExistsOption = $fileExistsOption; |
||
10864 | } |
||
10865 | $form->setDefaults(['if_exists' => $defaultFileExistsOption]); |
||
10866 | |||
10867 | // Check box options |
||
10868 | $form->addElement( |
||
10869 | 'checkbox', |
||
10870 | 'unzip', |
||
10871 | get_lang('Options'), |
||
10872 | get_lang('Uncompress') |
||
10873 | ); |
||
10874 | |||
10875 | $url = api_get_path(WEB_AJAX_PATH).'document.ajax.php?'.api_get_cidreq().'&a=upload_file&curdirpath='; |
||
10876 | $form->addMultipleUpload($url); |
||
10877 | $new = $this->display_document_form('add', 0); |
||
10878 | $frmReadOutText = $this->displayFrmReadOutText('add'); |
||
10879 | $tabs = Display::tabs( |
||
10880 | $headers, |
||
10881 | [$documentTree, $new, $frmReadOutText, $form->returnForm()], |
||
10882 | 'subtab' |
||
10883 | ); |
||
10884 | |||
10885 | return $tabs; |
||
10886 | } |
||
10887 | |||
10888 | /** |
||
10889 | * Creates a list with all the exercises (quiz) in it. |
||
10890 | * |
||
10891 | * @return string |
||
10892 | */ |
||
10893 | public function get_exercises() |
||
10894 | { |
||
10895 | $course_id = api_get_course_int_id(); |
||
10896 | $session_id = api_get_session_id(); |
||
10897 | $userInfo = api_get_user_info(); |
||
10898 | |||
10899 | // New for hotpotatoes. |
||
10900 | $uploadPath = DIR_HOTPOTATOES; //defined in main_api |
||
10901 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
10902 | $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST); |
||
10903 | $condition_session = api_get_session_condition($session_id, true, true); |
||
10904 | $setting = api_get_configuration_value('show_invisible_exercise_in_lp_list'); |
||
10905 | |||
10906 | $activeCondition = ' active <> -1 '; |
||
10907 | if ($setting) { |
||
10908 | $activeCondition = ' active = 1 '; |
||
10909 | } |
||
10910 | |||
10911 | $categoryCondition = ''; |
||
10912 | $categoryId = isset($_REQUEST['category_id']) ? (int) $_REQUEST['category_id'] : 0; |
||
10913 | if (api_get_configuration_value('allow_exercise_categories') && !empty($categoryId)) { |
||
10914 | $categoryCondition = " AND exercise_category_id = $categoryId "; |
||
10915 | } |
||
10916 | |||
10917 | $keywordCondition = ''; |
||
10918 | $keyword = isset($_REQUEST['keyword']) ? $_REQUEST['keyword'] : ''; |
||
10919 | |||
10920 | if (!empty($keyword)) { |
||
10921 | $keyword = Database::escape_string($keyword); |
||
10922 | $keywordCondition = " AND title LIKE '%$keyword%' "; |
||
10923 | } |
||
10924 | |||
10925 | $sql_quiz = "SELECT * FROM $tbl_quiz |
||
10926 | WHERE |
||
10927 | c_id = $course_id AND |
||
10928 | $activeCondition |
||
10929 | $condition_session |
||
10930 | $categoryCondition |
||
10931 | $keywordCondition |
||
10932 | ORDER BY title ASC"; |
||
10933 | |||
10934 | $sql_hot = "SELECT * FROM $tbl_doc |
||
10935 | WHERE |
||
10936 | c_id = $course_id AND |
||
10937 | path LIKE '".$uploadPath."/%/%htm%' |
||
10938 | $condition_session |
||
10939 | ORDER BY id ASC"; |
||
10940 | |||
10941 | $res_quiz = Database::query($sql_quiz); |
||
10942 | $res_hot = Database::query($sql_hot); |
||
10943 | |||
10944 | $currentUrl = api_get_self().'?'.api_get_cidreq().'&action=add_item&type=step&lp_id='.$this->lp_id.'#resource_tab-2'; |
||
10945 | |||
10946 | // Create a search-box |
||
10947 | $form = new FormValidator('search_simple', 'get', $currentUrl); |
||
10948 | $form->addHidden('action', 'add_item'); |
||
10949 | $form->addHidden('type', 'step'); |
||
10950 | $form->addHidden('lp_id', $this->lp_id); |
||
10951 | $form->addHidden('lp_build_selected', '2'); |
||
10952 | |||
10953 | $form->addCourseHiddenParams(); |
||
10954 | $form->addText( |
||
10955 | 'keyword', |
||
10956 | get_lang('Search'), |
||
10957 | false, |
||
10958 | [ |
||
10959 | 'aria-label' => get_lang('Search'), |
||
10960 | ] |
||
10961 | ); |
||
10962 | |||
10963 | if (api_get_configuration_value('allow_exercise_categories')) { |
||
10964 | $manager = new ExerciseCategoryManager(); |
||
10965 | $options = $manager->getCategoriesForSelect(api_get_course_int_id()); |
||
10966 | if (!empty($options)) { |
||
10967 | $form->addSelect( |
||
10968 | 'category_id', |
||
10969 | get_lang('Category'), |
||
10970 | $options, |
||
10971 | ['placeholder' => get_lang('SelectAnOption')] |
||
10972 | ); |
||
10973 | } |
||
10974 | } |
||
10975 | |||
10976 | $form->addButtonSearch(get_lang('Search')); |
||
10977 | $return = $form->returnForm(); |
||
10978 | |||
10979 | $return .= '<ul class="lp_resource">'; |
||
10980 | |||
10981 | $return .= '<li class="lp_resource_element">'; |
||
10982 | $return .= Display::return_icon('new_exercice.png'); |
||
10983 | $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise_admin.php?'.api_get_cidreq().'&lp_id='.$this->lp_id.'">'. |
||
10984 | get_lang('NewExercise').'</a>'; |
||
10985 | $return .= '</li>'; |
||
10986 | |||
10987 | $previewIcon = Display::return_icon( |
||
10988 | 'preview_view.png', |
||
10989 | get_lang('Preview') |
||
10990 | ); |
||
10991 | $quizIcon = Display::return_icon('quiz.png', '', [], ICON_SIZE_TINY); |
||
10992 | $moveIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
10993 | $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/showinframes.php?'.api_get_cidreq(); |
||
10994 | |||
10995 | // Display hotpotatoes |
||
10996 | while ($row_hot = Database::fetch_array($res_hot)) { |
||
10997 | $link = Display::url( |
||
10998 | $previewIcon, |
||
10999 | $exerciseUrl.'&file='.$row_hot['path'], |
||
11000 | ['target' => '_blank'] |
||
11001 | ); |
||
11002 | $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >'; |
||
11003 | $return .= '<a class="moved" href="#">'; |
||
11004 | $return .= Display::return_icon( |
||
11005 | 'move_everywhere.png', |
||
11006 | get_lang('Move'), |
||
11007 | [], |
||
11008 | ICON_SIZE_TINY |
||
11009 | ); |
||
11010 | $return .= '</a> '; |
||
11011 | $return .= Display::return_icon('hotpotatoes_s.png'); |
||
11012 | $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_HOTPOTATOES.'&file='.$row_hot['id'].'&lp_id='.$this->lp_id.'">'. |
||
11013 | ((!empty($row_hot['comment'])) ? $row_hot['comment'] : Security::remove_XSS($row_hot['title'])).$link.'</a>'; |
||
11014 | $return .= '</li>'; |
||
11015 | } |
||
11016 | |||
11017 | $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq(); |
||
11018 | while ($row_quiz = Database::fetch_array($res_quiz)) { |
||
11019 | $title = strip_tags( |
||
11020 | api_html_entity_decode($row_quiz['title']) |
||
11021 | ); |
||
11022 | |||
11023 | $visibility = api_get_item_visibility( |
||
11024 | ['real_id' => $course_id], |
||
11025 | TOOL_QUIZ, |
||
11026 | $row_quiz['iid'], |
||
11027 | $session_id |
||
11028 | ); |
||
11029 | |||
11030 | $link = Display::url( |
||
11031 | $previewIcon, |
||
11032 | $exerciseUrl.'&exerciseId='.$row_quiz['iid'], |
||
11033 | ['target' => '_blank'] |
||
11034 | ); |
||
11035 | $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['iid'].'" data_type="quiz" title="'.$title.'" >'; |
||
11036 | $return .= Display::url($moveIcon, '#', ['class' => 'moved']); |
||
11037 | $return .= $quizIcon; |
||
11038 | $sessionStar = api_get_session_image( |
||
11039 | $row_quiz['session_id'], |
||
11040 | $userInfo['status'] |
||
11041 | ); |
||
11042 | $return .= Display::url( |
||
11043 | Security::remove_XSS(cut($title, 80)).$link.$sessionStar, |
||
11044 | api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_QUIZ.'&file='.$row_quiz['iid'].'&lp_id='.$this->lp_id, |
||
11045 | [ |
||
11046 | 'class' => $visibility == 0 ? 'moved text-muted' : 'moved', |
||
11047 | ] |
||
11048 | ); |
||
11049 | $return .= '</li>'; |
||
11050 | } |
||
11051 | |||
11052 | $return .= '</ul>'; |
||
11053 | |||
11054 | return $return; |
||
11055 | } |
||
11056 | |||
11057 | /** |
||
11058 | * Creates a list with all the links in it. |
||
11059 | * |
||
11060 | * @return string |
||
11061 | */ |
||
11062 | public function get_links() |
||
11063 | { |
||
11064 | $selfUrl = api_get_self(); |
||
11065 | $courseIdReq = api_get_cidreq(); |
||
11066 | $course = api_get_course_info(); |
||
11067 | $userInfo = api_get_user_info(); |
||
11068 | |||
11069 | $course_id = $course['real_id']; |
||
11070 | $tbl_link = Database::get_course_table(TABLE_LINK); |
||
11071 | $linkCategoryTable = Database::get_course_table(TABLE_LINK_CATEGORY); |
||
11072 | $moveEverywhereIcon = Display::return_icon( |
||
11073 | 'move_everywhere.png', |
||
11074 | get_lang('Move'), |
||
11075 | [], |
||
11076 | ICON_SIZE_TINY |
||
11077 | ); |
||
11078 | |||
11079 | $session_id = api_get_session_id(); |
||
11080 | $condition_session = api_get_session_condition( |
||
11081 | $session_id, |
||
11082 | true, |
||
11083 | true, |
||
11084 | 'link.session_id' |
||
11085 | ); |
||
11086 | |||
11087 | $sql = "SELECT |
||
11088 | link.id as link_id, |
||
11089 | link.title as link_title, |
||
11090 | link.session_id as link_session_id, |
||
11091 | link.category_id as category_id, |
||
11092 | link_category.category_title as category_title |
||
11093 | FROM $tbl_link as link |
||
11094 | LEFT JOIN $linkCategoryTable as link_category |
||
11095 | ON (link.category_id = link_category.id AND link.c_id = link_category.c_id) |
||
11096 | WHERE link.c_id = $course_id $condition_session |
||
11097 | ORDER BY link_category.category_title ASC, link.title ASC"; |
||
11098 | $result = Database::query($sql); |
||
11099 | $categorizedLinks = []; |
||
11100 | $categories = []; |
||
11101 | |||
11102 | while ($link = Database::fetch_array($result)) { |
||
11103 | if (!$link['category_id']) { |
||
11104 | $link['category_title'] = get_lang('Uncategorized'); |
||
11105 | } |
||
11106 | $categories[$link['category_id']] = $link['category_title']; |
||
11107 | $categorizedLinks[$link['category_id']][$link['link_id']] = $link; |
||
11108 | } |
||
11109 | |||
11110 | $linksHtmlCode = |
||
11111 | '<script> |
||
11112 | function toggle_tool(tool, id) { |
||
11113 | if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){ |
||
11114 | document.getElementById(tool+"_"+id+"_content").style.display = "block"; |
||
11115 | document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('remove.gif').'"; |
||
11116 | } else { |
||
11117 | document.getElementById(tool+"_"+id+"_content").style.display = "none"; |
||
11118 | document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.gif').'"; |
||
11119 | } |
||
11120 | } |
||
11121 | </script> |
||
11122 | |||
11123 | <ul class="lp_resource"> |
||
11124 | <li class="lp_resource_element"> |
||
11125 | '.Display::return_icon('linksnew.gif').' |
||
11126 | <a href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" title="'.get_lang('LinkAdd').'">'. |
||
11127 | get_lang('LinkAdd').' |
||
11128 | </a> |
||
11129 | </li>'; |
||
11130 | |||
11131 | foreach ($categorizedLinks as $categoryId => $links) { |
||
11132 | $linkNodes = null; |
||
11133 | foreach ($links as $key => $linkInfo) { |
||
11134 | $title = $linkInfo['link_title']; |
||
11135 | $linkSessionId = $linkInfo['link_session_id']; |
||
11136 | |||
11137 | $link = Display::url( |
||
11138 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
11139 | api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$key, |
||
11140 | ['target' => '_blank'] |
||
11141 | ); |
||
11142 | |||
11143 | if (api_get_item_visibility($course, TOOL_LINK, $key, $session_id) != 2) { |
||
11144 | $sessionStar = api_get_session_image($linkSessionId, $userInfo['status']); |
||
11145 | $linkNodes .= |
||
11146 | '<li class="lp_resource_element" data_id="'.$key.'" data_type="'.TOOL_LINK.'" title="'.$title.'" > |
||
11147 | <a class="moved" href="#">'. |
||
11148 | $moveEverywhereIcon. |
||
11149 | '</a> |
||
11150 | '.Display::return_icon('links.png', '', [], ICON_SIZE_TINY).' |
||
11151 | <a class="moved" href="'.$selfUrl.'?'.$courseIdReq.'&action=add_item&type='. |
||
11152 | TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id.'">'. |
||
11153 | Security::remove_XSS($title).$sessionStar.$link. |
||
11154 | '</a> |
||
11155 | </li>'; |
||
11156 | } |
||
11157 | } |
||
11158 | $linksHtmlCode .= |
||
11159 | '<li> |
||
11160 | <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle"> |
||
11161 | <img src="'.Display::returnIconPath('add.gif').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener" |
||
11162 | align="absbottom" /> |
||
11163 | </a> |
||
11164 | <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span> |
||
11165 | </li> |
||
11166 | <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>'; |
||
11167 | } |
||
11168 | $linksHtmlCode .= '</ul>'; |
||
11169 | |||
11170 | return $linksHtmlCode; |
||
11171 | } |
||
11172 | |||
11173 | /** |
||
11174 | * Creates a list with all the surveys in it. |
||
11175 | * |
||
11176 | * @return string |
||
11177 | */ |
||
11178 | public function getSurveys() |
||
11179 | { |
||
11180 | $return = '<ul class="lp_resource">'; |
||
11181 | // First add link |
||
11182 | $return .= '<li class="lp_resource_element">'; |
||
11183 | $return .= Display::return_icon('new_survey.png', get_lang('CreateNewSurvey'), '', ICON_SIZE_MEDIUM); |
||
11184 | $return .= Display::url( |
||
11185 | get_lang('CreateANewSurvey'), |
||
11186 | api_get_path(WEB_CODE_PATH).'survey/create_new_survey.php?'.api_get_cidreq().'&'.http_build_query([ |
||
11187 | 'action' => 'add', |
||
11188 | 'lp_id' => $this->lp_id, |
||
11189 | ]), |
||
11190 | ['title' => get_lang('CreateNewSurvey')] |
||
11191 | ); |
||
11192 | $return .= '</li>'; |
||
11193 | |||
11194 | $surveys = SurveyManager::get_surveys(api_get_course_id(), api_get_session_id()); |
||
11195 | |||
11196 | foreach ($surveys as $survey) { |
||
11197 | if (!empty($survey['survey_id'])) { |
||
11198 | $surveyTitle = strip_tags($survey['title']); |
||
11199 | $return .= '<li class="lp_resource_element" data_id="'.$survey['survey_id'].'" data_type="'.TOOL_SURVEY.'" title="'.$surveyTitle.'" >'; |
||
11200 | $return .= '<a class="moved" href="#">'; |
||
11201 | $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
11202 | $return .= ' </a>'; |
||
11203 | $return .= Display::return_icon('survey.png', '', [], ICON_SIZE_TINY); |
||
11204 | $return .= '<a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_SURVEY.'&survey_id='.$survey['survey_id'].'&lp_id='.$this->lp_id.'" style="vertical-align:middle">'.$surveyTitle.'</a>'; |
||
11205 | $return .= '</li>'; |
||
11206 | } |
||
11207 | } |
||
11208 | |||
11209 | $return .= '</ul>'; |
||
11210 | |||
11211 | return $return; |
||
11212 | } |
||
11213 | |||
11214 | /** |
||
11215 | * Creates a list with all the student publications in it. |
||
11216 | * |
||
11217 | * @return string |
||
11218 | */ |
||
11219 | public function get_student_publications() |
||
11220 | { |
||
11221 | $return = '<ul class="lp_resource">'; |
||
11222 | $return .= '<li class="lp_resource_element">'; |
||
11223 | $return .= Display::return_icon('works_new.gif'); |
||
11224 | $return .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&lp_id='.$this->lp_id.'">'. |
||
11225 | get_lang('AddAssignmentPage').'</a>'; |
||
11226 | $return .= '</li>'; |
||
11227 | |||
11228 | require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php'; |
||
11229 | $works = getWorkListTeacher(0, 100, null, null, null); |
||
11230 | if (!empty($works)) { |
||
11231 | foreach ($works as $work) { |
||
11232 | $link = Display::url( |
||
11233 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
11234 | api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$work['iid'], |
||
11235 | ['target' => '_blank'] |
||
11236 | ); |
||
11237 | |||
11238 | $return .= '<li class="lp_resource_element" data_id="'.$work['iid'].'" data_type="'.TOOL_STUDENTPUBLICATION.'" title="'.Security::remove_XSS(cut(strip_tags($work['title']), 80)).'">'; |
||
11239 | $return .= '<a class="moved" href="#">'; |
||
11240 | $return .= Display::return_icon( |
||
11241 | 'move_everywhere.png', |
||
11242 | get_lang('Move'), |
||
11243 | [], |
||
11244 | ICON_SIZE_TINY |
||
11245 | ); |
||
11246 | $return .= '</a> '; |
||
11247 | |||
11248 | $return .= Display::return_icon('works.png', '', [], ICON_SIZE_TINY); |
||
11249 | $return .= ' <a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&file='.$work['iid'].'&lp_id='.$this->lp_id.'">'. |
||
11250 | Security::remove_XSS(cut(strip_tags($work['title']), 80)).' '.$link.' |
||
11251 | </a>'; |
||
11252 | |||
11253 | $return .= '</li>'; |
||
11254 | } |
||
11255 | } |
||
11256 | |||
11257 | $return .= '</ul>'; |
||
11258 | |||
11259 | return $return; |
||
11260 | } |
||
11261 | |||
11262 | /** |
||
11263 | * Creates a list with all the forums in it. |
||
11264 | * |
||
11265 | * @return string |
||
11266 | */ |
||
11267 | public function get_forums() |
||
11268 | { |
||
11269 | require_once '../forum/forumfunction.inc.php'; |
||
11270 | |||
11271 | $forumCategories = get_forum_categories(); |
||
11272 | $forumsInNoCategory = get_forums_in_category(0); |
||
11273 | if (!empty($forumsInNoCategory)) { |
||
11274 | $forumCategories = array_merge( |
||
11275 | $forumCategories, |
||
11276 | [ |
||
11277 | [ |
||
11278 | 'cat_id' => 0, |
||
11279 | 'session_id' => 0, |
||
11280 | 'visibility' => 1, |
||
11281 | 'cat_comment' => null, |
||
11282 | ], |
||
11283 | ] |
||
11284 | ); |
||
11285 | } |
||
11286 | |||
11287 | $forumList = get_forums(); |
||
11288 | $a_forums = []; |
||
11289 | foreach ($forumCategories as $forumCategory) { |
||
11290 | // The forums in this category. |
||
11291 | $forumsInCategory = get_forums_in_category($forumCategory['cat_id']); |
||
11292 | if (!empty($forumsInCategory)) { |
||
11293 | foreach ($forumList as $forum) { |
||
11294 | if (isset($forum['forum_category']) && |
||
11295 | $forum['forum_category'] == $forumCategory['cat_id'] |
||
11296 | ) { |
||
11297 | $a_forums[] = $forum; |
||
11298 | } |
||
11299 | } |
||
11300 | } |
||
11301 | } |
||
11302 | |||
11303 | $return = '<ul class="lp_resource">'; |
||
11304 | |||
11305 | // First add link |
||
11306 | $return .= '<li class="lp_resource_element">'; |
||
11307 | $return .= Display::return_icon('new_forum.png'); |
||
11308 | $return .= Display::url( |
||
11309 | get_lang('CreateANewForum'), |
||
11310 | api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq().'&'.http_build_query([ |
||
11311 | 'action' => 'add', |
||
11312 | 'content' => 'forum', |
||
11313 | 'lp_id' => $this->lp_id, |
||
11314 | ]), |
||
11315 | ['title' => get_lang('CreateANewForum')] |
||
11316 | ); |
||
11317 | $return .= '</li>'; |
||
11318 | |||
11319 | $return .= '<script> |
||
11320 | function toggle_forum(forum_id) { |
||
11321 | if (document.getElementById("forum_"+forum_id+"_content").style.display == "none") { |
||
11322 | document.getElementById("forum_"+forum_id+"_content").style.display = "block"; |
||
11323 | document.getElementById("forum_"+forum_id+"_opener").src = "'.Display::returnIconPath('remove.gif').'"; |
||
11324 | } else { |
||
11325 | document.getElementById("forum_"+forum_id+"_content").style.display = "none"; |
||
11326 | document.getElementById("forum_"+forum_id+"_opener").src = "'.Display::returnIconPath('add.gif').'"; |
||
11327 | } |
||
11328 | } |
||
11329 | </script>'; |
||
11330 | |||
11331 | foreach ($a_forums as $forum) { |
||
11332 | if (!empty($forum['forum_id'])) { |
||
11333 | $link = Display::url( |
||
11334 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
11335 | api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$forum['forum_id'], |
||
11336 | ['target' => '_blank'] |
||
11337 | ); |
||
11338 | |||
11339 | $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >'; |
||
11340 | $return .= '<a class="moved" href="#">'; |
||
11341 | $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
11342 | $return .= ' </a>'; |
||
11343 | $return .= Display::return_icon('forum.png', '', [], ICON_SIZE_TINY); |
||
11344 | $return .= '<a onclick="javascript:toggle_forum('.$forum['forum_id'].');" style="cursor:hand; vertical-align:middle"> |
||
11345 | <img src="'.Display::returnIconPath('add.gif').'" id="forum_'.$forum['forum_id'].'_opener" align="absbottom" /> |
||
11346 | </a> |
||
11347 | <a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_FORUM.'&forum_id='.$forum['forum_id'].'&lp_id='.$this->lp_id.'" style="vertical-align:middle">'. |
||
11348 | Security::remove_XSS($forum['forum_title']).' '.$link.'</a>'; |
||
11349 | |||
11350 | $return .= '</li>'; |
||
11351 | |||
11352 | $return .= '<div style="display:none" id="forum_'.$forum['forum_id'].'_content">'; |
||
11353 | $a_threads = get_threads($forum['forum_id']); |
||
11354 | if (is_array($a_threads)) { |
||
11355 | foreach ($a_threads as $thread) { |
||
11356 | $link = Display::url( |
||
11357 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
11358 | api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forum['forum_id'].'&thread='.$thread['thread_id'], |
||
11359 | ['target' => '_blank'] |
||
11360 | ); |
||
11361 | |||
11362 | $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >'; |
||
11363 | $return .= ' <a class="moved" href="#">'; |
||
11364 | $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
11365 | $return .= ' </a>'; |
||
11366 | $return .= Display::return_icon('forumthread.png', get_lang('Thread'), [], ICON_SIZE_TINY); |
||
11367 | $return .= '<a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_THREAD.'&thread_id='.$thread['thread_id'].'&lp_id='.$this->lp_id.'">'. |
||
11368 | Security::remove_XSS($thread['thread_title']).' '.$link.'</a>'; |
||
11369 | $return .= '</li>'; |
||
11370 | } |
||
11371 | } |
||
11372 | $return .= '</div>'; |
||
11373 | } |
||
11374 | } |
||
11375 | $return .= '</ul>'; |
||
11376 | |||
11377 | return $return; |
||
11378 | } |
||
11379 | |||
11380 | /** |
||
11381 | * // TODO: The output encoding should be equal to the system encoding. |
||
11382 | * |
||
11383 | * Exports the learning path as a SCORM package. This is the main function that |
||
11384 | * gathers the content, transforms it, writes the imsmanifest.xml file, zips the |
||
11385 | * whole thing and returns the zip. |
||
11386 | * |
||
11387 | * This method needs to be called in PHP5, as it will fail with non-adequate |
||
11388 | * XML package (like the ones for PHP4), and it is *not* a static method, so |
||
11389 | * you need to call it on a learnpath object. |
||
11390 | * |
||
11391 | * @TODO The method might be redefined later on in the scorm class itself to avoid |
||
11392 | * creating a SCORM structure if there is one already. However, if the initial SCORM |
||
11393 | * path has been modified, it should use the generic method here below. |
||
11394 | * |
||
11395 | * @return string Returns the zip package string, or null if error |
||
11396 | */ |
||
11397 | public function scormExport() |
||
11398 | { |
||
11399 | api_set_more_memory_and_time_limits(); |
||
11400 | |||
11401 | $_course = api_get_course_info(); |
||
11402 | $course_id = $_course['real_id']; |
||
11403 | // Create the zip handler (this will remain available throughout the method). |
||
11404 | $archivePath = api_get_path(SYS_ARCHIVE_PATH); |
||
11405 | $sys_course_path = api_get_path(SYS_COURSE_PATH); |
||
11406 | $temp_dir_short = uniqid('scorm_export', true); |
||
11407 | $temp_zip_dir = $archivePath.'/'.$temp_dir_short; |
||
11408 | $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip'; |
||
11409 | $zip_folder = new PclZip($temp_zip_file); |
||
11410 | $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path(); |
||
11411 | $root_path = $main_path = api_get_path(SYS_PATH); |
||
11412 | $files_cleanup = []; |
||
11413 | |||
11414 | // Place to temporarily stash the zip file. |
||
11415 | // create the temp dir if it doesn't exist |
||
11416 | // or do a cleanup before creating the zip file. |
||
11417 | if (!is_dir($temp_zip_dir)) { |
||
11418 | mkdir($temp_zip_dir, api_get_permissions_for_new_directories()); |
||
11419 | } else { |
||
11420 | // Cleanup: Check the temp dir for old files and delete them. |
||
11421 | $handle = opendir($temp_zip_dir); |
||
11422 | while (false !== ($file = readdir($handle))) { |
||
11423 | if ($file != '.' && $file != '..') { |
||
11424 | unlink("$temp_zip_dir/$file"); |
||
11425 | } |
||
11426 | } |
||
11427 | closedir($handle); |
||
11428 | } |
||
11429 | $zip_files = $zip_files_abs = $zip_files_dist = []; |
||
11430 | if (is_dir($current_course_path.'/scorm/'.$this->path) && |
||
11431 | is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml') |
||
11432 | ) { |
||
11433 | // Remove the possible . at the end of the path. |
||
11434 | $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path; |
||
11435 | $dest_path_to_scorm_folder = str_replace('//', '/', $temp_zip_dir.'/scorm/'.$dest_path_to_lp); |
||
11436 | mkdir( |
||
11437 | $dest_path_to_scorm_folder, |
||
11438 | api_get_permissions_for_new_directories(), |
||
11439 | true |
||
11440 | ); |
||
11441 | copyr( |
||
11442 | $current_course_path.'/scorm/'.$this->path, |
||
11443 | $dest_path_to_scorm_folder, |
||
11444 | ['imsmanifest'], |
||
11445 | $zip_files |
||
11446 | ); |
||
11447 | } |
||
11448 | |||
11449 | // Build a dummy imsmanifest structure. |
||
11450 | // Do not add to the zip yet (we still need it). |
||
11451 | // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content |
||
11452 | // Aggregation Model official document, section "2.3 Content Packaging". |
||
11453 | // We are going to build a UTF-8 encoded manifest. |
||
11454 | // Later we will recode it to the desired (and supported) encoding. |
||
11455 | $xmldoc = new DOMDocument('1.0'); |
||
11456 | $root = $xmldoc->createElement('manifest'); |
||
11457 | $root->setAttribute('identifier', 'SingleCourseManifest'); |
||
11458 | $root->setAttribute('version', '1.1'); |
||
11459 | $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2'); |
||
11460 | $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2'); |
||
11461 | $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); |
||
11462 | $root->setAttribute( |
||
11463 | 'xsi:schemaLocation', |
||
11464 | 'http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd' |
||
11465 | ); |
||
11466 | // Build mandatory sub-root container elements. |
||
11467 | $metadata = $xmldoc->createElement('metadata'); |
||
11468 | $md_schema = $xmldoc->createElement('schema', 'ADL SCORM'); |
||
11469 | $metadata->appendChild($md_schema); |
||
11470 | $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2'); |
||
11471 | $metadata->appendChild($md_schemaversion); |
||
11472 | $root->appendChild($metadata); |
||
11473 | |||
11474 | $organizations = $xmldoc->createElement('organizations'); |
||
11475 | $resources = $xmldoc->createElement('resources'); |
||
11476 | |||
11477 | // Build the only organization we will use in building our learnpaths. |
||
11478 | $organizations->setAttribute('default', 'chamilo_scorm_export'); |
||
11479 | $organization = $xmldoc->createElement('organization'); |
||
11480 | $organization->setAttribute('identifier', 'chamilo_scorm_export'); |
||
11481 | // To set the title of the SCORM entity (=organization), we take the name given |
||
11482 | // in Chamilo and convert it to HTML entities using the Chamilo charset (not the |
||
11483 | // learning path charset) as it is the encoding that defines how it is stored |
||
11484 | // in the database. Then we convert it to HTML entities again as the "&" character |
||
11485 | // alone is not authorized in XML (must be &). |
||
11486 | // The title is then decoded twice when extracting (see scorm::parse_manifest). |
||
11487 | $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name())); |
||
11488 | $organization->appendChild($org_title); |
||
11489 | $folder_name = 'document'; |
||
11490 | |||
11491 | // Removes the learning_path/scorm_folder path when exporting see #4841 |
||
11492 | $path_to_remove = ''; |
||
11493 | $path_to_replace = ''; |
||
11494 | $result = $this->generate_lp_folder($_course); |
||
11495 | if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) { |
||
11496 | $path_to_remove = 'document'.$result['dir']; |
||
11497 | $path_to_replace = $folder_name.'/'; |
||
11498 | } |
||
11499 | |||
11500 | // Fixes chamilo scorm exports |
||
11501 | if ($this->ref === 'chamilo_scorm_export') { |
||
11502 | $path_to_remove = 'scorm/'.$this->path.'/document/'; |
||
11503 | } |
||
11504 | |||
11505 | // For each element, add it to the imsmanifest structure, then add it to the zip. |
||
11506 | $link_updates = []; |
||
11507 | $links_to_create = []; |
||
11508 | foreach ($this->ordered_items as $index => $itemId) { |
||
11509 | /** @var learnpathItem $item */ |
||
11510 | $item = $this->items[$itemId]; |
||
11511 | if (!in_array($item->type, [TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION])) { |
||
11512 | // Get included documents from this item. |
||
11513 | if ($item->type === 'sco') { |
||
11514 | $inc_docs = $item->get_resources_from_source( |
||
11515 | null, |
||
11516 | $current_course_path.'/scorm/'.$this->path.'/'.$item->get_path() |
||
11517 | ); |
||
11518 | } else { |
||
11519 | $inc_docs = $item->get_resources_from_source(); |
||
11520 | } |
||
11521 | |||
11522 | // Give a child element <item> to the <organization> element. |
||
11523 | $my_item_id = $item->get_id(); |
||
11524 | $my_item = $xmldoc->createElement('item'); |
||
11525 | $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id); |
||
11526 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id); |
||
11527 | $my_item->setAttribute('isvisible', 'true'); |
||
11528 | // Give a child element <title> to the <item> element. |
||
11529 | $my_title = $xmldoc->createElement( |
||
11530 | 'title', |
||
11531 | htmlspecialchars( |
||
11532 | api_utf8_encode($item->get_title()), |
||
11533 | ENT_QUOTES, |
||
11534 | 'UTF-8' |
||
11535 | ) |
||
11536 | ); |
||
11537 | $my_item->appendChild($my_title); |
||
11538 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
11539 | $my_prereqs = $xmldoc->createElement( |
||
11540 | 'adlcp:prerequisites', |
||
11541 | $this->get_scorm_prereq_string($my_item_id) |
||
11542 | ); |
||
11543 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
11544 | $my_item->appendChild($my_prereqs); |
||
11545 | // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported. |
||
11546 | //$xmldoc->createElement('adlcp:maxtimeallowed',''); |
||
11547 | // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported. |
||
11548 | //$xmldoc->createElement('adlcp:timelimitaction',''); |
||
11549 | // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported. |
||
11550 | //$xmldoc->createElement('adlcp:datafromlms',''); |
||
11551 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
11552 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
11553 | $my_item->appendChild($my_masteryscore); |
||
11554 | |||
11555 | // Attach this item to the organization element or hits parent if there is one. |
||
11556 | if (!empty($item->parent) && $item->parent != 0) { |
||
11557 | $children = $organization->childNodes; |
||
11558 | $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent); |
||
11559 | if (is_object($possible_parent)) { |
||
11560 | $possible_parent->appendChild($my_item); |
||
11561 | } else { |
||
11562 | if ($this->debug > 0) { |
||
11563 | error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); |
||
11564 | } |
||
11565 | } |
||
11566 | } else { |
||
11567 | if ($this->debug > 0) { |
||
11568 | error_log('No parent'); |
||
11569 | } |
||
11570 | $organization->appendChild($my_item); |
||
11571 | } |
||
11572 | |||
11573 | // Get the path of the file(s) from the course directory root. |
||
11574 | $my_file_path = $item->get_file_path('scorm/'.$this->path.'/'); |
||
11575 | $my_xml_file_path = $my_file_path; |
||
11576 | if (!empty($path_to_remove)) { |
||
11577 | // From docs |
||
11578 | $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path); |
||
11579 | |||
11580 | // From quiz |
||
11581 | if ($this->ref === 'chamilo_scorm_export') { |
||
11582 | $path_to_remove = 'scorm/'.$this->path.'/'; |
||
11583 | $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path); |
||
11584 | } |
||
11585 | } |
||
11586 | |||
11587 | $my_sub_dir = dirname($my_file_path); |
||
11588 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
11589 | $my_xml_sub_dir = $my_sub_dir; |
||
11590 | // Give a <resource> child to the <resources> element |
||
11591 | $my_resource = $xmldoc->createElement('resource'); |
||
11592 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
11593 | $my_resource->setAttribute('type', 'webcontent'); |
||
11594 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
11595 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
11596 | if ($item->type === 'sco') { |
||
11597 | $my_resource->setAttribute('adlcp:scormtype', 'sco'); |
||
11598 | } else { |
||
11599 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
11600 | } |
||
11601 | // xml:base is the base directory to find the files declared in this resource. |
||
11602 | $my_resource->setAttribute('xml:base', ''); |
||
11603 | // Give a <file> child to the <resource> element. |
||
11604 | $my_file = $xmldoc->createElement('file'); |
||
11605 | $my_file->setAttribute('href', $my_xml_file_path); |
||
11606 | $my_resource->appendChild($my_file); |
||
11607 | |||
11608 | // Dependency to other files - not yet supported. |
||
11609 | $i = 1; |
||
11610 | if ($inc_docs) { |
||
11611 | foreach ($inc_docs as $doc_info) { |
||
11612 | if (count($doc_info) < 1 || empty($doc_info[0])) { |
||
11613 | continue; |
||
11614 | } |
||
11615 | $my_dep = $xmldoc->createElement('resource'); |
||
11616 | $res_id = 'RESOURCE_'.$item->get_id().'_'.$i; |
||
11617 | $my_dep->setAttribute('identifier', $res_id); |
||
11618 | $my_dep->setAttribute('type', 'webcontent'); |
||
11619 | $my_dep->setAttribute('adlcp:scormtype', 'asset'); |
||
11620 | $my_dep_file = $xmldoc->createElement('file'); |
||
11621 | // Check type of URL. |
||
11622 | if ($doc_info[1] == 'remote') { |
||
11623 | // Remote file. Save url as is. |
||
11624 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
11625 | $my_dep->setAttribute('xml:base', ''); |
||
11626 | } elseif ($doc_info[1] === 'local') { |
||
11627 | switch ($doc_info[2]) { |
||
11628 | case 'url': |
||
11629 | // Local URL - save path as url for now, don't zip file. |
||
11630 | $abs_path = api_get_path(SYS_PATH). |
||
11631 | str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
11632 | $current_dir = dirname($abs_path); |
||
11633 | $current_dir = str_replace('\\', '/', $current_dir); |
||
11634 | $file_path = realpath($abs_path); |
||
11635 | $file_path = str_replace('\\', '/', $file_path); |
||
11636 | $my_dep_file->setAttribute('href', $file_path); |
||
11637 | $my_dep->setAttribute('xml:base', ''); |
||
11638 | if (strstr($file_path, $main_path) !== false) { |
||
11639 | // The calculated real path is really inside Chamilo's root path. |
||
11640 | // Reduce file path to what's under the DocumentRoot. |
||
11641 | $replace = $file_path; |
||
11642 | $file_path = substr($file_path, strlen($root_path) - 1); |
||
11643 | $destinationFile = $file_path; |
||
11644 | |||
11645 | if (strstr($file_path, 'upload/users') !== false) { |
||
11646 | $pos = strpos($file_path, 'my_files/'); |
||
11647 | if ($pos !== false) { |
||
11648 | $onlyDirectory = str_replace( |
||
11649 | 'upload/users/', |
||
11650 | '', |
||
11651 | substr($file_path, $pos, strlen($file_path)) |
||
11652 | ); |
||
11653 | } |
||
11654 | $replace = './'.$onlyDirectory; |
||
11655 | $destinationFile = $replace; |
||
11656 | } |
||
11657 | |||
11658 | if (strpos($file_path, '/web') === 0) { |
||
11659 | $replace = str_replace('/web', 'web', $file_path); |
||
11660 | } |
||
11661 | |||
11662 | $zip_files_abs[] = $file_path; |
||
11663 | $link_updates[$my_file_path][] = [ |
||
11664 | 'orig' => $doc_info[0], |
||
11665 | 'dest' => $destinationFile, |
||
11666 | 'replace' => $replace, |
||
11667 | ]; |
||
11668 | $my_dep_file->setAttribute('href', $file_path); |
||
11669 | $my_dep->setAttribute('xml:base', ''); |
||
11670 | } elseif (empty($file_path)) { |
||
11671 | $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path; |
||
11672 | $file_path = str_replace('//', '/', $file_path); |
||
11673 | if (file_exists($file_path)) { |
||
11674 | // We get the relative path. |
||
11675 | $file_path = substr($file_path, strlen($current_dir)); |
||
11676 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
11677 | $link_updates[$my_file_path][] = [ |
||
11678 | 'orig' => $doc_info[0], |
||
11679 | 'dest' => $file_path, |
||
11680 | ]; |
||
11681 | $my_dep_file->setAttribute('href', $file_path); |
||
11682 | $my_dep->setAttribute('xml:base', ''); |
||
11683 | } |
||
11684 | } |
||
11685 | break; |
||
11686 | case 'abs': |
||
11687 | // Absolute path from DocumentRoot. Save file and leave path as is in the zip. |
||
11688 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
11689 | $my_dep->setAttribute('xml:base', ''); |
||
11690 | |||
11691 | // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas |
||
11692 | // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar |
||
11693 | $abs_img_path_without_subdir = $doc_info[0]; |
||
11694 | $relp = api_get_path(REL_PATH); // The url-append config param. |
||
11695 | $pos = strpos($abs_img_path_without_subdir, $relp); |
||
11696 | if ($pos === 0) { |
||
11697 | $abs_img_path_without_subdir = trim('/'.substr($abs_img_path_without_subdir, strlen($relp))); |
||
11698 | } |
||
11699 | |||
11700 | $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir); |
||
11701 | $file_path = str_replace(['\\', '//'], '/', $file_path); |
||
11702 | |||
11703 | // Prepare the current directory path (until just under 'document') with a trailing slash. |
||
11704 | $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/'; |
||
11705 | // Check if the current document is in that path. |
||
11706 | if (strstr($file_path, $cur_path) !== false) { |
||
11707 | $destinationFile = substr($file_path, strlen($cur_path)); |
||
11708 | $filePathNoCoursePart = substr($file_path, strlen($cur_path)); |
||
11709 | |||
11710 | $fileToTest = $cur_path.$my_file_path; |
||
11711 | if (!empty($path_to_remove)) { |
||
11712 | $fileToTest = str_replace( |
||
11713 | $path_to_remove.'/', |
||
11714 | $path_to_replace, |
||
11715 | $cur_path.$my_file_path |
||
11716 | ); |
||
11717 | } |
||
11718 | |||
11719 | $relative_path = api_get_relative_path($fileToTest, $file_path); |
||
11720 | |||
11721 | // Put the current document in the zip (this array is the array |
||
11722 | // that will manage documents already in the course folder - relative). |
||
11723 | $zip_files[] = $filePathNoCoursePart; |
||
11724 | // Update the links to the current document in the |
||
11725 | // containing document (make them relative). |
||
11726 | $link_updates[$my_file_path][] = [ |
||
11727 | 'orig' => $doc_info[0], |
||
11728 | 'dest' => $destinationFile, |
||
11729 | 'replace' => $relative_path, |
||
11730 | ]; |
||
11731 | |||
11732 | $my_dep_file->setAttribute('href', $file_path); |
||
11733 | $my_dep->setAttribute('xml:base', ''); |
||
11734 | } elseif (strstr($file_path, $main_path) !== false) { |
||
11735 | // The calculated real path is really inside Chamilo's root path. |
||
11736 | // Reduce file path to what's under the DocumentRoot. |
||
11737 | $file_path = substr($file_path, strlen($root_path)); |
||
11738 | $zip_files_abs[] = $file_path; |
||
11739 | $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path]; |
||
11740 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
11741 | $my_dep->setAttribute('xml:base', ''); |
||
11742 | } elseif (empty($file_path)) { |
||
11743 | // Probably this is an image inside "/main" directory |
||
11744 | $file_path = api_get_path(SYS_PATH).$abs_img_path_without_subdir; |
||
11745 | $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
11746 | |||
11747 | if (file_exists($file_path)) { |
||
11748 | $pos = strpos($file_path, 'main/default_course_document/'); |
||
11749 | if ($pos !== false) { |
||
11750 | // We get the relative path. |
||
11751 | $onlyDirectory = str_replace( |
||
11752 | 'main/default_course_document/', |
||
11753 | '', |
||
11754 | substr($file_path, $pos, strlen($file_path)) |
||
11755 | ); |
||
11756 | |||
11757 | $destinationFile = 'default_course_document/'.$onlyDirectory; |
||
11758 | $fileAbs = substr($file_path, strlen(api_get_path(SYS_PATH))); |
||
11759 | $zip_files_abs[] = $fileAbs; |
||
11760 | $link_updates[$my_file_path][] = [ |
||
11761 | 'orig' => $doc_info[0], |
||
11762 | 'dest' => $destinationFile, |
||
11763 | ]; |
||
11764 | $my_dep_file->setAttribute('href', 'document/'.$destinationFile); |
||
11765 | $my_dep->setAttribute('xml:base', ''); |
||
11766 | } |
||
11767 | } |
||
11768 | } |
||
11769 | break; |
||
11770 | case 'rel': |
||
11771 | // Path relative to the current document. |
||
11772 | // Save xml:base as current document's directory and save file in zip as subdir.file_path |
||
11773 | if (substr($doc_info[0], 0, 2) === '..') { |
||
11774 | // Relative path going up. |
||
11775 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
11776 | $current_dir = str_replace('\\', '/', $current_dir); |
||
11777 | $file_path = realpath($current_dir.$doc_info[0]); |
||
11778 | $file_path = str_replace('\\', '/', $file_path); |
||
11779 | if (strstr($file_path, $main_path) !== false) { |
||
11780 | // The calculated real path is really inside Chamilo's root path. |
||
11781 | // Reduce file path to what's under the DocumentRoot. |
||
11782 | $file_path = substr($file_path, strlen($root_path)); |
||
11783 | $zip_files_abs[] = $file_path; |
||
11784 | $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path]; |
||
11785 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
11786 | $my_dep->setAttribute('xml:base', ''); |
||
11787 | } |
||
11788 | } else { |
||
11789 | $zip_files[] = $my_sub_dir.'/'.$doc_info[0]; |
||
11790 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
11791 | $my_dep->setAttribute('xml:base', $my_xml_sub_dir); |
||
11792 | } |
||
11793 | break; |
||
11794 | default: |
||
11795 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
11796 | $my_dep->setAttribute('xml:base', ''); |
||
11797 | break; |
||
11798 | } |
||
11799 | } |
||
11800 | $my_dep->appendChild($my_dep_file); |
||
11801 | $resources->appendChild($my_dep); |
||
11802 | $dependency = $xmldoc->createElement('dependency'); |
||
11803 | $dependency->setAttribute('identifierref', $res_id); |
||
11804 | $my_resource->appendChild($dependency); |
||
11805 | $i++; |
||
11806 | } |
||
11807 | } |
||
11808 | $resources->appendChild($my_resource); |
||
11809 | $zip_files[] = $my_file_path; |
||
11810 | } else { |
||
11811 | // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it. |
||
11812 | switch ($item->type) { |
||
11813 | case TOOL_LINK: |
||
11814 | $my_item = $xmldoc->createElement('item'); |
||
11815 | $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id()); |
||
11816 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id()); |
||
11817 | $my_item->setAttribute('isvisible', 'true'); |
||
11818 | // Give a child element <title> to the <item> element. |
||
11819 | $my_title = $xmldoc->createElement( |
||
11820 | 'title', |
||
11821 | htmlspecialchars( |
||
11822 | api_utf8_encode($item->get_title()), |
||
11823 | ENT_QUOTES, |
||
11824 | 'UTF-8' |
||
11825 | ) |
||
11826 | ); |
||
11827 | $my_item->appendChild($my_title); |
||
11828 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
11829 | $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string()); |
||
11830 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
11831 | $my_item->appendChild($my_prereqs); |
||
11832 | // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported. |
||
11833 | //$xmldoc->createElement('adlcp:maxtimeallowed', ''); |
||
11834 | // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported. |
||
11835 | //$xmldoc->createElement('adlcp:timelimitaction', ''); |
||
11836 | // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported. |
||
11837 | //$xmldoc->createElement('adlcp:datafromlms', ''); |
||
11838 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
11839 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
11840 | $my_item->appendChild($my_masteryscore); |
||
11841 | |||
11842 | // Attach this item to the organization element or its parent if there is one. |
||
11843 | if (!empty($item->parent) && $item->parent != 0) { |
||
11844 | $children = $organization->childNodes; |
||
11845 | for ($i = 0; $i < $children->length; $i++) { |
||
11846 | $item_temp = $children->item($i); |
||
11847 | if ($item_temp->nodeName == 'item') { |
||
11848 | if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) { |
||
11849 | $item_temp->appendChild($my_item); |
||
11850 | } |
||
11851 | } |
||
11852 | } |
||
11853 | } else { |
||
11854 | $organization->appendChild($my_item); |
||
11855 | } |
||
11856 | |||
11857 | $my_file_path = 'link_'.$item->get_id().'.html'; |
||
11858 | $sql = 'SELECT url, title FROM '.Database::get_course_table(TABLE_LINK).' |
||
11859 | WHERE c_id = '.$course_id.' AND id = '.$item->path; |
||
11860 | $rs = Database::query($sql); |
||
11861 | if ($link = Database::fetch_array($rs)) { |
||
11862 | $url = $link['url']; |
||
11863 | $title = stripslashes($link['title']); |
||
11864 | $links_to_create[$my_file_path] = ['title' => $title, 'url' => $url]; |
||
11865 | $my_xml_file_path = $my_file_path; |
||
11866 | $my_sub_dir = dirname($my_file_path); |
||
11867 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
11868 | $my_xml_sub_dir = $my_sub_dir; |
||
11869 | // Give a <resource> child to the <resources> element. |
||
11870 | $my_resource = $xmldoc->createElement('resource'); |
||
11871 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
11872 | $my_resource->setAttribute('type', 'webcontent'); |
||
11873 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
11874 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
11875 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
11876 | // xml:base is the base directory to find the files declared in this resource. |
||
11877 | $my_resource->setAttribute('xml:base', ''); |
||
11878 | // give a <file> child to the <resource> element. |
||
11879 | $my_file = $xmldoc->createElement('file'); |
||
11880 | $my_file->setAttribute('href', $my_xml_file_path); |
||
11881 | $my_resource->appendChild($my_file); |
||
11882 | $resources->appendChild($my_resource); |
||
11883 | } |
||
11884 | break; |
||
11885 | case TOOL_QUIZ: |
||
11886 | $exe_id = $item->path; |
||
11887 | // Should be using ref when everything will be cleaned up in this regard. |
||
11888 | $exe = new Exercise(); |
||
11889 | $exe->read($exe_id); |
||
11890 | $my_item = $xmldoc->createElement('item'); |
||
11891 | $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id()); |
||
11892 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id()); |
||
11893 | $my_item->setAttribute('isvisible', 'true'); |
||
11894 | // Give a child element <title> to the <item> element. |
||
11895 | $my_title = $xmldoc->createElement( |
||
11896 | 'title', |
||
11897 | htmlspecialchars( |
||
11898 | api_utf8_encode($item->get_title()), |
||
11899 | ENT_QUOTES, |
||
11900 | 'UTF-8' |
||
11901 | ) |
||
11902 | ); |
||
11903 | $my_item->appendChild($my_title); |
||
11904 | $my_max_score = $xmldoc->createElement('max_score', $item->get_max()); |
||
11905 | $my_item->appendChild($my_max_score); |
||
11906 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
11907 | $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string()); |
||
11908 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
11909 | $my_item->appendChild($my_prereqs); |
||
11910 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
11911 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
11912 | $my_item->appendChild($my_masteryscore); |
||
11913 | |||
11914 | // Attach this item to the organization element or hits parent if there is one. |
||
11915 | if (!empty($item->parent) && $item->parent != 0) { |
||
11916 | $children = $organization->childNodes; |
||
11917 | $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent); |
||
11918 | if ($possible_parent) { |
||
11919 | if ($possible_parent->getAttribute('identifier') === 'ITEM_'.$item->parent) { |
||
11920 | $possible_parent->appendChild($my_item); |
||
11921 | } |
||
11922 | } |
||
11923 | } else { |
||
11924 | $organization->appendChild($my_item); |
||
11925 | } |
||
11926 | |||
11927 | // Get the path of the file(s) from the course directory root |
||
11928 | //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/'); |
||
11929 | $my_file_path = 'quiz_'.$item->get_id().'.html'; |
||
11930 | // Write the contents of the exported exercise into a (big) html file |
||
11931 | // to later pack it into the exported SCORM. The file will be removed afterwards. |
||
11932 | $scormExercise = new ScormExercise($exe, true); |
||
11933 | $contents = $scormExercise->export(); |
||
11934 | |||
11935 | $tmp_file_path = $archivePath.$temp_dir_short.'/'.$my_file_path; |
||
11936 | $res = file_put_contents($tmp_file_path, $contents); |
||
11937 | if ($res === false) { |
||
11938 | error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0); |
||
11939 | } |
||
11940 | $files_cleanup[] = $tmp_file_path; |
||
11941 | $my_xml_file_path = $my_file_path; |
||
11942 | $my_sub_dir = dirname($my_file_path); |
||
11943 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
11944 | $my_xml_sub_dir = $my_sub_dir; |
||
11945 | // Give a <resource> child to the <resources> element. |
||
11946 | $my_resource = $xmldoc->createElement('resource'); |
||
11947 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
11948 | $my_resource->setAttribute('type', 'webcontent'); |
||
11949 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
11950 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
11951 | $my_resource->setAttribute('adlcp:scormtype', 'sco'); |
||
11952 | // xml:base is the base directory to find the files declared in this resource. |
||
11953 | $my_resource->setAttribute('xml:base', ''); |
||
11954 | // Give a <file> child to the <resource> element. |
||
11955 | $my_file = $xmldoc->createElement('file'); |
||
11956 | $my_file->setAttribute('href', $my_xml_file_path); |
||
11957 | $my_resource->appendChild($my_file); |
||
11958 | |||
11959 | // Get included docs. |
||
11960 | $inc_docs = $item->get_resources_from_source(null, $tmp_file_path); |
||
11961 | |||
11962 | // Dependency to other files - not yet supported. |
||
11963 | $i = 1; |
||
11964 | foreach ($inc_docs as $doc_info) { |
||
11965 | if (count($doc_info) < 1 || empty($doc_info[0])) { |
||
11966 | continue; |
||
11967 | } |
||
11968 | $my_dep = $xmldoc->createElement('resource'); |
||
11969 | $res_id = 'RESOURCE_'.$item->get_id().'_'.$i; |
||
11970 | $my_dep->setAttribute('identifier', $res_id); |
||
11971 | $my_dep->setAttribute('type', 'webcontent'); |
||
11972 | $my_dep->setAttribute('adlcp:scormtype', 'asset'); |
||
11973 | $my_dep_file = $xmldoc->createElement('file'); |
||
11974 | // Check type of URL. |
||
11975 | if ($doc_info[1] == 'remote') { |
||
11976 | // Remote file. Save url as is. |
||
11977 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
11978 | $my_dep->setAttribute('xml:base', ''); |
||
11979 | } elseif ($doc_info[1] == 'local') { |
||
11980 | switch ($doc_info[2]) { |
||
11981 | case 'url': // Local URL - save path as url for now, don't zip file. |
||
11982 | // Save file but as local file (retrieve from URL). |
||
11983 | $abs_path = api_get_path(SYS_PATH). |
||
11984 | str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
11985 | $current_dir = dirname($abs_path); |
||
11986 | $current_dir = str_replace('\\', '/', $current_dir); |
||
11987 | $file_path = realpath($abs_path); |
||
11988 | $file_path = str_replace('\\', '/', $file_path); |
||
11989 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
11990 | $my_dep->setAttribute('xml:base', ''); |
||
11991 | if (strstr($file_path, $main_path) !== false) { |
||
11992 | // The calculated real path is really inside the chamilo root path. |
||
11993 | // Reduce file path to what's under the DocumentRoot. |
||
11994 | $file_path = substr($file_path, strlen($root_path)); |
||
11995 | $zip_files_abs[] = $file_path; |
||
11996 | $link_updates[$my_file_path][] = [ |
||
11997 | 'orig' => $doc_info[0], |
||
11998 | 'dest' => 'document/'.$file_path, |
||
11999 | ]; |
||
12000 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
12001 | $my_dep->setAttribute('xml:base', ''); |
||
12002 | } elseif (empty($file_path)) { |
||
12003 | $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path; |
||
12004 | $file_path = str_replace('//', '/', $file_path); |
||
12005 | if (file_exists($file_path)) { |
||
12006 | $file_path = substr($file_path, strlen($current_dir)); |
||
12007 | // We get the relative path. |
||
12008 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
12009 | $link_updates[$my_file_path][] = [ |
||
12010 | 'orig' => $doc_info[0], |
||
12011 | 'dest' => 'document/'.$file_path, |
||
12012 | ]; |
||
12013 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
12014 | $my_dep->setAttribute('xml:base', ''); |
||
12015 | } |
||
12016 | } |
||
12017 | break; |
||
12018 | case 'abs': |
||
12019 | // Absolute path from DocumentRoot. Save file and leave path as is in the zip. |
||
12020 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
12021 | $current_dir = str_replace('\\', '/', $current_dir); |
||
12022 | $file_path = realpath($doc_info[0]); |
||
12023 | $file_path = str_replace('\\', '/', $file_path); |
||
12024 | $my_dep_file->setAttribute('href', $file_path); |
||
12025 | $my_dep->setAttribute('xml:base', ''); |
||
12026 | |||
12027 | if (strstr($file_path, $main_path) !== false) { |
||
12028 | // The calculated real path is really inside the chamilo root path. |
||
12029 | // Reduce file path to what's under the DocumentRoot. |
||
12030 | $file_path = substr($file_path, strlen($root_path)); |
||
12031 | $zip_files_abs[] = $file_path; |
||
12032 | $link_updates[$my_file_path][] = [ |
||
12033 | 'orig' => $doc_info[0], |
||
12034 | 'dest' => $file_path, |
||
12035 | ]; |
||
12036 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
12037 | $my_dep->setAttribute('xml:base', ''); |
||
12038 | } elseif (empty($file_path)) { |
||
12039 | $docSysPartPath = str_replace( |
||
12040 | api_get_path(REL_COURSE_PATH), |
||
12041 | '', |
||
12042 | $doc_info[0] |
||
12043 | ); |
||
12044 | |||
12045 | $docSysPartPathNoCourseCode = str_replace( |
||
12046 | $_course['directory'].'/', |
||
12047 | '', |
||
12048 | $docSysPartPath |
||
12049 | ); |
||
12050 | |||
12051 | $docSysPath = api_get_path(SYS_COURSE_PATH).$docSysPartPath; |
||
12052 | if (file_exists($docSysPath)) { |
||
12053 | $file_path = $docSysPartPathNoCourseCode; |
||
12054 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
12055 | $link_updates[$my_file_path][] = [ |
||
12056 | 'orig' => $doc_info[0], |
||
12057 | 'dest' => $file_path, |
||
12058 | ]; |
||
12059 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
12060 | $my_dep->setAttribute('xml:base', ''); |
||
12061 | } |
||
12062 | } |
||
12063 | break; |
||
12064 | case 'rel': |
||
12065 | // Path relative to the current document. Save xml:base as current document's |
||
12066 | // directory and save file in zip as subdir.file_path |
||
12067 | if (substr($doc_info[0], 0, 2) === '..') { |
||
12068 | // Relative path going up. |
||
12069 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
12070 | $current_dir = str_replace('\\', '/', $current_dir); |
||
12071 | $file_path = realpath($current_dir.$doc_info[0]); |
||
12072 | $file_path = str_replace('\\', '/', $file_path); |
||
12073 | if (strstr($file_path, $main_path) !== false) { |
||
12074 | // The calculated real path is really inside Chamilo's root path. |
||
12075 | // Reduce file path to what's under the DocumentRoot. |
||
12076 | |||
12077 | $file_path = substr($file_path, strlen($root_path)); |
||
12078 | $file_path_dest = $file_path; |
||
12079 | |||
12080 | // File path is courses/CHAMILO/document/.... |
||
12081 | $info_file_path = explode('/', $file_path); |
||
12082 | if ($info_file_path[0] == 'courses') { |
||
12083 | // Add character "/" in file path. |
||
12084 | $file_path_dest = 'document/'.$file_path; |
||
12085 | } |
||
12086 | $zip_files_abs[] = $file_path; |
||
12087 | |||
12088 | $link_updates[$my_file_path][] = [ |
||
12089 | 'orig' => $doc_info[0], |
||
12090 | 'dest' => $file_path_dest, |
||
12091 | ]; |
||
12092 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
12093 | $my_dep->setAttribute('xml:base', ''); |
||
12094 | } |
||
12095 | } else { |
||
12096 | $zip_files[] = $my_sub_dir.'/'.$doc_info[0]; |
||
12097 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
12098 | $my_dep->setAttribute('xml:base', $my_xml_sub_dir); |
||
12099 | } |
||
12100 | break; |
||
12101 | default: |
||
12102 | $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/ |
||
12103 | $my_dep->setAttribute('xml:base', ''); |
||
12104 | break; |
||
12105 | } |
||
12106 | } |
||
12107 | $my_dep->appendChild($my_dep_file); |
||
12108 | $resources->appendChild($my_dep); |
||
12109 | $dependency = $xmldoc->createElement('dependency'); |
||
12110 | $dependency->setAttribute('identifierref', $res_id); |
||
12111 | $my_resource->appendChild($dependency); |
||
12112 | $i++; |
||
12113 | } |
||
12114 | $resources->appendChild($my_resource); |
||
12115 | $zip_files[] = $my_file_path; |
||
12116 | break; |
||
12117 | default: |
||
12118 | // Get the path of the file(s) from the course directory root |
||
12119 | $my_file_path = 'non_exportable.html'; |
||
12120 | //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8'); |
||
12121 | $my_xml_file_path = $my_file_path; |
||
12122 | $my_sub_dir = dirname($my_file_path); |
||
12123 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
12124 | //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8'); |
||
12125 | $my_xml_sub_dir = $my_sub_dir; |
||
12126 | // Give a <resource> child to the <resources> element. |
||
12127 | $my_resource = $xmldoc->createElement('resource'); |
||
12128 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
12129 | $my_resource->setAttribute('type', 'webcontent'); |
||
12130 | $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path); |
||
12131 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
12132 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
12133 | // xml:base is the base directory to find the files declared in this resource. |
||
12134 | $my_resource->setAttribute('xml:base', ''); |
||
12135 | // Give a <file> child to the <resource> element. |
||
12136 | $my_file = $xmldoc->createElement('file'); |
||
12137 | $my_file->setAttribute('href', 'document/'.$my_xml_file_path); |
||
12138 | $my_resource->appendChild($my_file); |
||
12139 | $resources->appendChild($my_resource); |
||
12140 | break; |
||
12141 | } |
||
12142 | } |
||
12143 | } |
||
12144 | $organizations->appendChild($organization); |
||
12145 | $root->appendChild($organizations); |
||
12146 | $root->appendChild($resources); |
||
12147 | $xmldoc->appendChild($root); |
||
12148 | |||
12149 | $copyAll = api_get_configuration_value('add_all_files_in_lp_export'); |
||
12150 | |||
12151 | // then add the file to the zip, then destroy the file (this is done automatically). |
||
12152 | // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138 |
||
12153 | foreach ($zip_files as $file_path) { |
||
12154 | if (empty($file_path)) { |
||
12155 | continue; |
||
12156 | } |
||
12157 | |||
12158 | $filePath = $sys_course_path.$_course['path'].'/'.$file_path; |
||
12159 | $dest_file = $archivePath.$temp_dir_short.'/'.$file_path; |
||
12160 | |||
12161 | if (!empty($path_to_remove) && !empty($path_to_replace)) { |
||
12162 | $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file); |
||
12163 | } |
||
12164 | |||
12165 | $this->create_path($dest_file); |
||
12166 | @copy($filePath, $dest_file); |
||
12167 | |||
12168 | // Check if the file needs a link update. |
||
12169 | if (in_array($file_path, array_keys($link_updates))) { |
||
12170 | $string = file_get_contents($dest_file); |
||
12171 | unlink($dest_file); |
||
12172 | foreach ($link_updates[$file_path] as $old_new) { |
||
12173 | // This is an ugly hack that allows .flv files to be found by the flv player that |
||
12174 | // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs |
||
12175 | // to find the flv to play in document/main/, so we replace main/ in the flv path by |
||
12176 | // ../../.. to return from inc/lib/flv_player to the document/main path. |
||
12177 | if (substr($old_new['dest'], -3) === 'flv' && |
||
12178 | substr($old_new['dest'], 0, 5) === 'main/' |
||
12179 | ) { |
||
12180 | $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']); |
||
12181 | } elseif (substr($old_new['dest'], -3) === 'flv' && |
||
12182 | substr($old_new['dest'], 0, 6) === 'video/' |
||
12183 | ) { |
||
12184 | $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']); |
||
12185 | } |
||
12186 | |||
12187 | // Fix to avoid problems with default_course_document |
||
12188 | if (strpos('main/default_course_document', $old_new['dest']) === false) { |
||
12189 | $newDestination = $old_new['dest']; |
||
12190 | if (isset($old_new['replace']) && !empty($old_new['replace'])) { |
||
12191 | $newDestination = $old_new['replace']; |
||
12192 | } |
||
12193 | } else { |
||
12194 | $newDestination = str_replace('document/', '', $old_new['dest']); |
||
12195 | } |
||
12196 | $string = str_replace($old_new['orig'], $newDestination, $string); |
||
12197 | |||
12198 | // Add files inside the HTMLs |
||
12199 | $new_path = str_replace(api_get_path(REL_COURSE_PATH), '', $old_new['orig']); |
||
12200 | $destinationFile = $archivePath.$temp_dir_short.'/'.$old_new['dest']; |
||
12201 | if (file_exists($sys_course_path.$new_path) && is_file($sys_course_path.$new_path)) { |
||
12202 | copy( |
||
12203 | $sys_course_path.$new_path, |
||
12204 | $destinationFile |
||
12205 | ); |
||
12206 | } |
||
12207 | } |
||
12208 | file_put_contents($dest_file, $string); |
||
12209 | } |
||
12210 | |||
12211 | if (file_exists($filePath) && $copyAll) { |
||
12212 | $extension = $this->get_extension($filePath); |
||
12213 | if (in_array($extension, ['html', 'html'])) { |
||
12214 | $containerOrigin = dirname($filePath); |
||
12215 | $containerDestination = dirname($dest_file); |
||
12216 | |||
12217 | $finder = new Finder(); |
||
12218 | $finder->files()->in($containerOrigin) |
||
12219 | ->notName('*_DELETED_*') |
||
12220 | ->exclude('share_folder') |
||
12221 | ->exclude('chat_files') |
||
12222 | ->exclude('certificates') |
||
12223 | ; |
||
12224 | |||
12225 | if (is_dir($containerOrigin) && |
||
12226 | is_dir($containerDestination) |
||
12227 | ) { |
||
12228 | $fs = new Filesystem(); |
||
12229 | $fs->mirror( |
||
12230 | $containerOrigin, |
||
12231 | $containerDestination, |
||
12232 | $finder |
||
12233 | ); |
||
12234 | } |
||
12235 | } |
||
12236 | } |
||
12237 | } |
||
12238 | |||
12239 | foreach ($zip_files_abs as $file_path) { |
||
12240 | if (empty($file_path)) { |
||
12241 | continue; |
||
12242 | } |
||
12243 | |||
12244 | if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) { |
||
12245 | continue; |
||
12246 | } |
||
12247 | |||
12248 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$file_path; |
||
12249 | if (strstr($file_path, 'upload/users') !== false) { |
||
12250 | $pos = strpos($file_path, 'my_files/'); |
||
12251 | if ($pos !== false) { |
||
12252 | $onlyDirectory = str_replace( |
||
12253 | 'upload/users/', |
||
12254 | '', |
||
12255 | substr($file_path, $pos, strlen($file_path)) |
||
12256 | ); |
||
12257 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$onlyDirectory; |
||
12258 | } |
||
12259 | } |
||
12260 | |||
12261 | if (strstr($file_path, 'default_course_document/') !== false) { |
||
12262 | $replace = str_replace('/main', '', $file_path); |
||
12263 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$replace; |
||
12264 | } |
||
12265 | |||
12266 | if (empty($dest_file)) { |
||
12267 | continue; |
||
12268 | } |
||
12269 | |||
12270 | $this->create_path($dest_file); |
||
12271 | copy($main_path.$file_path, $dest_file); |
||
12272 | // Check if the file needs a link update. |
||
12273 | if (in_array($file_path, array_keys($link_updates))) { |
||
12274 | $string = file_get_contents($dest_file); |
||
12275 | unlink($dest_file); |
||
12276 | foreach ($link_updates[$file_path] as $old_new) { |
||
12277 | // This is an ugly hack that allows .flv files to be found by the flv player that |
||
12278 | // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs |
||
12279 | // to find the flv to play in document/main/, so we replace main/ in the flv path by |
||
12280 | // ../../.. to return from inc/lib/flv_player to the document/main path. |
||
12281 | if (substr($old_new['dest'], -3) == 'flv' && |
||
12282 | substr($old_new['dest'], 0, 5) == 'main/' |
||
12283 | ) { |
||
12284 | $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']); |
||
12285 | } |
||
12286 | $string = str_replace($old_new['orig'], $old_new['dest'], $string); |
||
12287 | } |
||
12288 | file_put_contents($dest_file, $string); |
||
12289 | } |
||
12290 | } |
||
12291 | |||
12292 | if (is_array($links_to_create)) { |
||
12293 | foreach ($links_to_create as $file => $link) { |
||
12294 | $content = '<!DOCTYPE html><head> |
||
12295 | <meta charset="'.api_get_language_isocode().'" /> |
||
12296 | <title>'.$link['title'].'</title> |
||
12297 | </head> |
||
12298 | <body dir="'.api_get_text_direction().'"> |
||
12299 | <div style="text-align:center"> |
||
12300 | <a href="'.$link['url'].'">'.$link['title'].'</a></div> |
||
12301 | </body> |
||
12302 | </html>'; |
||
12303 | file_put_contents($archivePath.$temp_dir_short.'/'.$file, $content); |
||
12304 | } |
||
12305 | } |
||
12306 | |||
12307 | // Add non exportable message explanation. |
||
12308 | $lang_not_exportable = get_lang('ThisItemIsNotExportable'); |
||
12309 | $file_content = '<!DOCTYPE html><head> |
||
12310 | <meta charset="'.api_get_language_isocode().'" /> |
||
12311 | <title>'.$lang_not_exportable.'</title> |
||
12312 | <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" /> |
||
12313 | </head> |
||
12314 | <body dir="'.api_get_text_direction().'">'; |
||
12315 | $file_content .= |
||
12316 | <<<EOD |
||
12317 | <style> |
||
12318 | .error-message { |
||
12319 | font-family: arial, verdana, helvetica, sans-serif; |
||
12320 | border-width: 1px; |
||
12321 | border-style: solid; |
||
12322 | left: 50%; |
||
12323 | margin: 10px auto; |
||
12324 | min-height: 30px; |
||
12325 | padding: 5px; |
||
12326 | right: 50%; |
||
12327 | width: 500px; |
||
12328 | background-color: #FFD1D1; |
||
12329 | border-color: #FF0000; |
||
12330 | color: #000; |
||
12331 | } |
||
12332 | </style> |
||
12333 | <body> |
||
12334 | <div class="error-message"> |
||
12335 | $lang_not_exportable |
||
12336 | </div> |
||
12337 | </body> |
||
12338 | </html> |
||
12339 | EOD; |
||
12340 | if (!is_dir($archivePath.$temp_dir_short.'/document')) { |
||
12341 | @mkdir($archivePath.$temp_dir_short.'/document', api_get_permissions_for_new_directories()); |
||
12342 | } |
||
12343 | file_put_contents($archivePath.$temp_dir_short.'/document/non_exportable.html', $file_content); |
||
12344 | |||
12345 | // Add the extra files that go along with a SCORM package. |
||
12346 | $main_code_path = api_get_path(SYS_CODE_PATH).'lp/packaging/'; |
||
12347 | |||
12348 | $fs = new Filesystem(); |
||
12349 | $fs->mirror($main_code_path, $archivePath.$temp_dir_short); |
||
12350 | |||
12351 | // Finalize the imsmanifest structure, add to the zip, then return the zip. |
||
12352 | $manifest = @$xmldoc->saveXML(); |
||
12353 | $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now. |
||
12354 | file_put_contents($archivePath.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest); |
||
12355 | |||
12356 | $htmlIndex = new LpIndexGenerator($this); |
||
12357 | |||
12358 | file_put_contents( |
||
12359 | $archivePath.'/'.$temp_dir_short.'/index.html', |
||
12360 | $htmlIndex->generate() |
||
12361 | ); |
||
12362 | |||
12363 | $zip_folder->add( |
||
12364 | $archivePath.'/'.$temp_dir_short, |
||
12365 | PCLZIP_OPT_REMOVE_PATH, |
||
12366 | $archivePath.'/'.$temp_dir_short.'/' |
||
12367 | ); |
||
12368 | |||
12369 | // Clean possible temporary files. |
||
12370 | foreach ($files_cleanup as $file) { |
||
12371 | $res = unlink($file); |
||
12372 | if ($res === false) { |
||
12373 | error_log( |
||
12374 | 'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__, |
||
12375 | 0 |
||
12376 | ); |
||
12377 | } |
||
12378 | } |
||
12379 | $name = api_replace_dangerous_char($this->get_name()).'.zip'; |
||
12380 | DocumentManager::file_send_for_download($temp_zip_file, true, $name); |
||
12381 | } |
||
12382 | |||
12383 | /** |
||
12384 | * @param int $lp_id |
||
12385 | * |
||
12386 | * @return bool |
||
12387 | */ |
||
12388 | public function scorm_export_to_pdf($lp_id) |
||
12389 | { |
||
12390 | $lp_id = (int) $lp_id; |
||
12391 | $files_to_export = []; |
||
12392 | |||
12393 | $sessionId = api_get_session_id(); |
||
12394 | $course_data = api_get_course_info($this->cc); |
||
12395 | |||
12396 | if (!empty($course_data)) { |
||
12397 | $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path; |
||
12398 | $list = self::get_flat_ordered_items_list($lp_id); |
||
12399 | if (!empty($list)) { |
||
12400 | foreach ($list as $item_id) { |
||
12401 | $item = $this->items[$item_id]; |
||
12402 | switch ($item->type) { |
||
12403 | case 'document': |
||
12404 | // Getting documents from a LP with chamilo documents |
||
12405 | $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc); |
||
12406 | // Try loading document from the base course. |
||
12407 | if (empty($file_data) && !empty($sessionId)) { |
||
12408 | $file_data = DocumentManager::get_document_data_by_id( |
||
12409 | $item->path, |
||
12410 | $this->cc, |
||
12411 | false, |
||
12412 | 0 |
||
12413 | ); |
||
12414 | } |
||
12415 | $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path']; |
||
12416 | if (file_exists($file_path)) { |
||
12417 | $files_to_export[] = [ |
||
12418 | 'title' => $item->get_title(), |
||
12419 | 'path' => $file_path, |
||
12420 | ]; |
||
12421 | } |
||
12422 | break; |
||
12423 | case 'asset': //commes from a scorm package generated by chamilo |
||
12424 | case 'sco': |
||
12425 | $file_path = $scorm_path.'/'.$item->path; |
||
12426 | if (file_exists($file_path)) { |
||
12427 | $files_to_export[] = [ |
||
12428 | 'title' => $item->get_title(), |
||
12429 | 'path' => $file_path, |
||
12430 | ]; |
||
12431 | } |
||
12432 | break; |
||
12433 | case 'dir': |
||
12434 | $files_to_export[] = [ |
||
12435 | 'title' => $item->get_title(), |
||
12436 | 'path' => null, |
||
12437 | ]; |
||
12438 | break; |
||
12439 | } |
||
12440 | } |
||
12441 | } |
||
12442 | |||
12443 | $pdf = new PDF(); |
||
12444 | $result = $pdf->html_to_pdf( |
||
12445 | $files_to_export, |
||
12446 | $this->name, |
||
12447 | $this->cc, |
||
12448 | true, |
||
12449 | true, |
||
12450 | true, |
||
12451 | $this->get_name() |
||
12452 | ); |
||
12453 | |||
12454 | return $result; |
||
12455 | } |
||
12456 | |||
12457 | return false; |
||
12458 | } |
||
12459 | |||
12460 | /** |
||
12461 | * Temp function to be moved in main_api or the best place around for this. |
||
12462 | * Creates a file path if it doesn't exist. |
||
12463 | * |
||
12464 | * @param string $path |
||
12465 | */ |
||
12466 | public function create_path($path) |
||
12467 | { |
||
12468 | $path_bits = explode('/', dirname($path)); |
||
12469 | |||
12470 | // IS_WINDOWS_OS has been defined in main_api.lib.php |
||
12471 | $path_built = IS_WINDOWS_OS ? '' : '/'; |
||
12472 | foreach ($path_bits as $bit) { |
||
12473 | if (!empty($bit)) { |
||
12474 | $new_path = $path_built.$bit; |
||
12475 | if (is_dir($new_path)) { |
||
12476 | $path_built = $new_path.'/'; |
||
12477 | } else { |
||
12478 | mkdir($new_path, api_get_permissions_for_new_directories()); |
||
12479 | $path_built = $new_path.'/'; |
||
12480 | } |
||
12481 | } |
||
12482 | } |
||
12483 | } |
||
12484 | |||
12485 | /** |
||
12486 | * Delete the image relative to this learning path. No parameter. Only works on instanciated object. |
||
12487 | * |
||
12488 | * @return bool The results of the unlink function, or false if there was no image to start with |
||
12489 | */ |
||
12490 | public function delete_lp_image() |
||
12491 | { |
||
12492 | $img = $this->get_preview_image(); |
||
12493 | if ($img != '') { |
||
12494 | $del_file = $this->get_preview_image_path(null, 'sys'); |
||
12495 | if (isset($del_file) && file_exists($del_file)) { |
||
12496 | $del_file_2 = $this->get_preview_image_path(64, 'sys'); |
||
12497 | if (file_exists($del_file_2)) { |
||
12498 | unlink($del_file_2); |
||
12499 | } |
||
12500 | $this->set_preview_image(''); |
||
12501 | |||
12502 | return @unlink($del_file); |
||
12503 | } |
||
12504 | } |
||
12505 | |||
12506 | return false; |
||
12507 | } |
||
12508 | |||
12509 | /** |
||
12510 | * Uploads an author image to the upload/learning_path/images directory. |
||
12511 | * |
||
12512 | * @param array The image array, coming from the $_FILES superglobal |
||
12513 | * |
||
12514 | * @return bool True on success, false on error |
||
12515 | */ |
||
12516 | public function upload_image($image_array) |
||
12517 | { |
||
12518 | if (!empty($image_array['name'])) { |
||
12519 | $upload_ok = process_uploaded_file($image_array); |
||
12520 | $has_attachment = true; |
||
12521 | } |
||
12522 | |||
12523 | if ($upload_ok && $has_attachment) { |
||
12524 | $courseDir = api_get_course_path().'/upload/learning_path/images'; |
||
12525 | $sys_course_path = api_get_path(SYS_COURSE_PATH); |
||
12526 | $updir = $sys_course_path.$courseDir; |
||
12527 | // Try to add an extension to the file if it hasn't one. |
||
12528 | $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']); |
||
12529 | |||
12530 | if (filter_extension($new_file_name)) { |
||
12531 | $file_extension = explode('.', $image_array['name']); |
||
12532 | $file_extension = strtolower($file_extension[count($file_extension) - 1]); |
||
12533 | $filename = uniqid(''); |
||
12534 | $new_file_name = $filename.'.'.$file_extension; |
||
12535 | $new_path = $updir.'/'.$new_file_name; |
||
12536 | |||
12537 | // Resize the image. |
||
12538 | $temp = new Image($image_array['tmp_name']); |
||
12539 | $temp->resize(104); |
||
12540 | $result = $temp->send_image($new_path); |
||
12541 | |||
12542 | // Storing the image filename. |
||
12543 | if ($result) { |
||
12544 | $this->set_preview_image($new_file_name); |
||
12545 | |||
12546 | //Resize to 64px to use on course homepage |
||
12547 | $temp->resize(64); |
||
12548 | $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension); |
||
12549 | |||
12550 | return true; |
||
12551 | } |
||
12552 | } |
||
12553 | } |
||
12554 | |||
12555 | return false; |
||
12556 | } |
||
12557 | |||
12558 | /** |
||
12559 | * @param int $lp_id |
||
12560 | * @param string $status |
||
12561 | */ |
||
12562 | public function set_autolaunch($lp_id, $status) |
||
12563 | { |
||
12564 | $course_id = api_get_course_int_id(); |
||
12565 | $lp_id = (int) $lp_id; |
||
12566 | $status = (int) $status; |
||
12567 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
12568 | |||
12569 | // Setting everything to autolaunch = 0 |
||
12570 | $attributes['autolaunch'] = 0; |
||
12571 | $where = [ |
||
12572 | 'session_id = ? AND c_id = ? ' => [ |
||
12573 | api_get_session_id(), |
||
12574 | $course_id, |
||
12575 | ], |
||
12576 | ]; |
||
12577 | Database::update($lp_table, $attributes, $where); |
||
12578 | if ($status == 1) { |
||
12579 | //Setting my lp_id to autolaunch = 1 |
||
12580 | $attributes['autolaunch'] = 1; |
||
12581 | $where = [ |
||
12582 | 'iid = ? AND session_id = ? AND c_id = ?' => [ |
||
12583 | $lp_id, |
||
12584 | api_get_session_id(), |
||
12585 | $course_id, |
||
12586 | ], |
||
12587 | ]; |
||
12588 | Database::update($lp_table, $attributes, $where); |
||
12589 | } |
||
12590 | } |
||
12591 | |||
12592 | /** |
||
12593 | * Gets previous_item_id for the next element of the lp_item table. |
||
12594 | * |
||
12595 | * @author Isaac flores paz |
||
12596 | * |
||
12597 | * @return int Previous item ID |
||
12598 | */ |
||
12599 | public function select_previous_item_id() |
||
12600 | { |
||
12601 | $course_id = api_get_course_int_id(); |
||
12602 | $table_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
12603 | |||
12604 | // Get the max order of the items |
||
12605 | $sql = "SELECT max(display_order) AS display_order FROM $table_lp_item |
||
12606 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
12607 | $rs_max_order = Database::query($sql); |
||
12608 | $row_max_order = Database::fetch_object($rs_max_order); |
||
12609 | $max_order = $row_max_order->display_order; |
||
12610 | // Get the previous item ID |
||
12611 | $sql = "SELECT iid as previous FROM $table_lp_item |
||
12612 | WHERE |
||
12613 | c_id = $course_id AND |
||
12614 | lp_id = ".$this->lp_id." AND |
||
12615 | display_order = '$max_order' "; |
||
12616 | $rs_max = Database::query($sql); |
||
12617 | $row_max = Database::fetch_object($rs_max); |
||
12618 | |||
12619 | // Return the previous item ID |
||
12620 | return $row_max->previous; |
||
12621 | } |
||
12622 | |||
12623 | /** |
||
12624 | * Copies an LP. |
||
12625 | */ |
||
12626 | public function copy() |
||
12627 | { |
||
12628 | // Course builder |
||
12629 | $cb = new CourseBuilder(); |
||
12630 | |||
12631 | //Setting tools that will be copied |
||
12632 | $cb->set_tools_to_build(['learnpaths']); |
||
12633 | |||
12634 | //Setting elements that will be copied |
||
12635 | $cb->set_tools_specific_id_list(['learnpaths' => [$this->lp_id]]); |
||
12636 | |||
12637 | $course = $cb->build(); |
||
12638 | |||
12639 | //Course restorer |
||
12640 | $course_restorer = new CourseRestorer($course); |
||
12641 | $course_restorer->set_add_text_in_items(true); |
||
12642 | $course_restorer->set_tool_copy_settings( |
||
12643 | ['learnpaths' => ['reset_dates' => true]] |
||
12644 | ); |
||
12645 | $course_restorer->restore( |
||
12646 | api_get_course_id(), |
||
12647 | api_get_session_id(), |
||
12648 | false, |
||
12649 | false |
||
12650 | ); |
||
12651 | } |
||
12652 | |||
12653 | /** |
||
12654 | * Verify document size. |
||
12655 | * |
||
12656 | * @param string $s |
||
12657 | * |
||
12658 | * @return bool |
||
12659 | */ |
||
12660 | public static function verify_document_size($s) |
||
12661 | { |
||
12662 | $post_max = ini_get('post_max_size'); |
||
12663 | if (substr($post_max, -1, 1) == 'M') { |
||
12664 | $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024; |
||
12665 | } elseif (substr($post_max, -1, 1) == 'G') { |
||
12666 | $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024; |
||
12667 | } |
||
12668 | $upl_max = ini_get('upload_max_filesize'); |
||
12669 | if (substr($upl_max, -1, 1) == 'M') { |
||
12670 | $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024; |
||
12671 | } elseif (substr($upl_max, -1, 1) == 'G') { |
||
12672 | $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024; |
||
12673 | } |
||
12674 | $documents_total_space = DocumentManager::documents_total_space(); |
||
12675 | $course_max_space = DocumentManager::get_course_quota(); |
||
12676 | $total_size = filesize($s) + $documents_total_space; |
||
12677 | if (filesize($s) > $post_max || filesize($s) > $upl_max || $total_size > $course_max_space) { |
||
12678 | return true; |
||
12679 | } |
||
12680 | |||
12681 | return false; |
||
12682 | } |
||
12683 | |||
12684 | /** |
||
12685 | * Clear LP prerequisites. |
||
12686 | */ |
||
12687 | public function clear_prerequisites() |
||
12688 | { |
||
12689 | $course_id = $this->get_course_int_id(); |
||
12690 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
12691 | $lp_id = $this->get_id(); |
||
12692 | // Cleaning prerequisites |
||
12693 | $sql = "UPDATE $tbl_lp_item SET prerequisite = '' |
||
12694 | WHERE c_id = $course_id AND lp_id = $lp_id"; |
||
12695 | Database::query($sql); |
||
12696 | |||
12697 | // Cleaning mastery score for exercises |
||
12698 | $sql = "UPDATE $tbl_lp_item SET mastery_score = '' |
||
12699 | WHERE c_id = $course_id AND lp_id = $lp_id AND item_type = 'quiz'"; |
||
12700 | Database::query($sql); |
||
12701 | } |
||
12702 | |||
12703 | public function set_previous_step_as_prerequisite_for_all_items() |
||
12704 | { |
||
12705 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
12706 | $course_id = $this->get_course_int_id(); |
||
12707 | $lp_id = $this->get_id(); |
||
12708 | |||
12709 | if (!empty($this->items)) { |
||
12710 | $previous_item_id = null; |
||
12711 | $previous_item_max = 0; |
||
12712 | $previous_item_type = null; |
||
12713 | $last_item_not_dir = null; |
||
12714 | $last_item_not_dir_type = null; |
||
12715 | $last_item_not_dir_max = null; |
||
12716 | |||
12717 | foreach ($this->ordered_items as $itemId) { |
||
12718 | $item = $this->getItem($itemId); |
||
12719 | // if there was a previous item... (otherwise jump to set it) |
||
12720 | if (!empty($previous_item_id)) { |
||
12721 | $current_item_id = $item->get_id(); //save current id |
||
12722 | if ($item->get_type() != 'dir') { |
||
12723 | // Current item is not a folder, so it qualifies to get a prerequisites |
||
12724 | if ($last_item_not_dir_type == 'quiz') { |
||
12725 | // if previous is quiz, mark its max score as default score to be achieved |
||
12726 | $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_dir_max' |
||
12727 | WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $last_item_not_dir"; |
||
12728 | Database::query($sql); |
||
12729 | } |
||
12730 | // now simply update the prerequisite to set it to the last non-chapter item |
||
12731 | $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_dir' |
||
12732 | WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $current_item_id"; |
||
12733 | Database::query($sql); |
||
12734 | // record item as 'non-chapter' reference |
||
12735 | $last_item_not_dir = $item->get_id(); |
||
12736 | $last_item_not_dir_type = $item->get_type(); |
||
12737 | $last_item_not_dir_max = $item->get_max(); |
||
12738 | } |
||
12739 | } else { |
||
12740 | if ($item->get_type() != 'dir') { |
||
12741 | // Current item is not a folder (but it is the first item) so record as last "non-chapter" item |
||
12742 | $last_item_not_dir = $item->get_id(); |
||
12743 | $last_item_not_dir_type = $item->get_type(); |
||
12744 | $last_item_not_dir_max = $item->get_max(); |
||
12745 | } |
||
12746 | } |
||
12747 | // Saving the item as "previous item" for the next loop |
||
12748 | $previous_item_id = $item->get_id(); |
||
12749 | $previous_item_max = $item->get_max(); |
||
12750 | $previous_item_type = $item->get_type(); |
||
12751 | } |
||
12752 | } |
||
12753 | } |
||
12754 | |||
12755 | /** |
||
12756 | * @param array $params |
||
12757 | * |
||
12758 | * @throws \Doctrine\ORM\OptimisticLockException |
||
12759 | * |
||
12760 | * @return int |
||
12761 | */ |
||
12762 | public static function createCategory($params) |
||
12763 | { |
||
12764 | $em = Database::getManager(); |
||
12765 | $item = new CLpCategory(); |
||
12766 | $item->setName($params['name']); |
||
12767 | $item->setCId($params['c_id']); |
||
12768 | $em->persist($item); |
||
12769 | $em->flush(); |
||
12770 | |||
12771 | $id = $item->getId(); |
||
12772 | |||
12773 | $sessionId = api_get_session_id(); |
||
12774 | if (!empty($sessionId) && api_get_configuration_value('allow_session_lp_category')) { |
||
12775 | $table = Database::get_course_table(TABLE_LP_CATEGORY); |
||
12776 | $sql = "UPDATE $table SET session_id = $sessionId WHERE iid = $id"; |
||
12777 | Database::query($sql); |
||
12778 | } |
||
12779 | |||
12780 | api_item_property_update( |
||
12781 | api_get_course_info(), |
||
12782 | TOOL_LEARNPATH_CATEGORY, |
||
12783 | $id, |
||
12784 | 'visible', |
||
12785 | api_get_user_id() |
||
12786 | ); |
||
12787 | |||
12788 | return $item->getId(); |
||
12789 | } |
||
12790 | |||
12791 | /** |
||
12792 | * @param array $params |
||
12793 | */ |
||
12794 | public static function updateCategory($params) |
||
12795 | { |
||
12796 | $em = Database::getManager(); |
||
12797 | $item = self::getCategory($params['id']); |
||
12798 | |||
12799 | if ($item) { |
||
12800 | $item->setName($params['name']); |
||
12801 | $em->persist($item); |
||
12802 | $em->flush(); |
||
12803 | } |
||
12804 | } |
||
12805 | |||
12806 | /** |
||
12807 | * @param int $id |
||
12808 | */ |
||
12809 | public static function moveUpCategory($id) |
||
12810 | { |
||
12811 | $item = self::getCategory($id); |
||
12812 | if ($item) { |
||
12813 | $em = Database::getManager(); |
||
12814 | $position = $item->getPosition() - 1; |
||
12815 | $item->setPosition($position); |
||
12816 | $em->persist($item); |
||
12817 | $em->flush(); |
||
12818 | } |
||
12819 | } |
||
12820 | |||
12821 | /** |
||
12822 | * @param int $id |
||
12823 | * |
||
12824 | * @throws \Doctrine\ORM\ORMException |
||
12825 | * @throws \Doctrine\ORM\OptimisticLockException |
||
12826 | * @throws \Doctrine\ORM\TransactionRequiredException |
||
12827 | */ |
||
12828 | public static function moveDownCategory($id) |
||
12829 | { |
||
12830 | $item = self::getCategory($id); |
||
12831 | if ($item) { |
||
12832 | $em = Database::getManager(); |
||
12833 | $position = $item->getPosition() + 1; |
||
12834 | $item->setPosition($position); |
||
12835 | $em->persist($item); |
||
12836 | $em->flush(); |
||
12837 | } |
||
12838 | } |
||
12839 | |||
12840 | public static function getLpList($courseId, $sessionId, $onlyActiveLp = true) |
||
12841 | { |
||
12842 | $TABLE_LP = Database::get_course_table(TABLE_LP_MAIN); |
||
12843 | $TABLE_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY); |
||
12844 | $courseId = (int) $courseId; |
||
12845 | $sessionId = (int) $sessionId; |
||
12846 | |||
12847 | $sql = "SELECT lp.id, lp.name |
||
12848 | FROM $TABLE_LP lp |
||
12849 | INNER JOIN $TABLE_ITEM_PROPERTY ip |
||
12850 | ON lp.id = ip.ref |
||
12851 | WHERE lp.c_id = $courseId "; |
||
12852 | |||
12853 | if (!empty($sessionId)) { |
||
12854 | $sql .= "AND ip.session_id = $sessionId "; |
||
12855 | } |
||
12856 | |||
12857 | if ($onlyActiveLp) { |
||
12858 | $sql .= "AND ip.tool = 'learnpath' "; |
||
12859 | $sql .= "AND ip.visibility = 1 "; |
||
12860 | } |
||
12861 | |||
12862 | $sql .= "GROUP BY lp.id"; |
||
12863 | |||
12864 | $result = Database::query($sql); |
||
12865 | |||
12866 | return Database::store_result($result, 'ASSOC'); |
||
12867 | } |
||
12868 | |||
12869 | /** |
||
12870 | * @param int $courseId |
||
12871 | * |
||
12872 | * @throws \Doctrine\ORM\Query\QueryException |
||
12873 | * |
||
12874 | * @return int|mixed |
||
12875 | */ |
||
12876 | public static function getCountCategories($courseId) |
||
12877 | { |
||
12878 | if (empty($courseId)) { |
||
12879 | return 0; |
||
12880 | } |
||
12881 | $em = Database::getManager(); |
||
12882 | $query = $em->createQuery('SELECT COUNT(u.id) FROM ChamiloCourseBundle:CLpCategory u WHERE u.cId = :id'); |
||
12883 | $query->setParameter('id', $courseId); |
||
12884 | |||
12885 | return $query->getSingleScalarResult(); |
||
12886 | } |
||
12887 | |||
12888 | /** |
||
12889 | * @param int $courseId |
||
12890 | * |
||
12891 | * @return array|CLpCategory[] |
||
12892 | */ |
||
12893 | public static function getCategories($courseId, $withNoneCategory = false) |
||
12894 | { |
||
12895 | $em = Database::getManager(); |
||
12896 | |||
12897 | // Using doctrine extensions |
||
12898 | /** @var SortableRepository $repo */ |
||
12899 | $repo = $em->getRepository('ChamiloCourseBundle:CLpCategory'); |
||
12900 | |||
12901 | $categories = $repo->getBySortableGroupsQuery(['cId' => $courseId])->getResult(); |
||
12902 | |||
12903 | if ($withNoneCategory) { |
||
12904 | $categoryTest = new CLpCategory(); |
||
12905 | $categoryTest->setId(0) |
||
12906 | ->setName(get_lang('WithOutCategory')) |
||
12907 | ->setPosition(0); |
||
12908 | |||
12909 | array_unshift($categories, $categoryTest); |
||
12910 | } |
||
12911 | |||
12912 | return $categories; |
||
12913 | } |
||
12914 | |||
12915 | public static function getCategorySessionId($id) |
||
12916 | { |
||
12917 | if (false === api_get_configuration_value('allow_session_lp_category')) { |
||
12918 | return 0; |
||
12919 | } |
||
12920 | |||
12921 | $table = Database::get_course_table(TABLE_LP_CATEGORY); |
||
12922 | $id = (int) $id; |
||
12923 | |||
12924 | $sql = "SELECT session_id FROM $table WHERE iid = $id"; |
||
12925 | $result = Database::query($sql); |
||
12926 | $result = Database::fetch_array($result, 'ASSOC'); |
||
12927 | |||
12928 | if ($result) { |
||
12929 | return (int) $result['session_id']; |
||
12930 | } |
||
12931 | |||
12932 | return 0; |
||
12933 | } |
||
12934 | |||
12935 | /** |
||
12936 | * @param int $id |
||
12937 | * |
||
12938 | * @return CLpCategory |
||
12939 | */ |
||
12940 | public static function getCategory($id) |
||
12941 | { |
||
12942 | $id = (int) $id; |
||
12943 | $em = Database::getManager(); |
||
12944 | |||
12945 | return $em->find('ChamiloCourseBundle:CLpCategory', $id); |
||
12946 | } |
||
12947 | |||
12948 | /** |
||
12949 | * @param int $courseId |
||
12950 | * |
||
12951 | * @return CLpCategory[] |
||
12952 | */ |
||
12953 | public static function getCategoryByCourse($courseId) |
||
12954 | { |
||
12955 | $em = Database::getManager(); |
||
12956 | |||
12957 | return $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(['cId' => $courseId]); |
||
12958 | } |
||
12959 | |||
12960 | /** |
||
12961 | * @param int $id |
||
12962 | * |
||
12963 | * @return bool |
||
12964 | */ |
||
12965 | public static function deleteCategory($id) |
||
12966 | { |
||
12967 | $em = Database::getManager(); |
||
12968 | $id = (int) $id; |
||
12969 | $item = self::getCategory($id); |
||
12970 | if ($item) { |
||
12971 | $courseId = $item->getCId(); |
||
12972 | $query = $em->createQuery('SELECT u FROM ChamiloCourseBundle:CLp u WHERE u.cId = :id AND u.categoryId = :catId'); |
||
12973 | $query->setParameter('id', $courseId); |
||
12974 | $query->setParameter('catId', $item->getId()); |
||
12975 | $lps = $query->getResult(); |
||
12976 | |||
12977 | // Setting category = 0. |
||
12978 | if ($lps) { |
||
12979 | foreach ($lps as $lpItem) { |
||
12980 | $lpItem->setCategoryId(0); |
||
12981 | } |
||
12982 | } |
||
12983 | |||
12984 | if (api_get_configuration_value('allow_lp_subscription_to_usergroups')) { |
||
12985 | $table = Database::get_course_table(TABLE_LP_CATEGORY_REL_USERGROUP); |
||
12986 | $sql = "DELETE FROM $table |
||
12987 | WHERE |
||
12988 | lp_category_id = $id AND |
||
12989 | c_id = $courseId "; |
||
12990 | Database::query($sql); |
||
12991 | } |
||
12992 | |||
12993 | // Removing category. |
||
12994 | $em->remove($item); |
||
12995 | $em->flush(); |
||
12996 | |||
12997 | $courseInfo = api_get_course_info_by_id($courseId); |
||
12998 | $sessionId = api_get_session_id(); |
||
12999 | |||
13000 | // Delete link tool |
||
13001 | $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
13002 | $linkLikeValue = "lp/lp_controller.php?%&action=view_category&id=$id%"; |
||
13003 | // Delete tools |
||
13004 | $sql = "DELETE FROM $tbl_tool |
||
13005 | WHERE c_id = ".$courseId." AND (link LIKE '$linkLikeValue' AND image='lp_category.gif')"; |
||
13006 | Database::query($sql); |
||
13007 | |||
13008 | return true; |
||
13009 | } |
||
13010 | |||
13011 | return false; |
||
13012 | } |
||
13013 | |||
13014 | /** |
||
13015 | * @param int $courseId |
||
13016 | * @param bool $addSelectOption |
||
13017 | * |
||
13018 | * @return mixed |
||
13019 | */ |
||
13020 | public static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false) |
||
13021 | { |
||
13022 | $items = self::getCategoryByCourse($courseId); |
||
13023 | $cats = []; |
||
13024 | if ($addSelectOption) { |
||
13025 | $cats = [get_lang('SelectACategory')]; |
||
13026 | } |
||
13027 | |||
13028 | $sessionId = api_get_session_id(); |
||
13029 | $avoidCategoryInSession = false; |
||
13030 | if (empty($sessionId)) { |
||
13031 | $avoidCategoryInSession = true; |
||
13032 | } |
||
13033 | /*$checkSession = false; |
||
13034 | if (api_get_configuration_value('allow_session_lp_category')) { |
||
13035 | $checkSession = true; |
||
13036 | }*/ |
||
13037 | |||
13038 | if (!empty($items)) { |
||
13039 | foreach ($items as $cat) { |
||
13040 | $categoryId = $cat->getId(); |
||
13041 | if ($avoidCategoryInSession) { |
||
13042 | $inSession = self::getCategorySessionId($categoryId); |
||
13043 | if (!empty($inSession)) { |
||
13044 | continue; |
||
13045 | } |
||
13046 | } |
||
13047 | $cats[$categoryId] = $cat->getName(); |
||
13048 | } |
||
13049 | } |
||
13050 | |||
13051 | return $cats; |
||
13052 | } |
||
13053 | |||
13054 | /** |
||
13055 | * @param string $courseCode |
||
13056 | * @param int $lpId |
||
13057 | * @param int $user_id |
||
13058 | * |
||
13059 | * @return learnpath |
||
13060 | */ |
||
13061 | public static function getLpFromSession($courseCode, $lpId, $user_id) |
||
13062 | { |
||
13063 | $debug = 0; |
||
13064 | $learnPath = null; |
||
13065 | $lpObject = Session::read('lpobject'); |
||
13066 | if ($lpObject !== null) { |
||
13067 | $learnPath = UnserializeApi::unserialize('lp', $lpObject); |
||
13068 | if ($debug) { |
||
13069 | error_log('getLpFromSession: unserialize'); |
||
13070 | error_log('------getLpFromSession------'); |
||
13071 | error_log('------unserialize------'); |
||
13072 | error_log("lp_view_session_id: ".$learnPath->lp_view_session_id); |
||
13073 | error_log("api_get_sessionid: ".api_get_session_id()); |
||
13074 | } |
||
13075 | } |
||
13076 | |||
13077 | if (!is_object($learnPath)) { |
||
13078 | $learnPath = new learnpath($courseCode, $lpId, $user_id); |
||
13079 | if ($debug) { |
||
13080 | error_log('------getLpFromSession------'); |
||
13081 | error_log('getLpFromSession: create new learnpath'); |
||
13082 | error_log("create new LP with $courseCode - $lpId - $user_id"); |
||
13083 | error_log("lp_view_session_id: ".$learnPath->lp_view_session_id); |
||
13084 | error_log("api_get_sessionid: ".api_get_session_id()); |
||
13085 | } |
||
13086 | } |
||
13087 | |||
13088 | return $learnPath; |
||
13089 | } |
||
13090 | |||
13091 | /** |
||
13092 | * @param int $itemId |
||
13093 | * |
||
13094 | * @return learnpathItem|false |
||
13095 | */ |
||
13096 | public function getItem($itemId) |
||
13097 | { |
||
13098 | if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) { |
||
13099 | return $this->items[$itemId]; |
||
13100 | } |
||
13101 | |||
13102 | return false; |
||
13103 | } |
||
13104 | |||
13105 | /** |
||
13106 | * @return int |
||
13107 | */ |
||
13108 | public function getCurrentAttempt() |
||
13109 | { |
||
13110 | $attempt = $this->getItem($this->get_current_item_id()); |
||
13111 | if ($attempt) { |
||
13112 | $attemptId = $attempt->get_attempt_id(); |
||
13113 | |||
13114 | return $attemptId; |
||
13115 | } |
||
13116 | |||
13117 | return 0; |
||
13118 | } |
||
13119 | |||
13120 | /** |
||
13121 | * @return int |
||
13122 | */ |
||
13123 | public function getCategoryId() |
||
13124 | { |
||
13125 | return (int) $this->categoryId; |
||
13126 | } |
||
13127 | |||
13128 | /** |
||
13129 | * @param int $categoryId |
||
13130 | * |
||
13131 | * @return bool |
||
13132 | */ |
||
13133 | public function setCategoryId($categoryId) |
||
13134 | { |
||
13135 | $this->categoryId = (int) $categoryId; |
||
13136 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
13137 | $lp_id = $this->get_id(); |
||
13138 | $sql = "UPDATE $table SET category_id = ".$this->categoryId." |
||
13139 | WHERE iid = $lp_id"; |
||
13140 | Database::query($sql); |
||
13141 | |||
13142 | return true; |
||
13143 | } |
||
13144 | |||
13145 | /** |
||
13146 | * Get whether this is a learning path with the possibility to subscribe |
||
13147 | * users or not. |
||
13148 | * |
||
13149 | * @return int |
||
13150 | */ |
||
13151 | public function getSubscribeUsers() |
||
13152 | { |
||
13153 | return $this->subscribeUsers; |
||
13154 | } |
||
13155 | |||
13156 | /** |
||
13157 | * Set whether this is a learning path with the possibility to subscribe |
||
13158 | * users or not. |
||
13159 | * |
||
13160 | * @param int $value (0 = false, 1 = true) |
||
13161 | * |
||
13162 | * @return bool |
||
13163 | */ |
||
13164 | public function setSubscribeUsers($value) |
||
13165 | { |
||
13166 | $this->subscribeUsers = (int) $value; |
||
13167 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
13168 | $lp_id = $this->get_id(); |
||
13169 | $sql = "UPDATE $table SET subscribe_users = ".$this->subscribeUsers." |
||
13170 | WHERE iid = $lp_id"; |
||
13171 | Database::query($sql); |
||
13172 | |||
13173 | return true; |
||
13174 | } |
||
13175 | |||
13176 | /** |
||
13177 | * Calculate the count of stars for a user in this LP |
||
13178 | * This calculation is based on the following rules: |
||
13179 | * - the student gets one star when he gets to 50% of the learning path |
||
13180 | * - the student gets a second star when the average score of all tests inside the learning path >= 50% |
||
13181 | * - the student gets a third star when the average score of all tests inside the learning path >= 80% |
||
13182 | * - the student gets the final star when the score for the *last* test is >= 80%. |
||
13183 | * |
||
13184 | * @param int $sessionId Optional. The session ID |
||
13185 | * |
||
13186 | * @return int The count of stars |
||
13187 | */ |
||
13188 | public function getCalculateStars($sessionId = 0) |
||
13189 | { |
||
13190 | $stars = 0; |
||
13191 | $progress = self::getProgress( |
||
13192 | $this->lp_id, |
||
13193 | $this->user_id, |
||
13194 | $this->course_int_id, |
||
13195 | $sessionId |
||
13196 | ); |
||
13197 | |||
13198 | if ($progress >= 50) { |
||
13199 | $stars++; |
||
13200 | } |
||
13201 | |||
13202 | // Calculate stars chapters evaluation |
||
13203 | $exercisesItems = $this->getExercisesItems(); |
||
13204 | |||
13205 | if (!empty($exercisesItems)) { |
||
13206 | $totalResult = 0; |
||
13207 | |||
13208 | foreach ($exercisesItems as $exerciseItem) { |
||
13209 | $exerciseResultInfo = Event::getExerciseResultsByUser( |
||
13210 | $this->user_id, |
||
13211 | $exerciseItem->path, |
||
13212 | $this->course_int_id, |
||
13213 | $sessionId, |
||
13214 | $this->lp_id, |
||
13215 | $exerciseItem->db_id |
||
13216 | ); |
||
13217 | |||
13218 | $exerciseResultInfo = end($exerciseResultInfo); |
||
13219 | |||
13220 | if (!$exerciseResultInfo) { |
||
13221 | continue; |
||
13222 | } |
||
13223 | |||
13224 | if (!empty($exerciseResultInfo['exe_weighting'])) { |
||
13225 | $exerciseResult = $exerciseResultInfo['exe_result'] * 100 / $exerciseResultInfo['exe_weighting']; |
||
13226 | } else { |
||
13227 | $exerciseResult = 0; |
||
13228 | } |
||
13229 | $totalResult += $exerciseResult; |
||
13230 | } |
||
13231 | |||
13232 | $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1); |
||
13233 | |||
13234 | if ($totalExerciseAverage >= 50) { |
||
13235 | $stars++; |
||
13236 | } |
||
13237 | |||
13238 | if ($totalExerciseAverage >= 80) { |
||
13239 | $stars++; |
||
13240 | } |
||
13241 | } |
||
13242 | |||
13243 | // Calculate star for final evaluation |
||
13244 | $finalEvaluationItem = $this->getFinalEvaluationItem(); |
||
13245 | |||
13246 | if (!empty($finalEvaluationItem)) { |
||
13247 | $evaluationResultInfo = Event::getExerciseResultsByUser( |
||
13248 | $this->user_id, |
||
13249 | $finalEvaluationItem->path, |
||
13250 | $this->course_int_id, |
||
13251 | $sessionId, |
||
13252 | $this->lp_id, |
||
13253 | $finalEvaluationItem->db_id |
||
13254 | ); |
||
13255 | |||
13256 | $evaluationResultInfo = end($evaluationResultInfo); |
||
13257 | |||
13258 | if ($evaluationResultInfo) { |
||
13259 | $evaluationResult = $evaluationResultInfo['exe_result'] * 100 / $evaluationResultInfo['exe_weighting']; |
||
13260 | |||
13261 | if ($evaluationResult >= 80) { |
||
13262 | $stars++; |
||
13263 | } |
||
13264 | } |
||
13265 | } |
||
13266 | |||
13267 | return $stars; |
||
13268 | } |
||
13269 | |||
13270 | /** |
||
13271 | * Get the items of exercise type. |
||
13272 | * |
||
13273 | * @return array The items. Otherwise return false |
||
13274 | */ |
||
13275 | public function getExercisesItems() |
||
13276 | { |
||
13277 | $exercises = []; |
||
13278 | foreach ($this->items as $item) { |
||
13279 | if ($item->type != 'quiz') { |
||
13280 | continue; |
||
13281 | } |
||
13282 | $exercises[] = $item; |
||
13283 | } |
||
13284 | |||
13285 | array_pop($exercises); |
||
13286 | |||
13287 | return $exercises; |
||
13288 | } |
||
13289 | |||
13290 | /** |
||
13291 | * Get the item of exercise type (evaluation type). |
||
13292 | * |
||
13293 | * @return array The final evaluation. Otherwise return false |
||
13294 | */ |
||
13295 | public function getFinalEvaluationItem() |
||
13296 | { |
||
13297 | $exercises = []; |
||
13298 | foreach ($this->items as $item) { |
||
13299 | if ($item->type != 'quiz') { |
||
13300 | continue; |
||
13301 | } |
||
13302 | |||
13303 | $exercises[] = $item; |
||
13304 | } |
||
13305 | |||
13306 | return array_pop($exercises); |
||
13307 | } |
||
13308 | |||
13309 | /** |
||
13310 | * Calculate the total points achieved for the current user in this learning path. |
||
13311 | * |
||
13312 | * @param int $sessionId Optional. The session Id |
||
13313 | * |
||
13314 | * @return int |
||
13315 | */ |
||
13316 | public function getCalculateScore($sessionId = 0) |
||
13317 | { |
||
13318 | // Calculate stars chapters evaluation |
||
13319 | $exercisesItems = $this->getExercisesItems(); |
||
13320 | $finalEvaluationItem = $this->getFinalEvaluationItem(); |
||
13321 | $totalExercisesResult = 0; |
||
13322 | $totalEvaluationResult = 0; |
||
13323 | |||
13324 | if ($exercisesItems !== false) { |
||
13325 | foreach ($exercisesItems as $exerciseItem) { |
||
13326 | $exerciseResultInfo = Event::getExerciseResultsByUser( |
||
13327 | $this->user_id, |
||
13328 | $exerciseItem->path, |
||
13329 | $this->course_int_id, |
||
13330 | $sessionId, |
||
13331 | $this->lp_id, |
||
13332 | $exerciseItem->db_id |
||
13333 | ); |
||
13334 | |||
13335 | $exerciseResultInfo = end($exerciseResultInfo); |
||
13336 | |||
13337 | if (!$exerciseResultInfo) { |
||
13338 | continue; |
||
13339 | } |
||
13340 | |||
13341 | $totalExercisesResult += $exerciseResultInfo['exe_result']; |
||
13342 | } |
||
13343 | } |
||
13344 | |||
13345 | if (!empty($finalEvaluationItem)) { |
||
13346 | $evaluationResultInfo = Event::getExerciseResultsByUser( |
||
13347 | $this->user_id, |
||
13348 | $finalEvaluationItem->path, |
||
13349 | $this->course_int_id, |
||
13350 | $sessionId, |
||
13351 | $this->lp_id, |
||
13352 | $finalEvaluationItem->db_id |
||
13353 | ); |
||
13354 | |||
13355 | $evaluationResultInfo = end($evaluationResultInfo); |
||
13356 | |||
13357 | if ($evaluationResultInfo) { |
||
13358 | $totalEvaluationResult += $evaluationResultInfo['exe_result']; |
||
13359 | } |
||
13360 | } |
||
13361 | |||
13362 | return $totalExercisesResult + $totalEvaluationResult; |
||
13363 | } |
||
13364 | |||
13365 | /** |
||
13366 | * Check if URL is not allowed to be show in a iframe. |
||
13367 | * |
||
13368 | * @param string $src |
||
13369 | * |
||
13370 | * @return string |
||
13371 | */ |
||
13372 | public function fixBlockedLinks($src) |
||
13373 | { |
||
13374 | $urlInfo = parse_url($src); |
||
13375 | |||
13376 | $platformProtocol = 'https'; |
||
13377 | if (strpos(api_get_path(WEB_CODE_PATH), 'https') === false) { |
||
13378 | $platformProtocol = 'http'; |
||
13379 | } |
||
13380 | |||
13381 | $protocolFixApplied = false; |
||
13382 | //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme |
||
13383 | $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null; |
||
13384 | $host = isset($urlInfo['host']) ? $urlInfo['host'] : null; |
||
13385 | |||
13386 | if ($platformProtocol != $scheme) { |
||
13387 | Session::write('x_frame_source', $src); |
||
13388 | $src = 'blank.php?error=x_frames_options'; |
||
13389 | $protocolFixApplied = true; |
||
13390 | } |
||
13391 | |||
13392 | if ($protocolFixApplied == false) { |
||
13393 | if (strpos(api_get_path(WEB_PATH), $host) === false) { |
||
13394 | // Check X-Frame-Options |
||
13395 | $ch = curl_init(); |
||
13396 | $options = [ |
||
13397 | CURLOPT_URL => $src, |
||
13398 | CURLOPT_RETURNTRANSFER => true, |
||
13399 | CURLOPT_HEADER => true, |
||
13400 | CURLOPT_FOLLOWLOCATION => true, |
||
13401 | CURLOPT_ENCODING => "", |
||
13402 | CURLOPT_AUTOREFERER => true, |
||
13403 | CURLOPT_CONNECTTIMEOUT => 120, |
||
13404 | CURLOPT_TIMEOUT => 120, |
||
13405 | CURLOPT_MAXREDIRS => 10, |
||
13406 | ]; |
||
13407 | |||
13408 | $proxySettings = api_get_configuration_value('proxy_settings'); |
||
13409 | if (!empty($proxySettings) && |
||
13410 | isset($proxySettings['curl_setopt_array']) |
||
13411 | ) { |
||
13412 | $options[CURLOPT_PROXY] = $proxySettings['curl_setopt_array']['CURLOPT_PROXY']; |
||
13413 | $options[CURLOPT_PROXYPORT] = $proxySettings['curl_setopt_array']['CURLOPT_PROXYPORT']; |
||
13414 | } |
||
13415 | |||
13416 | curl_setopt_array($ch, $options); |
||
13417 | $response = curl_exec($ch); |
||
13418 | $httpCode = curl_getinfo($ch); |
||
13419 | $headers = substr($response, 0, $httpCode['header_size']); |
||
13420 | |||
13421 | $error = false; |
||
13422 | if (stripos($headers, 'X-Frame-Options: DENY') > -1 |
||
13423 | //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1 |
||
13424 | ) { |
||
13425 | $error = true; |
||
13426 | } |
||
13427 | |||
13428 | if ($error) { |
||
13429 | Session::write('x_frame_source', $src); |
||
13430 | $src = 'blank.php?error=x_frames_options'; |
||
13431 | } |
||
13432 | } |
||
13433 | } |
||
13434 | |||
13435 | return $src; |
||
13436 | } |
||
13437 | |||
13438 | /** |
||
13439 | * Check if this LP has a created forum in the basis course. |
||
13440 | * |
||
13441 | * @return bool |
||
13442 | */ |
||
13443 | public function lpHasForum() |
||
13444 | { |
||
13445 | $forumTable = Database::get_course_table(TABLE_FORUM); |
||
13446 | $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); |
||
13447 | |||
13448 | $fakeFrom = " |
||
13449 | $forumTable f |
||
13450 | INNER JOIN $itemProperty ip |
||
13451 | ON (f.forum_id = ip.ref AND f.c_id = ip.c_id) |
||
13452 | "; |
||
13453 | |||
13454 | $resultData = Database::select( |
||
13455 | 'COUNT(f.iid) AS qty', |
||
13456 | $fakeFrom, |
||
13457 | [ |
||
13458 | 'where' => [ |
||
13459 | 'ip.visibility != ? AND ' => 2, |
||
13460 | 'ip.tool = ? AND ' => TOOL_FORUM, |
||
13461 | 'f.c_id = ? AND ' => intval($this->course_int_id), |
||
13462 | 'f.lp_id = ?' => intval($this->lp_id), |
||
13463 | ], |
||
13464 | ], |
||
13465 | 'first' |
||
13466 | ); |
||
13467 | |||
13468 | return $resultData['qty'] > 0; |
||
13469 | } |
||
13470 | |||
13471 | /** |
||
13472 | * Get the forum for this learning path. |
||
13473 | * |
||
13474 | * @param int $sessionId |
||
13475 | * |
||
13476 | * @return bool |
||
13477 | */ |
||
13478 | public function getForum($sessionId = 0) |
||
13479 | { |
||
13480 | $forumTable = Database::get_course_table(TABLE_FORUM); |
||
13481 | $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); |
||
13482 | |||
13483 | $fakeFrom = "$forumTable f |
||
13484 | INNER JOIN $itemProperty ip "; |
||
13485 | |||
13486 | if ($this->lp_session_id == 0) { |
||
13487 | $fakeFrom .= " |
||
13488 | ON ( |
||
13489 | f.forum_id = ip.ref AND f.c_id = ip.c_id AND ( |
||
13490 | f.session_id = ip.session_id OR ip.session_id IS NULL |
||
13491 | ) |
||
13492 | ) |
||
13493 | "; |
||
13494 | } else { |
||
13495 | $fakeFrom .= " |
||
13496 | ON ( |
||
13497 | f.forum_id = ip.ref AND f.c_id = ip.c_id AND f.session_id = ip.session_id |
||
13498 | ) |
||
13499 | "; |
||
13500 | } |
||
13501 | |||
13502 | $resultData = Database::select( |
||
13503 | 'f.*', |
||
13504 | $fakeFrom, |
||
13505 | [ |
||
13506 | 'where' => [ |
||
13507 | 'ip.visibility != ? AND ' => 2, |
||
13508 | 'ip.tool = ? AND ' => TOOL_FORUM, |
||
13509 | 'f.session_id = ? AND ' => $sessionId, |
||
13510 | 'f.c_id = ? AND ' => intval($this->course_int_id), |
||
13511 | 'f.lp_id = ?' => intval($this->lp_id), |
||
13512 | ], |
||
13513 | ], |
||
13514 | 'first' |
||
13515 | ); |
||
13516 | |||
13517 | if (empty($resultData)) { |
||
13518 | return false; |
||
13519 | } |
||
13520 | |||
13521 | return $resultData; |
||
13522 | } |
||
13523 | |||
13524 | /** |
||
13525 | * Create a forum for this learning path. |
||
13526 | * |
||
13527 | * @param int $forumCategoryId |
||
13528 | * |
||
13529 | * @return int The forum ID if was created. Otherwise return false |
||
13530 | */ |
||
13531 | public function createForum($forumCategoryId) |
||
13532 | { |
||
13533 | require_once api_get_path(SYS_CODE_PATH).'/forum/forumfunction.inc.php'; |
||
13534 | |||
13535 | $forumId = store_forum( |
||
13536 | [ |
||
13537 | 'lp_id' => $this->lp_id, |
||
13538 | 'forum_title' => $this->name, |
||
13539 | 'forum_comment' => null, |
||
13540 | 'forum_category' => (int) $forumCategoryId, |
||
13541 | 'students_can_edit_group' => ['students_can_edit' => 0], |
||
13542 | 'allow_new_threads_group' => ['allow_new_threads' => 0], |
||
13543 | 'default_view_type_group' => ['default_view_type' => 'flat'], |
||
13544 | 'group_forum' => 0, |
||
13545 | 'public_private_group_forum_group' => ['public_private_group_forum' => 'public'], |
||
13546 | ], |
||
13547 | [], |
||
13548 | true |
||
13549 | ); |
||
13550 | |||
13551 | return $forumId; |
||
13552 | } |
||
13553 | |||
13554 | /** |
||
13555 | * Get the LP Final Item form. |
||
13556 | * |
||
13557 | * @throws Exception |
||
13558 | * @throws HTML_QuickForm_Error |
||
13559 | * |
||
13560 | * @return string |
||
13561 | */ |
||
13562 | public function getFinalItemForm() |
||
13563 | { |
||
13564 | $finalItem = $this->getFinalItem(); |
||
13565 | $title = ''; |
||
13566 | |||
13567 | if ($finalItem) { |
||
13568 | $title = $finalItem->get_title(); |
||
13569 | $buttonText = get_lang('Save'); |
||
13570 | $content = $this->getSavedFinalItem(); |
||
13571 | } else { |
||
13572 | $buttonText = get_lang('LPCreateDocument'); |
||
13573 | $content = $this->getFinalItemTemplate(); |
||
13574 | } |
||
13575 | |||
13576 | $courseInfo = api_get_course_info(); |
||
13577 | $result = $this->generate_lp_folder($courseInfo); |
||
13578 | $relative_path = api_substr($result['dir'], 1, strlen($result['dir'])); |
||
13579 | $relative_prefix = '../../'; |
||
13580 | |||
13581 | $editorConfig = [ |
||
13582 | 'ToolbarSet' => 'LearningPathDocuments', |
||
13583 | 'Width' => '100%', |
||
13584 | 'Height' => '500', |
||
13585 | 'FullPage' => true, |
||
13586 | 'CreateDocumentDir' => $relative_prefix, |
||
13587 | 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/', |
||
13588 | 'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path, |
||
13589 | ]; |
||
13590 | |||
13591 | $url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([ |
||
13592 | 'type' => 'document', |
||
13593 | 'lp_id' => $this->lp_id, |
||
13594 | ]); |
||
13595 | |||
13596 | $form = new FormValidator('final_item', 'POST', $url); |
||
13597 | $form->addText('title', get_lang('Title')); |
||
13598 | $form->addButtonSave($buttonText); |
||
13599 | |||
13600 | $renderer = $form->defaultRenderer(); |
||
13601 | $renderer->setElementTemplate(' {label}{element}', 'content_lp_certificate'); |
||
13602 | |||
13603 | $form->addHtmlEditor( |
||
13604 | 'content_lp_certificate', |
||
13605 | null, |
||
13606 | true, |
||
13607 | false, |
||
13608 | $editorConfig, |
||
13609 | true |
||
13610 | ); |
||
13611 | $form->addHtml( |
||
13612 | Display::return_message( |
||
13613 | get_lang('LPEndStepAddTagsToShowCertificateOrSkillAutomatically').'</br></br> <b>((certificate))</b> </br> <b>((skill))</b>', |
||
13614 | 'normal', |
||
13615 | false |
||
13616 | ) |
||
13617 | ); |
||
13618 | $form->addHidden('action', 'add_final_item'); |
||
13619 | $form->addHidden('path', Session::read('pathItem')); |
||
13620 | $form->addHidden('previous', $this->get_last()); |
||
13621 | $form->setDefaults(['title' => $title, 'content_lp_certificate' => $content]); |
||
13622 | |||
13623 | if ($form->validate()) { |
||
13624 | $values = $form->exportValues(); |
||
13625 | $lastItemId = $this->getLastInFirstLevel(); |
||
13626 | |||
13627 | if (!$finalItem) { |
||
13628 | $documentId = $this->create_document( |
||
13629 | $this->course_info, |
||
13630 | $values['content_lp_certificate'], |
||
13631 | $values['title'] |
||
13632 | ); |
||
13633 | $this->add_item( |
||
13634 | 0, |
||
13635 | $lastItemId, |
||
13636 | 'final_item', |
||
13637 | $documentId, |
||
13638 | $values['title'], |
||
13639 | '' |
||
13640 | ); |
||
13641 | |||
13642 | Display::addFlash( |
||
13643 | Display::return_message(get_lang('Added')) |
||
13644 | ); |
||
13645 | } else { |
||
13646 | $this->edit_document($this->course_info); |
||
13647 | } |
||
13648 | } |
||
13649 | |||
13650 | return $form->returnForm(); |
||
13651 | } |
||
13652 | |||
13653 | /** |
||
13654 | * Check if the current lp item is first, both, last or none from lp list. |
||
13655 | * |
||
13656 | * @param int $currentItemId |
||
13657 | * |
||
13658 | * @return string |
||
13659 | */ |
||
13660 | public function isFirstOrLastItem($currentItemId) |
||
13661 | { |
||
13662 | $lpItemId = []; |
||
13663 | $typeListNotToVerify = self::getChapterTypes(); |
||
13664 | |||
13665 | // Using get_toc() function instead $this->items because returns the correct order of the items |
||
13666 | foreach ($this->get_toc() as $item) { |
||
13667 | if (!in_array($item['type'], $typeListNotToVerify)) { |
||
13668 | $lpItemId[] = $item['id']; |
||
13669 | } |
||
13670 | } |
||
13671 | |||
13672 | $lastLpItemIndex = count($lpItemId) - 1; |
||
13673 | $position = array_search($currentItemId, $lpItemId); |
||
13674 | |||
13675 | switch ($position) { |
||
13676 | case 0: |
||
13677 | if (!$lastLpItemIndex) { |
||
13678 | $answer = 'both'; |
||
13679 | break; |
||
13680 | } |
||
13681 | |||
13682 | $answer = 'first'; |
||
13683 | break; |
||
13684 | case $lastLpItemIndex: |
||
13685 | $answer = 'last'; |
||
13686 | break; |
||
13687 | default: |
||
13688 | $answer = 'none'; |
||
13689 | } |
||
13690 | |||
13691 | return $answer; |
||
13692 | } |
||
13693 | |||
13694 | /** |
||
13695 | * Get whether this is a learning path with the accumulated SCORM time or not. |
||
13696 | * |
||
13697 | * @return int |
||
13698 | */ |
||
13699 | public function getAccumulateScormTime() |
||
13700 | { |
||
13701 | return $this->accumulateScormTime; |
||
13702 | } |
||
13703 | |||
13704 | /** |
||
13705 | * Set whether this is a learning path with the accumulated SCORM time or not. |
||
13706 | * |
||
13707 | * @param int $value (0 = false, 1 = true) |
||
13708 | * |
||
13709 | * @return bool Always returns true |
||
13710 | */ |
||
13711 | public function setAccumulateScormTime($value) |
||
13712 | { |
||
13713 | $this->accumulateScormTime = (int) $value; |
||
13714 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
13715 | $lp_id = $this->get_id(); |
||
13716 | $sql = "UPDATE $lp_table |
||
13717 | SET accumulate_scorm_time = ".$this->accumulateScormTime." |
||
13718 | WHERE iid = $lp_id"; |
||
13719 | Database::query($sql); |
||
13720 | |||
13721 | return true; |
||
13722 | } |
||
13723 | |||
13724 | /** |
||
13725 | * Returns an HTML-formatted link to a resource, to incorporate directly into |
||
13726 | * the new learning path tool. |
||
13727 | * |
||
13728 | * The function is a big switch on tool type. |
||
13729 | * In each case, we query the corresponding table for information and build the link |
||
13730 | * with that information. |
||
13731 | * |
||
13732 | * @author Yannick Warnier <[email protected]> - rebranding based on |
||
13733 | * previous work (display_addedresource_link_in_learnpath()) |
||
13734 | * |
||
13735 | * @param int $course_id Course code |
||
13736 | * @param int $learningPathId The learning path ID (in lp table) |
||
13737 | * @param int $id_in_path the unique index in the items table |
||
13738 | * @param int $lpViewId |
||
13739 | * @param int $lpSessionId |
||
13740 | * |
||
13741 | * @return string |
||
13742 | */ |
||
13743 | public static function rl_get_resource_link_for_learnpath( |
||
13744 | $course_id, |
||
13745 | $learningPathId, |
||
13746 | $id_in_path, |
||
13747 | $lpViewId, |
||
13748 | $lpSessionId = 0 |
||
13749 | ) { |
||
13750 | $session_id = api_get_session_id(); |
||
13751 | $course_info = api_get_course_info_by_id($course_id); |
||
13752 | |||
13753 | $learningPathId = (int) $learningPathId; |
||
13754 | $id_in_path = (int) $id_in_path; |
||
13755 | $lpViewId = (int) $lpViewId; |
||
13756 | |||
13757 | $em = Database::getManager(); |
||
13758 | $lpItemRepo = $em->getRepository('ChamiloCourseBundle:CLpItem'); |
||
13759 | |||
13760 | /** @var CLpItem $rowItem */ |
||
13761 | $rowItem = $lpItemRepo->findOneBy([ |
||
13762 | 'cId' => $course_id, |
||
13763 | 'lpId' => $learningPathId, |
||
13764 | 'iid' => $id_in_path, |
||
13765 | ]); |
||
13766 | |||
13767 | if (!$rowItem) { |
||
13768 | // Try one more time with "id" |
||
13769 | /** @var CLpItem $rowItem */ |
||
13770 | $rowItem = $lpItemRepo->findOneBy([ |
||
13771 | 'cId' => $course_id, |
||
13772 | 'lpId' => $learningPathId, |
||
13773 | 'id' => $id_in_path, |
||
13774 | ]); |
||
13775 | |||
13776 | if (!$rowItem) { |
||
13777 | return -1; |
||
13778 | } |
||
13779 | } |
||
13780 | |||
13781 | $course_code = $course_info['code']; |
||
13782 | $type = $rowItem->getItemType(); |
||
13783 | $id = empty($rowItem->getPath()) ? '0' : $rowItem->getPath(); |
||
13784 | $main_dir_path = api_get_path(WEB_CODE_PATH); |
||
13785 | $main_course_path = api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/'; |
||
13786 | $link = ''; |
||
13787 | $extraParams = api_get_cidreq(true, true, 'learnpath').'&session_id='.$session_id; |
||
13788 | // It adds lti parameter |
||
13789 | if (isset($_REQUEST['lti_launch_id'])) { |
||
13790 | $extraParams .= '<i_launch_id='.Security::remove_XSS($_REQUEST['lti_launch_id']); |
||
13791 | } |
||
13792 | |||
13793 | switch ($type) { |
||
13794 | case 'dir': |
||
13795 | return $main_dir_path.'lp/blank.php'; |
||
13796 | case TOOL_CALENDAR_EVENT: |
||
13797 | return $main_dir_path.'calendar/agenda.php?agenda_id='.$id.'&'.$extraParams; |
||
13798 | case TOOL_ANNOUNCEMENT: |
||
13799 | return $main_dir_path.'announcements/announcements.php?ann_id='.$id.'&'.$extraParams; |
||
13800 | case TOOL_LINK: |
||
13801 | $linkInfo = Link::getLinkInfo($id); |
||
13802 | if ($linkInfo) { |
||
13803 | $itemPropertyInfo = api_get_item_property_info($course_id, TOOL_LINK, $id, $session_id); |
||
13804 | if ($itemPropertyInfo && 0 === (int) $itemPropertyInfo['visibility']) { |
||
13805 | return ''; |
||
13806 | } |
||
13807 | if (isset($linkInfo['url'])) { |
||
13808 | return $linkInfo['url']; |
||
13809 | } |
||
13810 | } |
||
13811 | |||
13812 | return ''; |
||
13813 | case TOOL_QUIZ: |
||
13814 | if (empty($id)) { |
||
13815 | return ''; |
||
13816 | } |
||
13817 | |||
13818 | // Get the lp_item_view with the highest view_count. |
||
13819 | $learnpathItemViewResult = $em |
||
13820 | ->getRepository('ChamiloCourseBundle:CLpItemView') |
||
13821 | ->findBy( |
||
13822 | ['cId' => $course_id, 'lpItemId' => $rowItem->getId(), 'lpViewId' => $lpViewId], |
||
13823 | ['viewCount' => 'DESC'], |
||
13824 | 1 |
||
13825 | ); |
||
13826 | /** @var CLpItemView $learnpathItemViewData */ |
||
13827 | $learnpathItemViewData = current($learnpathItemViewResult); |
||
13828 | $learnpathItemViewId = $learnpathItemViewData ? $learnpathItemViewData->getId() : 0; |
||
13829 | |||
13830 | return $main_dir_path.'exercise/overview.php?'.$extraParams.'&' |
||
13831 | .http_build_query([ |
||
13832 | 'lp_init' => 1, |
||
13833 | 'learnpath_item_view_id' => (int) $learnpathItemViewId, |
||
13834 | 'learnpath_id' => (int) $learningPathId, |
||
13835 | 'learnpath_item_id' => (int) $id_in_path, |
||
13836 | 'exerciseId' => (int) $id, |
||
13837 | ]); |
||
13838 | case TOOL_HOTPOTATOES: //lowercase because of strtolower above |
||
13839 | $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT); |
||
13840 | $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND iid=$id"); |
||
13841 | $myrow = Database::fetch_array($result); |
||
13842 | $path = $myrow['path']; |
||
13843 | |||
13844 | return $main_dir_path.'exercise/showinframes.php?file='.$path.'&cid='.$course_code.'&uid=' |
||
13845 | .api_get_user_id().'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path |
||
13846 | .'&lp_view_id='.$lpViewId.'&'.$extraParams; |
||
13847 | case TOOL_FORUM: |
||
13848 | return $main_dir_path.'forum/viewforum.php?forum='.$id.'&lp=true&'.$extraParams; |
||
13849 | case TOOL_THREAD: |
||
13850 | // forum post |
||
13851 | $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD); |
||
13852 | if (empty($id)) { |
||
13853 | return ''; |
||
13854 | } |
||
13855 | $sql = "SELECT * FROM $tbl_topics WHERE c_id = $course_id AND thread_id=$id"; |
||
13856 | $result = Database::query($sql); |
||
13857 | $myrow = Database::fetch_array($result); |
||
13858 | |||
13859 | return $main_dir_path.'forum/viewthread.php?thread='.$id.'&forum='.$myrow['forum_id'].'&lp=true&' |
||
13860 | .$extraParams; |
||
13861 | case TOOL_POST: |
||
13862 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
13863 | $result = Database::query("SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=$id"); |
||
13864 | $myrow = Database::fetch_array($result); |
||
13865 | |||
13866 | return $main_dir_path.'forum/viewthread.php?post='.$id.'&thread='.$myrow['thread_id'].'&forum=' |
||
13867 | .$myrow['forum_id'].'&lp=true&'.$extraParams; |
||
13868 | case TOOL_READOUT_TEXT: |
||
13869 | return api_get_path(WEB_CODE_PATH). |
||
13870 | 'lp/readout_text.php?&id='.$id.'&lp_id='.$learningPathId.'&'.$extraParams; |
||
13871 | case TOOL_DOCUMENT: |
||
13872 | $repo = $em->getRepository('ChamiloCourseBundle:CDocument'); |
||
13873 | $document = $repo->findOneBy(['cId' => $course_id, 'iid' => $id]); |
||
13874 | |||
13875 | if (empty($document)) { |
||
13876 | // Try with normal id |
||
13877 | $document = $repo->findOneBy(['cId' => $course_id, 'id' => $id]); |
||
13878 | |||
13879 | if (empty($document)) { |
||
13880 | return ''; |
||
13881 | } |
||
13882 | } |
||
13883 | |||
13884 | $documentPathInfo = pathinfo($document->getPath()); |
||
13885 | $mediaSupportedFiles = ['mp3', 'mp4', 'ogv', 'ogg', 'flv', 'm4v', 'webm', 'wav']; |
||
13886 | $extension = isset($documentPathInfo['extension']) ? $documentPathInfo['extension'] : ''; |
||
13887 | $showDirectUrl = !in_array($extension, $mediaSupportedFiles); |
||
13888 | |||
13889 | $openmethod = 2; |
||
13890 | $officedoc = false; |
||
13891 | Session::write('openmethod', $openmethod); |
||
13892 | Session::write('officedoc', $officedoc); |
||
13893 | |||
13894 | // Same validation as in document/download.php |
||
13895 | $isVisible = DocumentManager::is_visible( |
||
13896 | $document->getPath(), |
||
13897 | api_get_course_info(), |
||
13898 | api_get_session_id() |
||
13899 | ); |
||
13900 | |||
13901 | if (!api_is_allowed_to_edit() && !$isVisible) { |
||
13902 | return ''; |
||
13903 | } |
||
13904 | |||
13905 | if ($showDirectUrl) { |
||
13906 | $file = $main_course_path.'document'.$document->getPath().'?'.$extraParams; |
||
13907 | if (api_get_configuration_value('allow_pdf_viewerjs_in_lp')) { |
||
13908 | if (Link::isPdfLink($file)) { |
||
13909 | return api_get_path(WEB_LIBRARY_PATH). |
||
13910 | 'javascript/ViewerJS/index.html?zoom=page-width#'.$file; |
||
13911 | } |
||
13912 | } |
||
13913 | |||
13914 | return $file; |
||
13915 | } |
||
13916 | |||
13917 | return api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.'&'.$extraParams; |
||
13918 | case TOOL_LP_FINAL_ITEM: |
||
13919 | return api_get_path(WEB_CODE_PATH).'lp/lp_final_item.php?&id='.$id.'&lp_id='.$learningPathId.'&' |
||
13920 | .$extraParams; |
||
13921 | case 'assignments': |
||
13922 | return $main_dir_path.'work/work.php?'.$extraParams; |
||
13923 | case TOOL_DROPBOX: |
||
13924 | return $main_dir_path.'dropbox/index.php?'.$extraParams; |
||
13925 | case 'introduction_text': //DEPRECATED |
||
13926 | return ''; |
||
13927 | case TOOL_COURSE_DESCRIPTION: |
||
13928 | return $main_dir_path.'course_description?'.$extraParams; |
||
13929 | case TOOL_GROUP: |
||
13930 | return $main_dir_path.'group/group.php?'.$extraParams; |
||
13931 | case TOOL_USER: |
||
13932 | return $main_dir_path.'user/user.php?'.$extraParams; |
||
13933 | case TOOL_STUDENTPUBLICATION: |
||
13934 | if (!empty($rowItem->getPath())) { |
||
13935 | $workId = $rowItem->getPath(); |
||
13936 | if (empty($lpSessionId) && !empty($session_id)) { |
||
13937 | // Check if a student publication with the same name exists in this session see BT#17700 |
||
13938 | $title = Database::escape_string($rowItem->getTitle()); |
||
13939 | $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION); |
||
13940 | $sql = "SELECT * FROM $table |
||
13941 | WHERE |
||
13942 | active = 1 AND |
||
13943 | parent_id = 0 AND |
||
13944 | c_id = $course_id AND |
||
13945 | session_id = $session_id AND |
||
13946 | title = '$title' |
||
13947 | LIMIT 1"; |
||
13948 | $result = Database::query($sql); |
||
13949 | if (Database::num_rows($result)) { |
||
13950 | $work = Database::fetch_array($result, 'ASSOC'); |
||
13951 | if ($work) { |
||
13952 | $workId = $work['iid']; |
||
13953 | } |
||
13954 | } |
||
13955 | } |
||
13956 | |||
13957 | return $main_dir_path.'work/work_list.php?id='.$workId.'&'.$extraParams; |
||
13958 | } |
||
13959 | |||
13960 | return $main_dir_path.'work/work.php?'.api_get_cidreq().'&id='.$rowItem->getPath().'&'.$extraParams; |
||
13961 | case TOOL_XAPI: |
||
13962 | $toolLaunch = $em->find(ToolLaunch::class, $id); |
||
13963 | |||
13964 | if (empty($toolLaunch)) { |
||
13965 | break; |
||
13966 | } |
||
13967 | |||
13968 | return api_get_path(WEB_PLUGIN_PATH).'xapi/' |
||
13969 | .('cmi5' === $toolLaunch->getActivityType() ? 'cmi5/view.php' : 'tincan/view.php') |
||
13970 | ."?id=$id&$extraParams"; |
||
13971 | case TOOL_H5P: |
||
13972 | if ('true' === api_get_plugin_setting('h5pimport', 'tool_enable')) { |
||
13973 | $toolLaunch = $em->find(H5pImport::class, $id); |
||
13974 | |||
13975 | if (empty($toolLaunch)) { |
||
13976 | break; |
||
13977 | } |
||
13978 | |||
13979 | return api_get_path(WEB_PLUGIN_PATH).'h5pimport/view.php'."?id=$id&$extraParams"; |
||
13980 | } |
||
13981 | break; |
||
13982 | case TOOL_SURVEY: |
||
13983 | $table = Database::get_course_table(TABLE_SURVEY); |
||
13984 | $sql = "SELECT code FROM $table WHERE c_id = $course_id AND survey_id =".(int) $id; |
||
13985 | $result = Database::query($sql); |
||
13986 | $surveyCode = Database::result($result, 0, 0); |
||
13987 | $autoSurveyLink = SurveyUtil::generateFillSurveyLink( |
||
13988 | 'auto', |
||
13989 | $course_info, |
||
13990 | api_get_session_id(), |
||
13991 | $surveyCode |
||
13992 | ); |
||
13993 | $lpParams = [ |
||
13994 | 'lp_id' => $learningPathId, |
||
13995 | 'lp_item_id' => $id_in_path, |
||
13996 | 'origin' => 'learnpath', |
||
13997 | ]; |
||
13998 | |||
13999 | return $autoSurveyLink.'&'.http_build_query($lpParams); |
||
14000 | } |
||
14001 | |||
14002 | return $link; |
||
14003 | } |
||
14004 | |||
14005 | /** |
||
14006 | * Gets the name of a resource (generally used in learnpath when no name is provided). |
||
14007 | * |
||
14008 | * @author Yannick Warnier <[email protected]> |
||
14009 | * |
||
14010 | * @param string $course_code Course code |
||
14011 | * @param int $learningPathId |
||
14012 | * @param int $id_in_path The resource ID |
||
14013 | * |
||
14014 | * @return string |
||
14015 | */ |
||
14016 | public static function rl_get_resource_name($course_code, $learningPathId, $id_in_path) |
||
14017 | { |
||
14018 | $_course = api_get_course_info($course_code); |
||
14019 | if (empty($_course)) { |
||
14020 | return ''; |
||
14021 | } |
||
14022 | $course_id = $_course['real_id']; |
||
14023 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
14024 | $learningPathId = (int) $learningPathId; |
||
14025 | $id_in_path = (int) $id_in_path; |
||
14026 | |||
14027 | $sql = "SELECT item_type, title, ref |
||
14028 | FROM $tbl_lp_item |
||
14029 | WHERE c_id = $course_id AND lp_id = $learningPathId AND iid = $id_in_path"; |
||
14030 | $res_item = Database::query($sql); |
||
14031 | |||
14032 | if (Database::num_rows($res_item) < 1) { |
||
14033 | return ''; |
||
14034 | } |
||
14035 | $row_item = Database::fetch_array($res_item); |
||
14036 | $type = strtolower($row_item['item_type']); |
||
14037 | $id = $row_item['ref']; |
||
14038 | $output = ''; |
||
14039 | |||
14040 | switch ($type) { |
||
14041 | case TOOL_CALENDAR_EVENT: |
||
14042 | $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA); |
||
14043 | $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id"); |
||
14044 | $myrow = Database::fetch_array($result); |
||
14045 | $output = $myrow['title']; |
||
14046 | break; |
||
14047 | case TOOL_ANNOUNCEMENT: |
||
14048 | $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT); |
||
14049 | $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id"); |
||
14050 | $myrow = Database::fetch_array($result); |
||
14051 | $output = $myrow['title']; |
||
14052 | break; |
||
14053 | case TOOL_LINK: |
||
14054 | // Doesn't take $target into account. |
||
14055 | $TABLETOOLLINK = Database::get_course_table(TABLE_LINK); |
||
14056 | $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id"); |
||
14057 | $myrow = Database::fetch_array($result); |
||
14058 | $output = $myrow['title']; |
||
14059 | break; |
||
14060 | case TOOL_QUIZ: |
||
14061 | $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST); |
||
14062 | $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE iid = $id"); |
||
14063 | $myrow = Database::fetch_array($result); |
||
14064 | $output = $myrow['title']; |
||
14065 | break; |
||
14066 | case TOOL_FORUM: |
||
14067 | $TBL_FORUMS = Database::get_course_table(TABLE_FORUM); |
||
14068 | $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id = $id"); |
||
14069 | $myrow = Database::fetch_array($result); |
||
14070 | $output = $myrow['forum_name']; |
||
14071 | break; |
||
14072 | case TOOL_THREAD: |
||
14073 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
14074 | // Grabbing the title of the post. |
||
14075 | $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id; |
||
14076 | $result_title = Database::query($sql_title); |
||
14077 | $myrow_title = Database::fetch_array($result_title); |
||
14078 | $output = $myrow_title['post_title']; |
||
14079 | break; |
||
14080 | case TOOL_POST: |
||
14081 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
14082 | $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id"; |
||
14083 | $result = Database::query($sql); |
||
14084 | $post = Database::fetch_array($result); |
||
14085 | $output = $post['post_title']; |
||
14086 | break; |
||
14087 | case 'dir': |
||
14088 | case TOOL_DOCUMENT: |
||
14089 | $title = $row_item['title']; |
||
14090 | $output = '-'; |
||
14091 | if (!empty($title)) { |
||
14092 | $output = $title; |
||
14093 | } |
||
14094 | break; |
||
14095 | case 'hotpotatoes': |
||
14096 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
14097 | $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND iid = $id"); |
||
14098 | $myrow = Database::fetch_array($result); |
||
14099 | $pathname = explode('/', $myrow['path']); // Making a correct name for the link. |
||
14100 | $last = count($pathname) - 1; // Making a correct name for the link. |
||
14101 | $filename = $pathname[$last]; // Making a correct name for the link. |
||
14102 | $myrow['path'] = rawurlencode($myrow['path']); |
||
14103 | $output = $filename; |
||
14104 | break; |
||
14105 | } |
||
14106 | |||
14107 | return stripslashes($output); |
||
14108 | } |
||
14109 | |||
14110 | /** |
||
14111 | * Get the parent names for the current item. |
||
14112 | * |
||
14113 | * @param int $newItemId Optional. The item ID |
||
14114 | * |
||
14115 | * @return array |
||
14116 | */ |
||
14117 | public function getCurrentItemParentNames($newItemId = 0) |
||
14118 | { |
||
14119 | $newItemId = $newItemId ?: $this->get_current_item_id(); |
||
14120 | $return = []; |
||
14121 | $item = $this->getItem($newItemId); |
||
14122 | $parent = $this->getItem($item->get_parent()); |
||
14123 | |||
14124 | while ($parent) { |
||
14125 | $return[] = $parent->get_title(); |
||
14126 | $parent = $this->getItem($parent->get_parent()); |
||
14127 | } |
||
14128 | |||
14129 | return array_reverse($return); |
||
14130 | } |
||
14131 | |||
14132 | /** |
||
14133 | * Reads and process "lp_subscription_settings" setting. |
||
14134 | * |
||
14135 | * @return array |
||
14136 | */ |
||
14137 | public static function getSubscriptionSettings() |
||
14138 | { |
||
14139 | $subscriptionSettings = api_get_configuration_value('lp_subscription_settings'); |
||
14140 | if (empty($subscriptionSettings)) { |
||
14141 | // By default allow both settings |
||
14142 | $subscriptionSettings = [ |
||
14143 | 'allow_add_users_to_lp' => true, |
||
14144 | 'allow_add_users_to_lp_category' => true, |
||
14145 | ]; |
||
14146 | } else { |
||
14147 | $subscriptionSettings = $subscriptionSettings['options']; |
||
14148 | } |
||
14149 | |||
14150 | return $subscriptionSettings; |
||
14151 | } |
||
14152 | |||
14153 | /** |
||
14154 | * Exports a LP to a courseBuilder zip file. It adds the documents related to the LP. |
||
14155 | */ |
||
14156 | public function exportToCourseBuildFormat() |
||
14157 | { |
||
14158 | if (!api_is_allowed_to_edit()) { |
||
14159 | return false; |
||
14160 | } |
||
14161 | |||
14162 | $courseBuilder = new CourseBuilder(); |
||
14163 | $itemList = []; |
||
14164 | /** @var learnpathItem $item */ |
||
14165 | foreach ($this->items as $item) { |
||
14166 | $itemList[$item->get_type()][] = $item->get_path(); |
||
14167 | } |
||
14168 | |||
14169 | if (empty($itemList)) { |
||
14170 | return false; |
||
14171 | } |
||
14172 | |||
14173 | if (isset($itemList['document'])) { |
||
14174 | // Get parents |
||
14175 | foreach ($itemList['document'] as $documentId) { |
||
14176 | $documentInfo = DocumentManager::get_document_data_by_id($documentId, api_get_course_id(), true); |
||
14177 | if (!empty($documentInfo['parents'])) { |
||
14178 | foreach ($documentInfo['parents'] as $parentInfo) { |
||
14179 | if (in_array($parentInfo['iid'], $itemList['document'])) { |
||
14180 | continue; |
||
14181 | } |
||
14182 | $itemList['document'][] = $parentInfo['iid']; |
||
14183 | } |
||
14184 | } |
||
14185 | } |
||
14186 | |||
14187 | $courseInfo = api_get_course_info(); |
||
14188 | foreach ($itemList['document'] as $documentId) { |
||
14189 | $documentInfo = DocumentManager::get_document_data_by_id($documentId, api_get_course_id()); |
||
14190 | $items = DocumentManager::get_resources_from_source_html( |
||
14191 | $documentInfo['absolute_path'], |
||
14192 | true, |
||
14193 | TOOL_DOCUMENT |
||
14194 | ); |
||
14195 | |||
14196 | if (!empty($items)) { |
||
14197 | foreach ($items as $item) { |
||
14198 | // Get information about source url |
||
14199 | $url = $item[0]; // url |
||
14200 | $scope = $item[1]; // scope (local, remote) |
||
14201 | $type = $item[2]; // type (rel, abs, url) |
||
14202 | |||
14203 | $origParseUrl = parse_url($url); |
||
14204 | $realOrigPath = isset($origParseUrl['path']) ? $origParseUrl['path'] : null; |
||
14205 | |||
14206 | if ($scope == 'local') { |
||
14207 | if ($type == 'abs' || $type == 'rel') { |
||
14208 | $documentFile = strstr($realOrigPath, 'document'); |
||
14209 | if (strpos($realOrigPath, $documentFile) !== false) { |
||
14210 | $documentFile = str_replace('document', '', $documentFile); |
||
14211 | $itemDocumentId = DocumentManager::get_document_id($courseInfo, $documentFile); |
||
14212 | // Document found! Add it to the list |
||
14213 | if ($itemDocumentId) { |
||
14214 | $itemList['document'][] = $itemDocumentId; |
||
14215 | } |
||
14216 | } |
||
14217 | } |
||
14218 | } |
||
14219 | } |
||
14220 | } |
||
14221 | } |
||
14222 | |||
14223 | $courseBuilder->build_documents( |
||
14224 | api_get_session_id(), |
||
14225 | $this->get_course_int_id(), |
||
14226 | true, |
||
14227 | $itemList['document'] |
||
14228 | ); |
||
14229 | } |
||
14230 | |||
14231 | if (isset($itemList['quiz'])) { |
||
14232 | $courseBuilder->build_quizzes( |
||
14233 | api_get_session_id(), |
||
14234 | $this->get_course_int_id(), |
||
14235 | true, |
||
14236 | $itemList['quiz'] |
||
14237 | ); |
||
14238 | } |
||
14239 | |||
14240 | require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php'; |
||
14241 | |||
14242 | /*if (!empty($itemList['thread'])) { |
||
14243 | $postList = []; |
||
14244 | foreach ($itemList['thread'] as $postId) { |
||
14245 | $post = get_post_information($postId); |
||
14246 | if ($post) { |
||
14247 | if (!isset($itemList['forum'])) { |
||
14248 | $itemList['forum'] = []; |
||
14249 | } |
||
14250 | $itemList['forum'][] = $post['forum_id']; |
||
14251 | $postList[] = $postId; |
||
14252 | } |
||
14253 | } |
||
14254 | |||
14255 | if (!empty($postList)) { |
||
14256 | $courseBuilder->build_forum_posts( |
||
14257 | $this->get_course_int_id(), |
||
14258 | null, |
||
14259 | null, |
||
14260 | $postList |
||
14261 | ); |
||
14262 | } |
||
14263 | }*/ |
||
14264 | |||
14265 | if (!empty($itemList['thread'])) { |
||
14266 | $threadList = []; |
||
14267 | $em = Database::getManager(); |
||
14268 | $repo = $em->getRepository('ChamiloCourseBundle:CForumThread'); |
||
14269 | foreach ($itemList['thread'] as $threadId) { |
||
14270 | /** @var \Chamilo\CourseBundle\Entity\CForumThread $thread */ |
||
14271 | $thread = $repo->find($threadId); |
||
14272 | if ($thread) { |
||
14273 | $itemList['forum'][] = $thread->getForumId(); |
||
14274 | $threadList[] = $thread->getIid(); |
||
14275 | } |
||
14276 | } |
||
14277 | |||
14278 | if (!empty($threadList)) { |
||
14279 | $courseBuilder->build_forum_topics( |
||
14280 | api_get_session_id(), |
||
14281 | $this->get_course_int_id(), |
||
14282 | null, |
||
14283 | $threadList |
||
14284 | ); |
||
14285 | } |
||
14286 | } |
||
14287 | |||
14288 | $forumCategoryList = []; |
||
14289 | if (isset($itemList['forum'])) { |
||
14290 | foreach ($itemList['forum'] as $forumId) { |
||
14291 | $forumInfo = get_forums($forumId); |
||
14292 | $forumCategoryList[] = $forumInfo['forum_category']; |
||
14293 | } |
||
14294 | } |
||
14295 | |||
14296 | if (!empty($forumCategoryList)) { |
||
14297 | $courseBuilder->build_forum_category( |
||
14298 | api_get_session_id(), |
||
14299 | $this->get_course_int_id(), |
||
14300 | true, |
||
14301 | $forumCategoryList |
||
14302 | ); |
||
14303 | } |
||
14304 | |||
14305 | if (!empty($itemList['forum'])) { |
||
14306 | $courseBuilder->build_forums( |
||
14307 | api_get_session_id(), |
||
14308 | $this->get_course_int_id(), |
||
14309 | true, |
||
14310 | $itemList['forum'] |
||
14311 | ); |
||
14312 | } |
||
14313 | |||
14314 | if (isset($itemList['link'])) { |
||
14315 | $courseBuilder->build_links( |
||
14316 | api_get_session_id(), |
||
14317 | $this->get_course_int_id(), |
||
14318 | true, |
||
14319 | $itemList['link'] |
||
14320 | ); |
||
14321 | } |
||
14322 | |||
14323 | if (!empty($itemList['student_publication'])) { |
||
14324 | $courseBuilder->build_works( |
||
14325 | api_get_session_id(), |
||
14326 | $this->get_course_int_id(), |
||
14327 | true, |
||
14328 | $itemList['student_publication'] |
||
14329 | ); |
||
14330 | } |
||
14331 | |||
14332 | $courseBuilder->build_learnpaths( |
||
14333 | api_get_session_id(), |
||
14334 | $this->get_course_int_id(), |
||
14335 | true, |
||
14336 | [$this->get_id()], |
||
14337 | false |
||
14338 | ); |
||
14339 | |||
14340 | $courseBuilder->restoreDocumentsFromList(); |
||
14341 | |||
14342 | $zipFile = CourseArchiver::createBackup($courseBuilder->course); |
||
14343 | $zipPath = CourseArchiver::getBackupDir().$zipFile; |
||
14344 | $result = DocumentManager::file_send_for_download( |
||
14345 | $zipPath, |
||
14346 | true, |
||
14347 | $this->get_name().'.zip' |
||
14348 | ); |
||
14349 | |||
14350 | if ($result) { |
||
14351 | api_not_allowed(); |
||
14352 | } |
||
14353 | |||
14354 | return true; |
||
14355 | } |
||
14356 | |||
14357 | /** |
||
14358 | * Get whether this is a learning path with the accumulated work time or not. |
||
14359 | * |
||
14360 | * @return int |
||
14361 | */ |
||
14362 | public function getAccumulateWorkTime() |
||
14363 | { |
||
14364 | return (int) $this->accumulateWorkTime; |
||
14365 | } |
||
14366 | |||
14367 | /** |
||
14368 | * Get whether this is a learning path with the accumulated work time or not. |
||
14369 | * |
||
14370 | * @return int |
||
14371 | */ |
||
14372 | public function getAccumulateWorkTimeTotalCourse() |
||
14373 | { |
||
14374 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14375 | $sql = "SELECT SUM(accumulate_work_time) AS total |
||
14376 | FROM $table |
||
14377 | WHERE c_id = ".$this->course_int_id; |
||
14378 | $result = Database::query($sql); |
||
14379 | $row = Database::fetch_array($result); |
||
14380 | |||
14381 | return (int) $row['total']; |
||
14382 | } |
||
14383 | |||
14384 | /** |
||
14385 | * Set whether this is a learning path with the accumulated work time or not. |
||
14386 | * |
||
14387 | * @param int $value (0 = false, 1 = true) |
||
14388 | * |
||
14389 | * @return bool |
||
14390 | */ |
||
14391 | public function setAccumulateWorkTime($value) |
||
14392 | { |
||
14393 | if (!api_get_configuration_value('lp_minimum_time')) { |
||
14394 | return false; |
||
14395 | } |
||
14396 | |||
14397 | $this->accumulateWorkTime = (int) $value; |
||
14398 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14399 | $lp_id = $this->get_id(); |
||
14400 | $sql = "UPDATE $table SET accumulate_work_time = ".$this->accumulateWorkTime." |
||
14401 | WHERE c_id = ".$this->course_int_id." AND id = $lp_id"; |
||
14402 | Database::query($sql); |
||
14403 | |||
14404 | return true; |
||
14405 | } |
||
14406 | |||
14407 | /** |
||
14408 | * @param int $lpId |
||
14409 | * @param int $courseId |
||
14410 | * |
||
14411 | * @return mixed |
||
14412 | */ |
||
14413 | public static function getAccumulateWorkTimePrerequisite($lpId, $courseId) |
||
14414 | { |
||
14415 | $lpId = (int) $lpId; |
||
14416 | $courseId = (int) $courseId; |
||
14417 | |||
14418 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14419 | $sql = "SELECT accumulate_work_time |
||
14420 | FROM $table |
||
14421 | WHERE c_id = $courseId AND id = $lpId"; |
||
14422 | $result = Database::query($sql); |
||
14423 | $row = Database::fetch_array($result); |
||
14424 | |||
14425 | return $row['accumulate_work_time']; |
||
14426 | } |
||
14427 | |||
14428 | /** |
||
14429 | * @param int $courseId |
||
14430 | * |
||
14431 | * @return int |
||
14432 | */ |
||
14433 | public static function getAccumulateWorkTimeTotal($courseId) |
||
14434 | { |
||
14435 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14436 | $courseId = (int) $courseId; |
||
14437 | $sql = "SELECT SUM(accumulate_work_time) AS total |
||
14438 | FROM $table |
||
14439 | WHERE c_id = $courseId"; |
||
14440 | $result = Database::query($sql); |
||
14441 | $row = Database::fetch_array($result); |
||
14442 | |||
14443 | return (int) $row['total']; |
||
14444 | } |
||
14445 | |||
14446 | /** |
||
14447 | * In order to use the lp icon option you need to create the "lp_icon" LP extra field |
||
14448 | * and put the images in. |
||
14449 | * |
||
14450 | * @return array |
||
14451 | */ |
||
14452 | public static function getIconSelect() |
||
14453 | { |
||
14454 | $theme = api_get_visual_theme(); |
||
14455 | $path = api_get_path(SYS_PUBLIC_PATH).'css/themes/'.$theme.'/lp_icons/'; |
||
14456 | $icons = ['' => get_lang('SelectAnOption')]; |
||
14457 | |||
14458 | if (is_dir($path)) { |
||
14459 | $finder = new Finder(); |
||
14460 | $finder->files()->in($path); |
||
14461 | $allowedExtensions = ['jpeg', 'jpg', 'png']; |
||
14462 | /** @var SplFileInfo $file */ |
||
14463 | foreach ($finder as $file) { |
||
14464 | if (in_array(strtolower($file->getExtension()), $allowedExtensions)) { |
||
14465 | $icons[$file->getFilename()] = $file->getFilename(); |
||
14466 | } |
||
14467 | } |
||
14468 | } |
||
14469 | |||
14470 | return $icons; |
||
14471 | } |
||
14472 | |||
14473 | /** |
||
14474 | * Get the learnpaths availables for next lp. |
||
14475 | * |
||
14476 | * @param int $courseId |
||
14477 | * |
||
14478 | * @return array |
||
14479 | */ |
||
14480 | public static function getNextLpsAvailable($courseId, $lpId) |
||
14481 | { |
||
14482 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14483 | |||
14484 | $sqlNextLp = "SELECT next_lp_id FROM $table WHERE c_id = $courseId AND next_lp_id > 0"; |
||
14485 | $sql = "SELECT iid, name |
||
14486 | FROM $table |
||
14487 | WHERE c_id = $courseId AND |
||
14488 | iid NOT IN($sqlNextLp)"; |
||
14489 | $rs = Database::query($sql); |
||
14490 | $lpsAvailable = [0 => get_lang('None')]; |
||
14491 | if (Database::num_rows($rs) > 0) { |
||
14492 | while ($row = Database::fetch_array($rs)) { |
||
14493 | if ($row['iid'] == $lpId) { |
||
14494 | continue; |
||
14495 | } |
||
14496 | $lpsAvailable[$row['iid']] = $row['name']; |
||
14497 | } |
||
14498 | } |
||
14499 | |||
14500 | return $lpsAvailable; |
||
14501 | } |
||
14502 | |||
14503 | /** |
||
14504 | * Get The next lp id. |
||
14505 | * |
||
14506 | * @param $lpId |
||
14507 | * @param $courseId |
||
14508 | * |
||
14509 | * @return int |
||
14510 | */ |
||
14511 | public static function getFlowNextLpId($lpId, $courseId) |
||
14512 | { |
||
14513 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14514 | |||
14515 | $sql = "SELECT next_lp_id |
||
14516 | FROM $table |
||
14517 | WHERE c_id = $courseId AND iid = $lpId"; |
||
14518 | $rs = Database::query($sql); |
||
14519 | $nextLpId = Database::result($rs, 0, 0); |
||
14520 | |||
14521 | return $nextLpId; |
||
14522 | } |
||
14523 | |||
14524 | /** |
||
14525 | * Get The previous lp id. |
||
14526 | * |
||
14527 | * @param $nextLpId |
||
14528 | * @param $courseId |
||
14529 | * |
||
14530 | * @return int |
||
14531 | */ |
||
14532 | public static function getFlowPrevLpId($nextLpId, $courseId) |
||
14533 | { |
||
14534 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14535 | |||
14536 | $sql = "SELECT iid |
||
14537 | FROM $table |
||
14538 | WHERE c_id = $courseId AND next_lp_id = $nextLpId"; |
||
14539 | $rs = Database::query($sql); |
||
14540 | $prevLpId = Database::result($rs, 0, 0); |
||
14541 | |||
14542 | return $prevLpId; |
||
14543 | } |
||
14544 | |||
14545 | /** |
||
14546 | * Save the next lp id. |
||
14547 | * |
||
14548 | * @param $lpId |
||
14549 | * @param $nextLpId |
||
14550 | */ |
||
14551 | public static function saveTheNextLp($lpId, $nextLpId) |
||
14552 | { |
||
14553 | $nextLpId = (int) $nextLpId; |
||
14554 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
14555 | Database::query("UPDATE $table SET next_lp_id = $nextLpId WHERE iid = $lpId"); |
||
14556 | } |
||
14557 | |||
14558 | /** |
||
14559 | * @param int $lpId |
||
14560 | * |
||
14561 | * @return string |
||
14562 | */ |
||
14563 | public static function getSelectedIcon($lpId) |
||
14564 | { |
||
14565 | $extraFieldValue = new ExtraFieldValue('lp'); |
||
14566 | $lpIcon = $extraFieldValue->get_values_by_handler_and_field_variable($lpId, 'lp_icon'); |
||
14567 | $icon = ''; |
||
14568 | if (!empty($lpIcon) && isset($lpIcon['value'])) { |
||
14569 | $icon = $lpIcon['value']; |
||
14570 | } |
||
14571 | |||
14572 | return $icon; |
||
14573 | } |
||
14574 | |||
14575 | /** |
||
14576 | * @param int $lpId |
||
14577 | * |
||
14578 | * @return string |
||
14579 | */ |
||
14580 | public static function getSelectedIconHtml($lpId) |
||
14581 | { |
||
14582 | $icon = self::getSelectedIcon($lpId); |
||
14583 | |||
14584 | if (empty($icon)) { |
||
14585 | return ''; |
||
14586 | } |
||
14587 | |||
14588 | $theme = api_get_visual_theme(); |
||
14589 | $path = api_get_path(WEB_PUBLIC_PATH).'css/themes/'.$theme.'/lp_icons/'.$icon; |
||
14590 | |||
14591 | return Display::img($path); |
||
14592 | } |
||
14593 | |||
14594 | /** |
||
14595 | * @param string $value |
||
14596 | * |
||
14597 | * @return string |
||
14598 | */ |
||
14599 | public function cleanItemTitle($value) |
||
14600 | { |
||
14601 | $value = Security::remove_XSS(strip_tags($value)); |
||
14602 | |||
14603 | return $value; |
||
14604 | } |
||
14605 | |||
14606 | public function setItemTitle(FormValidator $form) |
||
14607 | { |
||
14608 | if (api_get_configuration_value('save_titles_as_html')) { |
||
14609 | $form->addHtmlEditor( |
||
14610 | 'title', |
||
14611 | get_lang('Title'), |
||
14612 | true, |
||
14613 | false, |
||
14614 | ['ToolbarSet' => 'TitleAsHtml', 'id' => uniqid('editor')] |
||
14615 | ); |
||
14616 | } else { |
||
14617 | $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle', 'class' => 'learnpath_item_form']); |
||
14618 | $form->applyFilter('title', 'trim'); |
||
14619 | $form->applyFilter('title', 'html_filter'); |
||
14620 | } |
||
14621 | } |
||
14622 | |||
14623 | /** |
||
14624 | * @return array |
||
14625 | */ |
||
14626 | public function getItemsForForm($addParentCondition = false) |
||
14627 | { |
||
14628 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
14629 | $course_id = api_get_course_int_id(); |
||
14630 | |||
14631 | $sql = "SELECT * FROM $tbl_lp_item |
||
14632 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
14633 | |||
14634 | if ($addParentCondition) { |
||
14635 | $sql .= ' AND parent_item_id = 0 '; |
||
14636 | } |
||
14637 | $sql .= ' ORDER BY display_order ASC'; |
||
14638 | |||
14639 | $result = Database::query($sql); |
||
14640 | $arrLP = []; |
||
14641 | while ($row = Database::fetch_array($result)) { |
||
14642 | $arrLP[] = [ |
||
14643 | 'iid' => $row['iid'], |
||
14644 | 'id' => $row['iid'], |
||
14645 | 'item_type' => $row['item_type'], |
||
14646 | 'title' => $this->cleanItemTitle($row['title']), |
||
14647 | 'title_raw' => $row['title'], |
||
14648 | 'path' => $row['path'], |
||
14649 | 'description' => Security::remove_XSS($row['description']), |
||
14650 | 'parent_item_id' => $row['parent_item_id'], |
||
14651 | 'previous_item_id' => $row['previous_item_id'], |
||
14652 | 'next_item_id' => $row['next_item_id'], |
||
14653 | 'display_order' => $row['display_order'], |
||
14654 | 'max_score' => $row['max_score'], |
||
14655 | 'min_score' => $row['min_score'], |
||
14656 | 'mastery_score' => $row['mastery_score'], |
||
14657 | 'prerequisite' => $row['prerequisite'], |
||
14658 | 'max_time_allowed' => $row['max_time_allowed'], |
||
14659 | 'prerequisite_min_score' => $row['prerequisite_min_score'], |
||
14660 | 'prerequisite_max_score' => $row['prerequisite_max_score'], |
||
14661 | ]; |
||
14662 | } |
||
14663 | |||
14664 | return $arrLP; |
||
14665 | } |
||
14666 | |||
14667 | /** |
||
14668 | * Gets whether this SCORM learning path has been marked to use the score |
||
14669 | * as progress. Takes into account whether the learnpath matches (SCORM |
||
14670 | * content + less than 2 items). |
||
14671 | * |
||
14672 | * @return bool True if the score should be used as progress, false otherwise |
||
14673 | */ |
||
14674 | public function getUseScoreAsProgress() |
||
14675 | { |
||
14676 | // If not a SCORM, we don't care about the setting |
||
14677 | if ($this->get_type() != 2) { |
||
14678 | return false; |
||
14679 | } |
||
14680 | // If more than one step in the SCORM, we don't care about the setting |
||
14681 | if ($this->get_total_items_count() > 1) { |
||
14682 | return false; |
||
14683 | } |
||
14684 | $extraFieldValue = new ExtraFieldValue('lp'); |
||
14685 | $doUseScore = false; |
||
14686 | $useScore = $extraFieldValue->get_values_by_handler_and_field_variable($this->get_id(), 'use_score_as_progress'); |
||
14687 | if (!empty($useScore) && isset($useScore['value'])) { |
||
14688 | $doUseScore = $useScore['value']; |
||
14689 | } |
||
14690 | |||
14691 | return $doUseScore; |
||
14692 | } |
||
14693 | |||
14694 | /** |
||
14695 | * Get the user identifier (user_id or username |
||
14696 | * Depends on scorm_api_username_as_student_id in app/config/configuration.php. |
||
14697 | * |
||
14698 | * @return string User ID or username, depending on configuration setting |
||
14699 | */ |
||
14700 | public static function getUserIdentifierForExternalServices() |
||
14701 | { |
||
14702 | if (api_get_configuration_value('scorm_api_username_as_student_id')) { |
||
14703 | return api_get_user_info(api_get_user_id())['username']; |
||
14704 | } elseif (api_get_configuration_value('scorm_api_extrafield_to_use_as_student_id') != null) { |
||
14705 | $extraFieldValue = new ExtraFieldValue('user'); |
||
14706 | $extrafield = $extraFieldValue->get_values_by_handler_and_field_variable(api_get_user_id(), api_get_configuration_value('scorm_api_extrafield_to_use_as_student_id')); |
||
14707 | |||
14708 | return $extrafield['value']; |
||
14709 | } else { |
||
14710 | return api_get_user_id(); |
||
14711 | } |
||
14712 | } |
||
14713 | |||
14714 | /** |
||
14715 | * Save the new order for learning path items. |
||
14716 | * |
||
14717 | * We have to update parent_item_id, previous_item_id, next_item_id, display_order in the database. |
||
14718 | * |
||
14719 | * @param array $orderList A associative array with item ID as key and parent ID as value. |
||
14720 | * @param int $courseId |
||
14721 | */ |
||
14722 | public static function sortItemByOrderList(array $orderList, $courseId = 0) |
||
14723 | { |
||
14724 | $courseId = $courseId ?: api_get_course_int_id(); |
||
14725 | $itemList = new LpItemOrderList(); |
||
14726 | |||
14727 | foreach ($orderList as $id => $parentId) { |
||
14728 | $item = new LpOrderItem($id, $parentId); |
||
14729 | $itemList->add($item); |
||
14730 | } |
||
14731 | |||
14732 | $parents = $itemList->getListOfParents(); |
||
14733 | |||
14734 | foreach ($parents as $parentId) { |
||
14735 | $sameParentLpItemList = $itemList->getItemWithSameParent($parentId); |
||
14736 | $previous_item_id = 0; |
||
14737 | for ($i = 0; $i < count($sameParentLpItemList->list); $i++) { |
||
14738 | $item_id = $sameParentLpItemList->list[$i]->id; |
||
14739 | // display_order |
||
14740 | $display_order = $i + 1; |
||
14741 | $itemList->setParametersForId($item_id, $display_order, 'display_order'); |
||
14742 | // previous_item_id |
||
14743 | $itemList->setParametersForId($item_id, $previous_item_id, 'previous_item_id'); |
||
14744 | $previous_item_id = $item_id; |
||
14745 | // next_item_id |
||
14746 | $next_item_id = 0; |
||
14747 | if ($i < count($sameParentLpItemList->list) - 1) { |
||
14748 | $next_item_id = $sameParentLpItemList->list[$i + 1]->id; |
||
14749 | } |
||
14750 | $itemList->setParametersForId($item_id, $next_item_id, 'next_item_id'); |
||
14751 | } |
||
14752 | } |
||
14753 | |||
14754 | $table = Database::get_course_table(TABLE_LP_ITEM); |
||
14755 | |||
14756 | foreach ($itemList->list as $item) { |
||
14757 | $params = []; |
||
14758 | $params['display_order'] = $item->display_order; |
||
14759 | $params['previous_item_id'] = $item->previous_item_id; |
||
14760 | $params['next_item_id'] = $item->next_item_id; |
||
14761 | $params['parent_item_id'] = $item->parent_item_id; |
||
14762 | |||
14763 | Database::update( |
||
14764 | $table, |
||
14765 | $params, |
||
14766 | [ |
||
14767 | 'iid = ? AND c_id = ? ' => [ |
||
14768 | (int) $item->id, |
||
14769 | (int) $courseId, |
||
14770 | ], |
||
14771 | ] |
||
14772 | ); |
||
14773 | } |
||
14774 | } |
||
14775 | |||
14776 | public static function findLastView( |
||
14777 | int $lpId, |
||
14778 | int $studentId, |
||
14779 | int $courseId, |
||
14780 | int $sessionId = 0, |
||
14781 | bool $createIfNotExists = false |
||
14782 | ): array { |
||
14783 | $tblLpView = Database::get_course_table(TABLE_LP_VIEW); |
||
14784 | |||
14785 | $sessionCondition = api_get_session_condition($sessionId); |
||
14786 | |||
14787 | $sql = "SELECT iid FROM $tblLpView |
||
14788 | WHERE c_id = $courseId AND lp_id = $lpId AND user_id = $studentId $sessionCondition |
||
14789 | ORDER BY view_count DESC"; |
||
14790 | $result = Database::query($sql); |
||
14791 | |||
14792 | $lpView = Database::fetch_assoc($result); |
||
14793 | |||
14794 | if ($createIfNotExists && empty($lpView)) { |
||
14795 | $lpViewId = Database::insert( |
||
14796 | $tblLpView, |
||
14797 | [ |
||
14798 | 'c_id' => $courseId, |
||
14799 | 'lp_id' => $lpId, |
||
14800 | 'user_id' => $studentId, |
||
14801 | 'view_count' => 1, |
||
14802 | 'session_id' => $sessionId, |
||
14803 | ] |
||
14804 | ); |
||
14805 | Database::update($tblLpView, ['id' => $lpViewId], ['iid = ?' => $lpViewId]); |
||
14806 | |||
14807 | return ['iid' => $lpViewId]; |
||
14808 | } |
||
14809 | |||
14810 | return empty($lpView) ? [] : $lpView; |
||
14811 | } |
||
14812 | |||
14813 | /** |
||
14814 | * Check and obtain the lp final item if exist. |
||
14815 | * |
||
14816 | * @return learnpathItem |
||
14817 | */ |
||
14818 | public function getFinalItem() |
||
14819 | { |
||
14820 | if (empty($this->items)) { |
||
14821 | return null; |
||
14822 | } |
||
14823 | |||
14824 | foreach ($this->items as $item) { |
||
14825 | if ($item->type !== 'final_item') { |
||
14826 | continue; |
||
14827 | } |
||
14828 | |||
14829 | return $item; |
||
14830 | } |
||
14831 | } |
||
14832 | |||
14833 | /** |
||
14834 | * Get the depth level of LP item. |
||
14835 | * |
||
14836 | * @param array $items |
||
14837 | * @param int $currentItemId |
||
14838 | * |
||
14839 | * @return int |
||
14840 | */ |
||
14841 | private static function get_level_for_item($items, $currentItemId) |
||
14842 | { |
||
14843 | $parentItemId = 0; |
||
14844 | if (isset($items[$currentItemId])) { |
||
14845 | $parentItemId = $items[$currentItemId]->parent; |
||
14846 | } |
||
14847 | |||
14848 | if ($parentItemId == 0) { |
||
14849 | return 0; |
||
14850 | } else { |
||
14851 | return self::get_level_for_item($items, $parentItemId) + 1; |
||
14852 | } |
||
14853 | } |
||
14854 | |||
14855 | /** |
||
14856 | * Generate the link for a learnpath category as course tool. |
||
14857 | * |
||
14858 | * @param int $categoryId |
||
14859 | * |
||
14860 | * @return string |
||
14861 | */ |
||
14862 | private static function getCategoryLinkForTool($categoryId) |
||
14863 | { |
||
14864 | $categoryId = (int) $categoryId; |
||
14865 | $link = 'lp/lp_controller.php?'.api_get_cidreq().'&' |
||
14866 | .http_build_query( |
||
14867 | [ |
||
14868 | 'action' => 'view_category', |
||
14869 | 'id' => $categoryId, |
||
14870 | ] |
||
14871 | ); |
||
14872 | |||
14873 | return $link; |
||
14874 | } |
||
14875 | |||
14876 | /** |
||
14877 | * Return the scorm item type object with spaces replaced with _ |
||
14878 | * The return result is use to build a css classname like scorm_type_$return. |
||
14879 | * |
||
14880 | * @param $in_type |
||
14881 | * |
||
14882 | * @return mixed |
||
14883 | */ |
||
14884 | private static function format_scorm_type_item($in_type) |
||
14885 | { |
||
14886 | return str_replace(' ', '_', $in_type); |
||
14887 | } |
||
14888 | |||
14889 | /** |
||
14890 | * Get the LP Final Item Template. |
||
14891 | * |
||
14892 | * @return string |
||
14893 | */ |
||
14894 | private function getFinalItemTemplate() |
||
14895 | { |
||
14896 | return file_get_contents(api_get_path(SYS_CODE_PATH).'lp/final_item_template/template.html'); |
||
14897 | } |
||
14898 | |||
14899 | /** |
||
14900 | * Get the LP Final Item Url. |
||
14901 | * |
||
14902 | * @return string |
||
14903 | */ |
||
14904 | private function getSavedFinalItem() |
||
14905 | { |
||
14906 | $finalItem = $this->getFinalItem(); |
||
14907 | $doc = DocumentManager::get_document_data_by_id( |
||
14908 | $finalItem->path, |
||
14909 | $this->cc |
||
14910 | ); |
||
14911 | if ($doc && file_exists($doc['absolute_path'])) { |
||
14912 | return file_get_contents($doc['absolute_path']); |
||
14913 | } |
||
14914 | |||
14915 | return ''; |
||
14916 | } |
||
14917 | |||
14918 | /** |
||
14919 | * Gets the form to evaluate if it exists contains the extra field extra_authorlpitem |
||
14920 | * to establish authors when editing an item of an LP. |
||
14921 | */ |
||
14922 | private function setAuthorLpItem(FormValidator $form) |
||
14923 | { |
||
14924 | if ($form->hasElement('extra_authorlpitem')) { |
||
14925 | /** @var HTML_QuickForm_select $author */ |
||
14926 | $author = $form->getElement('extra_authorlpitem'); |
||
14927 | $options = []; |
||
14928 | $field = new ExtraField('user'); |
||
14929 | $authorLp = $field->get_handler_field_info_by_field_variable('authorlp'); |
||
14930 | $extraFieldId = isset($authorLp['id']) ? (int) $authorLp['id'] : 0; |
||
14931 | if ($extraFieldId != 0) { |
||
14932 | $extraFieldValueUser = new ExtraFieldValue('user'); |
||
14933 | $values = $extraFieldValueUser->get_item_id_from_field_variable_and_field_value( |
||
14934 | $authorLp['variable'], |
||
14935 | 1, |
||
14936 | true, |
||
14937 | false, |
||
14938 | true |
||
14939 | ); |
||
14940 | |||
14941 | if (!empty($values)) { |
||
14942 | foreach ($values as $item) { |
||
14943 | $teacher = api_get_user_info($item['item_id']); |
||
14944 | $options[$teacher['id']] = $teacher['complete_name']; |
||
14945 | } |
||
14946 | } |
||
14947 | } |
||
14948 | $author->setOptions($options); |
||
14949 | } |
||
14950 | } |
||
14951 | } |
||
14952 |
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: