Passed
Push — master ( dc373e...baaca7 )
by Julito
11:31
created

learnpath   F

Complexity

Total Complexity 1770

Size/Duplication

Total Lines 12547
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12547
rs 0.6314
c 0
b 0
f 0
wmc 1770

205 Methods

Rating   Name   Duplication   Size   Complexity  
A get_course_int_id() 0 3 2
A get_last() 0 12 3
A set_course_int_id() 0 3 1
F add_item() 0 237 13
C get_previous_index() 0 23 7
A get_js_lib() 0 7 2
C get_package_type() 0 74 18
F __construct() 0 332 54
A get_id() 0 6 2
B get_next_item_id() 0 18 6
F add_lp() 0 146 14
A get_progress() 0 9 3
A getCourseCode() 0 3 1
B get_navigation_bar() 0 50 4
D get_next_index() 0 31 10
A get_previous_item_id() 0 7 2
A get_level_for_item() 0 7 2
A get_total_items_count() 0 6 2
A get_first_item_id() 0 7 2
F edit_item() 0 237 24
B getSiblingDirectories() 0 35 6
B get_brother_items() 0 29 6
F first() 0 71 22
A get_current_item_id() 0 13 4
A close() 0 15 3
C get_mediaplayer() 0 70 10
F autocomplete_parents() 0 104 18
D edit_item_prereq() 0 46 10
B get_js_info() 0 42 6
B get_complete_items_count() 0 26 6
C delete_item() 0 73 9
A getTotalItemsCountWithoutDirs() 0 13 4
A get_common_index_terms_by_prefix() 0 16 3
A autosave() 0 4 2
D delete() 0 110 18
B delete_children_items() 0 22 5
A getProgressBar() 0 4 1
B next() 0 22 5
B get_iv_objectives_array() 0 32 3
D save_last() 0 44 9
A get_update_queue() 0 6 2
F get_link() 0 315 57
C prerequisites_match() 0 54 11
A previous() 0 14 2
B move_down() 0 54 8
A open() 0 12 2
A set_jslib() 0 16 3
C getChildrenToc() 0 61 12
A set_error_msg() 0 9 3
C start_current_item() 0 27 13
B set_publicated_on() 0 30 5
D set_current_item() 0 31 9
A get_author() 0 9 3
A set_prerequisite() 0 15 3
A set_modified_on() 0 16 3
A getHideTableOfContents() 0 3 1
B categoryIsPublished() 0 26 2
A get_proximity() 0 9 3
B get_type() 0 13 6
A get_maker() 0 9 3
A get_progress_bar() 0 12 1
A set_preview_image() 0 17 3
C toggle_publish() 0 80 11
A get_type_static() 0 15 3
A get_items_status_list() 0 12 3
A get_theme() 0 9 3
A get_preview_image() 0 9 3
C isBlockedByPrerequisite() 0 28 7
F get_progress_bar_text() 0 43 11
A get_teacher_toc_buttons() 0 21 4
A get_items_details_as_js() 0 13 4
A get_progress_bar_mode() 0 9 3
C set_terms_by_prefix() 0 68 10
A get_user_id() 0 9 3
A set_use_max_score() 0 19 3
A toggle_visibility() 0 14 2
A set_previous_item() 0 6 2
B save_current() 0 31 6
A set_theme() 0 16 3
A getChapterTypes() 0 4 1
C get_preview_image_path() 0 28 7
B restart() 0 39 6
A set_author() 0 16 3
B get_iv_interactions_array() 0 42 4
A set_proximity() 0 20 4
B set_expired_on() 0 31 5
C move_up() 0 51 8
A get_interactions_count_from_db() 0 15 2
C get_scorm_prereq_string() 0 70 10
B get_scorm_xml_node() 0 19 7
A has_audio() 0 13 4
B set_encoding() 0 21 5
B get_view() 0 41 6
F is_lp_visible_for_student() 0 123 21
B get_flat_ordered_items_list() 0 34 5
A getProgress() 0 22 2
A getCategoryLinkForTool() 0 11 1
A get_objectives_count_from_db() 0 16 2
F move_item() 0 152 27
B toggleCategoryVisibility() 0 29 3
C getParentToc() 0 67 14
F getListArrayToc() 0 79 12
A get_view_id() 0 9 3
C toggleCategoryPublish() 0 84 8
A set_hide_toc_frame() 0 20 4
A get_lp_session_id() 0 9 3
B get_toc() 0 26 5
C categoryIsVisibleForStudent() 0 59 11
A set_maker() 0 19 4
A get_name() 0 9 3
D save_item() 0 44 9
B set_name() 0 33 5
C update_default_view_mode() 0 39 8
A getCategory() 0 7 1
D display_edit_item() 0 116 17
B set_autolaunch() 0 27 2
F display_thread_form() 0 216 42
B getCalculateScore() 0 47 6
B copy() 0 26 1
A getCategoryFromCourseIntoSelect() 0 15 4
B switch_attempt_mode() 0 21 5
A getAccumulateScormTime() 0 3 1
F create_document() 0 169 30
A tree_array() 0 7 2
A createCategory() 0 8 1
B get_exercises() 0 103 5
B fixBlockedLinks() 0 55 9
A createForum() 0 21 1
B update_default_scorm_commit() 0 31 6
C stop_previous_item() 0 50 17
C set_previous_step_as_prerequisite_for_all_items() 0 48 7
A updateCategory() 0 9 2
B display_lp_prerequisites_list() 0 31 5
F display_link_form() 0 207 39
F display_document_form() 0 411 84
B getForum() 0 44 3
A getExercisesItems() 0 13 3
B getFinalItemForm() 0 91 4
A getCountCategories() 0 10 2
A moveDownCategory() 0 11 2
A sort_tree_array() 0 12 3
F scorm_export() 0 929 107
F display_item_form() 0 222 36
B select_previous_item_id() 0 25 2
B set_seriousgame_mode() 0 28 6
A getItem() 0 7 3
A getCategoryId() 0 3 1
F create_tree_array() 0 42 12
B get_links() 0 109 6
B create_path() 0 14 5
B getCurrentBuildingModeURL() 0 10 5
A display_document() 0 19 2
A setSubscribeUsers() 0 13 2
B upload_image() 0 39 6
D rl_get_resource_name() 0 98 14
A getSavedFinalItem() 0 12 3
A format_scorm_type_item() 0 3 1
A getCurrentItemParentNames() 0 14 3
A getSubscribeUsers() 0 3 1
B set_attempt_mode() 0 32 5
B deleteCategory() 0 32 4
B update_display_order() 0 27 5
B generate_lp_folder() 0 59 8
F display_quiz_form() 0 208 42
A moveUpCategory() 0 11 2
D display_item() 0 98 15
C overview() 0 54 10
D display_item_prerequisites_form() 0 140 16
D print_recursive() 0 38 10
A clear_prerequisites() 0 17 2
C scorm_export_to_pdf() 0 62 12
C isFirstOrLastItem() 0 36 7
C display_move_item() 0 57 11
C getCalculateStars() 0 80 12
F display_hotpotatoes_form() 0 184 39
B get_js_dropdown_array() 0 51 6
A getFinalEvaluationItem() 0 12 3
B build_action_menu() 0 117 5
A getCategoryByCourse() 0 8 1
A display_resources() 0 49 1
A getSubscriptionSettings() 0 14 2
B verify_document_size() 0 21 8
F display_manipulate() 0 116 15
A display_item_small_form() 0 13 1
B get_documents() 0 89 1
C get_forums() 0 112 11
D edit_document() 0 60 14
B delete_lp_image() 0 15 5
F display_forum_form() 0 207 40
B update_scorm_debug() 0 28 6
A getFinalItem() 0 12 4
A setAccumulateScormTime() 0 14 2
B update_reinit() 0 29 6
A getFinalItemTemplate() 0 3 1
A get_extension() 0 4 1
D rl_get_resource_link_for_learnpath() 0 149 29
A getLpFromSession() 0 13 3
B generate_learning_path_folder() 0 28 4
A getCategories() 0 17 1
B get_attempt_mode() 0 20 9
F return_new_tree() 0 399 48
A setCategoryId() 0 11 1
B get_student_publications() 0 44 4
B lpHasForum() 0 26 1
F display_student_publication_form() 0 199 38

How to fix   Complexity   

Complex Class

Complex classes like learnpath often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use learnpath, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
use Chamilo\CourseBundle\Entity\CLpCategory;
6
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
7
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
8
use Gedmo\Sortable\Entity\Repository\SortableRepository;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\Finder\Finder;
11
use Chamilo\CourseBundle\Entity\CLp;
12
use Chamilo\CourseBundle\Entity\CTool;
13
use Chamilo\UserBundle\Entity\User;
14
use Chamilo\CourseBundle\Entity\CLpItem;
15
use Chamilo\CourseBundle\Entity\CLpItemView;
16
use Chamilo\CourseBundle\Entity\CItemProperty;
17
use Chamilo\CoreBundle\Entity\Repository\CourseRepository;
18
use Chamilo\CoreBundle\Entity\Repository\ItemPropertyRepository;
19
20
/**
21
 * Class learnpath
22
 * This class defines the parent attributes and methods for Chamilo learnpaths
23
 * and SCORM learnpaths. It is used by the scorm class.
24
 * @todo decouple class
25
 * @package chamilo.learnpath
26
 * @author  Yannick Warnier <[email protected]>
27
 * @author  Julio Montoya   <[email protected]> Several improvements and fixes
28
 */
29
class learnpath
30
{
31
    public $attempt = 0; // The number for the current ID view.
32
    public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ...
33
    public $current; // Id of the current item the user is viewing.
34
    public $current_score; // The score of the current item.
35
    public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet).
36
    public $current_time_stop; // The time the user closed this resource.
37
    public $default_status = 'not attempted';
38
    public $encoding = 'UTF-8';
39
    public $error = '';
40
    public $extra_information = ''; // This string can be used by proprietary SCORM contents to store data about the current learnpath.
41
    public $force_commit = false; // For SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue().
42
    public $index; // The index of the active learnpath_item in $ordered_items array.
43
    public $items = [];
44
    public $last; // item_id of last item viewed in the learning path.
45
    public $last_item_seen = 0; // In case we have already come in this learnpath, reuse the last item seen if authorized.
46
    public $license; // Which license this course has been given - not used yet on 20060522.
47
    public $lp_id; // DB iid for this learnpath.
48
    public $lp_view_id; // DB ID for lp_view
49
    public $maker; // Which maker has conceived the content (ENI, Articulate, ...).
50
    public $message = '';
51
    public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded).
52
    public $name; // Learnpath name (they generally have one).
53
    public $ordered_items = []; // List of the learnpath items in the order they are to be read.
54
    public $path = ''; // Path inside the scorm directory (if scorm).
55
    public $theme; // The current theme of the learning path.
56
    public $preview_image; // The current image of the learning path.
57
    public $accumulateScormTime; // Flag to decide whether to accumulate SCORM time or not
58
59
    // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1).
60
    public $prevent_reinit = 1;
61
62
    // Describes the mode of progress bar display.
63
    public $seriousgame_mode = 0;
64
    public $progress_bar_mode = '%';
65
66
    // Percentage progress as saved in the db.
67
    public $progress_db = 0;
68
    public $proximity; // Wether the content is distant or local or unknown.
69
    public $refs_list = []; //list of items by ref => db_id. Used only for prerequisites match.
70
    // !!!This array (refs_list) is built differently depending on the nature of the LP.
71
    // If SCORM, uses ref, if Chamilo, uses id to keep a unique value.
72
    public $type; //type of learnpath. Could be 'chamilo', 'scorm', 'scorm2004', 'aicc', ...
73
    // TODO: Check if this type variable is useful here (instead of just in the controller script).
74
    public $user_id; //ID of the user that is viewing/using the course
75
    public $update_queue = [];
76
    public $scorm_debug = 0;
77
    public $arrMenu = []; // Array for the menu items.
78
    public $debug = 0; // Logging level.
79
    public $lp_session_id = 0;
80
    public $lp_view_session_id = 0; // The specific view might be bound to a session.
81
    public $prerequisite = 0;
82
    public $use_max_score = 1; // 1 or 0
83
    public $subscribeUsers = 0; // Subscribe users or not
84
    public $created_on = '';
85
    public $modified_on = '';
86
    public $publicated_on = '';
87
    public $expired_on = '';
88
    public $ref = null;
89
    public $course_int_id;
90
    public $course_info = [];
91
    public $categoryId;
92
93
    /**
94
     * Constructor.
95
     * Needs a database handler, a course code and a learnpath id from the database.
96
     * Also builds the list of items into $this->items.
97
     * @param   string $course Course code
98
     * @param   integer $lp_id
99
     * @param   integer $user_id
100
     */
101
    public function __construct($course, $lp_id, $user_id)
102
    {
103
        $this->encoding = api_get_system_encoding();
104
        if ($this->debug > 0) {
105
            error_log('New LP - In learnpath::__construct('.$course.','.$lp_id.','.$user_id.')', 0);
106
        }
107
        if (empty($course)) {
108
            $course = api_get_course_id();
109
        }
110
        $course_info = api_get_course_info($course);
111
        if (!empty($course_info)) {
112
            $this->cc = $course_info['code'];
113
            $this->course_info = $course_info;
114
            $course_id = $course_info['real_id'];
115
        } else {
116
            $this->error = 'Course code does not exist in database.';
117
        }
118
119
        $lp_id = (int) $lp_id;
120
        $course_id = (int) $course_id;
121
        $this->set_course_int_id($course_id);
122
        // Check learnpath ID.
123
        if (empty($lp_id) || empty($course_id)) {
124
            $this->error = "Parameter is empty: LpId:'$lp_id', courseId: '$lp_id'";
125
        } else {
126
            // TODO: Make it flexible to use any course_code (still using env course code here).
127
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
128
            $sql = "SELECT * FROM $lp_table
129
                    WHERE iid = $lp_id";
130
            if ($this->debug > 2) {
131
                error_log('New LP - learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0);
132
            }
133
            $res = Database::query($sql);
134
            if (Database::num_rows($res) > 0) {
135
                $this->lp_id = $lp_id;
136
                $row = Database::fetch_array($res);
137
                $this->type = $row['lp_type'];
138
                $this->name = stripslashes($row['name']);
139
                $this->proximity = $row['content_local'];
140
                $this->theme = $row['theme'];
141
                $this->maker = $row['content_maker'];
142
                $this->prevent_reinit = $row['prevent_reinit'];
143
                $this->seriousgame_mode = $row['seriousgame_mode'];
144
                $this->license = $row['content_license'];
145
                $this->scorm_debug = $row['debug'];
146
                $this->js_lib = $row['js_lib'];
147
                $this->path = $row['path'];
148
                $this->preview_image = $row['preview_image'];
149
                $this->author = $row['author'];
150
                $this->hide_toc_frame = $row['hide_toc_frame'];
151
                $this->lp_session_id = $row['session_id'];
152
                $this->use_max_score = $row['use_max_score'];
153
                $this->subscribeUsers = $row['subscribe_users'];
154
                $this->created_on = $row['created_on'];
155
                $this->modified_on = $row['modified_on'];
156
                $this->ref = $row['ref'];
157
                $this->categoryId = $row['category_id'];
158
                $this->accumulateScormTime = isset($row['accumulate_scorm_time']) ? $row['accumulate_scorm_time'] : 'true';
159
160
                if (!empty($row['publicated_on'])) {
161
                    $this->publicated_on = $row['publicated_on'];
162
                }
163
164
                if (!empty($row['expired_on'])) {
165
                    $this->expired_on = $row['expired_on'];
166
                }
167
                if ($this->type == 2) {
168
                    if ($row['force_commit'] == 1) {
169
                        $this->force_commit = true;
170
                    }
171
                }
172
                $this->mode = $row['default_view_mod'];
173
174
                // Check user ID.
175
                if (empty($user_id)) {
176
                    $this->error = 'User ID is empty';
177
                } else {
178
                    $user_info = api_get_user_info($user_id);
179
                    if (!empty($user_info)) {
180
                        $this->user_id = $user_info['user_id'];
181
                    } else {
182
                        $this->error = 'User ID does not exist in database ('.$sql.')';
183
                    }
184
                }
185
186
                // End of variables checking.
187
                $session_id = api_get_session_id();
188
                //  Get the session condition for learning paths of the base + session.
189
                $session = api_get_session_condition($session_id);
190
                // Now get the latest attempt from this user on this LP, if available, otherwise create a new one.
191
                $lp_table = Database::get_course_table(TABLE_LP_VIEW);
192
193
                // Selecting by view_count descending allows to get the highest view_count first.
194
                $sql = "SELECT * FROM $lp_table
195
                        WHERE 
196
                            c_id = $course_id AND 
197
                            lp_id = $lp_id AND 
198
                            user_id = $user_id 
199
                            $session
200
                        ORDER BY view_count DESC";
201
                $res = Database::query($sql);
202
                if ($this->debug > 2) {
203
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - querying lp_view: '.$sql, 0);
204
                }
205
206
                if (Database::num_rows($res) > 0) {
207
                    if ($this->debug > 2) {
208
                        error_log('New LP - learnpath::__construct() '.__LINE__.' - Found previous view');
209
                    }
210
                    $row = Database::fetch_array($res);
211
                    $this->attempt = $row['view_count'];
212
                    $this->lp_view_id = $row['id'];
213
                    $this->last_item_seen = $row['last_item'];
214
                    $this->progress_db = $row['progress'];
215
                    $this->lp_view_session_id = $row['session_id'];
216
                } elseif (!api_is_invitee()) {
217
                    if ($this->debug > 2) {
218
                        error_log('New LP - learnpath::__construct() '.__LINE__.' - NOT Found previous view');
219
                    }
220
                    $this->attempt = 1;
221
                    $params = [
222
                        'c_id' => $course_id,
223
                        'lp_id' => $lp_id,
224
                        'user_id' => $user_id,
225
                        'view_count' => 1,
226
                        'session_id' => $session_id,
227
                        'last_item' => 0
228
                    ];
229
                    $this->lp_view_id = Database::insert($lp_table, $params);
230
                    if (!empty($this->lp_view_id)) {
231
                        $sql = "UPDATE $lp_table SET id = iid
232
                                WHERE iid = ".$this->lp_view_id;
233
                        Database::query($sql);
234
                    }
235
                }
236
237
                // Initialise items.
238
                $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
239
                $sql = "SELECT * FROM $lp_item_table
240
                        WHERE c_id = $course_id AND lp_id = '".$this->lp_id."'
241
                        ORDER BY parent_item_id, display_order";
242
                $res = Database::query($sql);
243
244
                if ($this->debug > 2) {
245
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - query lp items: '.$sql);
246
                    error_log('-- Start while--');
247
                }
248
249
                $lp_item_id_list = [];
250
                while ($row = Database::fetch_array($res)) {
251
                    $lp_item_id_list[] = $row['iid'];
252
                    switch ($this->type) {
253
                        case 3: //aicc
254
                            $oItem = new aiccItem('db', $row['iid'], $course_id);
255
                            if (is_object($oItem)) {
256
                                $my_item_id = $oItem->get_id();
257
                                $oItem->set_lp_view($this->lp_view_id, $course_id);
258
                                $oItem->set_prevent_reinit($this->prevent_reinit);
259
                                // Don't use reference here as the next loop will make the pointed object change.
260
                                $this->items[$my_item_id] = $oItem;
261
                                $this->refs_list[$oItem->ref] = $my_item_id;
262
                                if ($this->debug > 2) {
263
                                    error_log(
264
                                        'New LP - learnpath::__construct() - '.
265
                                        'aicc object with id '.$my_item_id.
266
                                        ' set in items[]',
267
                                        0
268
                                    );
269
                                }
270
                            }
271
                            break;
272
                        case 2:
273
                            $oItem = new scormItem('db', $row['iid'], $course_id);
274
                            if (is_object($oItem)) {
275
                                $my_item_id = $oItem->get_id();
276
                                $oItem->set_lp_view($this->lp_view_id, $course_id);
277
                                $oItem->set_prevent_reinit($this->prevent_reinit);
278
                                // Don't use reference here as the next loop will make the pointed object change.
279
                                $this->items[$my_item_id] = $oItem;
280
                                $this->refs_list[$oItem->ref] = $my_item_id;
281
                                if ($this->debug > 2) {
282
                                    error_log('New LP - object with id '.$my_item_id.' set in items[]', 0);
283
                                }
284
                            }
285
                            break;
286
                        case 1:
287
                        default:
288
                            if ($this->debug > 2) {
289
                                error_log('New LP - learnpath::__construct() '.__LINE__.' - calling learnpathItem');
290
                            }
291
                            $oItem = new learnpathItem($row['iid'], $user_id, $course_id, $row);
292
293
                            if ($this->debug > 2) {
294
                                error_log('New LP - learnpath::__construct() '.__LINE__.' - end calling learnpathItem');
295
                            }
296
                            if (is_object($oItem)) {
297
                                $my_item_id = $oItem->get_id();
298
                                // Moved down to when we are sure the item_view exists.
299
                                //$oItem->set_lp_view($this->lp_view_id);
300
                                $oItem->set_prevent_reinit($this->prevent_reinit);
301
                                // Don't use reference here as the next loop will make the pointed object change.
302
                                $this->items[$my_item_id] = $oItem;
303
                                $this->refs_list[$my_item_id] = $my_item_id;
304
                                if ($this->debug > 2) {
305
                                    error_log(
306
                                        'New LP - learnpath::__construct() '.__LINE__.
307
                                        ' - object with id '.$my_item_id.' set in items[]'
308
                                    );
309
                                }
310
                            }
311
                            break;
312
                    }
313
314
                    // Setting the object level with variable $this->items[$i][parent]
315
                    foreach ($this->items as $itemLPObject) {
316
                        $level = self::get_level_for_item(
317
                            $this->items,
318
                            $itemLPObject->db_id
319
                        );
320
                        $itemLPObject->level = $level;
321
                    }
322
323
                    // Setting the view in the item object.
324
                    if (is_object($this->items[$row['iid']])) {
325
                        $this->items[$row['iid']]->set_lp_view($this->lp_view_id, $course_id);
326
                        if ($this->items[$row['iid']]->get_type() == TOOL_HOTPOTATOES) {
327
                            $this->items[$row['iid']]->current_start_time = 0;
328
                            $this->items[$row['iid']]->current_stop_time = 0;
329
                        }
330
                    }
331
                }
332
333
                if ($this->debug > 2) {
334
                    error_log('New LP - learnpath::__construct() '.__LINE__.' ----- end while ----');
335
                }
336
337
                if (!empty($lp_item_id_list)) {
338
                    $lp_item_id_list_to_string = implode("','", $lp_item_id_list);
339
                    if (!empty($lp_item_id_list_to_string)) {
340
                        // Get last viewing vars.
341
                        $itemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
342
                        // This query should only return one or zero result.
343
                        $sql = "SELECT lp_item_id, status
344
                                FROM $itemViewTable
345
                                WHERE
346
                                    c_id = $course_id AND
347
                                    lp_view_id = ".$this->lp_view_id." AND
348
                                    lp_item_id IN ('".$lp_item_id_list_to_string."')
349
                                ORDER BY view_count DESC ";
350
351
                        if ($this->debug > 2) {
352
                            error_log(
353
                                'New LP - learnpath::__construct() - Selecting item_views: '.$sql,
354
                                0
355
                            );
356
                        }
357
358
                        $status_list = [];
359
                        $res = Database::query($sql);
360
                        while ($row = Database:: fetch_array($res)) {
361
                            $status_list[$row['lp_item_id']] = $row['status'];
362
                        }
363
364
                        foreach ($lp_item_id_list as $item_id) {
365
                            if (isset($status_list[$item_id])) {
366
                                $status = $status_list[$item_id];
367
                                if (is_object($this->items[$item_id])) {
368
                                    $this->items[$item_id]->set_status($status);
369
                                    if (empty($status)) {
370
                                        $this->items[$item_id]->set_status(
371
                                            $this->default_status
372
                                        );
373
                                    }
374
                                }
375
                            } else {
376
                                if (!api_is_invitee()) {
377
                                    if (is_object($this->items[$item_id])) {
378
                                        $this->items[$item_id]->set_status(
379
                                            $this->default_status
380
                                        );
381
                                    }
382
383
                                    if (!empty($this->lp_view_id)) {
384
                                        // Add that row to the lp_item_view table so that
385
                                        // we have something to show in the stats page.
386
                                        $params = [
387
                                            'c_id' => $course_id,
388
                                            'lp_item_id' => $item_id,
389
                                            'lp_view_id' => $this->lp_view_id,
390
                                            'view_count' => 1,
391
                                            'status' => 'not attempted',
392
                                            'start_time' => time(),
393
                                            'total_time' => 0,
394
                                            'score' => 0
395
                                        ];
396
                                        $insertId = Database::insert($itemViewTable, $params);
397
398
                                        if ($insertId) {
399
                                            $sql = "UPDATE $itemViewTable SET id = iid
400
                                                    WHERE iid = $insertId";
401
                                            Database::query($sql);
402
                                        }
403
404
                                        $this->items[$item_id]->set_lp_view(
405
                                            $this->lp_view_id,
406
                                            $course_id
407
                                        );
408
                                    }
409
                                }
410
                            }
411
                        }
412
                    }
413
                }
414
415
                $this->ordered_items = self::get_flat_ordered_items_list(
416
                    $this->get_id(),
417
                    0,
418
                    $course_id
419
                );
420
                $this->max_ordered_items = 0;
421
                foreach ($this->ordered_items as $index => $dummy) {
422
                    if ($index > $this->max_ordered_items && !empty($dummy)) {
423
                        $this->max_ordered_items = $index;
424
                    }
425
                }
426
                // TODO: Define the current item better.
427
                $this->first();
428
                if ($this->debug > 2) {
429
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - End of learnpath constructor for learnpath '.$this->get_id(), 0);
430
                }
431
            } else {
432
                $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
433
            }
434
        }
435
    }
436
437
    /**
438
     * @return string
439
     */
440
    public function getCourseCode()
441
    {
442
        return $this->cc;
443
    }
444
445
    /**
446
     * @return int
447
     */
448
    public function get_course_int_id()
449
    {
450
        return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id();
451
    }
452
453
    /**
454
     * @param $course_id
455
     * @return int
456
     */
457
    public function set_course_int_id($course_id)
458
    {
459
        return $this->course_int_id = (int) $course_id;
460
    }
461
462
    /**
463
     * Get the depth level of LP item
464
     * @param array $items
465
     * @param int $currentItemId
466
     * @return int
467
     */
468
    private static function get_level_for_item($items, $currentItemId)
469
    {
470
        $parentItemId = $items[$currentItemId]->parent;
471
        if ($parentItemId == 0) {
472
            return 0;
473
        } else {
474
            return self::get_level_for_item($items, $parentItemId) + 1;
475
        }
476
    }
477
478
    /**
479
     * Function rewritten based on old_add_item() from Yannick Warnier.
480
     * Due the fact that users can decide where the item should come, I had to overlook this function and
481
     * I found it better to rewrite it. Old function is still available.
482
     * Added also the possibility to add a description.
483
     *
484
     * @param int $parent
485
     * @param int $previous
486
     * @param string $type
487
     * @param int $id resource ID (ref)
488
     * @param string $title
489
     * @param string $description
490
     * @param int $prerequisites
491
     * @param int $max_time_allowed
492
     * @param int $userId
493
     *
494
     * @return int
495
     */
496
    public function add_item(
497
        $parent,
498
        $previous,
499
        $type = 'dir',
500
        $id,
501
        $title,
502
        $description,
503
        $prerequisites = 0,
504
        $max_time_allowed = 0,
505
        $userId = 0
506
    ) {
507
        $course_id = $this->course_info['real_id'];
508
        if ($this->debug > 0) {
509
            error_log('New LP - In learnpath::add_item('.$parent.','.$previous.','.$type.','.$id.','.$title.')');
510
        }
511
        if (empty($course_id)) {
512
            // Sometimes Oogie doesn't catch the course info but sets $this->cc
513
            $this->course_info = api_get_course_info($this->cc);
514
            $course_id = $this->course_info['real_id'];
515
        }
516
        $userId = empty($userId) ? api_get_user_id() : $userId;
517
        $sessionId = api_get_session_id();
518
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
519
        $_course = $this->course_info;
520
        $parent = intval($parent);
521
        $previous = intval($previous);
522
        $id = intval($id);
523
        $max_time_allowed = htmlentities($max_time_allowed);
524
        if (empty($max_time_allowed)) {
525
            $max_time_allowed = 0;
526
        }
527
        $sql = "SELECT COUNT(iid) AS num
528
                FROM $tbl_lp_item
529
                WHERE
530
                    c_id = $course_id AND
531
                    lp_id = ".$this->get_id()." AND
532
                    parent_item_id = ".$parent;
533
534
        $res_count = Database::query($sql);
535
        $row = Database::fetch_array($res_count);
536
        $num = $row['num'];
537
538
        if ($num > 0) {
539
            if (empty($previous)) {
540
                $sql = "SELECT iid, next_item_id, display_order
541
                        FROM $tbl_lp_item
542
                        WHERE
543
                            c_id = $course_id AND
544
                            lp_id = ".$this->get_id()." AND
545
                            parent_item_id = $parent AND
546
                            previous_item_id = 0 OR
547
                            previous_item_id = $parent";
548
                $result = Database::query($sql);
549
                $row = Database::fetch_array($result);
550
                $tmp_previous = 0;
551
                $next = $row['iid'];
552
                $display_order = 0;
553
            } else {
554
                $previous = (int) $previous;
555
                $sql = "SELECT iid, previous_item_id, next_item_id, display_order
556
						FROM $tbl_lp_item
557
                        WHERE
558
                            c_id = $course_id AND
559
                            lp_id = ".$this->get_id()." AND
560
                            id = $previous";
561
                $result = Database::query($sql);
562
                $row = Database:: fetch_array($result);
563
                $tmp_previous = $row['iid'];
564
                $next = $row['next_item_id'];
565
                $display_order = $row['display_order'];
566
            }
567
        } else {
568
            $tmp_previous = 0;
569
            $next = 0;
570
            $display_order = 0;
571
        }
572
573
        $id = intval($id);
574
        $typeCleaned = Database::escape_string($type);
575
        if ($type == 'quiz') {
576
            $sql = 'SELECT SUM(ponderation)
577
                    FROM '.Database::get_course_table(TABLE_QUIZ_QUESTION).' as quiz_question
578
                    INNER JOIN '.Database::get_course_table(TABLE_QUIZ_TEST_QUESTION).' as quiz_rel_question
579
                    ON
580
                        quiz_question.id = quiz_rel_question.question_id AND
581
                        quiz_question.c_id = quiz_rel_question.c_id
582
                    WHERE
583
                        quiz_rel_question.exercice_id = '.$id." AND
584
                        quiz_question.c_id = $course_id AND
585
                        quiz_rel_question.c_id = $course_id ";
586
            $rsQuiz = Database::query($sql);
587
            $max_score = Database::result($rsQuiz, 0, 0);
588
589
            // Disabling the exercise if we add it inside a LP
590
            $exercise = new Exercise($course_id);
591
            $exercise->read($id);
592
            $exercise->disable();
593
            $exercise->save();
594
        } else {
595
            $max_score = 100;
596
        }
597
598
        $params = [
599
            "c_id" => $course_id,
600
            "lp_id" => $this->get_id(),
601
            "item_type" => $typeCleaned,
602
            "ref" => '',
603
            "title" => $title,
604
            "description" => $description,
605
            "path" => $id,
606
            "max_score" => $max_score,
607
            "parent_item_id" => $parent,
608
            "previous_item_id" => $previous,
609
            "next_item_id" => intval($next),
610
            "display_order" => $display_order + 1,
611
            "prerequisite" => $prerequisites,
612
            "max_time_allowed" => $max_time_allowed,
613
            'min_score' => 0,
614
            'launch_data' => ''
615
        ];
616
617
        if ($prerequisites != 0) {
618
            $params['prerequisite'] = $prerequisites;
619
        }
620
621
        $new_item_id = Database::insert($tbl_lp_item, $params);
622
623
        if ($this->debug > 2) {
624
            error_log('New LP - Inserting dir/chapter: '.$new_item_id, 0);
625
        }
626
627
        if ($new_item_id) {
628
            $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id";
629
            Database::query($sql);
630
631
            $sql = "UPDATE $tbl_lp_item
632
                    SET previous_item_id = $new_item_id 
633
                    WHERE c_id = $course_id AND id = $next";
634
            Database::query($sql);
635
636
            // Update the item that should be before the new item.
637
            $sql = "UPDATE $tbl_lp_item
638
                    SET next_item_id = $new_item_id
639
                    WHERE c_id = $course_id AND id = $tmp_previous";
640
            Database::query($sql);
641
642
            // Update all the items after the new item.
643
            $sql = "UPDATE $tbl_lp_item
644
                        SET display_order = display_order + 1
645
                    WHERE
646
                        c_id = $course_id AND
647
                        lp_id = ".$this->get_id()." AND
648
                        iid <> $new_item_id AND
649
                        parent_item_id = $parent AND
650
                        display_order > $display_order";
651
            Database::query($sql);
652
653
            // Update the item that should come after the new item.
654
            $sql = "UPDATE $tbl_lp_item
655
                    SET ref = $new_item_id
656
                    WHERE c_id = $course_id AND iid = $new_item_id";
657
            Database::query($sql);
658
659
            // Upload audio.
660
            if (!empty($_FILES['mp3']['name'])) {
661
                // Create the audio folder if it does not exist yet.
662
                $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
663
                if (!is_dir($filepath.'audio')) {
664
                    mkdir(
665
                        $filepath.'audio',
666
                        api_get_permissions_for_new_directories()
667
                    );
668
                    $audio_id = add_document(
669
                        $_course,
670
                        '/audio',
671
                        'folder',
672
                        0,
673
                        'audio',
674
                        '',
675
                        0,
676
                        true,
677
                        null,
678
                        $sessionId,
679
                        $userId
680
                    );
681
                    api_item_property_update(
682
                        $_course,
683
                        TOOL_DOCUMENT,
684
                        $audio_id,
685
                        'FolderCreated',
686
                        $userId,
687
                        null,
688
                        null,
689
                        null,
690
                        null,
691
                        $sessionId
692
                    );
693
                    api_item_property_update(
694
                        $_course,
695
                        TOOL_DOCUMENT,
696
                        $audio_id,
697
                        'invisible',
698
                        $userId,
699
                        null,
700
                        null,
701
                        null,
702
                        null,
703
                        $sessionId
704
                    );
705
                }
706
707
                $file_path = handle_uploaded_document(
708
                    $_course,
709
                    $_FILES['mp3'],
710
                    api_get_path(SYS_COURSE_PATH).$_course['path'].'/document',
711
                    '/audio',
712
                    $userId,
713
                    '',
714
                    '',
715
                    '',
716
                    '',
717
                    false
718
                );
719
720
                // Getting the filename only.
721
                $file_components = explode('/', $file_path);
722
                $file = $file_components[count($file_components) - 1];
723
724
                // Store the mp3 file in the lp_item table.
725
                $sql = "UPDATE $tbl_lp_item SET
726
                          audio = '".Database::escape_string($file)."'
727
                        WHERE iid = '" . intval($new_item_id)."'";
728
                Database::query($sql);
729
            }
730
        }
731
732
        return $new_item_id;
733
    }
734
735
    /**
736
     * Static admin function allowing addition of a learnpath to a course.
737
     * @param string Course code
738
     * @param string Learnpath name
739
     * @param string Learnpath description string, if provided
740
     * @param string Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
741
     * @param string Type of files origin (default = 'zip', others = 'dir','web_dir',...)
742
     * @param string $zipname Zip file containing the learnpath or directory containing the learnpath
743
     * @param string $publicated_on
744
     * @param string $expired_on
745
     * @param int $categoryId
746
     * @param int $userId
747
     * @return integer The new learnpath ID on success, 0 on failure
748
     */
749
    public static function add_lp(
750
        $courseCode,
751
        $name,
752
        $description = '',
753
        $learnpath = 'guess',
754
        $origin = 'zip',
755
        $zipname = '',
756
        $publicated_on = '',
757
        $expired_on = '',
758
        $categoryId = 0,
759
        $userId = 0
760
    ) {
761
        global $charset;
762
763
        if (!empty($courseCode)) {
764
            $courseInfo = api_get_course_info($courseCode);
765
            $course_id = $courseInfo['real_id'];
766
        } else {
767
            $course_id = api_get_course_int_id();
768
            $courseInfo = api_get_course_info();
769
        }
770
771
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
772
        // Check course code exists.
773
        // Check lp_name doesn't exist, otherwise append something.
774
        $i = 0;
775
        $name = Database::escape_string($name);
776
        $categoryId = intval($categoryId);
777
778
        // Session id.
779
        $session_id = api_get_session_id();
780
        $userId = empty($userId) ? api_get_user_id() : $userId;
781
        $check_name = "SELECT * FROM $tbl_lp
782
                       WHERE c_id = $course_id AND name = '$name'";
783
784
        $res_name = Database::query($check_name);
785
786
        if (empty($publicated_on)) {
787
            $publicated_on = null;
788
        } else {
789
            $publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
790
        }
791
792
        if (empty($expired_on)) {
793
            $expired_on = null;
794
        } else {
795
            $expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
796
        }
797
798
        while (Database::num_rows($res_name)) {
799
            // There is already one such name, update the current one a bit.
800
            $i++;
801
            $name = $name.' - '.$i;
802
            $check_name = "SELECT * FROM $tbl_lp 
803
                           WHERE c_id = $course_id AND name = '$name'";
804
            $res_name = Database::query($check_name);
805
        }
806
        // New name does not exist yet; keep it.
807
        // Escape description.
808
        // Kevin: added htmlentities().
809
        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset));
810
        $type = 1;
811
        switch ($learnpath) {
812
            case 'guess':
813
                break;
814
            case 'dokeos':
815
            case 'chamilo':
816
                $type = 1;
817
                break;
818
            case 'aicc':
819
                break;
820
        }
821
822
        switch ($origin) {
823
            case 'zip':
824
                // Check zip name string. If empty, we are currently creating a new Chamilo learnpath.
825
                break;
826
            case 'manual':
827
            default:
828
                $get_max = "SELECT MAX(display_order) 
829
                            FROM $tbl_lp WHERE c_id = $course_id";
830
                $res_max = Database::query($get_max);
831
                if (Database::num_rows($res_max) < 1) {
832
                    $dsp = 1;
833
                } else {
834
                    $row = Database::fetch_array($res_max);
835
                    $dsp = $row[0] + 1;
836
                }
837
838
                $params = [
839
                    'c_id' => $course_id,
840
                    'lp_type' => $type,
841
                    'name' => $name,
842
                    'description' => $description,
843
                    'path' => '',
844
                    'default_view_mod' => 'embedded',
845
                    'default_encoding' => 'UTF-8',
846
                    'display_order' => $dsp,
847
                    'content_maker' => 'Chamilo',
848
                    'content_local' => 'local',
849
                    'js_lib' => '',
850
                    'session_id' => $session_id,
851
                    'created_on' => api_get_utc_datetime(),
852
                    'modified_on'  => api_get_utc_datetime(),
853
                    'publicated_on' => $publicated_on,
854
                    'expired_on' => $expired_on,
855
                    'category_id' => $categoryId,
856
                    'force_commit' => 0,
857
                    'content_license' => '',
858
                    'debug' => 0,
859
                    'theme' => '',
860
                    'preview_image' => '',
861
                    'author' => '',
862
                    'prerequisite' => 0,
863
                    'hide_toc_frame' => 0,
864
                    'seriousgame_mode' => 0,
865
                    'autolaunch' => 0,
866
                    'max_attempts' => 0,
867
                    'subscribe_users' => 0,
868
                    'accumulate_scorm_time' => 1
869
                ];
870
                $id = Database::insert($tbl_lp, $params);
871
872
                if ($id > 0) {
873
                    $sql = "UPDATE $tbl_lp SET id = iid WHERE iid = $id";
874
                    Database::query($sql);
875
876
                    // Insert into item_property.
877
                    api_item_property_update(
878
                        $courseInfo,
879
                        TOOL_LEARNPATH,
880
                        $id,
881
                        'LearnpathAdded',
882
                        $userId
883
                    );
884
                    api_set_default_visibility(
885
                        $id,
886
                        TOOL_LEARNPATH,
887
                        0,
888
                        $courseInfo,
889
                        $session_id,
890
                        $userId
891
                    );
892
                    return $id;
893
                }
894
                break;
895
        }
896
    }
897
898
    /**
899
     * Auto completes the parents of an item in case it's been completed or passed
900
     * @param int $item Optional ID of the item from which to look for parents
901
     */
902
    public function autocomplete_parents($item)
903
    {
904
        $debug = $this->debug;
905
906
        if ($debug) {
907
            error_log('Learnpath::autocomplete_parents()');
908
        }
909
910
        if (empty($item)) {
911
            $item = $this->current;
912
        }
913
914
        $currentItem = $this->getItem($item);
915
        if ($currentItem) {
916
            $parent_id = $currentItem->get_parent();
917
            $parent = $this->getItem($parent_id);
918
            if ($parent) {
919
                // if $item points to an object and there is a parent.
920
                if ($debug) {
921
                    error_log(
922
                        'Autocompleting parent of item '.$item.' "'.$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) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
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
     * Auto saves the current results into the database for the whole learnpath
1012
     * @todo: Add save operations for the learnpath itself.
1013
     */
1014
    public function autosave()
1015
    {
1016
        if ($this->debug > 0) {
1017
            error_log('New LP - In learnpath::autosave()', 0);
1018
        }
1019
    }
1020
1021
    /**
1022
     * Closes the current resource
1023
     *
1024
     * Stops the timer
1025
     * Saves into the database if required
1026
     * Clears the current resource data from this object
1027
     * @return boolean    True on success, false on failure
1028
     */
1029
    public function close()
1030
    {
1031
        if ($this->debug > 0) {
1032
            error_log('New LP - In learnpath::close()', 0);
1033
        }
1034
        if (empty($this->lp_id)) {
1035
            $this->error = 'Trying to close this learnpath but no ID is set';
1036
            return false;
1037
        }
1038
        $this->current_time_stop = time();
1039
        $this->ordered_items = [];
1040
        $this->index = 0;
1041
        unset($this->lp_id);
1042
        //unset other stuff
1043
        return true;
1044
    }
1045
1046
    /**
1047
     * Static admin function allowing removal of a learnpath
1048
     * @param array $courseInfo
1049
     * @param integer $id    Learnpath ID
1050
     * @param string $delete Whether to delete data or keep it (default: 'keep', others: 'remove')
1051
     * @return boolean    True on success, false on failure (might change that to return number of elements deleted)
1052
     */
1053
    public function delete($courseInfo = null, $id = null, $delete = 'keep')
1054
    {
1055
        $course_id = api_get_course_int_id();
1056
        if (!empty($courseInfo)) {
1057
            $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id;
1058
        }
1059
1060
        // TODO: Implement a way of getting this to work when the current object is not set.
1061
        // In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
1062
        // If an ID is specifically given and the current LP is not the same, prevent delete.
1063
        if (!empty($id) && ($id != $this->lp_id)) {
1064
            return false;
1065
        }
1066
1067
        $lp = Database::get_course_table(TABLE_LP_MAIN);
1068
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1069
        $lp_view = Database::get_course_table(TABLE_LP_VIEW);
1070
        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1071
1072
        // Delete lp item id.
1073
        foreach ($this->items as $id => $dummy) {
1074
            $sql = "DELETE FROM $lp_item_view
1075
                    WHERE c_id = $course_id AND lp_item_id = '".$id."'";
1076
            Database::query($sql);
1077
        }
1078
1079
        // Proposed by Christophe (nickname: clefevre)
1080
        $sql = "DELETE FROM $lp_item
1081
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
1082
        Database::query($sql);
1083
1084
        $sql = "DELETE FROM $lp_view 
1085
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
1086
        Database::query($sql);
1087
1088
        self::toggle_publish($this->lp_id, 'i');
1089
1090
        if ($this->type == 2 || $this->type == 3) {
1091
            // This is a scorm learning path, delete the files as well.
1092
            $sql = "SELECT path FROM $lp
1093
                    WHERE iid = ".$this->lp_id;
1094
            $res = Database::query($sql);
1095
            if (Database::num_rows($res) > 0) {
1096
                $row = Database::fetch_array($res);
1097
                $path = $row['path'];
1098
                $sql = "SELECT id FROM $lp
1099
                        WHERE 
1100
                            c_id = $course_id AND
1101
                            path = '$path' AND 
1102
                            iid != ".$this->lp_id;
1103
                $res = Database::query($sql);
1104
                if (Database::num_rows($res) > 0) {
1105
                    // Another learning path uses this directory, so don't delete it.
1106
                    if ($this->debug > 2) {
1107
                        error_log('New LP - In learnpath::delete(), found other LP using path '.$path.', keeping directory', 0);
1108
                    }
1109
                } else {
1110
                    // No other LP uses that directory, delete it.
1111
                    $course_rel_dir = api_get_course_path().'/scorm/'; // scorm dir web path starting from /courses
1112
                    $course_scorm_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; // The absolute system path for this course.
1113
                    if ($delete == 'remove' && is_dir($course_scorm_dir.$path) && !empty($course_scorm_dir)) {
1114
                        if ($this->debug > 2) {
1115
                            error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: '.$course_scorm_dir.$path, 0);
1116
                        }
1117
                        // Proposed by Christophe (clefevre).
1118
                        if (strcmp(substr($path, -2), "/.") == 0) {
1119
                            $path = substr($path, 0, -1); // Remove "." at the end.
1120
                        }
1121
                        //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
1122
                        rmdirr($course_scorm_dir.$path);
1123
                    }
1124
                }
1125
            }
1126
        }
1127
1128
        $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
1129
        $link = 'lp/lp_controller.php?action=view&lp_id='.$this->lp_id;
1130
        // Delete tools
1131
        $sql = "DELETE FROM $tbl_tool
1132
                WHERE c_id = $course_id AND (link LIKE '$link%' AND image='scormbuilder.gif')";
1133
        Database::query($sql);
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
        api_item_property_update(
1142
            api_get_course_info(),
1143
            TOOL_LEARNPATH,
1144
            $this->lp_id,
1145
            'delete',
1146
            api_get_user_id()
1147
        );
1148
1149
        $link_info = GradebookUtils::isResourceInCourseGradebook(
1150
            api_get_course_id(),
1151
            4,
1152
            $id,
1153
            api_get_session_id()
1154
        );
1155
1156
        if ($link_info !== false) {
1157
            GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
1158
        }
1159
1160
        if (api_get_setting('search_enabled') == 'true') {
1161
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1162
            delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
1163
        }
1164
    }
1165
1166
    /**
1167
     * Removes all the children of one item - dangerous!
1168
     * @param    integer $id Element ID of which children have to be removed
1169
     * @return    integer    Total number of children removed
1170
     */
1171
    public function delete_children_items($id)
1172
    {
1173
        $course_id = $this->course_info['real_id'];
1174
        if ($this->debug > 0) {
1175
            error_log('New LP - In learnpath::delete_children_items('.$id.')', 0);
1176
        }
1177
        $num = 0;
1178
        if (empty($id) || $id != strval(intval($id))) {
1179
            return false;
1180
        }
1181
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1182
        $sql = "SELECT * FROM $lp_item 
1183
                WHERE c_id = ".$course_id." AND parent_item_id = $id";
1184
        $res = Database::query($sql);
1185
        while ($row = Database::fetch_array($res)) {
1186
            $num += $this->delete_children_items($row['iid']);
1187
            $sql = "DELETE FROM $lp_item 
1188
                    WHERE c_id = ".$course_id." AND iid = ".$row['iid'];
1189
            Database::query($sql);
1190
            $num++;
1191
        }
1192
        return $num;
1193
    }
1194
1195
    /**
1196
     * Removes an item from the current learnpath
1197
     * @param    integer $id Elem ID (0 if first)
1198
     * @param    integer $remove Whether to remove the resource/data from the
1199
     * system or leave it (default: 'keep', others 'remove')
1200
     * @return    integer    Number of elements moved
1201
     * @todo implement resource removal
1202
     */
1203
    public function delete_item($id, $remove = 'keep')
1204
    {
1205
        $course_id = api_get_course_int_id();
1206
        if ($this->debug > 0) {
1207
            error_log('New LP - In learnpath::delete_item()', 0);
1208
        }
1209
        // TODO: Implement the resource removal.
1210
        if (empty($id) || $id != strval(intval($id))) {
1211
            return false;
1212
        }
1213
        // First select item to get previous, next, and display order.
1214
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1215
        $sql_sel = "SELECT * FROM $lp_item WHERE iid = $id";
1216
        $res_sel = Database::query($sql_sel);
1217
        if (Database::num_rows($res_sel) < 1) {
1218
            return false;
1219
        }
1220
        $row = Database::fetch_array($res_sel);
1221
        $previous = $row['previous_item_id'];
1222
        $next = $row['next_item_id'];
1223
        $display = $row['display_order'];
1224
        $parent = $row['parent_item_id'];
1225
        $lp = $row['lp_id'];
1226
        // Delete children items.
1227
        $num = $this->delete_children_items($id);
1228
        if ($this->debug > 2) {
1229
            error_log('New LP - learnpath::delete_item() - deleted '.$num.' children of element '.$id, 0);
1230
        }
1231
        // Now delete the item.
1232
        $sql_del = "DELETE FROM $lp_item WHERE iid = $id";
1233
        if ($this->debug > 2) {
1234
            error_log('New LP - Deleting item: '.$sql_del, 0);
1235
        }
1236
        Database::query($sql_del);
1237
        // Now update surrounding items.
1238
        $sql_upd = "UPDATE $lp_item SET next_item_id = $next
1239
                    WHERE iid = $previous";
1240
        Database::query($sql_upd);
1241
        $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous
1242
                    WHERE iid = $next";
1243
        Database::query($sql_upd);
1244
        // Now update all following items with new display order.
1245
        $sql_all = "UPDATE $lp_item SET display_order = display_order-1
1246
                    WHERE 
1247
                        c_id = $course_id AND 
1248
                        lp_id = $lp AND 
1249
                        parent_item_id = $parent AND 
1250
                        display_order > $display";
1251
        Database::query($sql_all);
1252
1253
        //Removing prerequisites since the item will not longer exist
1254
        $sql_all = "UPDATE $lp_item SET prerequisite = '' 
1255
                    WHERE c_id = $course_id AND prerequisite = $id";
1256
        Database::query($sql_all);
1257
1258
        // Remove from search engine if enabled.
1259
        if (api_get_setting('search_enabled') == 'true') {
1260
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1261
            $sql = 'SELECT * FROM %s 
1262
                    WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d 
1263
                    LIMIT 1';
1264
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1265
            $res = Database::query($sql);
1266
            if (Database::num_rows($res) > 0) {
1267
                $row2 = Database::fetch_array($res);
1268
                $di = new ChamiloIndexer();
1269
                $di->remove_document($row2['search_did']);
1270
            }
1271
            $sql = 'DELETE 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
            Database::query($sql);
1276
        }
1277
    }
1278
1279
    /**
1280
     * Updates an item's content in place
1281
     * @param   integer $id Element ID
1282
     * @param   integer $parent Parent item ID
1283
     * @param   integer $previous Previous item ID
1284
     * @param   string  $title Item title
1285
     * @param   string  $description Item description
1286
     * @param   string  $prerequisites Prerequisites (optional)
1287
     * @param   array   $audio The array resulting of the $_FILES[mp3] element
1288
     * @param   int     $max_time_allowed
1289
     * @param   string  $url
1290
     * @return  boolean True on success, false on error
1291
     */
1292
    public function edit_item(
1293
        $id,
1294
        $parent,
1295
        $previous,
1296
        $title,
1297
        $description,
1298
        $prerequisites = '0',
1299
        $audio = [],
1300
        $max_time_allowed = 0,
1301
        $url = ''
1302
    ) {
1303
        $course_id = api_get_course_int_id();
1304
        $_course = api_get_course_info();
1305
1306
        if ($this->debug > 0) {
1307
            error_log('New LP - In learnpath::edit_item()', 0);
1308
        }
1309
        if (empty($max_time_allowed)) {
1310
            $max_time_allowed = 0;
1311
        }
1312
        if (empty($id) || ($id != strval(intval($id))) || empty($title)) {
1313
            return false;
1314
        }
1315
1316
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
1317
        $sql = "SELECT * FROM $tbl_lp_item 
1318
                WHERE iid = $id";
1319
        $res_select = Database::query($sql);
1320
        $row_select = Database::fetch_array($res_select);
1321
        $audio_update_sql = '';
1322
        if (is_array($audio) && !empty($audio['tmp_name']) && $audio['error'] === 0) {
1323
            // Create the audio folder if it does not exist yet.
1324
            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
1325
            if (!is_dir($filepath.'audio')) {
1326
                mkdir($filepath.'audio', api_get_permissions_for_new_directories());
1327
                $audio_id = add_document(
1328
                    $_course,
1329
                    '/audio',
1330
                    'folder',
1331
                    0,
1332
                    'audio'
1333
                );
1334
                api_item_property_update(
1335
                    $_course,
1336
                    TOOL_DOCUMENT,
1337
                    $audio_id,
1338
                    'FolderCreated',
1339
                    api_get_user_id(),
1340
                    null,
1341
                    null,
1342
                    null,
1343
                    null,
1344
                    api_get_session_id()
1345
                );
1346
                api_item_property_update(
1347
                    $_course,
1348
                    TOOL_DOCUMENT,
1349
                    $audio_id,
1350
                    'invisible',
1351
                    api_get_user_id(),
1352
                    null,
1353
                    null,
1354
                    null,
1355
                    null,
1356
                    api_get_session_id()
1357
                );
1358
            }
1359
1360
            // Upload file in documents.
1361
            $pi = pathinfo($audio['name']);
1362
            if ($pi['extension'] == 'mp3') {
1363
                $c_det = api_get_course_info($this->cc);
1364
                $bp = api_get_path(SYS_COURSE_PATH).$c_det['path'].'/document';
1365
                $path = handle_uploaded_document(
1366
                    $c_det,
1367
                    $audio,
1368
                    $bp,
1369
                    '/audio',
1370
                    api_get_user_id(),
1371
                    0,
1372
                    null,
1373
                    0,
1374
                    'rename',
1375
                    false,
1376
                    0
1377
                );
1378
                $path = substr($path, 7);
1379
                // Update reference in lp_item - audio path is the path from inside de document/audio/ dir.
1380
                $audio_update_sql = ", audio = '".Database::escape_string($path)."' ";
1381
            }
1382
        }
1383
1384
        $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
1385
        $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;
1386
1387
        // TODO: htmlspecialchars to be checked for encoding related problems.
1388
        if ($same_parent && $same_previous) {
1389
            // Only update title and description.
1390
            $sql = "UPDATE $tbl_lp_item
1391
                    SET title = '" . Database::escape_string($title)."',
1392
                        prerequisite = '".$prerequisites."',
1393
                        description = '".Database::escape_string($description)."'
1394
                        ".$audio_update_sql.",
1395
                        max_time_allowed = '".Database::escape_string($max_time_allowed)."'
1396
                    WHERE iid = $id";
1397
            Database::query($sql);
1398
        } else {
1399
            $old_parent = $row_select['parent_item_id'];
1400
            $old_previous = $row_select['previous_item_id'];
1401
            $old_next = $row_select['next_item_id'];
1402
            $old_order = $row_select['display_order'];
1403
            $old_prerequisite = $row_select['prerequisite'];
1404
            $old_max_time_allowed = $row_select['max_time_allowed'];
1405
1406
            /* BEGIN -- virtually remove the current item id */
1407
            /* for the next and previous item it is like the current item doesn't exist anymore */
1408
            if ($old_previous != 0) {
1409
                // Next
1410
                $sql = "UPDATE $tbl_lp_item
1411
                        SET next_item_id = $old_next
1412
                        WHERE iid = $old_previous";
1413
                Database::query($sql);
1414
            }
1415
1416
            if ($old_next != 0) {
1417
                // Previous
1418
                $sql = "UPDATE $tbl_lp_item
1419
                        SET previous_item_id = $old_previous
1420
                        WHERE iid = $old_next";
1421
                Database::query($sql);
1422
            }
1423
1424
            // display_order - 1 for every item with a display_order
1425
            // bigger then the display_order of the current item.
1426
            $sql = "UPDATE $tbl_lp_item
1427
                    SET display_order = display_order - 1
1428
                    WHERE
1429
                        c_id = $course_id AND
1430
                        display_order > $old_order AND
1431
                        lp_id = ".$this->lp_id." AND
1432
                        parent_item_id = $old_parent";
1433
            Database::query($sql);
1434
            /* END -- virtually remove the current item id */
1435
1436
            /* BEGIN -- update the current item id to his new location */
1437
            if ($previous == 0) {
1438
                // Select the data of the item that should come after the current item.
1439
                $sql = "SELECT id, display_order
1440
                        FROM $tbl_lp_item
1441
                        WHERE
1442
                            c_id = $course_id AND
1443
                            lp_id = ".$this->lp_id." AND
1444
                            parent_item_id = $parent AND
1445
                            previous_item_id = $previous";
1446
                $res_select_old = Database::query($sql);
1447
                $row_select_old = Database::fetch_array($res_select_old);
1448
1449
                // If the new parent didn't have children before.
1450
                if (Database::num_rows($res_select_old) == 0) {
1451
                    $new_next = 0;
1452
                    $new_order = 1;
1453
                } else {
1454
                    $new_next = $row_select_old['id'];
1455
                    $new_order = $row_select_old['display_order'];
1456
                }
1457
            } else {
1458
                // Select the data of the item that should come before the current item.
1459
                $sql = "SELECT next_item_id, display_order
1460
                        FROM $tbl_lp_item
1461
                        WHERE iid = $previous";
1462
                $res_select_old = Database::query($sql);
1463
                $row_select_old = Database::fetch_array($res_select_old);
1464
                $new_next = $row_select_old['next_item_id'];
1465
                $new_order = $row_select_old['display_order'] + 1;
1466
            }
1467
1468
            // TODO: htmlspecialchars to be checked for encoding related problems.
1469
            // Update the current item with the new data.
1470
            $sql = "UPDATE $tbl_lp_item
1471
                    SET
1472
                        title = '".Database::escape_string($title)."',
1473
                        description = '".Database::escape_string($description)."',
1474
                        parent_item_id = $parent,
1475
                        previous_item_id = $previous,
1476
                        next_item_id = $new_next,
1477
                        display_order = $new_order
1478
                        $audio_update_sql
1479
                    WHERE iid = $id";
1480
            Database::query($sql);
1481
1482
            if ($previous != 0) {
1483
                // Update the previous item's next_item_id.
1484
                $sql = "UPDATE $tbl_lp_item
1485
                        SET next_item_id = $id
1486
                        WHERE iid = $previous";
1487
                Database::query($sql);
1488
            }
1489
1490
            if ($new_next != 0) {
1491
                // Update the next item's previous_item_id.
1492
                $sql = "UPDATE $tbl_lp_item
1493
                        SET previous_item_id = $id
1494
                        WHERE iid = $new_next";
1495
                Database::query($sql);
1496
            }
1497
1498
            if ($old_prerequisite != $prerequisites) {
1499
                $sql = "UPDATE $tbl_lp_item
1500
                        SET prerequisite = '$prerequisites'
1501
                        WHERE iid = $id";
1502
                Database::query($sql);
1503
            }
1504
1505
            if ($old_max_time_allowed != $max_time_allowed) {
1506
                // update max time allowed
1507
                $sql = "UPDATE $tbl_lp_item
1508
                        SET max_time_allowed = $max_time_allowed
1509
                        WHERE iid = $id";
1510
                Database::query($sql);
1511
            }
1512
1513
            // Update all the items with the same or a bigger display_order than the current item.
1514
            $sql = "UPDATE $tbl_lp_item
1515
                    SET display_order = display_order + 1
1516
                    WHERE
1517
                       c_id = $course_id AND
1518
                       lp_id = ".$this->get_id()." AND
1519
                       iid <> $id AND
1520
                       parent_item_id = $parent AND
1521
                       display_order >= $new_order";
1522
            Database::query($sql);
1523
        }
1524
1525
        if ($row_select['item_type'] == 'link') {
1526
            $link = new Link();
1527
            $linkId = $row_select['path'];
1528
            $link->updateLink($linkId, $url);
1529
        }
1530
    }
1531
1532
    /**
1533
     * Updates an item's prereq in place
1534
     * @param    integer $id Element ID
1535
     * @param    string $prerequisite_id Prerequisite Element ID
1536
     * @param    int $mastery_score Prerequisite min score
1537
     * @param    int $max_score Prerequisite max score
1538
     *
1539
     * @return    boolean    True on success, false on error
1540
     */
1541
    public function edit_item_prereq(
1542
        $id,
1543
        $prerequisite_id,
1544
        $mastery_score = 0,
1545
        $max_score = 100
1546
    ) {
1547
        $course_id = api_get_course_int_id();
1548
        if ($this->debug > 0) {
1549
            error_log('New LP - In learnpath::edit_item_prereq('.$id.','.$prerequisite_id.','.$mastery_score.','.$max_score.')', 0);
1550
        }
1551
1552
        if (empty($id) || ($id != strval(intval($id))) || empty($prerequisite_id)) {
1553
            return false;
1554
        }
1555
1556
        $prerequisite_id = intval($prerequisite_id);
1557
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
1558
1559
        if (!is_numeric($mastery_score) || $mastery_score < 0) {
1560
            $mastery_score = 0;
1561
        }
1562
1563
        if (!is_numeric($max_score) || $max_score < 0) {
1564
            $max_score = 100;
1565
        }
1566
1567
        /*if ($mastery_score > $max_score) {
1568
            $max_score = $mastery_score;
1569
        }*/
1570
1571
        if (!is_numeric($prerequisite_id)) {
1572
            $prerequisite_id = 'NULL';
1573
        }
1574
1575
        $mastery_score = floatval($mastery_score);
1576
        $max_score = floatval($max_score);
1577
1578
        $sql = " UPDATE $tbl_lp_item
1579
                 SET
1580
                    prerequisite = $prerequisite_id ,
1581
                    prerequisite_min_score = $mastery_score ,
1582
                    prerequisite_max_score = $max_score
1583
                 WHERE iid = $id";
1584
        Database::query($sql);
1585
        // TODO: Update the item object (can be ignored for now because refreshed).
1586
        return true;
1587
    }
1588
1589
    /**
1590
     * Gets all the chapters belonging to the same parent as the item/chapter given
1591
     * Can also be called as abstract method
1592
     * @param    integer $id Item ID
1593
     * @return    array    A list of all the "brother items" (or an empty array on failure)
1594
     */
1595
    public function getSiblingDirectories($id)
1596
    {
1597
        $course_id = api_get_course_int_id();
1598
        if ($this->debug > 0) {
1599
            error_log('New LP - In learnpath::getSiblingDirectories()', 0);
1600
        }
1601
1602
        if (empty($id) || $id != strval(intval($id))) {
1603
            return [];
1604
        }
1605
1606
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1607
        $sql_parent = "SELECT * FROM $lp_item
1608
                       WHERE iid = $id AND item_type='dir'";
1609
        $res_parent = Database::query($sql_parent);
1610
        if (Database::num_rows($res_parent) > 0) {
1611
            $row_parent = Database::fetch_array($res_parent);
1612
            $parent = $row_parent['parent_item_id'];
1613
            $sql = "SELECT * FROM $lp_item
1614
                    WHERE
1615
                        parent_item_id = $parent AND
1616
                        iid = $id AND
1617
                        item_type='dir'
1618
                    ORDER BY display_order";
1619
            $res_bros = Database::query($sql);
1620
1621
            $list = [];
1622
            while ($row_bro = Database::fetch_array($res_bros)) {
1623
                $list[] = $row_bro;
1624
            }
1625
1626
            return $list;
1627
        }
1628
1629
        return [];
1630
    }
1631
1632
    /**
1633
     * Gets all the items belonging to the same parent as the item given
1634
     * Can also be called as abstract method
1635
     * @param    integer $id Item ID
1636
     * @return    array    A list of all the "brother items" (or an empty array on failure)
1637
     */
1638
    public function get_brother_items($id)
1639
    {
1640
        $course_id = api_get_course_int_id();
1641
        if ($this->debug > 0) {
1642
            error_log('New LP - In learnpath::get_brother_items('.$id.')', 0);
1643
        }
1644
1645
        if (empty($id) || $id != strval(intval($id))) {
1646
            return [];
1647
        }
1648
1649
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1650
        $sql_parent = "SELECT * FROM $lp_item 
1651
                       WHERE iid = $id";
1652
        $res_parent = Database::query($sql_parent);
1653
        if (Database::num_rows($res_parent) > 0) {
1654
            $row_parent = Database::fetch_array($res_parent);
1655
            $parent = $row_parent['parent_item_id'];
1656
            $sql = "SELECT * FROM $lp_item 
1657
                    WHERE c_id = $course_id AND parent_item_id = $parent
1658
                    ORDER BY display_order";
1659
            $res_bros = Database::query($sql);
1660
            $list = [];
1661
            while ($row_bro = Database::fetch_array($res_bros)) {
1662
                $list[] = $row_bro;
1663
            }
1664
            return $list;
1665
        }
1666
        return [];
1667
    }
1668
1669
    /**
1670
     * Get the specific prefix index terms of this learning path
1671
     * @param string $prefix
1672
     * @return  array Array of terms
1673
     */
1674
    public function get_common_index_terms_by_prefix($prefix)
1675
    {
1676
        require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1677
        $terms = get_specific_field_values_list_by_prefix(
1678
            $prefix,
1679
            $this->cc,
1680
            TOOL_LEARNPATH,
1681
            $this->lp_id
1682
        );
1683
        $prefix_terms = [];
1684
        if (!empty($terms)) {
1685
            foreach ($terms as $term) {
1686
                $prefix_terms[] = $term['value'];
1687
            }
1688
        }
1689
        return $prefix_terms;
1690
    }
1691
1692
    /**
1693
     * Gets the number of items currently completed
1694
     * @param bool $failedStatusException flag to determine the failed status is not considered progressed
1695
     * @return integer The number of items currently completed
1696
     */
1697
    public function get_complete_items_count($failedStatusException = false)
1698
    {
1699
        if ($this->debug > 0) {
1700
            error_log('New LP - In learnpath::get_complete_items_count()', 0);
1701
        }
1702
        $i = 0;
1703
        $completedStatusList = [
1704
            'completed',
1705
            'passed',
1706
            'succeeded',
1707
            'browsed'
1708
        ];
1709
1710
        if (!$failedStatusException) {
1711
            $completedStatusList[] = 'failed';
1712
        }
1713
1714
        foreach ($this->items as $id => $dummy) {
1715
            // Trying failed and browsed considered "progressed" as well.
1716
            if ($this->items[$id]->status_is($completedStatusList) &&
1717
                $this->items[$id]->get_type() != 'dir'
1718
            ) {
1719
                $i++;
1720
            }
1721
        }
1722
        return $i;
1723
    }
1724
1725
    /**
1726
     * Gets the current item ID
1727
     * @return    integer    The current learnpath item id
1728
     */
1729
    public function get_current_item_id()
1730
    {
1731
        $current = 0;
1732
        if ($this->debug > 0) {
1733
            error_log('New LP - In learnpath::get_current_item_id()', 0);
1734
        }
1735
        if (!empty($this->current)) {
1736
            $current = $this->current;
1737
        }
1738
        if ($this->debug > 2) {
1739
            error_log('New LP - In learnpath::get_current_item_id() - Returning '.$current, 0);
1740
        }
1741
        return $current;
1742
    }
1743
1744
    /**
1745
     * Force to get the first learnpath item id
1746
     * @return    integer    The current learnpath item id
1747
     */
1748
    public function get_first_item_id()
1749
    {
1750
        $current = 0;
1751
        if (is_array($this->ordered_items)) {
1752
            $current = $this->ordered_items[0];
1753
        }
1754
        return $current;
1755
    }
1756
1757
    /**
1758
     * Gets the total number of items available for viewing in this SCORM
1759
     * @return    integer    The total number of items
1760
     */
1761
    public function get_total_items_count()
1762
    {
1763
        if ($this->debug > 0) {
1764
            error_log('New LP - In learnpath::get_total_items_count()', 0);
1765
        }
1766
        return count($this->items);
1767
    }
1768
1769
    /**
1770
     * Gets the total number of items available for viewing in this SCORM but without chapters
1771
     * @return    integer    The total no-chapters number of items
1772
     */
1773
    public function getTotalItemsCountWithoutDirs()
1774
    {
1775
        if ($this->debug > 0) {
1776
            error_log('New LP - In learnpath::getTotalItemsCountWithoutDirs()', 0);
1777
        }
1778
        $total = 0;
1779
        $typeListNotToCount = self::getChapterTypes();
1780
        foreach ($this->items as $temp2) {
1781
            if (!in_array($temp2->get_type(), $typeListNotToCount)) {
1782
                $total++;
1783
            }
1784
        }
1785
        return $total;
1786
    }
1787
1788
    /**
1789
    *  Sets the first element URL.
1790
     */
1791
    public function first()
1792
    {
1793
        if ($this->debug > 0) {
1794
            error_log('New LP - In learnpath::first()', 0);
1795
            error_log('$this->last_item_seen '.$this->last_item_seen);
1796
        }
1797
1798
        // Test if the last_item_seen exists and is not a dir.
1799
        if (count($this->ordered_items) == 0) {
1800
            $this->index = 0;
1801
        }
1802
1803
        if ($this->debug > 0) {
1804
            if (isset($this->items[$this->last_item_seen])) {
1805
                $status = $this->items[$this->last_item_seen]->get_status();
1806
            }
1807
        }
1808
1809
        if (!empty($this->last_item_seen) &&
1810
            !empty($this->items[$this->last_item_seen]) &&
1811
            $this->items[$this->last_item_seen]->get_type() != 'dir'
1812
            //with this change (below) the LP will NOT go to the next item, it will take lp item we left
1813
            //&& !$this->items[$this->last_item_seen]->is_done()
1814
        ) {
1815
            if ($this->debug > 2) {
1816
                error_log('New LP - In learnpath::first() - Last item seen is '.$this->last_item_seen.' of type '.$this->items[$this->last_item_seen]->get_type(), 0);
1817
            }
1818
            $index = -1;
1819
            foreach ($this->ordered_items as $myindex => $item_id) {
1820
                if ($item_id == $this->last_item_seen) {
1821
                    $index = $myindex;
1822
                    break;
1823
                }
1824
            }
1825
            if ($index == -1) {
1826
                // Index hasn't changed, so item not found - panic (this shouldn't happen).
1827
                if ($this->debug > 2) {
1828
                    error_log('New LP - Last item ('.$this->last_item_seen.') was found in items but not in ordered_items, panic!', 0);
1829
                }
1830
                return false;
1831
            } else {
1832
                $this->last     = $this->last_item_seen;
1833
                $this->current  = $this->last_item_seen;
1834
                $this->index    = $index;
1835
            }
1836
        } else {
1837
            if ($this->debug > 2) {
1838
                error_log('New LP - In learnpath::first() - No last item seen', 0);
1839
            }
1840
            $index = 0;
1841
            // Loop through all ordered items and stop at the first item that is
1842
            // not a directory *and* that has not been completed yet.
1843
            while (!empty($this->ordered_items[$index]) &&
1844
                is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') &&
1845
                (
1846
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dir' ||
1847
                    $this->items[$this->ordered_items[$index]]->is_done() === true
1848
                ) && $index < $this->max_ordered_items) {
1849
                $index++;
1850
            }
1851
            $this->last = $this->current;
1852
            // current is
1853
            $this->current = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null;
1854
            $this->index = $index;
1855
            if ($this->debug > 2) {
1856
                error_log('$index '.$index);
1857
                error_log('New LP - In learnpath::first() - No last item seen. New last = '.$this->last.'('.$this->ordered_items[$index].')', 0);
1858
            }
1859
        }
1860
        if ($this->debug > 2) {
1861
            error_log('New LP - In learnpath::first() - First item is '.$this->get_current_item_id());
1862
        }
1863
    }
1864
1865
    /**
1866
     * Gets the information about an item in a format usable as JavaScript to update
1867
     * the JS API by just printing this content into the <head> section of the message frame
1868
     * @param   int $item_id
1869
     * @return  string
1870
     */
1871
    public function get_js_info($item_id = 0)
1872
    {
1873
        if ($this->debug > 0) {
1874
            error_log('New LP - In learnpath::get_js_info('.$item_id.')', 0);
1875
        }
1876
1877
        $info = '';
1878
        $item_id = intval($item_id);
1879
1880
        if (!empty($item_id) && is_object($this->items[$item_id])) {
1881
            //if item is defined, return values from DB
1882
            $oItem = $this->items[$item_id];
1883
            $info .= '<script language="javascript">';
1884
            $info .= "top.set_score(".$oItem->get_score().");\n";
1885
            $info .= "top.set_max(".$oItem->get_max().");\n";
1886
            $info .= "top.set_min(".$oItem->get_min().");\n";
1887
            $info .= "top.set_lesson_status('".$oItem->get_status()."');";
1888
            $info .= "top.set_session_time('".$oItem->get_scorm_time('js')."');";
1889
            $info .= "top.set_suspend_data('".$oItem->get_suspend_data()."');";
1890
            $info .= "top.set_saved_lesson_status('".$oItem->get_status()."');";
1891
            $info .= "top.set_flag_synchronized();";
1892
            $info .= '</script>';
1893
            if ($this->debug > 2) {
1894
                error_log('New LP - in learnpath::get_js_info('.$item_id.') - returning: '.$info, 0);
1895
            }
1896
            return $info;
1897
        } else {
1898
            // If item_id is empty, just update to default SCORM data.
1899
            $info .= '<script language="javascript">';
1900
            $info .= "top.set_score(".learnpathItem::get_score().");\n";
1901
            $info .= "top.set_max(".learnpathItem::get_max().");\n";
1902
            $info .= "top.set_min(".learnpathItem::get_min().");\n";
1903
            $info .= "top.set_lesson_status('".learnpathItem::get_status()."');";
1904
            $info .= "top.set_session_time('".learnpathItem::getScormTimeFromParameter('js')."');";
1905
            $info .= "top.set_suspend_data('".learnpathItem::get_suspend_data()."');";
1906
            $info .= "top.set_saved_lesson_status('".learnpathItem::get_status()."');";
1907
            $info .= "top.set_flag_synchronized();";
1908
            $info .= '</script>';
1909
            if ($this->debug > 2) {
1910
                error_log('New LP - in learnpath::get_js_info('.$item_id.') - returning: '.$info, 0);
1911
            }
1912
            return $info;
1913
        }
1914
    }
1915
1916
    /**
1917
     * Gets the js library from the database
1918
     * @return    string    The name of the javascript library to be used
1919
     */
1920
    public function get_js_lib()
1921
    {
1922
        $lib = '';
1923
        if (!empty($this->js_lib)) {
1924
            $lib = $this->js_lib;
1925
        }
1926
        return $lib;
1927
    }
1928
1929
    /**
1930
     * Gets the learnpath database ID
1931
     * @return	integer	Learnpath ID in the lp table
1932
     */
1933
    public function get_id()
1934
    {
1935
        if (!empty($this->lp_id)) {
1936
            return $this->lp_id;
1937
        } else {
1938
            return 0;
1939
        }
1940
    }
1941
1942
    /**
1943
     * Gets the last element URL.
1944
     * @return string URL to load into the viewer
1945
     */
1946
    public function get_last()
1947
    {
1948
        if ($this->debug > 0) {
1949
            error_log('New LP - In learnpath::get_last()', 0);
1950
        }
1951
        //This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices"
1952
        if (count($this->ordered_items) > 0) {
1953
            $this->index = count($this->ordered_items) - 1;
1954
            return $this->ordered_items[$this->index];
1955
        }
1956
1957
        return false;
1958
    }
1959
1960
    /**
1961
     * Gets the navigation bar for the learnpath display screen
1962
     * @return	string	The HTML string to use as a navigation bar
1963
     */
1964
    public function get_navigation_bar($idBar = null, $display = null)
1965
    {
1966
        if ($this->debug > 0) {
1967
            error_log('New LP - In learnpath::get_navigation_bar()', 0);
1968
        }
1969
        if (empty($idBar)) {
1970
            $idBar = 'control-top';
1971
        }
1972
1973
        $navbar = null;
1974
        $lp_id = $this->lp_id;
1975
        $mycurrentitemid = $this->get_current_item_id();
1976
1977
        $reportingText = get_lang('Reporting');
1978
        $previousText = get_lang('ScormPrevious');
1979
        $nextText = get_lang('ScormNext');
1980
        $fullScreenText = get_lang('ScormExitFullScreen');
1981
1982
        if ($this->mode == 'fullscreen') {
1983
            $navbar = '
1984
                  <span id="'.$idBar.'" class="buttons">
1985
                    <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="'.$reportingText.'" id="stats_link">
1986
                        <span class="fa fa-info"></span><span class="sr-only">' . $reportingText.'</span>
1987
                    </a>
1988
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'">
1989
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . $previousText.'</span>
1990
                    </a>
1991
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid.',\'next\');return false;" title="'.$nextText.'">
1992
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . $nextText.'</span>
1993
                    </a>
1994
                    <a class="icon-toolbar" id="view-embedded" href="lp_controller.php?action=mode&mode=embedded" target="_top" title="'.$fullScreenText.'">
1995
                        <span class="fa fa-columns"></span><span class="sr-only">' . $fullScreenText.'</span>
1996
                    </a>
1997
                  </span>';
1998
        } else {
1999
            $navbar = '
2000
            <span id="'.$idBar.'" class="buttons text-right">
2001
                <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="'.$reportingText.'" id="stats_link">
2002
                    <span class="fa fa-info"></span><span class="sr-only">' . $reportingText.'</span>
2003
                </a>
2004
                <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'">
2005
                    <span class="fa fa-chevron-left"></span><span class="sr-only">' . $previousText.'</span>
2006
                </a>
2007
                <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid.',\'next\');return false;" title="'.$nextText.'">
2008
                    <span class="fa fa-chevron-right"></span><span class="sr-only">' . $nextText.'</span>
2009
                </a>
2010
            </span>';
2011
        }
2012
2013
        return $navbar;
2014
    }
2015
2016
    /**
2017
     * Gets the next resource in queue (url).
2018
     * @return	string	URL to load into the viewer
2019
     */
2020
    public function get_next_index()
2021
    {
2022
        if ($this->debug > 0) {
2023
            error_log('New LP - In learnpath::get_next_index()', 0);
2024
        }
2025
        // TODO
2026
        $index = $this->index;
2027
        $index++;
2028
        if ($this->debug > 2) {
2029
            error_log('New LP - Now looking at ordered_items['.($index).'] - type is '.$this->items[$this->ordered_items[$index]]->type, 0);
2030
        }
2031
        while (
2032
            !empty($this->ordered_items[$index]) && ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') &&
2033
            $index < $this->max_ordered_items
2034
        ) {
2035
            $index++;
2036
            if ($index == $this->max_ordered_items) {
2037
                if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') {
2038
                    return $this->index;
2039
                } else {
2040
                    return $index;
2041
                }
2042
            }
2043
        }
2044
        if (empty($this->ordered_items[$index])) {
2045
            return $this->index;
2046
        }
2047
        if ($this->debug > 2) {
2048
            error_log('New LP - index is now '.$index, 0);
2049
        }
2050
        return $index;
2051
    }
2052
2053
    /**
2054
     * Gets item_id for the next element
2055
     * @return	integer	Next item (DB) ID
2056
     */
2057
    public function get_next_item_id()
2058
    {
2059
        if ($this->debug > 0) {
2060
            error_log('New LP - In learnpath::get_next_item_id()', 0);
2061
        }
2062
        $new_index = $this->get_next_index();
2063
        if (!empty($new_index)) {
2064
            if (isset($this->ordered_items[$new_index])) {
2065
                if ($this->debug > 2) {
2066
                    error_log('New LP - In learnpath::get_next_index() - Returning '.$this->ordered_items[$new_index], 0);
2067
                }
2068
                return $this->ordered_items[$new_index];
2069
            }
2070
        }
2071
        if ($this->debug > 2) {
2072
            error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
2073
        }
2074
        return 0;
2075
    }
2076
2077
    /**
2078
     * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
2079
     *
2080
     * Generally, the package provided is in the form of a zip file, so the function
2081
     * has been written to test a zip file. If not a zip, the function will return the
2082
     * default return value: ''
2083
     * @param	string	the path to the file
2084
     * @param	string 	the original name of the file
2085
     * @return	string	'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
2086
     */
2087
    public static function get_package_type($file_path, $file_name)
2088
    {
2089
        // Get name of the zip file without the extension.
2090
        $file_info = pathinfo($file_name);
2091
        $filename = $file_info['basename']; // Name including extension.
2092
        $extension = $file_info['extension']; // Extension only.
2093
        if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), [
2094
                'dll',
2095
                'exe'
2096
            ])) {
2097
            return 'oogie';
2098
        }
2099
        if (!empty($_POST['woogie']) && !in_array(strtolower($extension), [
2100
                'dll',
2101
                'exe'
2102
            ])) {
2103
            return 'woogie';
2104
        }
2105
2106
        $zipFile = new PclZip($file_path);
2107
        // Check the zip content (real size and file extension).
2108
        $zipContentArray = $zipFile->listContent();
2109
        $package_type = '';
2110
        $at_root = false;
2111
        $manifest = '';
2112
        $aicc_match_crs = 0;
2113
        $aicc_match_au = 0;
2114
        $aicc_match_des = 0;
2115
        $aicc_match_cst = 0;
2116
2117
        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
2118
        if (is_array($zipContentArray) && count($zipContentArray) > 0) {
2119
            foreach ($zipContentArray as $thisContent) {
2120
                if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2121
                    // New behaviour: Don't do anything. These files will be removed in scorm::import_package.
2122
                } elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
2123
                    $manifest = $thisContent['filename']; // Just the relative directory inside scorm/
2124
                    $package_type = 'scorm';
2125
                    break; // Exit the foreach loop.
2126
                } elseif (
2127
                    preg_match('/aicc\//i', $thisContent['filename']) ||
2128
                    in_array(
2129
                        strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)),
2130
                        ['crs', 'au', 'des', 'cst']
2131
                    )
2132
                ) {
2133
                    $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
2134
                    switch ($ext) {
2135
                        case 'crs':
2136
                            $aicc_match_crs = 1;
2137
                            break;
2138
                        case 'au':
2139
                            $aicc_match_au = 1;
2140
                            break;
2141
                        case 'des':
2142
                            $aicc_match_des = 1;
2143
                            break;
2144
                        case 'cst':
2145
                            $aicc_match_cst = 1;
2146
                            break;
2147
                        default:
2148
                            break;
2149
                    }
2150
                    //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
2151
                } else {
2152
                    $package_type = '';
2153
                }
2154
            }
2155
        }
2156
        if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) {
2157
            // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)).
2158
            $package_type = 'aicc';
2159
        }
2160
        return $package_type;
2161
    }
2162
2163
    /**
2164
     * Gets the previous resource in queue (url). Also initialises time values for this viewing
2165
     * @return string URL to load into the viewer
2166
     */
2167
    public function get_previous_index()
2168
    {
2169
        if ($this->debug > 0) {
2170
            error_log('New LP - In learnpath::get_previous_index()', 0);
2171
        }
2172
        $index = $this->index;
2173
        if (isset($this->ordered_items[$index - 1])) {
2174
            $index--;
2175
            while (isset($this->ordered_items[$index]) &&
2176
                ($this->items[$this->ordered_items[$index]]->get_type() == 'dir')
2177
            ) {
2178
                $index--;
2179
                if ($index < 0) {
2180
                    return $this->index;
2181
                }
2182
            }
2183
        } else {
2184
            if ($this->debug > 2) {
2185
                error_log('New LP - get_previous_index() - there was no previous index available, reusing '.$index, 0);
2186
            }
2187
            // There is no previous item.
2188
        }
2189
        return $index;
2190
    }
2191
2192
    /**
2193
     * Gets item_id for the next element
2194
     * @return	integer	Previous item (DB) ID
2195
     */
2196
    public function get_previous_item_id()
2197
    {
2198
        if ($this->debug > 0) {
2199
            error_log('New LP - In learnpath::get_previous_item_id()', 0);
2200
        }
2201
        $new_index = $this->get_previous_index();
2202
        return $this->ordered_items[$new_index];
2203
    }
2204
2205
    /**
2206
     * Gets the progress value from the progress_db attribute
2207
     * @return	integer	Current progress value
2208
     * @deprecated This method does not seem to be used as of 20170514
2209
     */
2210
    public function get_progress()
2211
    {
2212
        if ($this->debug > 0) {
2213
            error_log('New LP - In learnpath::get_progress()', 0);
2214
        }
2215
        if (!empty($this->progress_db)) {
2216
            return $this->progress_db;
2217
        }
2218
        return 0;
2219
    }
2220
2221
    /**
2222
     * Returns the HTML necessary to print a mediaplayer block inside a page
2223
     * @param int $lpItemId
2224
     * @param string $autostart
2225
     * @return string	The mediaplayer HTML
2226
     */
2227
    public function get_mediaplayer($lpItemId, $autostart = 'true')
2228
    {
2229
        $course_id = api_get_course_int_id();
2230
        $_course = api_get_course_info();
2231
        if (empty($_course)) {
2232
            return '';
2233
        }
2234
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
2235
        $tbl_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2236
2237
        // Getting all the information about the item.
2238
        $sql = "SELECT * FROM $tbl_lp_item as lpi
2239
                INNER JOIN $tbl_lp_item_view as lp_view
2240
                ON (lpi.iid = lp_view.lp_item_id)
2241
                WHERE
2242
                    lpi.iid = $lpItemId AND
2243
                    lp_view.c_id = $course_id";
2244
        $result = Database::query($sql);
2245
        $row = Database::fetch_assoc($result);
2246
        $output = '';
2247
2248
        if (!empty($row['audio'])) {
2249
            $list = $_SESSION['oLP']->get_toc();
2250
            $type_quiz = false;
2251
2252
            foreach ($list as $toc) {
2253
                if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type'] == 'quiz')) {
2254
                    $type_quiz = true;
2255
                }
2256
            }
2257
2258
            if ($type_quiz) {
2259
                if ($_SESSION['oLP']->prevent_reinit == 1) {
2260
                    $autostart_audio = $row['status'] === 'completed' ? 'false' : 'true';
2261
                } else {
2262
                    $autostart_audio = $autostart;
2263
                }
2264
            } else {
2265
                $autostart_audio = 'true';
2266
            }
2267
2268
            $courseInfo = api_get_course_info();
2269
            $audio = $row['audio'];
2270
2271
            $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio;
2272
            $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio.'?'.api_get_cidreq();
2273
2274
            if (!file_exists($file)) {
2275
                $lpPathInfo = $_SESSION['oLP']->generate_lp_folder(api_get_course_info());
2276
                $file = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio;
2277
                $url = api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio.'?'.api_get_cidreq();
2278
            }
2279
2280
            $player = Display::getMediaPlayer(
2281
                $file,
2282
                [
2283
                    'id' => 'lp_audio_media_player',
2284
                    'url' => $url,
2285
                    'autoplay' => $autostart_audio,
2286
                    'width' => '100%'
2287
                ]
2288
            );
2289
2290
            // The mp3 player.
2291
            $output  = '<div id="container">';
2292
            $output .= $player;
2293
            $output .= '</div>';
2294
        }
2295
2296
        return $output;
2297
    }
2298
2299
    /**
2300
     * @param int $studentId
2301
     * @param int $prerequisite
2302
     * @param array $courseInfo
2303
     * @param int $sessionId
2304
     *
2305
     * @return bool
2306
     *
2307
     */
2308
    public static function isBlockedByPrerequisite(
2309
        $studentId,
2310
        $prerequisite,
2311
        $courseInfo,
2312
        $sessionId
2313
    ) {
2314
        $allow = api_get_configuration_value('allow_teachers_to_access_blocked_lp_by_prerequisite');
2315
        if ($allow) {
2316
            if (api_is_allowed_to_edit() || api_is_platform_admin() || api_is_drh()) {
2317
                return false;
2318
            }
2319
        }
2320
2321
        $isBlocked = false;
2322
2323
        if (!empty($prerequisite)) {
2324
            $progress = self::getProgress(
2325
                $prerequisite,
2326
                $studentId,
2327
                $courseInfo['real_id'],
2328
                $sessionId
2329
            );
2330
            if ($progress < 100) {
2331
                $isBlocked = true;
2332
            }
2333
        }
2334
2335
        return $isBlocked;
2336
    }
2337
2338
    /**
2339
     * Checks if the learning path is visible for student after the progress
2340
     * of its prerequisite is completed, considering the time availability and
2341
     * the LP visibility.
2342
     * @param int $lp_id
2343
     * @param int $student_id
2344
     * @param string Course code (optional)
2345
     * @param int $sessionId
2346
     * @return	bool
2347
     */
2348
    public static function is_lp_visible_for_student(
2349
        $lp_id,
2350
        $student_id,
2351
        $courseCode = null,
2352
        $sessionId = 0
2353
    ) {
2354
        $courseInfo = api_get_course_info($courseCode);
2355
        $lp_id = (int) $lp_id;
2356
        $sessionId = (int) $sessionId;
2357
2358
        if (empty($courseInfo)) {
2359
            return false;
2360
        }
2361
2362
        if (empty($sessionId)) {
2363
            $sessionId = api_get_session_id();
2364
        }
2365
2366
        $itemInfo = api_get_item_property_info(
2367
            $courseInfo['real_id'],
2368
            TOOL_LEARNPATH,
2369
            $lp_id,
2370
            $sessionId
2371
        );
2372
2373
        // If the item was deleted.
2374
        if (isset($itemInfo['visibility']) && $itemInfo['visibility'] == 2) {
2375
            return false;
2376
        }
2377
2378
        // @todo remove this query and load the row info as a parameter
2379
        $tbl_learnpath = Database::get_course_table(TABLE_LP_MAIN);
2380
        // Get current prerequisite
2381
        $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on
2382
                FROM $tbl_learnpath
2383
                WHERE iid = $lp_id";
2384
        $rs  = Database::query($sql);
2385
        $now = time();
2386
        if (Database::num_rows($rs) > 0) {
2387
            $row = Database::fetch_array($rs, 'ASSOC');
2388
            $prerequisite = $row['prerequisite'];
2389
            $is_visible = true;
2390
2391
            $isBlocked = self::isBlockedByPrerequisite(
2392
                $student_id,
2393
                $prerequisite,
2394
                $courseInfo,
2395
                $sessionId
2396
            );
2397
2398
            if ($isBlocked) {
2399
                $is_visible = false;
2400
            }
2401
2402
            // Also check the time availability of the LP
2403
            if ($is_visible) {
2404
                // Adding visibility restrictions
2405
                if (!empty($row['publicated_on'])) {
2406
                    if ($now < api_strtotime($row['publicated_on'], 'UTC')) {
2407
                        $is_visible = false;
2408
                    }
2409
                }
2410
                // Blocking empty start times see BT#2800
2411
                global $_custom;
2412
                if (isset($_custom['lps_hidden_when_no_start_date']) &&
2413
                    $_custom['lps_hidden_when_no_start_date']
2414
                ) {
2415
                    if (empty($row['publicated_on'])) {
2416
                        $is_visible = false;
2417
                    }
2418
                }
2419
2420
                if (!empty($row['expired_on'])) {
2421
                    if ($now > api_strtotime($row['expired_on'], 'UTC')) {
2422
                        $is_visible = false;
2423
                    }
2424
                }
2425
            }
2426
2427
            // Check if the subscription users/group to a LP is ON
2428
            if (isset($row['subscribe_users']) && $row['subscribe_users'] == 1) {
2429
                // Try group
2430
                $is_visible = false;
2431
                // Checking only the user visibility
2432
                $userVisibility = api_get_item_visibility(
2433
                    $courseInfo,
2434
                    'learnpath',
2435
                    $row['id'],
2436
                    $sessionId,
2437
                    $student_id,
2438
                    'LearnpathSubscription'
2439
                );
2440
2441
                if ($userVisibility == 1) {
2442
                    $is_visible = true;
2443
                } else {
2444
                    $userGroups = GroupManager::getAllGroupPerUserSubscription($student_id);
2445
                    if (!empty($userGroups)) {
2446
                        foreach ($userGroups as $groupInfo) {
2447
                            $groupId = $groupInfo['iid'];
2448
                            $userVisibility = api_get_item_visibility(
2449
                                $courseInfo,
2450
                                'learnpath',
2451
                                $row['id'],
2452
                                $sessionId,
2453
                                null,
2454
                                'LearnpathSubscription',
2455
                                $groupId
2456
                            );
2457
2458
                            if ($userVisibility == 1) {
2459
                                $is_visible = true;
2460
                                break;
2461
                            }
2462
                        }
2463
                    }
2464
                }
2465
            }
2466
2467
            return $is_visible;
2468
        }
2469
2470
        return false;
2471
    }
2472
2473
    /**
2474
     * @param int $lpId
2475
     * @param int $userId
2476
     * @param int $courseId
2477
     * @param int $sessionId
2478
     * @return int
2479
     */
2480
    public static function getProgress($lpId, $userId, $courseId, $sessionId = 0)
2481
    {
2482
        $lpId = intval($lpId);
2483
        $userId = intval($userId);
2484
        $courseId = intval($courseId);
2485
        $sessionId = intval($sessionId);
2486
        $progress = 0;
2487
2488
        $sessionCondition = api_get_session_condition($sessionId);
2489
        $table = Database::get_course_table(TABLE_LP_VIEW);
2490
        $sql = "SELECT * FROM $table
2491
                WHERE
2492
                    c_id = $courseId AND
2493
                    lp_id = $lpId AND
2494
                    user_id = $userId $sessionCondition ";
2495
        $res = Database::query($sql);
2496
        if (Database::num_rows($res) > 0) {
2497
            $row = Database:: fetch_array($res);
2498
            $progress = $row['progress'];
2499
        }
2500
2501
        return (int) $progress;
2502
    }
2503
2504
    /**
2505
     * Displays a progress bar
2506
     * completed so far.
2507
     * @param	integer	$percentage Progress value to display
2508
     * @param	string	$text_add Text to display near the progress value
2509
     * @return	string	HTML string containing the progress bar
2510
     */
2511
    public static function get_progress_bar($percentage = -1, $text_add = '')
2512
    {
2513
        $text = $percentage.$text_add;
2514
        $output = '<div class="progress">
2515
            <div id="progress_bar_value" 
2516
                class="progress-bar progress-bar-success" role="progressbar" 
2517
                aria-valuenow="' .$percentage.'" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';">
2518
            '. $text.'
2519
            </div>
2520
        </div>';
2521
2522
        return $output;
2523
    }
2524
2525
    /**
2526
     * @param string $mode can be '%' or 'abs'
2527
     * otherwise this value will be used $this->progress_bar_mode
2528
     * @return string
2529
     */
2530
    public function getProgressBar($mode = null)
2531
    {
2532
        list($percentage, $text_add) = $this->get_progress_bar_text($mode);
2533
        return self::get_progress_bar($percentage, $text_add);
2534
    }
2535
2536
    /**
2537
     * Gets the progress bar info to display inside the progress bar.
2538
     * Also used by scorm_api.php
2539
     * @param	string	$mode Mode of display (can be '%' or 'abs').abs means
2540
     * we display a number of completed elements per total elements
2541
     * @param	integer	$add Additional steps to fake as completed
2542
     * @return array Percentage or number and symbol (% or /xx)
2543
     */
2544
    public function get_progress_bar_text($mode = '', $add = 0)
2545
    {
2546
        if ($this->debug > 0) {
2547
            error_log('New LP - In learnpath::get_progress_bar_text()', 0);
2548
        }
2549
        if (empty($mode)) {
2550
            $mode = $this->progress_bar_mode;
2551
        }
2552
        $total_items = $this->getTotalItemsCountWithoutDirs();
2553
        if ($this->debug > 2) {
2554
            error_log('New LP - Total items available in this learnpath: '.$total_items, 0);
2555
        }
2556
        $completeItems = $this->get_complete_items_count();
2557
        if ($this->debug > 2) {
2558
            error_log('New LP - Items completed so far: '.$completeItems, 0);
2559
        }
2560
        if ($add != 0) {
2561
            $completeItems += $add;
2562
            if ($this->debug > 2) {
2563
                error_log('New LP - Items completed so far (+modifier): '.$completeItems, 0);
2564
            }
2565
        }
2566
        $text = '';
2567
        if ($completeItems > $total_items) {
2568
            $completeItems = $total_items;
2569
        }
2570
        $percentage = 0;
2571
        if ($mode == '%') {
2572
            if ($total_items > 0) {
2573
                $percentage = ((float) $completeItems / (float) $total_items) * 100;
2574
            } else {
2575
                $percentage = 0;
2576
            }
2577
            $percentage = number_format($percentage, 0);
2578
            $text = '%';
2579
        } elseif ($mode == 'abs') {
2580
            $percentage = $completeItems;
2581
            $text = '/'.$total_items;
2582
        }
2583
2584
        return [
2585
            $percentage,
2586
            $text
2587
        ];
2588
    }
2589
2590
    /**
2591
     * Gets the progress bar mode
2592
     * @return	string	The progress bar mode attribute
2593
     */
2594
    public function get_progress_bar_mode()
2595
    {
2596
        if ($this->debug > 0) {
2597
            error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
2598
        }
2599
        if (!empty($this->progress_bar_mode)) {
2600
            return $this->progress_bar_mode;
2601
        } else {
2602
            return '%';
2603
        }
2604
    }
2605
2606
    /**
2607
     * Gets the learnpath proximity (remote or local)
2608
     * @return	string	Learnpath proximity
2609
     */
2610
    public function get_proximity()
2611
    {
2612
        if ($this->debug > 0) {
2613
            error_log('New LP - In learnpath::get_proximity()', 0);
2614
        }
2615
        if (!empty($this->proximity)) {
2616
            return $this->proximity;
2617
        } else {
2618
            return '';
2619
        }
2620
    }
2621
2622
    /**
2623
     * Gets the learnpath theme (remote or local)
2624
     * @return	string	Learnpath theme
2625
     */
2626
    public function get_theme()
2627
    {
2628
        if ($this->debug > 0) {
2629
            error_log('New LP - In learnpath::get_theme()', 0);
2630
        }
2631
        if (!empty($this->theme)) {
2632
            return $this->theme;
2633
        } else {
2634
            return '';
2635
        }
2636
    }
2637
2638
    /**
2639
     * Gets the learnpath session id
2640
     * @return int
2641
     */
2642
    public function get_lp_session_id()
2643
    {
2644
        if ($this->debug > 0) {
2645
            error_log('New LP - In learnpath::get_lp_session_id()', 0);
2646
        }
2647
        if (!empty($this->lp_session_id)) {
2648
            return (int) $this->lp_session_id;
2649
        } else {
2650
            return 0;
2651
        }
2652
    }
2653
2654
    /**
2655
     * Gets the learnpath image
2656
     * @return	string	Web URL of the LP image
2657
     */
2658
    public function get_preview_image()
2659
    {
2660
        if ($this->debug > 0) {
2661
            error_log('New LP - In learnpath::get_preview_image()', 0);
2662
        }
2663
        if (!empty($this->preview_image)) {
2664
            return $this->preview_image;
2665
        } else {
2666
            return '';
2667
        }
2668
    }
2669
2670
    /**
2671
     * @param string $size
2672
     * @param string $path_type
2673
     * @return bool|string
2674
     */
2675
    public function get_preview_image_path($size = null, $path_type = 'web')
2676
    {
2677
        $preview_image = $this->get_preview_image();
2678
        if (isset($preview_image) && !empty($preview_image)) {
2679
            $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2680
            $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2681
2682
            if (isset($size)) {
2683
                $info = pathinfo($preview_image);
2684
                $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension'];
2685
2686
                if (file_exists($image_sys_path.$image_custom_size)) {
2687
                    if ($path_type == 'web') {
2688
                        return $image_path.$image_custom_size;
2689
                    } else {
2690
                        return $image_sys_path.$image_custom_size;
2691
                    }
2692
                }
2693
            } else {
2694
                if ($path_type == 'web') {
2695
                    return $image_path.$preview_image;
2696
                } else {
2697
                    return $image_sys_path.$preview_image;
2698
                }
2699
            }
2700
        }
2701
2702
        return false;
2703
    }
2704
2705
    /**
2706
     * Gets the learnpath author
2707
     * @return string	LP's author
2708
     */
2709
    public function get_author()
2710
    {
2711
        if ($this->debug > 0) {
2712
            error_log('New LP - In learnpath::get_author()', 0);
2713
        }
2714
        if (!empty($this->author)) {
2715
            return $this->author;
2716
        } else {
2717
            return '';
2718
        }
2719
    }
2720
2721
    /**
2722
     * Gets hide table of contents
2723
     * @return int
2724
     */
2725
    public function getHideTableOfContents()
2726
    {
2727
        return (int) $this->hide_toc_frame;
2728
    }
2729
2730
    /**
2731
     * Generate a new prerequisites string for a given item. If this item was a sco and
2732
     * its prerequisites were strings (instead of IDs), then transform those strings into
2733
     * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
2734
     * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
2735
     * same rule as the scorm_export() method
2736
     * @param	integer		Item ID
2737
     * @return	string		Prerequisites string ready for the export as SCORM
2738
     */
2739
    public function get_scorm_prereq_string($item_id)
2740
    {
2741
        if ($this->debug > 0) {
2742
            error_log('New LP - In learnpath::get_scorm_prereq_string()');
2743
        }
2744
        if (!is_object($this->items[$item_id])) {
2745
            return false;
2746
        }
2747
        /** @var learnpathItem $oItem */
2748
        $oItem = $this->items[$item_id];
2749
        $prereq = $oItem->get_prereq_string();
2750
2751
        if (empty($prereq)) {
2752
            return '';
2753
        }
2754
        if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) {
2755
            // If the prerequisite is a simple integer ID and this ID exists as an item ID,
2756
            // then simply return it (with the ITEM_ prefix).
2757
            //return 'ITEM_' . $prereq;
2758
            return $this->items[$prereq]->ref;
2759
        } else {
2760
            if (isset($this->refs_list[$prereq])) {
2761
                // It's a simple string item from which the ID can be found in the refs list,
2762
                // so we can transform it directly to an ID for export.
2763
                return $this->items[$this->refs_list[$prereq]]->ref;
2764
            } elseif (isset($this->refs_list['ITEM_'.$prereq])) {
2765
                return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref;
2766
            } else {
2767
                // The last case, if it's a complex form, then find all the IDs (SCORM strings)
2768
                // and replace them, one by one, by the internal IDs (chamilo db)
2769
                // TODO: Modify the '*' replacement to replace the multiplier in front of it
2770
                // by a space as well.
2771
                $find = [
2772
                    '&',
2773
                    '|',
2774
                    '~',
2775
                    '=',
2776
                    '<>',
2777
                    '{',
2778
                    '}',
2779
                    '*',
2780
                    '(',
2781
                    ')'
2782
                ];
2783
                $replace = [
2784
                    ' ',
2785
                    ' ',
2786
                    ' ',
2787
                    ' ',
2788
                    ' ',
2789
                    ' ',
2790
                    ' ',
2791
                    ' ',
2792
                    ' ',
2793
                    ' '
2794
                ];
2795
                $prereq_mod = str_replace($find, $replace, $prereq);
2796
                $ids = explode(' ', $prereq_mod);
2797
                foreach ($ids as $id) {
2798
                    $id = trim($id);
2799
                    if (isset($this->refs_list[$id])) {
2800
                        $prereq = preg_replace(
2801
                            '/[^a-zA-Z_0-9]('.$id.')[^a-zA-Z_0-9]/',
2802
                            'ITEM_'.$this->refs_list[$id],
2803
                            $prereq
2804
                        );
2805
                    }
2806
                }
2807
2808
                return $prereq;
2809
            }
2810
        }
2811
    }
2812
2813
    /**
2814
     * Returns the XML DOM document's node
2815
     * @param	resource	Reference to a list of objects to search for the given ITEM_*
2816
     * @param	string		The identifier to look for
2817
     * @return	mixed		The reference to the element found with that identifier. False if not found
2818
     */
2819
    public function get_scorm_xml_node(& $children, $id)
2820
    {
2821
        for ($i = 0; $i < $children->length; $i++) {
2822
            $item_temp = $children->item($i);
2823
            if ($item_temp->nodeName == 'item') {
2824
                if ($item_temp->getAttribute('identifier') == $id) {
2825
                    return $item_temp;
2826
                }
2827
            }
2828
            $subchildren = $item_temp->childNodes;
2829
            if ($subchildren && $subchildren->length > 0) {
2830
                $val = $this->get_scorm_xml_node($subchildren, $id);
2831
                if (is_object($val)) {
2832
                    return $val;
2833
                }
2834
            }
2835
        }
2836
2837
        return false;
2838
    }
2839
2840
    /**
2841
     * Gets the status list for all LP's items
2842
     * @return	array	Array of [index] => [item ID => current status]
2843
     */
2844
    public function get_items_status_list()
2845
    {
2846
        if ($this->debug > 0) {
2847
            error_log('New LP - In learnpath::get_items_status_list()', 0);
2848
        }
2849
        $list = [];
2850
        foreach ($this->ordered_items as $item_id) {
2851
            $list[] = [
2852
                $item_id => $this->items[$item_id]->get_status()
2853
            ];
2854
        }
2855
        return $list;
2856
    }
2857
2858
    /**
2859
     * Return the number of interactions for the given learnpath Item View ID.
2860
     * This method can be used as static.
2861
     * @param	integer	Item View ID
2862
     * @param   integer course id
2863
     * @return	integer	Number of interactions
2864
     */
2865
    public static function get_interactions_count_from_db($lp_iv_id, $course_id)
2866
    {
2867
        $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2868
        $lp_iv_id = intval($lp_iv_id);
2869
        $course_id = intval($course_id);
2870
2871
        $sql = "SELECT count(*) FROM $table
2872
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2873
        $res = Database::query($sql);
2874
        $num = 0;
2875
        if (Database::num_rows($res)) {
2876
            $row = Database::fetch_array($res);
2877
            $num = $row[0];
2878
        }
2879
        return $num;
2880
    }
2881
2882
    /**
2883
     * Return the interactions as an array for the given lp_iv_id.
2884
     * This method can be used as static.
2885
     * @param	integer	Learnpath Item View ID
2886
     * @return	array
2887
     * @todo 	Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
2888
     */
2889
    public static function get_iv_interactions_array($lp_iv_id)
2890
    {
2891
        $course_id = api_get_course_int_id();
2892
        $list = [];
2893
        $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2894
2895
        if (empty($lp_iv_id)) {
2896
            return [];
2897
        }
2898
2899
        $sql = "SELECT * FROM $table
2900
                WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id
2901
                ORDER BY order_id ASC";
2902
        $res = Database::query($sql);
2903
        $num = Database::num_rows($res);
2904
        if ($num > 0) {
2905
            $list[] = [
2906
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2907
                'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES),
2908
                'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES),
2909
                'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES),
2910
                'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES),
2911
                'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES),
2912
                'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES),
2913
                'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES)
2914
            ];
2915
            while ($row = Database::fetch_array($res)) {
2916
                $list[] = [
2917
                    'order_id' => ($row['order_id'] + 1),
2918
                    'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
2919
                    'type' => $row['interaction_type'],
2920
                    'time' => $row['completion_time'],
2921
                    //'correct_responses' => $row['correct_responses'],
2922
                    'correct_responses' => '', // Hide correct responses from students.
2923
                    'student_response' => $row['student_response'],
2924
                    'result' => $row['result'],
2925
                    'latency' => $row['latency']
2926
                ];
2927
            }
2928
        }
2929
2930
        return $list;
2931
    }
2932
2933
    /**
2934
     * Return the number of objectives for the given learnpath Item View ID.
2935
     * This method can be used as static.
2936
     * @param	integer	Item View ID
2937
     * @return	integer	Number of objectives
2938
     */
2939
    public static function get_objectives_count_from_db($lp_iv_id, $course_id)
2940
    {
2941
        $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2942
        $course_id = intval($course_id);
2943
        $lp_iv_id = intval($lp_iv_id);
2944
        $sql = "SELECT count(*) FROM $table
2945
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2946
        //@todo seems that this always returns 0
2947
        $res = Database::query($sql);
2948
        $num = 0;
2949
        if (Database::num_rows($res)) {
2950
            $row = Database::fetch_array($res);
2951
            $num = $row[0];
2952
        }
2953
2954
        return $num;
2955
    }
2956
2957
    /**
2958
     * Return the objectives as an array for the given lp_iv_id.
2959
     * This method can be used as static.
2960
     * @param	integer	Learnpath Item View ID
2961
     * @return	array
2962
     * @todo 	Translate labels
2963
     */
2964
    public static function get_iv_objectives_array($lp_iv_id = 0)
2965
    {
2966
        $course_id = api_get_course_int_id();
2967
        $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2968
        $sql = "SELECT * FROM $table
2969
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id
2970
                ORDER BY order_id ASC";
2971
        $res = Database::query($sql);
2972
        $num = Database::num_rows($res);
2973
        $list = [];
2974
        if ($num > 0) {
2975
            $list[] = [
2976
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2977
                'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES),
2978
                'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES),
2979
                'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES),
2980
                'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES),
2981
                'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES)
2982
            ];
2983
            while ($row = Database::fetch_array($res)) {
2984
                $list[] = [
2985
                    'order_id' => ($row['order_id'] + 1),
2986
                    'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F
2987
                    'score_raw' => $row['score_raw'],
2988
                    'score_max' => $row['score_max'],
2989
                    'score_min' => $row['score_min'],
2990
                    'status' => $row['status']
2991
                ];
2992
            }
2993
        }
2994
2995
        return $list;
2996
    }
2997
2998
    /**
2999
     * Generate and return the table of contents for this learnpath. The (flat) table returned can be
3000
     * used by get_html_toc() to be ready to display
3001
     * @return	array	TOC as a table with 4 elements per row: title, link, status and level
3002
     */
3003
    public function get_toc()
3004
    {
3005
        if ($this->debug > 0) {
3006
            error_log('learnpath::get_toc()', 0);
3007
        }
3008
        $toc = [];
3009
        foreach ($this->ordered_items as $item_id) {
3010
            if ($this->debug > 2) {
3011
                error_log('learnpath::get_toc(): getting info for item '.$item_id, 0);
3012
            }
3013
            // TODO: Change this link generation and use new function instead.
3014
            $toc[] = [
3015
                'id' => $item_id,
3016
                'title' => $this->items[$item_id]->get_title(),
3017
                'status' => $this->items[$item_id]->get_status(),
3018
                'level' => $this->items[$item_id]->get_level(),
3019
                'type' => $this->items[$item_id]->get_type(),
3020
                'description' => $this->items[$item_id]->get_description(),
3021
                'path' => $this->items[$item_id]->get_path(),
3022
                'parent' => $this->items[$item_id]->get_parent(),
3023
            ];
3024
        }
3025
        if ($this->debug > 2) {
3026
            error_log('New LP - In learnpath::get_toc() - TOC array: '.print_r($toc, true), 0);
3027
        }
3028
        return $toc;
3029
    }
3030
3031
    /**
3032
     * Generate and return the table of contents for this learnpath. The JS
3033
     * table returned is used inside of scorm_api.php
3034
     * @param string $varname
3035
     * @return  string  A JS array vairiable construction
3036
     */
3037
    public function get_items_details_as_js($varname = 'olms.lms_item_types')
3038
    {
3039
        if ($this->debug > 0) {
3040
            error_log('New LP - In learnpath::get_items_details_as_js()', 0);
3041
        }
3042
        $toc = $varname.' = new Array();';
3043
        foreach ($this->ordered_items as $item_id) {
3044
            $toc .= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';";
3045
        }
3046
        if ($this->debug > 2) {
3047
            error_log('New LP - In learnpath::get_items_details_as_js() - TOC array: '.print_r($toc, true), 0);
3048
        }
3049
        return $toc;
3050
    }
3051
3052
    /**
3053
     * Gets the learning path type
3054
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3055
     * @return	mixed		Type ID or name, depending on the parameter
3056
     */
3057
    public function get_type($get_name = false)
3058
    {
3059
        $res = false;
3060
        if ($this->debug > 0) {
3061
            error_log('New LP - In learnpath::get_type()', 0);
3062
        }
3063
        if (!empty($this->type) && (!$get_name)) {
3064
            $res = $this->type;
3065
        }
3066
        if ($this->debug > 2) {
3067
            error_log('New LP - In learnpath::get_type() - Returning '.($res ? $res : 'false'), 0);
3068
        }
3069
        return $res;
3070
    }
3071
3072
    /**
3073
     * Gets the learning path type as static method
3074
     * @param	int $lp_id
3075
     * @return	mixed		Type ID or name, depending on the parameter
3076
     */
3077
    public static function get_type_static($lp_id = 0)
3078
    {
3079
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
3080
        $lp_id = intval($lp_id);
3081
        $sql = "SELECT lp_type FROM $tbl_lp
3082
                WHERE iid = $lp_id";
3083
        $res = Database::query($sql);
3084
        if ($res === false) {
3085
            return null;
3086
        }
3087
        if (Database::num_rows($res) <= 0) {
3088
            return null;
3089
        }
3090
        $row = Database::fetch_array($res);
3091
        return $row['lp_type'];
3092
    }
3093
3094
    /**
3095
     * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
3096
     * This method can be used as abstract and is recursive
3097
     * @param	integer	Learnpath ID
3098
     * @param	integer	Parent ID of the items to look for
3099
     * @return	array	Ordered list of item IDs (empty array on error)
3100
     */
3101
    public static function get_flat_ordered_items_list($lp, $parent = 0, $course_id = 0)
3102
    {
3103
        if (empty($course_id)) {
3104
            $course_id = api_get_course_int_id();
3105
        } else {
3106
            $course_id = intval($course_id);
3107
        }
3108
        $list = [];
3109
3110
        if (empty($lp)) {
3111
            return $list;
3112
        }
3113
3114
        $lp = intval($lp);
3115
        $parent = intval($parent);
3116
3117
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
3118
        $sql = "SELECT iid FROM $tbl_lp_item
3119
                WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent
3120
                ORDER BY display_order";
3121
3122
        $res = Database::query($sql);
3123
        while ($row = Database::fetch_array($res)) {
3124
            $sublist = self::get_flat_ordered_items_list(
3125
                $lp,
3126
                $row['iid'],
3127
                $course_id
3128
            );
3129
            $list[] = $row['iid'];
3130
            foreach ($sublist as $item) {
3131
                $list[] = $item;
3132
            }
3133
        }
3134
        return $list;
3135
    }
3136
3137
    /**
3138
     * @return array
3139
     */
3140
    public static function getChapterTypes()
3141
    {
3142
        return [
3143
            'dir'
3144
        ];
3145
    }
3146
3147
    /**
3148
     * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display
3149
     * @return	string	HTML TOC ready to display
3150
     */
3151
    public function getParentToc($tree)
3152
    {
3153
        if ($this->debug > 0) {
3154
            error_log('In learnpath::get_html_toc()', 0);
3155
        }
3156
        if (empty($tree)) {
3157
            $tree = $this->get_toc();
3158
        }
3159
        $dirTypes = self::getChapterTypes();
3160
        $myCurrentId = $this->get_current_item_id();
3161
        $listParent = [];
3162
        $listChildren = [];
3163
        $listNotParent = [];
3164
        $list = [];
3165
        foreach ($tree as $subtree) {
3166
            if (in_array($subtree['type'], $dirTypes)) {
3167
                $listChildren = $this->getChildrenToc($tree, $subtree['id']);
3168
                $subtree['children'] = $listChildren;
3169
                if (!empty($subtree['children'])) {
3170
                    foreach ($subtree['children'] as $subItem) {
3171
                        if ($subItem['id'] == $this->current) {
3172
                            $subtree['parent_current'] = 'in';
3173
                            $subtree['current'] = 'on';
3174
                        }
3175
                    }
3176
                }
3177
                $listParent[] =  $subtree;
3178
            }
3179
            if (!in_array($subtree['type'], $dirTypes) && $subtree['parent'] == null) {
3180
                $classStatus = [
3181
                    'not attempted' => 'scorm_not_attempted',
3182
                    'incomplete' => 'scorm_not_attempted',
3183
                    'failed' => 'scorm_failed',
3184
                    'completed' => 'scorm_completed',
3185
                    'passed' => 'scorm_completed',
3186
                    'succeeded' => 'scorm_completed',
3187
                    'browsed' => 'scorm_completed',
3188
                ];
3189
3190
                if (isset($classStatus[$subtree['status']])) {
3191
                    $cssStatus = $classStatus[$subtree['status']];
3192
                }
3193
3194
                $title = Security::remove_XSS($subtree['title']);
3195
                unset($subtree['title']);
3196
3197
                if (empty($title)) {
3198
                    $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']);
3199
                }
3200
                $classStyle = null;
3201
                if ($subtree['id'] == $this->current) {
3202
                    $classStyle = 'scorm_item_normal '. $classStyle . 'scorm_highlight';
3203
                } elseif (!in_array($subtree['type'], $dirTypes)) {
3204
                    $classStyle = 'scorm_item_normal '. $classStyle . ' ';
3205
                }
3206
                $subtree['title'] = $title;
3207
                $subtree['class'] = $cssStatus . ' ' .$classStyle;
3208
                $subtree['url'] = $this->get_link('http', $subtree['id'], $tree);
3209
                $subtree['current_id'] = $myCurrentId;
3210
                $listNotParent[] = $subtree;
3211
            }
3212
        }
3213
3214
        $list['are_parents'] = $listParent;
3215
        $list['not_parents'] = $listNotParent;
3216
3217
        return $list;
3218
    }
3219
3220
    /**
3221
     * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display
3222
     * @return	string	HTML TOC ready to display
3223
     */
3224
    public function getChildrenToc($tree, $id, $parent = true)
3225
    {
3226
        if ($this->debug > 0) {
3227
            error_log('In learnpath::get_html_toc()', 0);
3228
        }
3229
        if (empty($tree)) {
3230
            $tree = $this->get_toc();
3231
        }
3232
3233
        $dirTypes = self::getChapterTypes();
3234
        $mycurrentitemid = $this->get_current_item_id();
3235
        $list = [];
3236
        $classStatus = [
3237
            'not attempted' => 'scorm_not_attempted',
3238
            'incomplete' => 'scorm_not_attempted',
3239
            'failed' => 'scorm_failed',
3240
            'completed' => 'scorm_completed',
3241
            'passed' => 'scorm_completed',
3242
            'succeeded' => 'scorm_completed',
3243
            'browsed' => 'scorm_completed',
3244
        ];
3245
3246
        foreach ($tree as $subtree) {
3247
            $subtree['tree'] = null;
3248
3249
            if (!in_array($subtree['type'], $dirTypes) && $subtree['parent'] == $id) {
3250
                if ($subtree['id'] == $this->current) {
3251
                    $subtree['current'] = 'active';
3252
                } else {
3253
                    $subtree['current'] = null;
3254
                }
3255
                if (isset($classStatus[$subtree['status']])) {
3256
                    $cssStatus = $classStatus[$subtree['status']];
3257
                }
3258
3259
                $title = Security::remove_XSS($subtree['title']);
3260
                unset($subtree['title']);
3261
                if (empty($title)) {
3262
                    $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']);
3263
                }
3264
3265
                $classStyle = null;
3266
                if ($subtree['id'] == $this->current) {
3267
                    $classStyle = 'scorm_item_normal '. $classStyle . 'scorm_highlight';
3268
                } elseif (!in_array($subtree['type'], $dirTypes)) {
3269
                    $classStyle = 'scorm_item_normal '. $classStyle . ' ';
3270
                }
3271
3272
                if (in_array($subtree['type'], $dirTypes)) {
3273
                    $subtree['title'] = stripslashes($title);
3274
                } else {
3275
                    $subtree['title'] = $title;
3276
                    $subtree['class'] = $cssStatus . ' ' .$classStyle;
3277
                    $subtree['url'] = $this->get_link('http', $subtree['id'], $tree);
3278
                    $subtree['current_id'] = $mycurrentitemid;
3279
                }
3280
                $list[] =  $subtree;
3281
            }
3282
        }
3283
3284
        return $list;
3285
    }
3286
3287
    /**
3288
     * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
3289
     * @param array $toc_list
3290
     * @return	string	HTML TOC ready to display
3291
     */
3292
    public function getListArrayToc($toc_list = [])
3293
    {
3294
        if ($this->debug > 0) {
3295
            error_log('In learnpath::get_html_toc()', 0);
3296
        }
3297
        if (empty($toc_list)) {
3298
            $toc_list = $this->get_toc();
3299
        }
3300
        // Temporary variables.
3301
        $mycurrentitemid = $this->get_current_item_id();
3302
        $list = [];
3303
        $arrayList = [];
3304
        $classStatus = [
3305
            'not attempted' => 'scorm_not_attempted',
3306
            'incomplete' => 'scorm_not_attempted',
3307
            'failed' => 'scorm_failed',
3308
            'completed' => 'scorm_completed',
3309
            'passed' => 'scorm_completed',
3310
            'succeeded' => 'scorm_completed',
3311
            'browsed' => 'scorm_completed',
3312
        ];
3313
3314
        foreach ($toc_list as $item) {
3315
            $list['id'] = $item['id'];
3316
            $list['status'] = $item['status'];
3317
            $cssStatus = null;
3318
3319
            if (isset($classStatus[$item['status']])) {
3320
                $cssStatus = $classStatus[$item['status']];
3321
            }
3322
3323
            $classStyle = ' ';
3324
            $dirTypes = self::getChapterTypes();
3325
3326
            if (in_array($item['type'], $dirTypes)) {
3327
                $classStyle = 'scorm_item_section ';
3328
            }
3329
            if ($item['id'] == $this->current) {
3330
                $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight';
3331
            } elseif (!in_array($item['type'], $dirTypes)) {
3332
                $classStyle = 'scorm_item_normal '.$classStyle.' ';
3333
            }
3334
            $title = $item['title'];
3335
            if (empty($title)) {
3336
                $title = self::rl_get_resource_name(
3337
                    api_get_course_id(),
3338
                    $this->get_id(),
3339
                    $item['id']
3340
                );
3341
            }
3342
            $title = Security::remove_XSS($item['title']);
3343
3344
            if (empty($item['description'])) {
3345
                $list['description'] = $title;
3346
            } else {
3347
                $list['description'] = $item['description'];
3348
            }
3349
3350
            $list['class'] = $classStyle.' '.$cssStatus;
3351
            $list['level'] = $item['level'];
3352
            $list['type'] = $item['type'];
3353
3354
            if (in_array($item['type'], $dirTypes)) {
3355
                $list['css_level'] = 'level_'.$item['level'];
3356
            } else {
3357
                $list['css_level'] = 'level_'.$item['level'].' scorm_type_'.learnpath::format_scorm_type_item($item['type']);
3358
            }
3359
3360
            if (in_array($item['type'], $dirTypes)) {
3361
                $list['title'] = stripslashes($title);
3362
            } else {
3363
                $list['title'] = stripslashes($title);
3364
                $list['url'] = $this->get_link('http', $item['id'], $toc_list);
3365
                $list['current_id'] = $mycurrentitemid;
3366
            }
3367
            $arrayList[] = $list;
3368
        }
3369
3370
        return $arrayList;
3371
    }
3372
3373
    /**
3374
     * Returns an HTML-formatted string ready to display with teacher buttons
3375
     * in LP view menu
3376
     * @return	string	HTML TOC ready to display
3377
     */
3378
    public function get_teacher_toc_buttons()
3379
    {
3380
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3381
        $hide_teacher_icons_lp = api_get_configuration_value('hide_teacher_icons_lp');
3382
        $html = '';
3383
        if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
3384
            if ($this->get_lp_session_id() == api_get_session_id()) {
3385
                $html .= '<div id="actions_lp" class="actions_lp"><hr>';
3386
                $html .= '<div class="btn-group">';
3387
                $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'>".
3388
                    Display::returnFontAwesomeIcon('street-view').get_lang('Overview')."</a>";
3389
                $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'>".
3390
                    Display::returnFontAwesomeIcon('pencil').get_lang('Edit')."</a>";
3391
                $html .= '<a class="btn btn-sm btn-default" href="lp_controller.php?'.api_get_cidreq()."&action=edit&lp_id=".$this->lp_id.'&isStudentView=false">'.
3392
                    Display::returnFontAwesomeIcon('cog').get_lang('Settings').'</a>';
3393
                $html .= '</div>';
3394
                $html .= '</div>';
3395
            }
3396
        }
3397
3398
        return $html;
3399
    }
3400
3401
    /**
3402
     * Gets the learnpath maker name - generally the editor's name
3403
     * @return	string	Learnpath maker name
3404
     */
3405
    public function get_maker()
3406
    {
3407
        if ($this->debug > 0) {
3408
            error_log('New LP - In learnpath::get_maker()', 0);
3409
        }
3410
        if (!empty($this->maker)) {
3411
            return $this->maker;
3412
        } else {
3413
            return '';
3414
        }
3415
    }
3416
3417
    /**
3418
     * Gets the learnpath name/title
3419
     * @return	string	Learnpath name/title
3420
     */
3421
    public function get_name()
3422
    {
3423
        if ($this->debug > 0) {
3424
            error_log('New LP - In learnpath::get_name()', 0);
3425
        }
3426
        if (!empty($this->name)) {
3427
            return $this->name;
3428
        } else {
3429
            return 'N/A';
3430
        }
3431
    }
3432
3433
    /**
3434
     * Gets a link to the resource from the present location, depending on item ID.
3435
     * @param	string	$type Type of link expected
3436
     * @param	integer	$item_id Learnpath item ID
3437
     * @return	string	$provided_toc Link to the lp_item resource
3438
     */
3439
    public function get_link($type = 'http', $item_id = null, $provided_toc = false)
3440
    {
3441
        $course_id = $this->get_course_int_id();
3442
3443
        if ($this->debug > 0) {
3444
            error_log('New LP - In learnpath::get_link('.$type.','.$item_id.')', 0);
3445
        }
3446
        if (empty($item_id)) {
3447
            if ($this->debug > 2) {
3448
                error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: '.$this->get_current_item_id(), 0);
3449
            }
3450
            $item_id = $this->get_current_item_id();
3451
        }
3452
3453
        if (empty($item_id)) {
3454
            if ($this->debug > 2) {
3455
                error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
3456
            }
3457
            //still empty, this means there was no item_id given and we are not in an object context or
3458
            //the object property is empty, return empty link
3459
            $item_id = $this->first();
3460
            return '';
3461
        }
3462
3463
        $file = '';
3464
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3465
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3466
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3467
        $item_id = intval($item_id);
3468
3469
        $sql = "SELECT
3470
                    l.lp_type as ltype,
3471
                    l.path as lpath,
3472
                    li.item_type as litype,
3473
                    li.path as lipath,
3474
                    li.parameters as liparams
3475
        		FROM $lp_table l
3476
                INNER JOIN $lp_item_table li
3477
                ON (li.lp_id = l.iid)
3478
        		WHERE 
3479
        		    li.iid = $item_id 
3480
        		";
3481
        if ($this->debug > 2) {
3482
            error_log('New LP - In learnpath::get_link() - selecting item '.$sql, 0);
3483
        }
3484
        $res = Database::query($sql);
3485
        if (Database::num_rows($res) > 0) {
3486
            $row = Database::fetch_array($res);
3487
            $lp_type = $row['ltype'];
3488
            $lp_path = $row['lpath'];
3489
            $lp_item_type = $row['litype'];
3490
            $lp_item_path = $row['lipath'];
3491
            $lp_item_params = $row['liparams'];
3492
3493
            if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) {
3494
                list($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
3495
            }
3496
            $sys_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
3497
            if ($type == 'http') {
3498
                //web path
3499
                $course_path = api_get_path(WEB_COURSE_PATH).api_get_course_path();
3500
            } else {
3501
                $course_path = $sys_course_path; //system path
3502
            }
3503
3504
            // Fixed issue BT#1272 - If the item type is a Chamilo Item (quiz, link, etc),
3505
            // then change the lp type to thread it as a normal Chamilo LP not a SCO.
3506
            if (in_array(
3507
                $lp_item_type,
3508
                ['quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication']
3509
            )
3510
            ) {
3511
                $lp_type = 1;
3512
            }
3513
3514
            if ($this->debug > 2) {
3515
                error_log('New LP - In learnpath::get_link() - $lp_type '.$lp_type, 0);
3516
                error_log('New LP - In learnpath::get_link() - $lp_item_type '.$lp_item_type, 0);
3517
            }
3518
3519
            // Now go through the specific cases to get the end of the path
3520
            // @todo Use constants instead of int values.
3521
            switch ($lp_type) {
3522
                case 1:
3523
                    $file = self::rl_get_resource_link_for_learnpath(
3524
                        $course_id,
3525
                        $this->get_id(),
3526
                        $item_id,
3527
                        $this->get_view_id()
3528
                    );
3529
                    if ($this->debug > 0) {
3530
                        error_log('rl_get_resource_link_for_learnpath - file: '.$file, 0);
3531
                    }
3532
3533
                    switch ($lp_item_type) {
3534
                        case 'dir':
3535
                            $file = 'lp_content.php?type=dir';
3536
                            break;
3537
                        case 'link':
3538
                            if (Link::is_youtube_link($file)) {
3539
                                $src = Link::get_youtube_video_id($file);
3540
                                $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=youtube&source='.$src;
3541
                            } elseif (Link::isVimeoLink($file)) {
3542
                                $src = Link::getVimeoLinkId($file);
3543
                                $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=vimeo&source='.$src;
3544
                            } else {
3545
                                // If the current site is HTTPS and the link is
3546
                                // HTTP, browsers will refuse opening the link
3547
                                $urlId = api_get_current_access_url_id();
3548
                                $url = api_get_access_url($urlId, false);
3549
                                $protocol = substr($url['url'], 0, 5);
3550
                                if ($protocol === 'https') {
3551
                                    $linkProtocol = substr($file, 0, 5);
3552
                                    if ($linkProtocol === 'http:') {
3553
                                        //this is the special intervention case
3554
                                        $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=nonhttps&source='.urlencode($file);
3555
                                    }
3556
                                }
3557
                            }
3558
                            break;
3559
                        case 'quiz':
3560
                            // Check how much attempts of a exercise exits in lp
3561
                            $lp_item_id = $this->get_current_item_id();
3562
                            $lp_view_id = $this->get_view_id();
3563
3564
                            $prevent_reinit = null;
3565
                            if (isset($this->items[$this->current])) {
3566
                                $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
3567
                            }
3568
3569
                            if (empty($provided_toc)) {
3570
                                if ($this->debug > 0) {
3571
                                    error_log('In learnpath::get_link() Loading get_toc ', 0);
3572
                                }
3573
                                $list = $this->get_toc();
3574
                            } else {
3575
                                if ($this->debug > 0) {
3576
                                    error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0);
3577
                                }
3578
                                $list = $provided_toc;
3579
                            }
3580
3581
                            $type_quiz = false;
3582
3583
                            foreach ($list as $toc) {
3584
                                if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
3585
                                    $type_quiz = true;
3586
                                }
3587
                            }
3588
3589
                            if ($type_quiz) {
3590
                                $lp_item_id = intval($lp_item_id);
3591
                                $lp_view_id = intval($lp_view_id);
3592
                                $sql = "SELECT count(*) FROM $lp_item_view_table
3593
                                        WHERE
3594
                                            c_id = $course_id AND
3595
                                            lp_item_id='".$lp_item_id."' AND
3596
                                            lp_view_id ='".$lp_view_id."' AND
3597
                                            status='completed'";
3598
                                $result = Database::query($sql);
3599
                                $row_count = Database:: fetch_row($result);
3600
                                $count_item_view = (int) $row_count[0];
3601
                                $not_multiple_attempt = 0;
3602
                                if ($prevent_reinit === 1 && $count_item_view > 0) {
3603
                                    $not_multiple_attempt = 1;
3604
                                }
3605
                                $file .= '&not_multiple_attempt='.$not_multiple_attempt;
3606
                            }
3607
                            break;
3608
                    }
3609
3610
                    $tmp_array = explode('/', $file);
3611
                    $document_name = $tmp_array[count($tmp_array) - 1];
3612
                    if (strpos($document_name, '_DELETED_')) {
3613
                        $file = 'blank.php?error=document_deleted';
3614
                    }
3615
3616
                    break;
3617
                case 2:
3618
                    if ($this->debug > 2) {
3619
                        error_log('New LP - In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0);
3620
                    }
3621
3622
                    if ($lp_item_type != 'dir') {
3623
                        // Quite complex here:
3624
                        // We want to make sure 'http://' (and similar) links can
3625
                        // be loaded as is (withouth the Chamilo path in front) but
3626
                        // some contents use this form: resource.htm?resource=http://blablabla
3627
                        // which means we have to find a protocol at the path's start, otherwise
3628
                        // it should not be considered as an external URL.
3629
                        // if ($this->prerequisites_match($item_id)) {
3630
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3631
                            if ($this->debug > 2) {
3632
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0);
3633
                            }
3634
                            // Distant url, return as is.
3635
                            $file = $lp_item_path;
3636
                        } else {
3637
                            if ($this->debug > 2) {
3638
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0);
3639
                            }
3640
                            // Prevent getting untranslatable urls.
3641
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3642
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3643
                            // Prepare the path.
3644
                            $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path;
3645
                            // TODO: Fix this for urls with protocol header.
3646
                            $file = str_replace('//', '/', $file);
3647
                            $file = str_replace(':/', '://', $file);
3648
                            if (substr($lp_path, -1) == '/') {
3649
                                $lp_path = substr($lp_path, 0, -1);
3650
                            }
3651
3652
                            if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$lp_item_path))) {
3653
                                // if file not found.
3654
                                $decoded = html_entity_decode($lp_item_path);
3655
                                list($decoded) = explode('?', $decoded);
3656
                                if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$decoded))) {
3657
                                    $file = self::rl_get_resource_link_for_learnpath(
3658
                                        $course_id,
3659
                                        $this->get_id(),
3660
                                        $item_id,
3661
                                        $this->get_view_id()
3662
                                    );
3663
                                    if (empty($file)) {
3664
                                        $file = 'blank.php?error=document_not_found';
3665
                                    } else {
3666
                                        $tmp_array = explode('/', $file);
3667
                                        $document_name = $tmp_array[count($tmp_array) - 1];
3668
                                        if (strpos($document_name, '_DELETED_')) {
3669
                                            $file = 'blank.php?error=document_deleted';
3670
                                        } else {
3671
                                            $file = 'blank.php?error=document_not_found';
3672
                                        }
3673
                                    }
3674
                                } else {
3675
                                    $file = $course_path.'/scorm/'.$lp_path.'/'.$decoded;
3676
                                }
3677
                            }
3678
                        }
3679
3680
                        // We want to use parameters if they were defined in the imsmanifest
3681
                        if (strpos($file, 'blank.php') === false) {
3682
                            $lp_item_params = ltrim($lp_item_params, '?');
3683
                            $file .= (strstr($file, '?') === false ? '?' : '').$lp_item_params;
3684
                        }
3685
                    } else {
3686
                        $file = 'lp_content.php?type=dir';
3687
                    }
3688
                    break;
3689
                case 3:
3690
                    if ($this->debug > 2) {
3691
                        error_log('New LP - In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0);
3692
                    }
3693
                    // Formatting AICC HACP append URL.
3694
                    $aicc_append = '?aicc_sid='.urlencode(session_id()).'&aicc_url='.urlencode(api_get_path(WEB_CODE_PATH).'lp/aicc_hacp.php').'&';
3695
                    if (!empty($lp_item_params)) {
3696
                        $aicc_append .= $lp_item_params.'&';
3697
                    }
3698
                    if ($lp_item_type != 'dir') {
3699
                        // Quite complex here:
3700
                        // We want to make sure 'http://' (and similar) links can
3701
                        // be loaded as is (withouth the Chamilo path in front) but
3702
                        // some contents use this form: resource.htm?resource=http://blablabla
3703
                        // which means we have to find a protocol at the path's start, otherwise
3704
                        // it should not be considered as an external URL.
3705
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3706
                            if ($this->debug > 2) {
3707
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0);
3708
                            }
3709
                            // Distant url, return as is.
3710
                            $file = $lp_item_path;
3711
                            // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
3712
                            /*
3713
                            if (stristr($file,'<servername>') !== false) {
3714
                                $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path);
3715
                            }
3716
                            */
3717
                            if (stripos($file, '<servername>') !== false) {
3718
                                //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
3719
                                $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
3720
                                $file = str_replace('<servername>', $web_course_path.'/scorm/'.$lp_path, $lp_item_path);
3721
                            }
3722
                            //
3723
                            $file .= $aicc_append;
3724
                        } else {
3725
                            if ($this->debug > 2) {
3726
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0);
3727
                            }
3728
                            // Prevent getting untranslatable urls.
3729
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3730
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3731
                            // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name.
3732
                            $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path;
3733
                            // TODO: Fix this for urls with protocol header.
3734
                            $file = str_replace('//', '/', $file);
3735
                            $file = str_replace(':/', '://', $file);
3736
                            $file .= $aicc_append;
3737
                        }
3738
                    } else {
3739
                        $file = 'lp_content.php?type=dir';
3740
                    }
3741
                    break;
3742
                case 4:
3743
                    break;
3744
                default:
3745
                    break;
3746
            }
3747
            // Replace &amp; by & because &amp; will break URL with params
3748
            $file = !empty($file) ? str_replace('&amp;', '&', $file) : '';
3749
        }
3750
        if ($this->debug > 2) {
3751
            error_log('New LP - In learnpath::get_link() - returning "'.$file.'" from get_link', 0);
3752
        }
3753
        return $file;
3754
    }
3755
3756
    /**
3757
     * Gets the latest usable view or generate a new one
3758
     * @param	integer	Optional attempt number. If none given, takes the highest from the lp_view table
3759
     * @return	integer	DB lp_view id
3760
     */
3761
    public function get_view($attempt_num = 0)
3762
    {
3763
        if ($this->debug > 0) {
3764
            error_log('New LP - In learnpath::get_view()', 0);
3765
        }
3766
        $search = '';
3767
        // Use $attempt_num to enable multi-views management (disabled so far).
3768
        if ($attempt_num != 0 && intval(strval($attempt_num)) == $attempt_num) {
3769
            $search = 'AND view_count = '.$attempt_num;
3770
        }
3771
        // When missing $attempt_num, search for a unique lp_view record for this lp and user.
3772
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3773
3774
        $course_id = api_get_course_int_id();
3775
        $sessionId = api_get_session_id();
3776
3777
        $sql = "SELECT iid, view_count FROM $lp_view_table
3778
        		WHERE
3779
        		    c_id = $course_id AND
3780
        		    lp_id = " . $this->get_id()." AND
3781
        		    user_id = " . $this->get_user_id()." AND
3782
        		    session_id = $sessionId
3783
        		    $search
3784
                ORDER BY view_count DESC";
3785
        $res = Database::query($sql);
3786
        if (Database::num_rows($res) > 0) {
3787
            $row = Database::fetch_array($res);
3788
            $this->lp_view_id = $row['iid'];
3789
        } elseif (!api_is_invitee()) {
3790
            // There is no database record, create one.
3791
            $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) VALUES
3792
            		($course_id, ".$this->get_id().",".$this->get_user_id().", 1, $sessionId)";
3793
            Database::query($sql);
3794
            $id = Database::insert_id();
3795
            $this->lp_view_id = $id;
3796
3797
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $id";
3798
            Database::query($sql);
3799
        }
3800
3801
        return $this->lp_view_id;
3802
    }
3803
3804
    /**
3805
     * Gets the current view id
3806
     * @return	integer	View ID (from lp_view)
3807
     */
3808
    public function get_view_id()
3809
    {
3810
        if ($this->debug > 0) {
3811
            error_log('New LP - In learnpath::get_view_id()', 0);
3812
        }
3813
        if (!empty($this->lp_view_id)) {
3814
            return $this->lp_view_id;
3815
        } else {
3816
            return 0;
3817
        }
3818
    }
3819
3820
    /**
3821
     * Gets the update queue
3822
     * @return	array	Array containing IDs of items to be updated by JavaScript
3823
     */
3824
    public function get_update_queue()
3825
    {
3826
        if ($this->debug > 0) {
3827
            error_log('New LP - In learnpath::get_update_queue()', 0);
3828
        }
3829
        return $this->update_queue;
3830
    }
3831
3832
    /**
3833
     * Gets the user ID
3834
     * @return	integer	User ID
3835
     */
3836
    public function get_user_id()
3837
    {
3838
        if ($this->debug > 0) {
3839
            error_log('New LP - In learnpath::get_user_id()', 0);
3840
        }
3841
        if (!empty($this->user_id)) {
3842
            return $this->user_id;
3843
        } else {
3844
            return false;
3845
        }
3846
    }
3847
3848
    /**
3849
     * Checks if any of the items has an audio element attached
3850
     * @return  bool    True or false
3851
     */
3852
    public function has_audio()
3853
    {
3854
        if ($this->debug > 1) {
3855
            error_log('New LP - In learnpath::has_audio()', 0);
3856
        }
3857
        $has = false;
3858
        foreach ($this->items as $i => $item) {
3859
            if (!empty($this->items[$i]->audio)) {
3860
                $has = true;
3861
                break;
3862
            }
3863
        }
3864
        return $has;
3865
    }
3866
3867
    /**
3868
     * Moves an item up and down at its level
3869
     * @param	integer	Item to move up and down
3870
     * @param	string	Direction 'up' or 'down'
3871
     * @return	integer	New display order, or false on error
3872
     */
3873
    public function move_item($id, $direction)
3874
    {
3875
        $course_id = api_get_course_int_id();
3876
        if ($this->debug > 0) {
3877
            error_log('New LP - In learnpath::move_item('.$id.','.$direction.')', 0);
3878
        }
3879
        if (empty($id) || empty($direction)) {
3880
            return false;
3881
        }
3882
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
3883
        $sql_sel = "SELECT *
3884
                    FROM $tbl_lp_item
3885
                    WHERE  
3886
                        iid = $id
3887
                    ";
3888
        $res_sel = Database::query($sql_sel);
3889
        // Check if elem exists.
3890
        if (Database::num_rows($res_sel) < 1) {
3891
            return false;
3892
        }
3893
        // Gather data.
3894
        $row = Database::fetch_array($res_sel);
3895
        $previous = $row['previous_item_id'];
3896
        $next = $row['next_item_id'];
3897
        $display = $row['display_order'];
3898
        $parent = $row['parent_item_id'];
3899
        $lp = $row['lp_id'];
3900
        // Update the item (switch with previous/next one).
3901
        switch ($direction) {
3902
            case 'up':
3903
                if ($this->debug > 2) {
3904
                    error_log('Movement up detected', 0);
3905
                }
3906
                if ($display > 1) {
3907
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item
3908
                                 WHERE iid = $previous";
3909
3910
                    if ($this->debug > 2) {
3911
                        error_log('Selecting previous: '.$sql_sel2, 0);
3912
                    }
3913
                    $res_sel2 = Database::query($sql_sel2);
3914
                    if (Database::num_rows($res_sel2) < 1) {
3915
                        $previous_previous = 0;
3916
                    }
3917
                    // Gather data.
3918
                    $row2 = Database::fetch_array($res_sel2);
3919
                    $previous_previous = $row2['previous_item_id'];
3920
                    // Update previous_previous item (switch "next" with current).
3921
                    if ($previous_previous != 0) {
3922
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3923
                                        next_item_id = $id
3924
                                    WHERE iid = $previous_previous";
3925
                        if ($this->debug > 2) {
3926
                            error_log($sql_upd2, 0);
3927
                        }
3928
                        Database::query($sql_upd2);
3929
                    }
3930
                    // Update previous item (switch with current).
3931
                    if ($previous != 0) {
3932
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3933
                                    next_item_id = $next,
3934
                                    previous_item_id = $id,
3935
                                    display_order = display_order +1
3936
                                    WHERE iid = $previous";
3937
                        if ($this->debug > 2) {
3938
                            error_log($sql_upd2, 0);
3939
                        }
3940
                        Database::query($sql_upd2);
3941
                    }
3942
3943
                    // Update current item (switch with previous).
3944
                    if ($id != 0) {
3945
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3946
                                        next_item_id = $previous,
3947
                                        previous_item_id = $previous_previous,
3948
                                        display_order = display_order-1
3949
                                    WHERE c_id = ".$course_id." AND id = $id";
3950
                        if ($this->debug > 2) {
3951
                            error_log($sql_upd2, 0);
3952
                        }
3953
                        Database::query($sql_upd2);
3954
                    }
3955
                    // Update next item (new previous item).
3956
                    if ($next != 0) {
3957
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous
3958
                                     WHERE iid = $next";
3959
                        if ($this->debug > 2) {
3960
                            error_log($sql_upd2, 0);
3961
                        }
3962
                        Database::query($sql_upd2);
3963
                    }
3964
                    $display = $display - 1;
3965
                }
3966
                break;
3967
            case 'down':
3968
                if ($this->debug > 2) {
3969
                    error_log('Movement down detected', 0);
3970
                }
3971
                if ($next != 0) {
3972
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item 
3973
                                 WHERE iid = $next";
3974
                    if ($this->debug > 2) {
3975
                        error_log('Selecting next: '.$sql_sel2, 0);
3976
                    }
3977
                    $res_sel2 = Database::query($sql_sel2);
3978
                    if (Database::num_rows($res_sel2) < 1) {
3979
                        $next_next = 0;
3980
                    }
3981
                    // Gather data.
3982
                    $row2 = Database::fetch_array($res_sel2);
3983
                    $next_next = $row2['next_item_id'];
3984
                    // Update previous item (switch with current).
3985
                    if ($previous != 0) {
3986
                        $sql_upd2 = "UPDATE $tbl_lp_item 
3987
                                     SET next_item_id = $next
3988
                                     WHERE iid = $previous";
3989
                        Database::query($sql_upd2);
3990
                    }
3991
                    // Update current item (switch with previous).
3992
                    if ($id != 0) {
3993
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3994
                                     previous_item_id = $next, 
3995
                                     next_item_id = $next_next, 
3996
                                     display_order = display_order + 1
3997
                                     WHERE iid = $id";
3998
                        Database::query($sql_upd2);
3999
                    }
4000
4001
                    // Update next item (new previous item).
4002
                    if ($next != 0) {
4003
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
4004
                                     previous_item_id = $previous, 
4005
                                     next_item_id = $id, 
4006
                                     display_order = display_order-1
4007
                                     WHERE iid = $next";
4008
                        Database::query($sql_upd2);
4009
                    }
4010
4011
                    // Update next_next item (switch "previous" with current).
4012
                    if ($next_next != 0) {
4013
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
4014
                                     previous_item_id = $id
4015
                                     WHERE iid = $next_next";
4016
                        Database::query($sql_upd2);
4017
                    }
4018
                    $display = $display + 1;
4019
                }
4020
                break;
4021
            default:
4022
                return false;
4023
        }
4024
        return $display;
4025
    }
4026
4027
    /**
4028
     * Move a LP up (display_order)
4029
     * @param int $lp_id Learnpath ID
4030
     * @param int $categoryId
4031
     * @return bool
4032
     */
4033
    public static function move_up($lp_id, $categoryId = 0)
4034
    {
4035
        $courseId = api_get_course_int_id();
4036
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4037
4038
        $categoryCondition = '';
4039
        if (!empty($categoryId)) {
4040
            $categoryId = (int) $categoryId;
4041
            $categoryCondition = " AND category_id = $categoryId";
4042
        }
4043
        $sql = "SELECT * FROM $lp_table
4044
                WHERE c_id = $courseId
4045
                $categoryCondition
4046
                ORDER BY display_order";
4047
        $res = Database::query($sql);
4048
        if ($res === false) {
4049
            return false;
4050
        }
4051
4052
        $lps = [];
4053
        $lp_order = [];
4054
        $num = Database::num_rows($res);
4055
        // First check the order is correct, globally (might be wrong because
4056
        // of versions < 1.8.4)
4057
        if ($num > 0) {
4058
            $i = 1;
4059
            while ($row = Database::fetch_array($res)) {
4060
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
4061
                    $sql = "UPDATE $lp_table SET display_order = $i
4062
                            WHERE iid = ".$row['iid'];
4063
                    Database::query($sql);
4064
                }
4065
                $row['display_order'] = $i;
4066
                $lps[$row['iid']] = $row;
4067
                $lp_order[$i] = $row['iid'];
4068
                $i++;
4069
            }
4070
        }
4071
        if ($num > 1) { // If there's only one element, no need to sort.
4072
            $order = $lps[$lp_id]['display_order'];
4073
            if ($order > 1) { // If it's the first element, no need to move up.
4074
                $sql = "UPDATE $lp_table SET display_order = $order
4075
                        WHERE iid = ".$lp_order[$order - 1];
4076
                Database::query($sql);
4077
                $sql = "UPDATE $lp_table SET display_order = ".($order - 1)."
4078
                        WHERE iid = $lp_id";
4079
                Database::query($sql);
4080
            }
4081
        }
4082
4083
        return true;
4084
    }
4085
4086
    /**
4087
     * Move a learnpath down (display_order)
4088
     * @param int $lp_id Learnpath ID
4089
     * @param int $categoryId
4090
     * @return bool
4091
     */
4092
    public static function move_down($lp_id, $categoryId = 0)
4093
    {
4094
        $courseId = api_get_course_int_id();
4095
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4096
4097
        $categoryCondition = '';
4098
        if (!empty($categoryId)) {
4099
            $categoryId = (int) $categoryId;
4100
            $categoryCondition = " AND category_id = $categoryId";
4101
        }
4102
4103
        $sql = "SELECT * FROM $lp_table
4104
                WHERE c_id = $courseId
4105
                $categoryCondition
4106
                ORDER BY display_order";
4107
        $res = Database::query($sql);
4108
        if ($res === false) {
4109
            return false;
4110
        }
4111
        $lps = [];
4112
        $lp_order = [];
4113
        $num = Database::num_rows($res);
4114
        $max = 0;
4115
        // First check the order is correct, globally (might be wrong because
4116
        // of versions < 1.8.4).
4117
        if ($num > 0) {
4118
            $i = 1;
4119
            while ($row = Database::fetch_array($res)) {
4120
                $max = $i;
4121
                if ($row['display_order'] != $i) {
4122
                    // If we find a gap in the order, we need to fix it.
4123
                    $sql = "UPDATE $lp_table SET display_order = $i
4124
                              WHERE iid = ".$row['iid'];
4125
                    Database::query($sql);
4126
                }
4127
                $row['display_order'] = $i;
4128
                $lps[$row['iid']] = $row;
4129
                $lp_order[$i] = $row['iid'];
4130
                $i++;
4131
            }
4132
        }
4133
        if ($num > 1) { // If there's only one element, no need to sort.
4134
            $order = $lps[$lp_id]['display_order'];
4135
            if ($order < $max) { // If it's the first element, no need to move up.
4136
                $sql = "UPDATE $lp_table SET display_order = $order
4137
                        WHERE iid = ".$lp_order[$order + 1];
4138
                Database::query($sql);
4139
                $sql = "UPDATE $lp_table SET display_order = ".($order + 1)."
4140
                        WHERE iid = $lp_id";
4141
                Database::query($sql);
4142
            }
4143
        }
4144
4145
        return true;
4146
    }
4147
4148
    /**
4149
     * Updates learnpath attributes to point to the next element
4150
     * The last part is similar to set_current_item but processing the other way around
4151
     */
4152
    public function next()
4153
    {
4154
        if ($this->debug > 0) {
4155
            error_log('New LP - In learnpath::next()', 0);
4156
        }
4157
        $this->last = $this->get_current_item_id();
4158
        $this->items[$this->last]->save(
4159
            false,
4160
            $this->prerequisites_match($this->last)
4161
        );
4162
        $this->autocomplete_parents($this->last);
4163
        $new_index = $this->get_next_index();
4164
        if ($this->debug > 2) {
4165
            error_log('New LP - New index: '.$new_index, 0);
4166
        }
4167
        $this->index = $new_index;
4168
        if ($this->debug > 2) {
4169
            error_log('New LP - Now having orderedlist['.$new_index.'] = '.$this->ordered_items[$new_index], 0);
4170
        }
4171
        $this->current = $this->ordered_items[$new_index];
4172
        if ($this->debug > 2) {
4173
            error_log('New LP - new item id is '.$this->current.'-'.$this->get_current_item_id(), 0);
4174
        }
4175
    }
4176
4177
    /**
4178
     * Open a resource = initialise all local variables relative to this resource. Depending on the child
4179
     * class, this might be redefined to allow several behaviours depending on the document type.
4180
     * @param integer Resource ID
4181
     */
4182
    public function open($id)
4183
    {
4184
        if ($this->debug > 0) {
4185
            error_log('New LP - In learnpath::open()', 0);
4186
        }
4187
        // TODO:
4188
        // set the current resource attribute to this resource
4189
        // switch on element type (redefine in child class?)
4190
        // set status for this item to "opened"
4191
        // start timer
4192
        // initialise score
4193
        $this->index = 0; //or = the last item seen (see $this->last)
4194
    }
4195
4196
    /**
4197
     * Check that all prerequisites are fulfilled. Returns true and an
4198
     * empty string on success, returns false
4199
     * and the prerequisite string on error.
4200
     * This function is based on the rules for aicc_script language as
4201
     * described in the SCORM 1.2 CAM documentation page 108.
4202
     * @param	integer	$itemId Optional item ID. If none given, uses the current open item.
4203
     * @return	boolean	True if prerequisites are matched, false otherwise -
4204
     * Empty string if true returned, prerequisites string otherwise.
4205
     */
4206
    public function prerequisites_match($itemId = null)
4207
    {
4208
        $debug = $this->debug;
4209
        if ($debug > 0) {
4210
            error_log('In learnpath::prerequisites_match()', 0);
4211
        }
4212
4213
        if (empty($itemId)) {
4214
            $itemId = $this->current;
4215
        }
4216
4217
        $currentItem = $this->getItem($itemId);
4218
4219
        if ($currentItem) {
4220
            if ($this->type == 2) {
4221
                // Getting prereq from scorm
4222
                $prereq_string = $this->get_scorm_prereq_string($itemId);
4223
            } else {
4224
                $prereq_string = $currentItem->get_prereq_string();
4225
            }
4226
4227
            if (empty($prereq_string)) {
4228
                if ($debug > 0) {
4229
                    error_log('Found prereq_string is empty return true');
4230
                }
4231
                return true;
4232
            }
4233
            // Clean spaces.
4234
            $prereq_string = str_replace(' ', '', $prereq_string);
4235
            if ($debug > 0) {
4236
                error_log('Found prereq_string: '.$prereq_string, 0);
4237
            }
4238
            // Now send to the parse_prereq() function that will check this component's prerequisites.
4239
            $result = $currentItem->parse_prereq(
4240
                $prereq_string,
4241
                $this->items,
4242
                $this->refs_list,
4243
                $this->get_user_id()
4244
            );
4245
4246
            if ($result === false) {
4247
                $this->set_error_msg($currentItem->prereq_alert);
4248
            }
4249
        } else {
4250
            $result = true;
4251
            if ($debug > 1) {
4252
                error_log('$this->items['.$itemId.'] was not an object', 0);
4253
            }
4254
        }
4255
4256
        if ($debug > 1) {
4257
            error_log('End of prerequisites_match(). Error message is now '.$this->error, 0);
4258
        }
4259
        return $result;
4260
    }
4261
4262
    /**
4263
     * Updates learnpath attributes to point to the previous element
4264
     * The last part is similar to set_current_item but processing the other way around
4265
     */
4266
    public function previous()
4267
    {
4268
        if ($this->debug > 0) {
4269
            error_log('New LP - In learnpath::previous()', 0);
4270
        }
4271
        $this->last = $this->get_current_item_id();
4272
        $this->items[$this->last]->save(
4273
            false,
4274
            $this->prerequisites_match($this->last)
4275
        );
4276
        $this->autocomplete_parents($this->last);
4277
        $new_index = $this->get_previous_index();
4278
        $this->index = $new_index;
4279
        $this->current = $this->ordered_items[$new_index];
4280
    }
4281
4282
    /**
4283
     * Publishes a learnpath. This basically means show or hide the learnpath
4284
     * to normal users.
4285
     * Can be used as abstract
4286
     * @param integer    Learnpath ID
4287
     * @param int   New visibility
4288
     * @return bool
4289
     */
4290
    public static function toggle_visibility($lp_id, $set_visibility = 1)
4291
    {
4292
        $action = 'visible';
4293
        if ($set_visibility != 1) {
4294
            $action = 'invisible';
4295
            self::toggle_publish($lp_id, 'i');
4296
        }
4297
4298
        return api_item_property_update(
4299
            api_get_course_info(),
4300
            TOOL_LEARNPATH,
4301
            $lp_id,
4302
            $action,
4303
            api_get_user_id()
4304
        );
4305
    }
4306
4307
    /**
4308
     * Publishes a learnpath category.
4309
     * This basically means show or hide the learnpath category to normal users.
4310
     * @param int $id
4311
     * @param int $visibility
4312
     * @return bool
4313
     */
4314
    public static function toggleCategoryVisibility($id, $visibility = 1)
4315
    {
4316
        $action = 'visible';
4317
4318
        if ($visibility != 1) {
4319
            $action = 'invisible';
4320
            $list = new LearnpathList(
4321
                api_get_user_id(),
4322
                null,
4323
                null,
4324
                null,
4325
                false,
4326
                $id
4327
            );
4328
4329
            $lpList = $list->get_flat_list();
4330
            foreach ($lpList as $lp) {
4331
                learnpath::toggle_visibility($lp['iid'], 0);
4332
            }
4333
4334
            learnpath::toggleCategoryPublish($id, 0);
4335
        }
4336
4337
        return api_item_property_update(
4338
            api_get_course_info(),
4339
            TOOL_LEARNPATH_CATEGORY,
4340
            $id,
4341
            $action,
4342
            api_get_user_id()
4343
        );
4344
    }
4345
4346
    /**
4347
     * Publishes a learnpath. This basically means show or hide the learnpath
4348
     * on the course homepage
4349
     * Can be used as abstract
4350
     * @param	integer	$lp_id Learnpath id
4351
     * @param	string	$set_visibility New visibility (v/i - visible/invisible)
4352
     * @return bool
4353
     */
4354
    public static function toggle_publish($lp_id, $set_visibility = 'v')
4355
    {
4356
        $course_id = api_get_course_int_id();
4357
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4358
        $lp_id = (int) $lp_id;
4359
        $sql = "SELECT * FROM $tbl_lp
4360
                WHERE iid = $lp_id";
4361
        $result = Database::query($sql);
4362
        if (Database::num_rows($result)) {
4363
            $row = Database::fetch_array($result);
4364
            $name = Database::escape_string($row['name']);
4365
            if ($set_visibility == 'i') {
4366
                $v = 0;
4367
            }
4368
            if ($set_visibility == 'v') {
4369
                $v = 1;
4370
            }
4371
4372
            $session_id = api_get_session_id();
4373
            $session_condition = api_get_session_condition($session_id);
4374
4375
            $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
4376
            $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4377
            $oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4378
4379
            $sql = "SELECT * FROM $tbl_tool
4380
                    WHERE
4381
                        c_id = $course_id AND
4382
                        (link = '$link' OR link = '$oldLink') AND
4383
                        image = 'scormbuilder.gif' AND
4384
                        (
4385
                            link LIKE '$link%' OR
4386
                            link LIKE '$oldLink%'
4387
                        )
4388
                        $session_condition
4389
                    ";
4390
4391
            $result = Database::query($sql);
4392
            $num = Database::num_rows($result);
4393
            if ($set_visibility == 'i' && $num > 0) {
4394
                $sql = "DELETE FROM $tbl_tool
4395
                        WHERE 
4396
                            c_id = $course_id AND 
4397
                            (link = '$link' OR link = '$oldLink') AND 
4398
                            image='scormbuilder.gif' 
4399
                            $session_condition";
4400
                Database::query($sql);
4401
            } elseif ($set_visibility == 'v' && $num == 0) {
4402
                $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
4403
                        ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
4404
                Database::query($sql);
4405
                $insertId = Database::insert_id();
4406
                if ($insertId) {
4407
                    $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
4408
                    Database::query($sql);
4409
                }
4410
            } elseif ($set_visibility == 'v' && $num > 0) {
4411
                $sql = "UPDATE $tbl_tool SET
4412
                            c_id = $course_id,
4413
                            name = '$name',
4414
                            link = '$link',
4415
                            image = 'scormbuilder.gif',
4416
                            visibility = '$v',
4417
                            admin = '0',
4418
                            address = 'pastillegris.gif',
4419
                            added_tool = 0,
4420
                            session_id = $session_id
4421
                        WHERE
4422
                            c_id = ".$course_id." AND
4423
                            (link = '$link' OR link = '$oldLink') AND 
4424
                            image='scormbuilder.gif' 
4425
                            $session_condition
4426
                        ";
4427
                Database::query($sql);
4428
            } else {
4429
                // Parameter and database incompatible, do nothing, exit.
4430
                return false;
4431
            }
4432
        } else {
4433
            return false;
4434
        }
4435
    }
4436
4437
    /**
4438
     * Generate the link for a learnpath category as course tool
4439
     * @param int $categoryId
4440
     * @return string
4441
     */
4442
    private static function getCategoryLinkForTool($categoryId)
4443
    {
4444
        $link = 'lp/lp_controller.php?'.api_get_cidreq().'&'
4445
            .http_build_query(
4446
                [
4447
                    'action' => 'view_category',
4448
                    'id' => $categoryId
4449
                ]
4450
            );
4451
4452
        return $link;
4453
    }
4454
4455
    /**
4456
     * Publishes a learnpath.
4457
     * Show or hide the learnpath category on the course homepage
4458
     * @param int $id
4459
     * @param int $setVisibility
4460
     * @return bool
4461
     */
4462
    public static function toggleCategoryPublish($id, $setVisibility = 1)
4463
    {
4464
        $courseId = api_get_course_int_id();
4465
        $sessionId = api_get_session_id();
4466
        $sessionCondition = api_get_session_condition(
4467
            $sessionId,
4468
            true,
4469
            false,
4470
            't.sessionId'
4471
        );
4472
4473
        $em = Database::getManager();
4474
4475
        /** @var CLpCategory $category */
4476
        $category = $em->find('ChamiloCourseBundle:CLpCategory', $id);
4477
4478
        if (!$category) {
4479
            return false;
4480
        }
4481
4482
        $link = self::getCategoryLinkForTool($id);
4483
4484
        /** @var CTool $tool */
4485
        $tool = $em->createQuery("
4486
                SELECT t FROM ChamiloCourseBundle:CTool t
4487
                WHERE
4488
                    t.cId = :course AND
4489
                    t.link = :link1 AND
4490
                    t.image = 'lp_category.gif' AND
4491
                    t.link LIKE :link2
4492
                    $sessionCondition
4493
            ")
4494
            ->setParameters([
4495
                'course' => (int) $courseId,
4496
                'link1' => $link,
4497
                'link2' => "$link%"
4498
            ])
4499
            ->getOneOrNullResult();
4500
4501
        if ($setVisibility == 0 && $tool) {
4502
            $em->remove($tool);
4503
            $em->flush();
4504
4505
            return true;
4506
        }
4507
4508
        if ($setVisibility == 1 && !$tool) {
4509
            $tool = new CTool();
4510
            $tool
4511
                ->setCategory('authoring')
4512
                ->setCId($courseId)
4513
                ->setName(strip_tags($category->getName()))
4514
                ->setLink($link)
4515
                ->setImage('lp_category.gif')
4516
                ->setVisibility(1)
4517
                ->setAdmin(0)
4518
                ->setAddress('pastillegris.gif')
4519
                ->setAddedTool(0)
4520
                ->setSessionId($sessionId)
4521
                ->setTarget('_self');
4522
4523
            $em->persist($tool);
4524
            $em->flush();
4525
4526
            $tool->setId($tool->getIid());
4527
4528
            $em->persist($tool);
4529
            $em->flush();
4530
4531
            return true;
4532
        }
4533
4534
        if ($setVisibility == 1 && $tool) {
4535
            $tool
4536
                ->setName(strip_tags($category->getName()))
4537
                ->setVisibility(1);
4538
4539
            $em->persist($tool);
4540
            $em->flush();
4541
4542
            return true;
4543
        }
4544
4545
        return false;
4546
    }
4547
4548
    /**
4549
     * Check if the learnpath category is visible for a user
4550
     * @param CLpCategory $category
4551
     * @param User $user
4552
     * @return bool
4553
     */
4554
    public static function categoryIsVisibleForStudent(
4555
        CLpCategory $category,
4556
        User $user
4557
    ) {
4558
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
4559
4560
        if ($isAllowedToEdit) {
4561
            return true;
4562
        }
4563
4564
        $users = $category->getUsers();
4565
4566
        if (empty($users) || !$users->count()) {
4567
            return true;
4568
        }
4569
4570
        if ($category->hasUserAdded($user)) {
4571
            return true;
4572
        }
4573
4574
        $groups = GroupManager::getAllGroupPerUserSubscription($user->getId());
4575
        if (!empty($groups)) {
4576
            $em = Database::getManager();
4577
4578
            /** @var ItemPropertyRepository $itemRepo */
4579
            $itemRepo = $em->getRepository('ChamiloCourseBundle:CItemProperty');
4580
4581
            /** @var CourseRepository $courseRepo */
4582
            $courseRepo = $em->getRepository('ChamiloCoreBundle:Course');
4583
            $sessionId = api_get_session_id();
4584
            $session = null;
4585
            if (!empty($sessionId)) {
4586
                $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
4587
            }
4588
4589
            $course = $courseRepo->find(api_get_course_int_id());
4590
4591
            // Subscribed groups to a LP
4592
            $subscribedGroupsInLp = $itemRepo->getGroupsSubscribedToItem(
4593
                TOOL_LEARNPATH_CATEGORY,
4594
                $category->getId(),
4595
                $course,
4596
                $session
4597
            );
4598
4599
            if (!empty($subscribedGroupsInLp)) {
4600
                $groups = array_column($groups, 'iid');
4601
                /** @var CItemProperty $item */
4602
                foreach ($subscribedGroupsInLp as $item) {
4603
                    if ($item->getGroup() &&
4604
                        in_array($item->getGroup()->getId(), $groups)
4605
                    ) {
4606
                        return true;
4607
                    }
4608
                }
4609
            }
4610
        }
4611
4612
        return false;
4613
    }
4614
4615
    /**
4616
     * Check if a learnpath category is published as course tool
4617
     * @param CLpCategory $category
4618
     * @param int $courseId
4619
     * @return bool
4620
     */
4621
    public static function categoryIsPublished(
4622
        CLpCategory $category,
4623
        $courseId
4624
    ) {
4625
        $link = self::getCategoryLinkForTool($category->getId());
4626
        $em = Database::getManager();
4627
4628
        $tools = $em
4629
            ->createQuery("
4630
                SELECT t FROM ChamiloCourseBundle:CTool t
4631
                WHERE t.cId = :course AND 
4632
                    t.name = :name AND
4633
                    t.image = 'lp_category.gif' AND
4634
                    t.link LIKE :link
4635
            ")
4636
            ->setParameters([
4637
                'course' => $courseId,
4638
                'name' => strip_tags($category->getName()),
4639
                'link' => "$link%"
4640
            ])
4641
            ->getResult();
4642
4643
        /** @var CTool $tool */
4644
        $tool = current($tools);
4645
4646
        return $tool ? $tool->getVisibility() : false;
4647
    }
4648
4649
    /**
4650
     * Restart the whole learnpath. Return the URL of the first element.
4651
     * Make sure the results are saved with anoter method. This method should probably be
4652
     * redefined in children classes.
4653
     * To use a similar method  statically, use the create_new_attempt() method
4654
     * @return bool
4655
     */
4656
    public function restart()
4657
    {
4658
        if ($this->debug > 0) {
4659
            error_log('New LP - In learnpath::restart()', 0);
4660
        }
4661
        // TODO
4662
        // Call autosave method to save the current progress.
4663
        //$this->index = 0;
4664
        if (api_is_invitee()) {
4665
            return false;
4666
        }
4667
        $session_id = api_get_session_id();
4668
        $course_id = api_get_course_int_id();
4669
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
4670
        $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id)
4671
                VALUES ($course_id, ".$this->lp_id.",".$this->get_user_id().",".($this->attempt + 1).", $session_id)";
4672
        if ($this->debug > 2) {
4673
            error_log('New LP - Inserting new lp_view for restart: '.$sql, 0);
4674
        }
4675
        Database::query($sql);
4676
        $view_id = Database::insert_id();
4677
4678
        if ($view_id) {
4679
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $view_id";
4680
            Database::query($sql);
4681
            $this->lp_view_id = $view_id;
4682
            $this->attempt = $this->attempt + 1;
4683
        } else {
4684
            $this->error = 'Could not insert into item_view table...';
4685
            return false;
4686
        }
4687
        $this->autocomplete_parents($this->current);
4688
        foreach ($this->items as $index => $dummy) {
4689
            $this->items[$index]->restart();
4690
            $this->items[$index]->set_lp_view($this->lp_view_id);
4691
        }
4692
        $this->first();
4693
4694
        return true;
4695
    }
4696
4697
    /**
4698
     * Saves the current item
4699
     * @return	boolean
4700
     */
4701
    public function save_current()
4702
    {
4703
        $debug = $this->debug;
4704
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4705
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4706
        if ($debug) {
4707
            error_log('New LP - save_current() saving item '.$this->current, 0);
4708
            error_log(''.print_r($this->items, true), 0);
4709
        }
4710
        if (isset($this->items[$this->current]) &&
4711
            is_object($this->items[$this->current])
4712
        ) {
4713
            if ($debug) {
4714
                error_log('Before save last_scorm_session_time: '.$this->items[$this->current]->last_scorm_session_time);
4715
            }
4716
4717
            $res = $this->items[$this->current]->save(
4718
                false,
4719
                $this->prerequisites_match($this->current)
4720
            );
4721
            $this->autocomplete_parents($this->current);
4722
            $status = $this->items[$this->current]->get_status();
4723
            $this->update_queue[$this->current] = $status;
4724
4725
            if ($debug) {
4726
                error_log('After save last_scorm_session_time: '.$this->items[$this->current]->last_scorm_session_time);
4727
            }
4728
4729
            return $res;
4730
        }
4731
        return false;
4732
    }
4733
4734
    /**
4735
     * Saves the given item
4736
     * @param integer	$item_id Optional (will take from $_REQUEST if null)
4737
     * @param bool $from_outside Save from url params (true) or from current attributes (false). Default true
4738
     * @return	bool
4739
     */
4740
    public function save_item($item_id = null, $from_outside = true)
4741
    {
4742
        $debug = $this->debug;
4743
        if ($debug) {
4744
            error_log('In learnpath::save_item('.$item_id.','.intval($from_outside).')', 0);
4745
        }
4746
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4747
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4748
        if (empty($item_id)) {
4749
            $item_id = intval($_REQUEST['id']);
4750
        }
4751
        if (empty($item_id)) {
4752
            $item_id = $this->get_current_item_id();
4753
        }
4754
        if (isset($this->items[$item_id]) &&
4755
            is_object($this->items[$item_id])
4756
        ) {
4757
            if ($debug) {
4758
                error_log('Object exists');
4759
            }
4760
4761
            // Saving the item.
4762
            $res = $this->items[$item_id]->save(
4763
                $from_outside,
4764
                $this->prerequisites_match($item_id)
4765
            );
4766
4767
            if ($debug) {
4768
                error_log('update_queue before:');
4769
                error_log(print_r($this->update_queue, 1));
4770
            }
4771
            $this->autocomplete_parents($item_id);
4772
4773
            $status = $this->items[$item_id]->get_status();
4774
            $this->update_queue[$item_id] = $status;
4775
4776
            if ($debug) {
4777
                error_log('get_status(): '.$status);
4778
                error_log('update_queue after:');
4779
                error_log(print_r($this->update_queue, 1));
4780
            }
4781
            return $res;
4782
        }
4783
        return false;
4784
    }
4785
4786
    /**
4787
     * Saves the last item seen's ID only in case
4788
     */
4789
    public function save_last()
4790
    {
4791
        $course_id = api_get_course_int_id();
4792
        if ($this->debug > 0) {
4793
            error_log('New LP - In learnpath::save_last()', 0);
4794
        }
4795
        $session_condition = api_get_session_condition(
4796
            api_get_session_id(),
4797
            true,
4798
            false
4799
        );
4800
        $table = Database::get_course_table(TABLE_LP_VIEW);
4801
4802
        if (isset($this->current) && !api_is_invitee()) {
4803
            if ($this->debug > 2) {
4804
                error_log('New LP - Saving current item ('.$this->current.') for later review', 0);
4805
            }
4806
            $sql = "UPDATE $table SET
4807
                        last_item = ".intval($this->get_current_item_id())."
4808
                    WHERE
4809
                        c_id = $course_id AND
4810
                        lp_id = ".$this->get_id()." AND
4811
                        user_id = ".$this->get_user_id()." ".$session_condition;
4812
4813
            if ($this->debug > 2) {
4814
                error_log('New LP - Saving last item seen : '.$sql, 0);
4815
            }
4816
            Database::query($sql);
4817
        }
4818
4819
        if (!api_is_invitee()) {
4820
            // Save progress.
4821
            list($progress, ) = $this->get_progress_bar_text('%');
4822
            if ($progress >= 0 && $progress <= 100) {
4823
                $progress = (int) $progress;
4824
                $sql = "UPDATE $table SET
4825
                            progress = $progress
4826
                        WHERE
4827
                            c_id = $course_id AND
4828
                            lp_id = " . $this->get_id()." AND
4829
                            user_id = " . $this->get_user_id()." ".$session_condition;
4830
                // Ignore errors as some tables might not have the progress field just yet.
4831
                Database::query($sql);
4832
                $this->progress_db = $progress;
4833
            }
4834
        }
4835
    }
4836
4837
    /**
4838
     * Sets the current item ID (checks if valid and authorized first)
4839
     * @param	integer	$item_id New item ID. If not given or not authorized, defaults to current
4840
     */
4841
    public function set_current_item($item_id = null)
4842
    {
4843
        if ($this->debug > 0) {
4844
            error_log('New LP - In learnpath::set_current_item('.$item_id.')', 0);
4845
        }
4846
        if (empty($item_id)) {
4847
            if ($this->debug > 2) {
4848
                error_log('New LP - No new current item given, ignore...', 0);
4849
            }
4850
            // Do nothing.
4851
        } else {
4852
            if ($this->debug > 2) {
4853
                error_log('New LP - New current item given is '.$item_id.'...', 0);
4854
            }
4855
            if (is_numeric($item_id)) {
4856
                $item_id = intval($item_id);
4857
                // TODO: Check in database here.
4858
                $this->last = $this->current;
4859
                $this->current = $item_id;
4860
                // TODO: Update $this->index as well.
4861
                foreach ($this->ordered_items as $index => $item) {
4862
                    if ($item == $this->current) {
4863
                        $this->index = $index;
4864
                        break;
4865
                    }
4866
                }
4867
                if ($this->debug > 2) {
4868
                    error_log('New LP - set_current_item('.$item_id.') done. Index is now : '.$this->index, 0);
4869
                }
4870
            } else {
4871
                error_log('New LP - set_current_item('.$item_id.') failed. Not a numeric value: ', 0);
4872
            }
4873
        }
4874
    }
4875
4876
    /**
4877
     * Sets the encoding
4878
     * @param	string	New encoding
4879
     * @return bool
4880
     * TODO (as of Chamilo 1.8.8): Check in the future whether this method is needed.
4881
     */
4882
    public function set_encoding($enc = 'UTF-8')
4883
    {
4884
        if ($this->debug > 0) {
4885
            error_log('New LP - In learnpath::set_encoding()', 0);
4886
        }
4887
4888
        $enc = api_refine_encoding_id($enc);
4889
        if (empty($enc)) {
4890
            $enc = api_get_system_encoding();
4891
        }
4892
        if (api_is_encoding_supported($enc)) {
4893
            $lp = $this->get_id();
4894
            if ($lp != 0) {
4895
                $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4896
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' 
4897
                        WHERE iid = ".$lp;
4898
                $res = Database::query($sql);
4899
                return $res;
4900
            }
4901
        }
4902
        return false;
4903
    }
4904
4905
    /**
4906
     * Sets the JS lib setting in the database directly.
4907
     * This is the JavaScript library file this lp needs to load on startup
4908
     * @param	string	Proximity setting
4909
     * @return  boolean True on update success. False otherwise.
4910
     */
4911
    public function set_jslib($lib = '')
4912
    {
4913
        if ($this->debug > 0) {
4914
            error_log('New LP - In learnpath::set_jslib()', 0);
4915
        }
4916
        $lp = $this->get_id();
4917
        $course_id = api_get_course_int_id();
4918
4919
        if ($lp != 0) {
4920
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4921
            $sql = "UPDATE $tbl_lp SET js_lib = '$lib' 
4922
                    WHERE iid = $lp";
4923
            $res = Database::query($sql);
4924
            return $res;
4925
        } else {
4926
            return false;
4927
        }
4928
    }
4929
4930
    /**
4931
     * Sets the name of the LP maker (publisher) (and save)
4932
     * @param	string	Optional string giving the new content_maker of this learnpath
4933
     * @return  boolean True
4934
     */
4935
    public function set_maker($name = '')
4936
    {
4937
        if ($this->debug > 0) {
4938
            error_log('New LP - In learnpath::set_maker()', 0);
4939
        }
4940
        if (empty($name)) {
4941
            return false;
4942
        }
4943
        $this->maker = $name;
4944
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4945
        $lp_id = $this->get_id();
4946
        $sql = "UPDATE $lp_table SET
4947
                content_maker = '".Database::escape_string($this->maker)."'
4948
                WHERE iid = $lp_id";
4949
        if ($this->debug > 2) {
4950
            error_log('New LP - lp updated with new content_maker : '.$this->maker, 0);
4951
        }
4952
        Database::query($sql);
4953
        return true;
4954
    }
4955
4956
    /**
4957
     * Sets the name of the current learnpath (and save)
4958
     * @param	string	$name Optional string giving the new name of this learnpath
4959
     * @return  boolean True/False
4960
     */
4961
    public function set_name($name = null)
4962
    {
4963
        if ($this->debug > 0) {
4964
            error_log('New LP - In learnpath::set_name()', 0);
4965
        }
4966
        if (empty($name)) {
4967
            return false;
4968
        }
4969
        $this->name = Database::escape_string($name);
4970
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4971
        $lp_id = $this->get_id();
4972
        $course_id = $this->course_info['real_id'];
4973
        $sql = "UPDATE $lp_table SET
4974
                name = '".Database::escape_string($this->name)."'
4975
                WHERE iid = $lp_id";
4976
        if ($this->debug > 2) {
4977
            error_log('New LP - lp updated with new name : '.$this->name, 0);
4978
        }
4979
        $result = Database::query($sql);
4980
        // If the lp is visible on the homepage, change his name there.
4981
        if (Database::affected_rows($result)) {
4982
            $session_id = api_get_session_id();
4983
            $session_condition = api_get_session_condition($session_id);
4984
            $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
4985
            $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4986
            $sql = "UPDATE $tbl_tool SET name = '$this->name'
4987
            	    WHERE
4988
            	        c_id = $course_id AND
4989
            	        (link='$link' AND image='scormbuilder.gif' $session_condition)";
4990
            Database::query($sql);
4991
            return true;
4992
        } else {
4993
            return false;
4994
        }
4995
    }
4996
4997
    /**
4998
     * Set index specified prefix terms for all items in this path
4999
     * @param   string  Comma-separated list of terms
5000
     * @param   string Xapian term prefix
5001
     * @return  boolean False on error, true otherwise
5002
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment Comma-separated at position 0 could not be parsed: Unknown type name 'Comma-separated' at position 0 in Comma-separated.
Loading history...
5003
    public function set_terms_by_prefix($terms_string, $prefix)
5004
    {
5005
        $course_id = api_get_course_int_id();
5006
        if (api_get_setting('search_enabled') !== 'true') {
5007
            return false;
5008
        }
5009
5010
        if (!extension_loaded('xapian')) {
5011
            return false;
5012
        }
5013
5014
        $terms_string = trim($terms_string);
5015
        $terms = explode(',', $terms_string);
5016
        array_walk($terms, 'trim_value');
5017
        $stored_terms = $this->get_common_index_terms_by_prefix($prefix);
5018
5019
        // Don't do anything if no change, verify only at DB, not the search engine.
5020
        if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0)) {
5021
            return false;
5022
        }
5023
5024
        require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API.
5025
        require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php';
5026
5027
        $items_table = Database::get_course_table(TABLE_LP_ITEM);
5028
        // TODO: Make query secure agains XSS : use member attr instead of post var.
5029
        $lp_id = intval($_POST['lp_id']);
5030
        $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id";
5031
        $result = Database::query($sql);
5032
        $di = new ChamiloIndexer();
5033
5034
        while ($lp_item = Database::fetch_array($result)) {
5035
            // Get search_did.
5036
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
5037
            $sql = 'SELECT * FROM %s
5038
                    WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d
5039
                    LIMIT 1';
5040
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);
5041
5042
            //echo $sql; echo '<br>';
5043
            $res = Database::query($sql);
5044
            if (Database::num_rows($res) > 0) {
5045
                $se_ref = Database::fetch_array($res);
5046
5047
                // Compare terms.
5048
                $doc = $di->get_document($se_ref['search_did']);
5049
                $xapian_terms = xapian_get_doc_terms($doc, $prefix);
5050
                $xterms = [];
5051
                foreach ($xapian_terms as $xapian_term) {
5052
                    $xterms[] = substr($xapian_term['name'], 1);
5053
                }
5054
5055
                $dterms = $terms;
5056
                $missing_terms = array_diff($dterms, $xterms);
5057
                $deprecated_terms = array_diff($xterms, $dterms);
5058
5059
                // Save it to search engine.
5060
                foreach ($missing_terms as $term) {
5061
                    $doc->add_term($prefix.$term, 1);
5062
                }
5063
                foreach ($deprecated_terms as $term) {
5064
                    $doc->remove_term($prefix.$term);
5065
                }
5066
                $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
5067
                $di->getDb()->flush();
5068
            }
5069
        }
5070
        return true;
5071
    }
5072
5073
    /**
5074
     * Sets the theme of the LP (local/remote) (and save)
5075
     * @param	string	Optional string giving the new theme of this learnpath
5076
     * @return   bool    Returns true if theme name is not empty
5077
     */
5078
    public function set_theme($name = '')
5079
    {
5080
        if ($this->debug > 0) {
5081
            error_log('New LP - In learnpath::set_theme()', 0);
5082
        }
5083
        $this->theme = $name;
5084
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5085
        $lp_id = $this->get_id();
5086
        $sql = "UPDATE $lp_table SET theme = '".Database::escape_string($this->theme)."'
5087
                WHERE iid = $lp_id";
5088
        if ($this->debug > 2) {
5089
            error_log('New LP - lp updated with new theme : '.$this->theme, 0);
5090
        }
5091
        Database::query($sql);
5092
5093
        return true;
5094
    }
5095
5096
    /**
5097
     * Sets the image of an LP (and save)
5098
     * @param	 string	Optional string giving the new image of this learnpath
5099
     * @return bool   Returns true if theme name is not empty
5100
     */
5101
    public function set_preview_image($name = '')
5102
    {
5103
        if ($this->debug > 0) {
5104
            error_log('New LP - In learnpath::set_preview_image()', 0);
5105
        }
5106
5107
        $this->preview_image = $name;
5108
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5109
        $lp_id = $this->get_id();
5110
        $sql = "UPDATE $lp_table SET
5111
                preview_image = '".Database::escape_string($this->preview_image)."'
5112
                WHERE iid = $lp_id";
5113
        if ($this->debug > 2) {
5114
            error_log('New LP - lp updated with new preview image : '.$this->preview_image, 0);
5115
        }
5116
        Database::query($sql);
5117
        return true;
5118
    }
5119
5120
    /**
5121
     * Sets the author of a LP (and save)
5122
     * @param	string	Optional string giving the new author of this learnpath
5123
     * @return   bool    Returns true if author's name is not empty
5124
     */
5125
    public function set_author($name = '')
5126
    {
5127
        if ($this->debug > 0) {
5128
            error_log('New LP - In learnpath::set_author()', 0);
5129
        }
5130
        $this->author = $name;
5131
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5132
        $lp_id = $this->get_id();
5133
        $sql = "UPDATE $lp_table SET author = '".Database::escape_string($name)."'
5134
                WHERE iid = $lp_id";
5135
        if ($this->debug > 2) {
5136
            error_log('New LP - lp updated with new preview author : '.$this->author, 0);
5137
        }
5138
        Database::query($sql);
5139
5140
        return true;
5141
    }
5142
5143
    /**
5144
     * Sets the hide_toc_frame parameter of a LP (and save)
5145
     * @param	int	1 if frame is hidden 0 then else
5146
     * @return   bool    Returns true if author's name is not empty
5147
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 1 at position 0 could not be parsed: Unknown type name '1' at position 0 in 1.
Loading history...
5148
    public function set_hide_toc_frame($hide)
5149
    {
5150
        if ($this->debug > 0) {
5151
            error_log('New LP - In learnpath::set_hide_toc_frame()', 0);
5152
        }
5153
        if (intval($hide) == $hide) {
5154
            $this->hide_toc_frame = $hide;
5155
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5156
            $lp_id = $this->get_id();
5157
            $sql = "UPDATE $lp_table SET
5158
                    hide_toc_frame = '".(int) $this->hide_toc_frame."'
5159
                    WHERE iid = $lp_id";
5160
            if ($this->debug > 2) {
5161
                error_log('New LP - lp updated with new preview hide_toc_frame : '.$this->author, 0);
5162
            }
5163
            Database::query($sql);
5164
5165
            return true;
5166
        } else {
5167
            return false;
5168
        }
5169
    }
5170
5171
    /**
5172
     * Sets the prerequisite of a LP (and save)
5173
     * @param	int		integer giving the new prerequisite of this learnpath
5174
     * @return 	bool 	returns true if prerequisite is not empty
5175
     */
5176
    public function set_prerequisite($prerequisite)
5177
    {
5178
        if ($this->debug > 0) {
5179
            error_log('New LP - In learnpath::set_prerequisite()', 0);
5180
        }
5181
        $this->prerequisite = intval($prerequisite);
5182
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5183
        $lp_id = $this->get_id();
5184
        $sql = "UPDATE $lp_table SET prerequisite = '".$this->prerequisite."'
5185
                WHERE iid = $lp_id";
5186
        if ($this->debug > 2) {
5187
            error_log('New LP - lp updated with new preview requisite : '.$this->requisite, 0);
5188
        }
5189
        Database::query($sql);
5190
        return true;
5191
    }
5192
5193
    /**
5194
     * Sets the location/proximity of the LP (local/remote) (and save)
5195
     * @param	string	Optional string giving the new location of this learnpath
5196
     * @return  boolean True on success / False on error
5197
     */
5198
    public function set_proximity($name = '')
5199
    {
5200
        if ($this->debug > 0) {
5201
            error_log('New LP - In learnpath::set_proximity()', 0);
5202
        }
5203
        if (empty($name)) {
5204
            return false;
5205
        }
5206
5207
        $this->proximity = $name;
5208
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5209
        $lp_id = $this->get_id();
5210
        $sql = "UPDATE $lp_table SET
5211
                    content_local = '".Database::escape_string($name)."'
5212
                WHERE iid = $lp_id";
5213
        if ($this->debug > 2) {
5214
            error_log('New LP - lp updated with new proximity : '.$this->proximity, 0);
5215
        }
5216
        Database::query($sql);
5217
        return true;
5218
    }
5219
5220
    /**
5221
     * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
5222
     * @param	integer	DB ID of the item
5223
     */
5224
    public function set_previous_item($id)
5225
    {
5226
        if ($this->debug > 0) {
5227
            error_log('New LP - In learnpath::set_previous_item()', 0);
5228
        }
5229
        $this->last = $id;
5230
    }
5231
5232
    /**
5233
     * Sets use_max_score
5234
     * @param   int  $use_max_score Optional string giving the new location of this learnpath
5235
     * @return  boolean True on success / False on error
5236
     */
5237
    public function set_use_max_score($use_max_score = 1)
5238
    {
5239
        if ($this->debug > 0) {
5240
            error_log('New LP - In learnpath::set_use_max_score()', 0);
5241
        }
5242
        $use_max_score = intval($use_max_score);
5243
        $this->use_max_score = $use_max_score;
5244
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5245
        $lp_id = $this->get_id();
5246
        $sql = "UPDATE $lp_table SET
5247
                    use_max_score = '".$this->use_max_score."'
5248
                WHERE iid = $lp_id";
5249
5250
        if ($this->debug > 2) {
5251
            error_log('New LP - lp updated with new use_max_score : '.$this->use_max_score, 0);
5252
        }
5253
        Database::query($sql);
5254
5255
        return true;
5256
    }
5257
5258
    /**
5259
     * Sets and saves the expired_on date
5260
     * @param   string  $expired_on Optional string giving the new author of this learnpath
5261
     * @return   bool    Returns true if author's name is not empty
5262
     */
5263
    public function set_expired_on($expired_on)
5264
    {
5265
        if ($this->debug > 0) {
5266
            error_log('New LP - In learnpath::set_expired_on()', 0);
5267
        }
5268
5269
        $em = Database::getManager();
5270
        /** @var CLp $lp */
5271
        $lp = $em
5272
            ->getRepository('ChamiloCourseBundle:CLp')
5273
            ->findOneBy(
5274
                [
5275
                    'iid' => $this->get_id()
5276
                ]
5277
            );
5278
5279
        if (!$lp) {
5280
            return false;
5281
        }
5282
5283
        $this->expired_on = !empty($expired_on) ? api_get_utc_datetime($expired_on, false, true) : null;
5284
5285
        $lp->setExpiredOn($this->expired_on);
5286
        $em->persist($lp);
5287
        $em->flush();
5288
5289
        if ($this->debug > 2) {
5290
            error_log('New LP - lp updated with new expired_on : '.$this->expired_on, 0);
5291
        }
5292
5293
        return true;
5294
    }
5295
5296
    /**
5297
     * Sets and saves the publicated_on date
5298
     * @param   string  $publicated_on Optional string giving the new author of this learnpath
5299
     * @return   bool    Returns true if author's name is not empty
5300
     */
5301
    public function set_publicated_on($publicated_on)
5302
    {
5303
        if ($this->debug > 0) {
5304
            error_log('New LP - In learnpath::set_expired_on()', 0);
5305
        }
5306
5307
        $em = Database::getManager();
5308
        /** @var CLp $lp */
5309
        $lp = $em
5310
            ->getRepository('ChamiloCourseBundle:CLp')
5311
            ->findOneBy(
5312
                [
5313
                    'iid' => $this->get_id()
5314
                ]
5315
            );
5316
5317
        if (!$lp) {
5318
            return false;
5319
        }
5320
5321
        $this->publicated_on = !empty($publicated_on) ? api_get_utc_datetime($publicated_on, false, true) : null;
5322
        $lp->setPublicatedOn($this->publicated_on);
5323
        $em->persist($lp);
5324
        $em->flush();
5325
5326
        if ($this->debug > 2) {
5327
            error_log('New LP - lp updated with new publicated_on : '.$this->publicated_on, 0);
5328
        }
5329
5330
        return true;
5331
    }
5332
5333
    /**
5334
     * Sets and saves the expired_on date
5335
     * @return   bool    Returns true if author's name is not empty
5336
     */
5337
    public function set_modified_on()
5338
    {
5339
        if ($this->debug > 0) {
5340
            error_log('New LP - In learnpath::set_expired_on()', 0);
5341
        }
5342
        $this->modified_on = api_get_utc_datetime();
5343
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5344
        $lp_id = $this->get_id();
5345
        $sql = "UPDATE $lp_table SET modified_on = '".$this->modified_on."'
5346
                WHERE iid = $lp_id";
5347
        if ($this->debug > 2) {
5348
            error_log('New LP - lp updated with new expired_on : '.$this->modified_on, 0);
5349
        }
5350
        Database::query($sql);
5351
5352
        return true;
5353
    }
5354
5355
    /**
5356
     * Sets the object's error message
5357
     * @param	string	Error message. If empty, reinits the error string
5358
     * @return 	void
5359
     */
5360
    public function set_error_msg($error = '')
5361
    {
5362
        if ($this->debug > 0) {
5363
            error_log('New LP - In learnpath::set_error_msg()', 0);
5364
        }
5365
        if (empty($error)) {
5366
            $this->error = '';
5367
        } else {
5368
            $this->error .= $error;
5369
        }
5370
    }
5371
5372
    /**
5373
     * Launches the current item if not 'sco'
5374
     * (starts timer and make sure there is a record ready in the DB)
5375
     * @param  boolean  $allow_new_attempt Whether to allow a new attempt or not
5376
     * @return boolean
5377
     */
5378
    public function start_current_item($allow_new_attempt = false)
5379
    {
5380
        if ($this->debug > 0) {
5381
            error_log('New LP - In learnpath::start_current_item()', 0);
5382
        }
5383
        if ($this->current != 0 && is_object($this->items[$this->current])) {
5384
            $type = $this->get_type();
5385
            $item_type = $this->items[$this->current]->get_type();
5386
            if (($type == 2 && $item_type != 'sco') ||
5387
                ($type == 3 && $item_type != 'au') ||
5388
                ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)
5389
            ) {
5390
                $this->items[$this->current]->open($allow_new_attempt);
5391
                $this->autocomplete_parents($this->current);
5392
                $prereq_check = $this->prerequisites_match($this->current);
5393
                $this->items[$this->current]->save(false, $prereq_check);
5394
                //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5395
            }
5396
            // If sco, then it is supposed to have been updated by some other call.
5397
            if ($item_type == 'sco') {
5398
                $this->items[$this->current]->restart();
5399
            }
5400
        }
5401
        if ($this->debug > 0) {
5402
            error_log('New LP - End of learnpath::start_current_item()', 0);
5403
        }
5404
        return true;
5405
    }
5406
5407
    /**
5408
     * Stops the processing and counters for the old item (as held in $this->last)
5409
     * @return boolean  True/False
5410
     */
5411
    public function stop_previous_item()
5412
    {
5413
        if ($this->debug > 0) {
5414
            error_log('New LP - In learnpath::stop_previous_item()', 0);
5415
        }
5416
5417
        if ($this->last != 0 && $this->last != $this->current && is_object($this->items[$this->last])) {
5418
            if ($this->debug > 2) {
5419
                error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' is object', 0);
5420
            }
5421
            switch ($this->get_type()) {
5422
                case '3':
5423
                    if ($this->items[$this->last]->get_type() != 'au') {
5424
                        if ($this->debug > 2) {
5425
                            error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 3 is <> au', 0);
5426
                        }
5427
                        $this->items[$this->last]->close();
5428
                    } else {
5429
                        if ($this->debug > 2) {
5430
                            error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
5431
                        }
5432
                    }
5433
                    break;
5434
                case '2':
5435
                    if ($this->items[$this->last]->get_type() != 'sco') {
5436
                        if ($this->debug > 2) {
5437
                            error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 2 is <> sco', 0);
5438
                        }
5439
                        $this->items[$this->last]->close();
5440
                    } else {
5441
                        if ($this->debug > 2) {
5442
                            error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
5443
                        }
5444
                    }
5445
                    break;
5446
                case '1':
5447
                default:
5448
                    if ($this->debug > 2) {
5449
                        error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 1 is asset', 0);
5450
                    }
5451
                    $this->items[$this->last]->close();
5452
                    break;
5453
            }
5454
        } else {
5455
            if ($this->debug > 2) {
5456
                error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
5457
            }
5458
            return false;
5459
        }
5460
        return true;
5461
    }
5462
5463
    /**
5464
     * Updates the default view mode from fullscreen to embedded and inversely
5465
     * @return	string The current default view mode ('fullscreen' or 'embedded')
5466
     */
5467
    public function update_default_view_mode()
5468
    {
5469
        if ($this->debug > 0) {
5470
            error_log('New LP - In learnpath::update_default_view_mode()', 0);
5471
        }
5472
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5473
        $sql = "SELECT * FROM $lp_table
5474
                WHERE iid = ".$this->get_id();
5475
        $res = Database::query($sql);
5476
        if (Database::num_rows($res) > 0) {
5477
            $row = Database::fetch_array($res);
5478
            $default_view_mode = $row['default_view_mod'];
5479
            $view_mode = $default_view_mode;
5480
            switch ($default_view_mode) {
5481
                case 'fullscreen': // default with popup
5482
                    $view_mode = 'embedded';
5483
                    break;
5484
                case 'embedded': // default view with left menu
5485
                    $view_mode = 'embedframe';
5486
                    break;
5487
                case 'embedframe': //folded menu
5488
                    $view_mode = 'impress';
5489
                    break;
5490
                case 'impress':
5491
                    $view_mode = 'fullscreen';
5492
                    break;
5493
            }
5494
            $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode'
5495
                    WHERE iid = ".$this->get_id();
5496
            Database::query($sql);
5497
            $this->mode = $view_mode;
5498
5499
            return $view_mode;
5500
        } else {
5501
            if ($this->debug > 2) {
5502
                error_log('New LP - Problem in update_default_view() - could not find LP '.$this->get_id().' in DB', 0);
5503
            }
5504
        }
5505
        return -1;
5506
    }
5507
5508
    /**
5509
     * Updates the default behaviour about auto-commiting SCORM updates
5510
     * @return	boolean	True if auto-commit has been set to 'on', false otherwise
5511
     */
5512
    public function update_default_scorm_commit()
5513
    {
5514
        if ($this->debug > 0) {
5515
            error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
5516
        }
5517
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5518
        $sql = "SELECT * FROM $lp_table
5519
                WHERE iid = ".$this->get_id();
5520
        $res = Database::query($sql);
5521
        if (Database::num_rows($res) > 0) {
5522
            $row = Database::fetch_array($res);
5523
            $force = $row['force_commit'];
5524
            if ($force == 1) {
5525
                $force = 0;
5526
                $force_return = false;
5527
            } elseif ($force == 0) {
5528
                $force = 1;
5529
                $force_return = true;
5530
            }
5531
            $sql = "UPDATE $lp_table SET force_commit = $force
5532
                    WHERE iid = ".$this->get_id();
5533
            Database::query($sql);
5534
            $this->force_commit = $force_return;
5535
5536
            return $force_return;
5537
        } else {
5538
            if ($this->debug > 2) {
5539
                error_log('New LP - Problem in update_default_scorm_commit() - could not find LP '.$this->get_id().' in DB', 0);
5540
            }
5541
        }
5542
        return -1;
5543
    }
5544
5545
    /**
5546
     * Updates the order of learning paths (goes through all of them by order and fills the gaps)
5547
     * @return	bool	True on success, false on failure
5548
     */
5549
    public function update_display_order()
5550
    {
5551
        $course_id = api_get_course_int_id();
5552
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5553
5554
        $sql = "SELECT * FROM $lp_table WHERE c_id = $course_id ORDER BY display_order";
5555
        $res = Database::query($sql);
5556
        if ($res === false) {
5557
            return false;
5558
        }
5559
5560
        $num = Database::num_rows($res);
5561
        // First check the order is correct, globally (might be wrong because
5562
        // of versions < 1.8.4).
5563
        if ($num > 0) {
5564
            $i = 1;
5565
            while ($row = Database::fetch_array($res)) {
5566
                if ($row['display_order'] != $i) {
5567
                    // If we find a gap in the order, we need to fix it.
5568
                    $sql = "UPDATE $lp_table SET display_order = $i
5569
                            WHERE iid = ".$row['iid'];
5570
                    Database::query($sql);
5571
                }
5572
                $i++;
5573
            }
5574
        }
5575
        return true;
5576
    }
5577
5578
    /**
5579
     * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
5580
     * @return	boolean	True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
5581
     */
5582
    public function update_reinit()
5583
    {
5584
        $course_id = api_get_course_int_id();
5585
        if ($this->debug > 0) {
5586
            error_log('New LP - In learnpath::update_reinit()', 0);
5587
        }
5588
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5589
        $sql = "SELECT * FROM $lp_table
5590
                WHERE iid = ".$this->get_id();
5591
        $res = Database::query($sql);
5592
        if (Database::num_rows($res) > 0) {
5593
            $row = Database::fetch_array($res);
5594
            $force = $row['prevent_reinit'];
5595
            if ($force == 1) {
5596
                $force = 0;
5597
            } elseif ($force == 0) {
5598
                $force = 1;
5599
            }
5600
            $sql = "UPDATE $lp_table SET prevent_reinit = $force
5601
                    WHERE iid = ".$this->get_id();
5602
            Database::query($sql);
5603
            $this->prevent_reinit = $force;
5604
            return $force;
5605
        } else {
5606
            if ($this->debug > 2) {
5607
                error_log('New LP - Problem in update_reinit() - could not find LP '.$this->get_id().' in DB', 0);
5608
            }
5609
        }
5610
        return -1;
5611
    }
5612
5613
    /**
5614
     * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag
5615
     *
5616
     * @return string 'single', 'multi' or 'seriousgame'
5617
     * @author ndiechburg <[email protected]>
5618
     **/
5619
    public function get_attempt_mode()
5620
    {
5621
        //Set default value for seriousgame_mode
5622
        if (!isset($this->seriousgame_mode)) {
5623
            $this->seriousgame_mode = 0;
5624
        }
5625
        // Set default value for prevent_reinit
5626
        if (!isset($this->prevent_reinit)) {
5627
            $this->prevent_reinit = 1;
5628
        }
5629
        if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) {
5630
            return 'seriousgame';
5631
        }
5632
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) {
5633
            return 'single';
5634
        }
5635
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) {
5636
            return 'multiple';
5637
        }
5638
        return 'single';
5639
    }
5640
5641
    /**
5642
     * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags
5643
     *
5644
     * @param string 'seriousgame', 'single' or 'multiple'
5645
     * @return boolean
5646
     * @author ndiechburg <[email protected]>
5647
     **/
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'seriousgame', 'single' at position 0 could not be parsed: Unknown type name ''seriousgame'' at position 0 in 'seriousgame', 'single'.
Loading history...
5648
    public function set_attempt_mode($mode)
5649
    {
5650
        switch ($mode) {
5651
            case 'seriousgame':
5652
                $sg_mode = 1;
5653
                $prevent_reinit = 1;
5654
                break;
5655
            case 'single':
5656
                $sg_mode = 0;
5657
                $prevent_reinit = 1;
5658
                break;
5659
            case 'multiple':
5660
                $sg_mode = 0;
5661
                $prevent_reinit = 0;
5662
                break;
5663
            default:
5664
                $sg_mode = 0;
5665
                $prevent_reinit = 0;
5666
                break;
5667
        }
5668
        $this->prevent_reinit = $prevent_reinit;
5669
        $this->seriousgame_mode = $sg_mode;
5670
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5671
        $sql = "UPDATE $lp_table SET
5672
                prevent_reinit = $prevent_reinit ,
5673
                seriousgame_mode = $sg_mode
5674
                WHERE iid = ".$this->get_id();
5675
        $res = Database::query($sql);
5676
        if ($res) {
5677
            return true;
5678
        } else {
5679
            return false;
5680
        }
5681
    }
5682
5683
    /**
5684
     * Switch between multiple attempt, single attempt or serious_game mode (only for scorm)
5685
     *
5686
     * @author ndiechburg <[email protected]>
5687
     **/
5688
    public function switch_attempt_mode()
5689
    {
5690
        if ($this->debug > 0) {
5691
            error_log('New LP - In learnpath::switch_attempt_mode()', 0);
5692
        }
5693
        $mode = $this->get_attempt_mode();
5694
        switch ($mode) {
5695
            case 'single':
5696
                $next_mode = 'multiple';
5697
                break;
5698
            case 'multiple':
5699
                $next_mode = 'seriousgame';
5700
                break;
5701
            case 'seriousgame':
5702
                $next_mode = 'single';
5703
                break;
5704
            default:
5705
                $next_mode = 'single';
5706
                break;
5707
        }
5708
        $this->set_attempt_mode($next_mode);
5709
    }
5710
5711
    /**
5712
     * Switch the lp in ktm mode. This is a special scorm mode with unique attempt
5713
     * but possibility to do again a completed item.
5714
     *
5715
     * @return boolean true if seriousgame_mode has been set to 1, false otherwise
5716
     * @author ndiechburg <[email protected]>
5717
     **/
5718
    public function set_seriousgame_mode()
5719
    {
5720
        if ($this->debug > 0) {
5721
            error_log('New LP - In learnpath::set_seriousgame_mode()', 0);
5722
        }
5723
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5724
        $sql = "SELECT * FROM $lp_table 
5725
                WHERE iid = ".$this->get_id();
5726
        $res = Database::query($sql);
5727
        if (Database::num_rows($res) > 0) {
5728
            $row = Database::fetch_array($res);
5729
            $force = $row['seriousgame_mode'];
5730
            if ($force == 1) {
5731
                $force = 0;
5732
            } elseif ($force == 0) {
5733
                $force = 1;
5734
            }
5735
            $sql = "UPDATE $lp_table SET seriousgame_mode = $force
5736
			        WHERE iid = ".$this->get_id();
5737
            Database::query($sql);
5738
            $this->seriousgame_mode = $force;
5739
            return $force;
5740
        } else {
5741
            if ($this->debug > 2) {
5742
                error_log('New LP - Problem in set_seriousgame_mode() - could not find LP '.$this->get_id().' in DB', 0);
5743
            }
5744
        }
5745
        return -1;
5746
    }
5747
5748
    /**
5749
     * Updates the "scorm_debug" value that shows or hide the debug window
5750
     * @return	boolean	True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
5751
     */
5752
    public function update_scorm_debug()
5753
    {
5754
        if ($this->debug > 0) {
5755
            error_log('New LP - In learnpath::update_scorm_debug()', 0);
5756
        }
5757
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5758
        $sql = "SELECT * FROM $lp_table
5759
                WHERE iid = ".$this->get_id();
5760
        $res = Database::query($sql);
5761
        if (Database::num_rows($res) > 0) {
5762
            $row = Database::fetch_array($res);
5763
            $force = $row['debug'];
5764
            if ($force == 1) {
5765
                $force = 0;
5766
            } elseif ($force == 0) {
5767
                $force = 1;
5768
            }
5769
            $sql = "UPDATE $lp_table SET debug = $force
5770
                    WHERE iid = ".$this->get_id();
5771
            Database::query($sql);
5772
            $this->scorm_debug = $force;
5773
            return $force;
5774
        } else {
5775
            if ($this->debug > 2) {
5776
                error_log('New LP - Problem in update_scorm_debug() - could not find LP '.$this->get_id().' in DB', 0);
5777
            }
5778
        }
5779
        return -1;
5780
    }
5781
5782
    /**
5783
     * Function that makes a call to the function sort_tree_array and create_tree_array
5784
     * @author Kevin Van Den Haute
5785
     * @param  array
5786
     */
5787
    public function tree_array($array)
5788
    {
5789
        if ($this->debug > 1) {
5790
            error_log('New LP - In learnpath::tree_array()', 0);
5791
        }
5792
        $array = $this->sort_tree_array($array);
5793
        $this->create_tree_array($array);
5794
    }
5795
5796
    /**
5797
     * Creates an array with the elements of the learning path tree in it
5798
     *
5799
     * @author Kevin Van Den Haute
5800
     * @param array $array
5801
     * @param int $parent
5802
     * @param int $depth
5803
     * @param array $tmp
5804
     */
5805
    public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = [])
5806
    {
5807
        if ($this->debug > 1) {
5808
            error_log('New LP - In learnpath::create_tree_array())', 0);
5809
        }
5810
5811
        if (is_array($array)) {
5812
            for ($i = 0; $i < count($array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
5813
                if ($array[$i]['parent_item_id'] == $parent) {
5814
                    if (!in_array($array[$i]['parent_item_id'], $tmp)) {
5815
                        $tmp[] = $array[$i]['parent_item_id'];
5816
                        $depth++;
5817
                    }
5818
                    $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
5819
                    $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null;
5820
                    $path = isset($array[$i]['path']) ? $array[$i]['path'] : null;
5821
5822
                    $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null;
5823
                    $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null;
5824
                    $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : '';
5825
                    $this->arrMenu[] = [
5826
                        'id' => $array[$i]['id'],
5827
                        'ref' => $ref,
5828
                        'item_type' => $array[$i]['item_type'],
5829
                        'title' => $array[$i]['title'],
5830
                        'path' => $path,
5831
                        'description' => $array[$i]['description'],
5832
                        'parent_item_id' => $array[$i]['parent_item_id'],
5833
                        'previous_item_id' => $array[$i]['previous_item_id'],
5834
                        'next_item_id' => $array[$i]['next_item_id'],
5835
                        'min_score' => $array[$i]['min_score'],
5836
                        'max_score' => $array[$i]['max_score'],
5837
                        'mastery_score' => $array[$i]['mastery_score'],
5838
                        'display_order' => $array[$i]['display_order'],
5839
                        'prerequisite' => $preq,
5840
                        'depth' => $depth,
5841
                        'audio' => $audio,
5842
                        'prerequisite_min_score' => $prerequisiteMinScore,
5843
                        'prerequisite_max_score' => $prerequisiteMaxScore
5844
                    ];
5845
5846
                    $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
5847
                }
5848
            }
5849
        }
5850
    }
5851
5852
    /**
5853
     * Sorts a multi dimensional array by parent id and display order
5854
     * @author Kevin Van Den Haute
5855
     *
5856
     * @param array $array (array with al the learning path items in it)
5857
     *
5858
     * @return array
5859
     */
5860
    public function sort_tree_array($array)
5861
    {
5862
        foreach ($array as $key => $row) {
5863
            $parent[$key] = $row['parent_item_id'];
5864
            $position[$key] = $row['display_order'];
5865
        }
5866
5867
        if (count($array) > 0) {
5868
            array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);
5869
        }
5870
5871
        return $array;
5872
    }
5873
5874
    /**
5875
     * Function that creates a html list of learning path items so that we can add audio files to them
5876
     * @author Kevin Van Den Haute
5877
     * @return string
5878
     */
5879
    public function overview()
5880
    {
5881
        if ($this->debug > 0) {
5882
            error_log('New LP - In learnpath::overview()', 0);
5883
        }
5884
5885
        $return = '';
5886
        $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null;
5887
5888
        // we need to start a form when we want to update all the mp3 files
5889
        if ($update_audio == 'true') {
5890
            $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">';
5891
        }
5892
        $return .= '<div id="message"></div>';
5893
        if (count($this->items) == 0) {
5894
            $return .= Display::return_message(get_lang('YouShouldAddItemsBeforeAttachAudio'), 'normal');
5895
        } else {
5896
            $return_audio = '<table class="data_table">';
5897
            $return_audio .= '<tr>';
5898
            $return_audio .= '<th width="40%">'.get_lang('Title').'</th>';
5899
            $return_audio .= '<th>'.get_lang('Audio').'</th>';
5900
            $return_audio .= '</tr>';
5901
5902
            if ($update_audio != 'true') {
5903
                $return .= '<div class="col-md-12">';
5904
                $return .= self::return_new_tree($update_audio);
5905
                $return .= '</div>';
5906
                $return .= Display::div(
5907
                    Display::url(get_lang('Save'), '#', ['id' => 'listSubmit', 'class' => 'btn btn-primary']),
5908
                    ['style' => 'float:left; margin-top:15px;width:100%']
5909
                );
5910
            } else {
5911
                $return_audio .= self::return_new_tree($update_audio);
5912
                $return .= $return_audio.'</table>';
5913
            }
5914
5915
            // We need to close the form when we are updating the mp3 files.
5916
            if ($update_audio == 'true') {
5917
                $return .= '<div class="footer-audio">';
5918
                $return .= Display::button(
5919
                    'save_audio',
5920
                    '<em class="fa fa-file-audio-o"></em> '.get_lang('SaveAudioAndOrganization'),
5921
                    ['class' => 'btn btn-primary', 'type' => 'submit']
5922
                );
5923
                $return .= '</div>';
5924
            }
5925
        }
5926
5927
        // We need to close the form when we are updating the mp3 files.
5928
        if ($update_audio == 'true' && isset($this->arrMenu) && count($this->arrMenu) != 0) {
5929
            $return .= '</form>';
5930
        }
5931
5932
        return $return;
5933
    }
5934
5935
    /**
5936
     * @param string string $update_audio
5937
     * @param bool $drop_element_here
5938
     * @return string
5939
     */
5940
    public function return_new_tree($update_audio = 'false', $drop_element_here = false)
5941
    {
5942
        $return = '';
5943
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
5944
        $course_id = api_get_course_int_id();
5945
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
5946
5947
        $sql = "SELECT * FROM $tbl_lp_item
5948
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
5949
5950
        $result = Database::query($sql);
5951
        $arrLP = [];
5952
        while ($row = Database::fetch_array($result)) {
5953
            $arrLP[] = [
5954
                'id' => $row['id'],
5955
                'item_type' => $row['item_type'],
5956
                'title' => Security::remove_XSS($row['title']),
5957
                'path' => $row['path'],
5958
                'description' => Security::remove_XSS($row['description']),
5959
                'parent_item_id' => $row['parent_item_id'],
5960
                'previous_item_id' => $row['previous_item_id'],
5961
                'next_item_id' => $row['next_item_id'],
5962
                'max_score' => $row['max_score'],
5963
                'min_score' => $row['min_score'],
5964
                'mastery_score' => $row['mastery_score'],
5965
                'prerequisite' => $row['prerequisite'],
5966
                'display_order' => $row['display_order'],
5967
                'audio' => $row['audio'],
5968
                'prerequisite_max_score' => $row['prerequisite_max_score'],
5969
                'prerequisite_min_score' => $row['prerequisite_min_score']
5970
            ];
5971
        }
5972
5973
        $this->tree_array($arrLP);
5974
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
5975
        unset($this->arrMenu);
5976
        $default_data = null;
5977
        $default_content = null;
5978
        $elements = [];
5979
        $return_audio = null;
5980
5981
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
5982
            $title = $arrLP[$i]['title'];
5983
            $title_cut = cut($arrLP[$i]['title'], 25);
5984
5985
            // Link for the documents
5986
            if ($arrLP[$i]['item_type'] == 'document') {
5987
                $url = api_get_self().'?'.api_get_cidreq().'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
5988
                $title_cut = Display::url(
5989
                    $title_cut,
5990
                    $url,
5991
                    [
5992
                        'class' => 'ajax moved',
5993
                        'data-title' => $title,
5994
                        'title' => $title
5995
                    ]
5996
                );
5997
            }
5998
5999
            // Detect if type is FINAL_ITEM to set path_id to SESSION
6000
            if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
6001
                Session::write('pathItem', $arrLP[$i]['path']);
6002
            }
6003
6004
            if (($i % 2) == 0) {
6005
                $oddClass = 'row_odd';
6006
            } else {
6007
                $oddClass = 'row_even';
6008
            }
6009
            $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'].'" class="'.$oddClass.'">';
6010
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
6011
6012
            if (file_exists('../img/lp_'.$icon_name.'.png')) {
6013
                $icon = Display::return_icon('lp_'.$icon_name.'.png');
6014
            } else {
6015
                if (file_exists('../img/lp_'.$icon_name.'.gif')) {
6016
                    $icon = Display::return_icon('lp_'.$icon_name.'.gif');
6017
                } else {
6018
                    if ($arrLP[$i]['item_type'] === TOOL_LP_FINAL_ITEM) {
6019
                        $icon = Display::return_icon('certificate.png');
6020
                    } else {
6021
                        $icon = Display::return_icon('folder_document.gif');
6022
                    }
6023
                }
6024
            }
6025
6026
            // The audio column.
6027
            $return_audio .= '<td align="left" style="padding-left:10px;">';
6028
            $audio = '';
6029
            if (!$update_audio || $update_audio <> 'true') {
6030
                if (empty($arrLP[$i]['audio'])) {
6031
                    $audio .= '';
6032
                }
6033
            } else {
6034
                $types = self::getChapterTypes();
6035
                if (!in_array($arrLP[$i]['item_type'], $types)) {
6036
                    $audio .= '<input type="file" name="mp3file'.$arrLP[$i]['id'].'" id="mp3file" />';
6037
                    if (!empty($arrLP[$i]['audio'])) {
6038
                        $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br />
6039
                        <input type="checkbox" name="removemp3' . $arrLP[$i]['id'].'" id="checkbox'.$arrLP[$i]['id'].'" />'.get_lang('RemoveAudio');
6040
                    }
6041
                }
6042
            }
6043
6044
            $return_audio .= Display::span($icon.' '.$title).
6045
            Display::tag(
6046
                'td',
6047
                $audio,
6048
                ['style' => '']
6049
            );
6050
            $return_audio .= '</td>';
6051
            $move_icon = '';
6052
            $move_item_icon = '';
6053
            $edit_icon = '';
6054
            $delete_icon = '';
6055
            $audio_icon = '';
6056
            $prerequisities_icon = '';
6057
            $forumIcon = '';
6058
            $previewIcon = '';
6059
6060
            if ($is_allowed_to_edit) {
6061
                if (!$update_audio || $update_audio <> 'true') {
6062
                    if ($arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
6063
                        $move_icon .= '<a class="moved" href="#">';
6064
                        $move_icon .= Display::return_icon(
6065
                            'move_everywhere.png',
6066
                            get_lang('Move'),
6067
                            [],
6068
                            ICON_SIZE_TINY
6069
                        );
6070
                        $move_icon .= '</a>';
6071
                    }
6072
                }
6073
6074
                // No edit for this item types
6075
                if (!in_array($arrLP[$i]['item_type'], ['sco', 'asset', 'final_item'])) {
6076
                    if ($arrLP[$i]['item_type'] != 'dir') {
6077
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">';
6078
                        $edit_icon .= Display::return_icon(
6079
                            'edit.png',
6080
                            get_lang('LearnpathEditModule'),
6081
                            [],
6082
                            ICON_SIZE_TINY
6083
                        );
6084
                        $edit_icon .= '</a>';
6085
6086
                        if (!in_array($arrLP[$i]['item_type'], ['forum', 'thread'])) {
6087
                            $forumThread = $this->items[$arrLP[$i]['id']]->getForumThread(
6088
                                $this->course_int_id,
6089
                                $this->lp_session_id
6090
                            );
6091
                            if ($forumThread) {
6092
                                $forumIconUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
6093
                                    'action' => 'dissociate_forum',
6094
                                    'id' => $arrLP[$i]['id'],
6095
                                    'lp_id' => $this->lp_id
6096
                                ]);
6097
                                $forumIcon = Display::url(
6098
                                    Display::return_icon('forum.png', get_lang('DissociateForumToLPItem'), [], ICON_SIZE_TINY),
6099
                                    $forumIconUrl,
6100
                                    ['class' => 'btn btn-default lp-btn-dissociate-forum']
6101
                                );
6102
                            } else {
6103
                                $forumIconUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
6104
                                    'action' => 'create_forum',
6105
                                    'id' => $arrLP[$i]['id'],
6106
                                    'lp_id' => $this->lp_id
6107
                                ]);
6108
                                $forumIcon = Display::url(
6109
                                    Display::return_icon('forum.png', get_lang('AssociateForumToLPItem'), [], ICON_SIZE_TINY),
6110
                                    $forumIconUrl,
6111
                                    ['class' => "btn btn-default lp-btn-associate-forum"]
6112
                                );
6113
                            }
6114
                        }
6115
                    } else {
6116
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">';
6117
                        $edit_icon .= Display::return_icon(
6118
                            'edit.png',
6119
                            get_lang('LearnpathEditModule'),
6120
                            [],
6121
                            ICON_SIZE_TINY
6122
                        );
6123
                        $edit_icon .= '</a>';
6124
                    }
6125
                } else {
6126
                    if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
6127
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" class="btn btn-default">';
6128
                        $edit_icon .= Display::return_icon(
6129
                            'edit.png',
6130
                            get_lang('Edit'),
6131
                            [],
6132
                            ICON_SIZE_TINY
6133
                        );
6134
                        $edit_icon .= '</a>';
6135
                    }
6136
                }
6137
6138
                $delete_icon .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=delete_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" onclick="return confirmation(\''.addslashes($title).'\');" class="btn btn-default">';
6139
                $delete_icon .= Display::return_icon(
6140
                    'delete.png',
6141
                    get_lang('LearnpathDeleteModule'),
6142
                    [],
6143
                    ICON_SIZE_TINY
6144
                );
6145
                $delete_icon .= '</a>';
6146
6147
                $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
6148
                $previewImage = Display::return_icon(
6149
                    'preview_view.png',
6150
                    get_lang('Preview'),
6151
                    [],
6152
                    ICON_SIZE_TINY
6153
                );
6154
6155
                switch ($arrLP[$i]['item_type']) {
6156
                    case TOOL_DOCUMENT:
6157
                    case TOOL_LP_FINAL_ITEM:
6158
                        $urlPreviewLink = api_get_self().'?'.api_get_cidreq().'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
6159
                        $previewIcon = Display::url(
6160
                            $previewImage,
6161
                            $urlPreviewLink,
6162
                            [
6163
                                'target' => '_blank',
6164
                                'class' => 'btn btn-default',
6165
                                'data-title' => $arrLP[$i]['title'],
6166
                                'title' => $arrLP[$i]['title']
6167
                            ]
6168
                        );
6169
                        break;
6170
                    case TOOL_THREAD:
6171
                    case TOOL_FORUM:
6172
                    case TOOL_QUIZ:
6173
                    case TOOL_STUDENTPUBLICATION:
6174
                    case TOOL_LP_FINAL_ITEM:
6175
                    case TOOL_LINK:
6176
                        //$target = '';
6177
                        //$class = 'btn btn-default ajax';
6178
                        //if ($arrLP[$i]['item_type'] == TOOL_LINK) {
6179
                        $class = 'btn btn-default';
6180
                        $target = '_blank';
6181
                        //}
6182
6183
                        $link = self::rl_get_resource_link_for_learnpath(
6184
                            $this->course_int_id,
6185
                            $this->lp_id,
6186
                            $arrLP[$i]['id'],
6187
                            0,
6188
                            ''
6189
                        );
6190
                        $previewIcon = Display::url(
6191
                            $previewImage,
6192
                            $link,
6193
                            [
6194
                                'class' => $class,
6195
                                'data-title' => $arrLP[$i]['title'],
6196
                                'title' => $arrLP[$i]['title'],
6197
                                'target' => $target
6198
                            ]
6199
                        );
6200
                        break;
6201
                    default:
6202
                        $previewIcon = Display::url(
6203
                            $previewImage,
6204
                            $url.'&action=view_item',
6205
                            ['class' => 'btn btn-default', 'target' => '_blank']
6206
                        );
6207
                        break;
6208
                }
6209
6210
                if ($arrLP[$i]['item_type'] != 'dir') {
6211
                    $prerequisities_icon = Display::url(
6212
                        Display::return_icon(
6213
                            'accept.png',
6214
                            get_lang('LearnpathPrerequisites'),
6215
                            [],
6216
                            ICON_SIZE_TINY
6217
                        ),
6218
                        $url.'&action=edit_item_prereq',
6219
                        ['class' => 'btn btn-default']
6220
                    );
6221
                    if ($arrLP[$i]['item_type'] != 'final_item') {
6222
                        $move_item_icon = Display::url(
6223
                            Display::return_icon(
6224
                                'move.png',
6225
                                get_lang('Move'),
6226
                                [],
6227
                                ICON_SIZE_TINY
6228
                            ),
6229
                            $url.'&action=move_item',
6230
                            ['class' => 'btn btn-default']
6231
                        );
6232
                    }
6233
                    $audio_icon = Display::url(
6234
                        Display::return_icon(
6235
                            'audio.png',
6236
                            get_lang('UplUpload'),
6237
                            [],
6238
                            ICON_SIZE_TINY
6239
                        ),
6240
                        $url.'&action=add_audio',
6241
                        ['class' => 'btn btn-default']
6242
                    );
6243
                }
6244
            }
6245
            if ($update_audio != 'true') {
6246
                $row = $move_icon.' '.$icon.
6247
                    Display::span($title_cut).
6248
                    Display::tag(
6249
                        'div',
6250
                        "<div class=\"btn-group btn-group-xs\">$previewIcon $audio $edit_icon $forumIcon $prerequisities_icon $move_item_icon $audio_icon $delete_icon</div>",
6251
                        ['class'=>'btn-toolbar button_actions']
6252
                    );
6253
            } else {
6254
                $row = Display::span($title.$icon).Display::span($audio, ['class'=>'button_actions']);
6255
            }
6256
6257
            $parent_id = $arrLP[$i]['parent_item_id'];
6258
            $default_data[$arrLP[$i]['id']] = $row;
6259
            $default_content[$arrLP[$i]['id']] = $arrLP[$i];
6260
6261
            if (empty($parent_id)) {
6262
                $elements[$arrLP[$i]['id']]['data'] = $row;
6263
                $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
6264
            } else {
6265
                $parent_arrays = [];
6266
                if ($arrLP[$i]['depth'] > 1) {
6267
                    //Getting list of parents
6268
                    for ($j = 0; $j < $arrLP[$i]['depth']; $j++) {
6269
                        foreach ($arrLP as $item) {
6270
                            if ($item['id'] == $parent_id) {
6271
                                if ($item['parent_item_id'] == 0) {
6272
                                    $parent_id = $item['id'];
6273
                                    break;
6274
                                } else {
6275
                                    $parent_id = $item['parent_item_id'];
6276
                                    if (empty($parent_arrays)) {
6277
                                        $parent_arrays[] = intval($item['id']);
6278
                                    }
6279
                                    $parent_arrays[] = $parent_id;
6280
                                    break;
6281
                                }
6282
                            }
6283
                        }
6284
                    }
6285
                }
6286
6287
                if (!empty($parent_arrays)) {
6288
                    $parent_arrays = array_reverse($parent_arrays);
6289
                    $val = '$elements';
6290
                    $x = 0;
6291
                    foreach ($parent_arrays as $item) {
6292
                        if ($x != count($parent_arrays) - 1) {
6293
                            $val .= '["'.$item.'"]["children"]';
6294
                        } else {
6295
                            $val .= '["'.$item.'"]["children"]';
6296
                        }
6297
                        $x++;
6298
                    }
6299
                    $val .= "";
6300
                    $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; ";
6301
                    eval($code_str);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
6302
                } else {
6303
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row;
6304
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
6305
                }
6306
            }
6307
        }
6308
6309
        $list = '<ul id="lp_item_list">';
6310
        $tree = self::print_recursive(
6311
            $elements,
6312
            $default_data,
6313
            $default_content
6314
        );
6315
6316
        if (!empty($tree)) {
6317
            $list .= $tree;
6318
        } else {
6319
            if ($drop_element_here) {
6320
                $list .= Display::return_message(get_lang("DragAndDropAnElementHere"));
6321
            }
6322
        }
6323
        $list .= '</ul>';
6324
6325
        $return .= Display::panelCollapse(
6326
            $this->name,
6327
            $list,
6328
            'scorm-list',
6329
            null,
6330
            'scorm-list-accordion',
6331
            'scorm-list-collapse'
6332
        );
6333
6334
        if ($update_audio == 'true') {
6335
            $return = $return_audio;
6336
        }
6337
6338
        return $return;
6339
    }
6340
6341
    /**
6342
     * @param array $elements
6343
     * @param array $default_data
6344
     * @param array $default_content
6345
     * @return string
6346
     */
6347
    public function print_recursive($elements, $default_data, $default_content)
6348
    {
6349
        $return = '';
6350
        foreach ($elements as $key => $item) {
6351
            if (isset($item['load_data']) || empty($item['data'])) {
6352
                $item['data'] = $default_data[$item['load_data']];
6353
                $item['type'] = $default_content[$item['load_data']]['item_type'];
6354
            }
6355
            $sub_list = '';
6356
            if (isset($item['type']) && $item['type'] == 'dir') {
6357
                $sub_list = Display::tag('li', '', ['class'=>'sub_item empty']); // empty value
6358
            }
6359
            if (empty($item['children'])) {
6360
                $sub_list = Display::tag('ul', $sub_list, ['id'=>'UL_'.$key, 'class'=>'record li_container']);
6361
                $active = null;
6362
                if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) {
6363
                    $active = 'active';
6364
                }
6365
                $return .= Display::tag(
6366
                    'li',
6367
                    Display::div($item['data'], ['class'=>"item_data $active"]).$sub_list,
6368
                    ['id'=>$key, 'class'=>'record li_container']
6369
                );
6370
            } else {
6371
                // Sections
6372
                if (isset($item['children'])) {
6373
                    $data = self::print_recursive($item['children'], $default_data, $default_content);
6374
                }
6375
                $sub_list = Display::tag('ul', $sub_list.$data, ['id'=>'UL_'.$key, 'class'=>'record li_container']);
6376
                $return .= Display::tag(
6377
                    'li',
6378
                    Display::div($item['data'], ['class'=>'item_data']).$sub_list,
6379
                    ['id'=>$key, 'class'=>'record li_container']
6380
                );
6381
            }
6382
        }
6383
6384
        return $return;
6385
    }
6386
6387
    /**
6388
     * This function builds the action menu
6389
     * @param bool $returnContent Optional
6390
     * @param bool $showRequirementButtons Optional. Allow show the requirements button
6391
     * @param bool $isConfigPage Optional. If is the config page, show the edit button
6392
     * @param bool $allowExpand Optional. Allow show the expand/contract button
6393
     * @return string
6394
     */
6395
    public function build_action_menu(
6396
        $returnContent = false,
6397
        $showRequirementButtons = true,
6398
        $isConfigPage = false,
6399
        $allowExpand = true
6400
    ) {
6401
        $actionsLeft = '';
6402
        $actionsRight = '';
6403
6404
        $actionsLeft .= Display::url(
6405
            Display::return_icon(
6406
                'preview_view.png',
6407
                get_lang('Preview'),
6408
                '',
6409
                ICON_SIZE_MEDIUM
6410
            ),
6411
            'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6412
                'action' => 'view',
6413
                'lp_id' => $this->lp_id,
6414
                'isStudentView' => 'true'
6415
            ])
6416
        );
6417
        $actionsLeft .= Display::url(
6418
            Display::return_icon(
6419
                'upload_audio.png',
6420
                get_lang('UpdateAllAudioFragments'),
6421
                '',
6422
                ICON_SIZE_MEDIUM
6423
            ),
6424
            'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6425
                'action' => 'admin_view',
6426
                'lp_id' => $this->lp_id,
6427
                'updateaudio' => 'true'
6428
            ])
6429
        );
6430
6431
        if (!$isConfigPage) {
6432
            $actionsLeft .= Display::url(
6433
                Display::return_icon(
6434
                    'settings.png',
6435
                    get_lang('CourseSettings'),
6436
                    '',
6437
                    ICON_SIZE_MEDIUM
6438
                ),
6439
                'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6440
                    'action' => 'edit',
6441
                    'lp_id' => $this->lp_id
6442
                ])
6443
            );
6444
        } else {
6445
            $actionsLeft .= Display::url(
6446
                Display::return_icon(
6447
                    'edit.png',
6448
                    get_lang('Edit'),
6449
                    '',
6450
                    ICON_SIZE_MEDIUM
6451
                ),
6452
                'lp_controller.php?'.http_build_query([
6453
                    'action' => 'build',
6454
                    'lp_id' => $this->lp_id
6455
                ]).'&'.api_get_cidreq()
6456
            );
6457
        }
6458
6459
        if ($allowExpand) {
6460
            $actionsLeft .= Display::url(
6461
                Display::return_icon(
6462
                    'expand.png',
6463
                    get_lang('Expand'),
6464
                    ['id' => 'expand'],
6465
                    ICON_SIZE_MEDIUM
6466
                ).
6467
                Display::return_icon(
6468
                    'contract.png',
6469
                    get_lang('Collapse'),
6470
                    ['id' => 'contract', 'class' => 'hide'],
6471
                    ICON_SIZE_MEDIUM
6472
                ),
6473
                '#',
6474
                ['role' => 'button', 'id' => 'hide_bar_template']
6475
            );
6476
        }
6477
6478
        if ($showRequirementButtons) {
6479
            $buttons = [
6480
                [
6481
                    'title' => get_lang('SetPrerequisiteForEachItem'),
6482
                    'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6483
                        'action' => 'set_previous_step_as_prerequisite',
6484
                        'lp_id' => $this->lp_id
6485
                    ])
6486
                ],
6487
                [
6488
                    'title' => get_lang('ClearAllPrerequisites'),
6489
                    'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6490
                        'action' => 'clear_prerequisites',
6491
                        'lp_id' => $this->lp_id
6492
                    ])
6493
                ],
6494
            ];
6495
            $actionsRight = Display::groupButtonWithDropDown(
6496
                get_lang('PrerequisitesOptions'),
6497
                $buttons,
6498
                true
6499
            );
6500
        }
6501
6502
        $toolbar = Display::toolbarAction(
6503
            'actions-lp-controller',
6504
            [$actionsLeft, $actionsRight]
6505
        );
6506
6507
        if ($returnContent) {
6508
            return $toolbar;
6509
        }
6510
6511
        echo $toolbar;
6512
    }
6513
6514
    /**
6515
     * Creates the default learning path folder
6516
     * @param array $course
6517
     * @param int $creatorId
6518
     *
6519
     * @return bool
6520
     */
6521
    public static function generate_learning_path_folder($course, $creatorId = 0)
6522
    {
6523
        // Creating learning_path folder
6524
        $dir = '/learning_path';
6525
        $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document';
6526
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6527
6528
        $folder = false;
6529
        if (!is_dir($filepath.'/'.$dir)) {
6530
            $folderData = create_unexisting_directory(
6531
                $course,
6532
                $creatorId,
6533
                0,
6534
                0,
6535
                0,
6536
                $filepath,
6537
                $dir,
6538
                get_lang('LearningPaths'),
6539
                0
6540
            );
6541
            if (!empty($folderData)) {
6542
                $folder = true;
6543
            }
6544
        } else {
6545
            $folder = true;
6546
        }
6547
6548
        return $folder;
6549
    }
6550
6551
    /**
6552
     * @param array $course
6553
     * @param string $lp_name
6554
     * @param int $creatorId
6555
     *
6556
     * @return array
6557
     */
6558
    public function generate_lp_folder($course, $lp_name = '', $creatorId = 0)
6559
    {
6560
        $filepath = '';
6561
        $dir = '/learning_path/';
6562
6563
        if (empty($lp_name)) {
6564
            $lp_name = $this->name;
6565
        }
6566
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6567
6568
        $folder = self::generate_learning_path_folder($course, $creatorId);
6569
6570
        // Limits title size
6571
        $title = api_substr(api_replace_dangerous_char($lp_name), 0, 80);
6572
        $dir = $dir.$title;
6573
6574
        // Creating LP folder
6575
        $documentId = null;
6576
6577
        if ($folder) {
6578
            $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document';
6579
            if (!is_dir($filepath.'/'.$dir)) {
6580
                $folderData = create_unexisting_directory(
6581
                    $course,
6582
                    $creatorId,
6583
                    0,
6584
                    0,
6585
                    0,
6586
                    $filepath,
6587
                    $dir,
6588
                    $lp_name
6589
                );
6590
                if (!empty($folderData)) {
6591
                    $folder = true;
6592
                }
6593
6594
                $documentId = $folderData['id'];
6595
            } else {
6596
                $folder = true;
6597
            }
6598
            $dir = $dir.'/';
6599
            if ($folder) {
6600
                $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document'.$dir;
6601
            }
6602
        }
6603
6604
        if (empty($documentId)) {
6605
            $dir = api_remove_trailing_slash($dir);
6606
            $documentId = DocumentManager::get_document_id($course, $dir, 0);
6607
        }
6608
6609
        $array = [
6610
            'dir' => $dir,
6611
            'filepath' => $filepath,
6612
            'folder' => $folder,
6613
            'id' => $documentId
6614
        ];
6615
6616
        return $array;
6617
    }
6618
6619
    /**
6620
     * Create a new document //still needs some finetuning
6621
     * @param array $courseInfo
6622
     * @param string $content
6623
     * @param string $title
6624
     * @param string $extension
6625
     * @param int $parentId
6626
     * @param int $creatorId creator id
6627
     *
6628
     * @return int
6629
     */
6630
    public function create_document(
6631
        $courseInfo,
6632
        $content = '',
6633
        $title = '',
6634
        $extension = 'html',
6635
        $parentId = 0,
6636
        $creatorId = 0
6637
    ) {
6638
        if (!empty($courseInfo)) {
6639
            $course_id = $courseInfo['real_id'];
6640
        } else {
6641
            $course_id = api_get_course_int_id();
6642
        }
6643
6644
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6645
        $sessionId = api_get_session_id();
6646
6647
        // Generates folder
6648
        $result = $this->generate_lp_folder($courseInfo);
6649
        $dir = $result['dir'];
6650
6651
        if (empty($parentId) || $parentId == '/') {
6652
            $postDir = isset($_POST['dir']) ? $_POST['dir'] : $dir;
6653
            $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting.
6654
6655
            if ($parentId === '/') {
6656
                $dir = '/';
6657
            }
6658
6659
            // Please, do not modify this dirname formatting.
6660
            if (strstr($dir, '..')) {
6661
                $dir = '/';
6662
            }
6663
6664
            if (!empty($dir[0]) && $dir[0] == '.') {
6665
                $dir = substr($dir, 1);
6666
            }
6667
            if (!empty($dir[0]) && $dir[0] != '/') {
6668
                $dir = '/'.$dir;
6669
            }
6670
            if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6671
                $dir .= '/';
6672
            }
6673
        } else {
6674
            $parentInfo = DocumentManager::get_document_data_by_id(
6675
                $parentId,
6676
                $courseInfo['code']
6677
            );
6678
            if (!empty($parentInfo)) {
6679
                $dir = $parentInfo['path'].'/';
6680
            }
6681
        }
6682
6683
        $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir;
6684
        if (!is_dir($filepath)) {
6685
            $dir = '/';
6686
            $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir;
6687
        }
6688
6689
        // stripslashes() before calling api_replace_dangerous_char() because $_POST['title']
6690
        // is already escaped twice when it gets here.
6691
6692
        $originalTitle = !empty($title) ? $title : $_POST['title'];
6693
        if (!empty($title)) {
6694
            $title = api_replace_dangerous_char(stripslashes($title));
6695
        } else {
6696
            $title = api_replace_dangerous_char(stripslashes($_POST['title']));
6697
        }
6698
6699
        $title = disable_dangerous_file($title);
6700
        $filename = $title;
6701
        $content = !empty($content) ? $content : $_POST['content_lp'];
6702
        $tmp_filename = $filename;
6703
6704
        $i = 0;
6705
        while (file_exists($filepath.$tmp_filename.'.'.$extension)) {
6706
            $tmp_filename = $filename.'_'.++ $i;
6707
        }
6708
6709
        $filename = $tmp_filename.'.'.$extension;
6710
        if ($extension == 'html') {
6711
            $content = stripslashes($content);
6712
            $content = str_replace(
6713
                api_get_path(WEB_COURSE_PATH),
6714
                api_get_path(REL_PATH).'courses/',
6715
                $content
6716
            );
6717
6718
            // Change the path of mp3 to absolute.
6719
6720
            // The first regexp deals with :// urls.
6721
            $content = preg_replace(
6722
                "|(flashvars=\"file=)([^:/]+)/|",
6723
                "$1".api_get_path(
6724
                    REL_COURSE_PATH
6725
                ).$courseInfo['path'].'/document/',
6726
                $content
6727
            );
6728
            // The second regexp deals with audio/ urls.
6729
            $content = preg_replace(
6730
                "|(flashvars=\"file=)([^/]+)/|",
6731
                "$1".api_get_path(
6732
                    REL_COURSE_PATH
6733
                ).$courseInfo['path'].'/document/$2/',
6734
                $content
6735
            );
6736
            // For flv player: To prevent edition problem with firefox, we have to use a strange tip (don't blame me please).
6737
            $content = str_replace(
6738
                '</body>',
6739
                '<style type="text/css">body{}</style></body>',
6740
                $content
6741
            );
6742
        }
6743
6744
        if (!file_exists($filepath.$filename)) {
6745
            if ($fp = @ fopen($filepath.$filename, 'w')) {
6746
                fputs($fp, $content);
6747
                fclose($fp);
6748
6749
                $file_size = filesize($filepath.$filename);
6750
                $save_file_path = $dir.$filename;
6751
6752
                $document_id = add_document(
6753
                    $courseInfo,
6754
                    $save_file_path,
6755
                    'file',
6756
                    $file_size,
6757
                    $tmp_filename,
6758
                    '',
6759
                    0, //readonly
6760
                    true,
6761
                    null,
6762
                    $sessionId,
6763
                    $creatorId
6764
                );
6765
6766
                if ($document_id) {
6767
                    api_item_property_update(
6768
                        $courseInfo,
6769
                        TOOL_DOCUMENT,
6770
                        $document_id,
6771
                        'DocumentAdded',
6772
                        $creatorId,
6773
                        null,
6774
                        null,
6775
                        null,
6776
                        null,
6777
                        $sessionId
6778
                    );
6779
6780
                    $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : '';
6781
                    $new_title = $originalTitle;
6782
6783
                    if ($new_comment || $new_title) {
6784
                        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6785
                        $ct = '';
6786
                        if ($new_comment) {
6787
                            $ct .= ", comment='".Database::escape_string($new_comment)."'";
6788
                        }
6789
                        if ($new_title) {
6790
                            $ct .= ", title='".Database::escape_string($new_title)."' ";
6791
                        }
6792
6793
                        $sql = "UPDATE ".$tbl_doc." SET ".substr($ct, 1)."
6794
                               WHERE c_id = ".$course_id." AND id = ".$document_id;
6795
                        Database::query($sql);
6796
                    }
6797
                }
6798
                return $document_id;
6799
            }
6800
        }
6801
    }
6802
6803
    /**
6804
     * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
6805
     * @param 	array $_course array
6806
     * @return 	void
6807
     */
6808
    public function edit_document($_course)
6809
    {
6810
        $course_id = api_get_course_int_id();
6811
        $urlAppend = api_get_configuration_value('url_append');
6812
        // Please, do not modify this dirname formatting.
6813
        $postDir = isset($_POST['dir']) ? $_POST['dir'] : '';
6814
        $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir;
6815
6816
        if (strstr($dir, '..')) {
6817
            $dir = '/';
6818
        }
6819
6820
        if (isset($dir[0]) && $dir[0] == '.') {
6821
            $dir = substr($dir, 1);
6822
        }
6823
6824
        if (isset($dir[0]) && $dir[0] != '/') {
6825
            $dir = '/'.$dir;
6826
        }
6827
6828
        if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6829
            $dir .= '/';
6830
        }
6831
6832
        $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$dir;
6833
6834
        if (!is_dir($filepath)) {
6835
            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
6836
        }
6837
6838
        $table_doc = Database::get_course_table(TABLE_DOCUMENT);
6839
6840
        if (isset($_POST['path']) && !empty($_POST['path'])) {
6841
            $document_id = intval($_POST['path']);
6842
            $sql = "SELECT path FROM ".$table_doc."
6843
                    WHERE c_id = $course_id AND id = ".$document_id;
6844
            $res = Database::query($sql);
6845
            $row = Database::fetch_array($res);
6846
            $content = stripslashes($_POST['content_lp']);
6847
            $file = $filepath.$row['path'];
6848
6849
            if ($fp = @fopen($file, 'w')) {
6850
                $content = str_replace(
6851
                    api_get_path(WEB_COURSE_PATH),
6852
                    $urlAppend.api_get_path(REL_COURSE_PATH),
6853
                    $content
6854
                );
6855
                // Change the path of mp3 to absolute.
6856
                // The first regexp deals with :// urls.
6857
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/', $content);
6858
                // The second regexp deals with audio/ urls.
6859
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/$2/', $content);
6860
                fputs($fp, $content);
6861
                fclose($fp);
6862
6863
                $sql = "UPDATE ".$table_doc." SET
6864
                            title='".Database::escape_string($_POST['title'])."'
6865
                        WHERE c_id = ".$course_id." AND id = ".$document_id;
6866
6867
                Database::query($sql);
6868
            }
6869
        }
6870
    }
6871
6872
    /**
6873
     * Displays the selected item, with a panel for manipulating the item
6874
     * @param int $item_id
6875
     * @param string $msg
6876
     * @param bool $show_actions
6877
     * @return string
6878
     */
6879
    public function display_item($item_id, $msg = null, $show_actions = true)
6880
    {
6881
        $course_id = api_get_course_int_id();
6882
        $return = '';
6883
        if (is_numeric($item_id)) {
6884
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
6885
            $sql = "SELECT lp.* FROM $tbl_lp_item as lp
6886
                    WHERE lp.iid = ".intval($item_id);
6887
            $result = Database::query($sql);
6888
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6889
                $_SESSION['parent_item_id'] = $row['item_type'] == 'dir' ? $item_id : 0;
6890
6891
                // Prevents wrong parent selection for document, see Bug#1251.
6892
                if ($row['item_type'] != 'dir') {
6893
                    $_SESSION['parent_item_id'] = $row['parent_item_id'];
6894
                }
6895
6896
                if ($show_actions) {
6897
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6898
                }
6899
                $return .= '<div style="padding:10px;">';
6900
6901
                if ($msg != '') {
6902
                    $return .= $msg;
6903
                }
6904
6905
                $return .= '<h3>'.$row['title'].'</h3>';
6906
6907
                switch ($row['item_type']) {
6908
                    case TOOL_THREAD:
6909
                        $link = $this->rl_get_resource_link_for_learnpath(
6910
                            $course_id,
6911
                            $row['lp_id'],
6912
                            $item_id,
6913
                            0
6914
                        );
6915
                        $return .= Display::url(
6916
                            get_lang('GoToThread'),
6917
                            $link,
6918
                            ['class' => 'btn btn-primary']
6919
                        );
6920
                        break;
6921
                    case TOOL_FORUM:
6922
                        $return .= Display::url(
6923
                            get_lang('GoToForum'),
6924
                            api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$row['path'],
6925
                            ['class' => 'btn btn-primary']
6926
                        );
6927
                        break;
6928
                    case TOOL_QUIZ:
6929
                        if (!empty($row['path'])) {
6930
                            $exercise = new Exercise();
6931
                            $exercise->read($row['path']);
6932
                            $return .= $exercise->description.'<br />';
6933
                            $return .= Display::url(
6934
                                get_lang('GoToExercise'),
6935
                                api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&exerciseId='.$exercise->id,
6936
                                ['class' => 'btn btn-primary']
6937
                            );
6938
                        }
6939
                        break;
6940
                    case TOOL_LP_FINAL_ITEM:
6941
                        $return .= $this->getSavedFinalItem();
6942
                        break;
6943
                    case TOOL_DOCUMENT:
6944
                        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6945
                        $sql_doc = "SELECT path FROM ".$tbl_doc."
6946
                                    WHERE c_id = ".$course_id." AND iid = ".intval($row['path']);
6947
                        $result = Database::query($sql_doc);
6948
                        $path_file = Database::result($result, 0, 0);
6949
                        $path_parts = pathinfo($path_file);
6950
                        // TODO: Correct the following naive comparisons.
6951
                        if (in_array($path_parts['extension'], [
6952
                            'html',
6953
                            'txt',
6954
                            'png',
6955
                            'jpg',
6956
                            'JPG',
6957
                            'jpeg',
6958
                            'JPEG',
6959
                            'gif',
6960
                            'swf',
6961
                            'pdf',
6962
                            'htm'
6963
                        ])) {
6964
                            $return .= $this->display_document($row['path'], true, true);
6965
                        }
6966
                        break;
6967
                    case TOOL_HOTPOTATOES:
6968
                        $return .= $this->display_document($row['path'], false, true);
6969
                        break;
6970
6971
                }
6972
                $return .= '</div>';
6973
            }
6974
        }
6975
6976
        return $return;
6977
    }
6978
6979
    /**
6980
     * Shows the needed forms for editing a specific item
6981
     * @param int $item_id
6982
     * @return string
6983
     */
6984
    public function display_edit_item($item_id)
6985
    {
6986
        $course_id = api_get_course_int_id();
6987
        $return = '';
6988
        if (is_numeric($item_id)) {
6989
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
6990
            $sql = "SELECT * FROM $tbl_lp_item
6991
                    WHERE iid = ".intval($item_id);
6992
            $res = Database::query($sql);
6993
            $row = Database::fetch_array($res);
6994
            switch ($row['item_type']) {
6995
                case 'dir':
6996
                case 'asset':
6997
                case 'sco':
6998
                    if (isset($_GET['view']) && $_GET['view'] == 'build') {
6999
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
7000
                        $return .= $this->display_item_form(
7001
                            $row['item_type'],
7002
                            get_lang('EditCurrentChapter').' :',
7003
                            'edit',
7004
                            $item_id,
7005
                            $row
7006
                        );
7007
                    } else {
7008
                        $return .= $this->display_item_small_form(
7009
                            $row['item_type'],
7010
                            get_lang('EditCurrentChapter').' :',
7011
                            $row
7012
                        );
7013
                    }
7014
                    break;
7015
                case TOOL_DOCUMENT:
7016
                    $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
7017
                    $sql = "SELECT lp.*, doc.path as dir
7018
                            FROM $tbl_lp_item as lp
7019
                            LEFT JOIN $tbl_doc as doc
7020
                            ON (doc.iid = lp.path AND lp.c_id = doc.c_id)
7021
                            WHERE
7022
                                doc.c_id = $course_id AND
7023
                                lp.iid = ".intval($item_id);
7024
                    $res_step = Database::query($sql);
7025
                    $row_step = Database::fetch_array($res_step, 'ASSOC');
7026
                    $return .= $this->display_manipulate(
7027
                        $item_id,
7028
                        $row['item_type']
7029
                    );
7030
                    $return .= $this->display_document_form(
7031
                        'edit',
7032
                        $item_id,
7033
                        $row_step
7034
                    );
7035
                    break;
7036
                case TOOL_LINK:
7037
                    $link_id = (string) $row['path'];
7038
                    if (ctype_digit($link_id)) {
7039
                        $tbl_link = Database::get_course_table(TABLE_LINK);
7040
                        $sql_select = 'SELECT url FROM '.$tbl_link.'
7041
                                       WHERE c_id = '.$course_id.' AND iid = '.intval($link_id);
7042
                        $res_link = Database::query($sql_select);
7043
                        $row_link = Database::fetch_array($res_link);
7044
                        if (is_array($row_link)) {
7045
                            $row['url'] = $row_link['url'];
7046
                        }
7047
                    }
7048
                    $return .= $this->display_manipulate(
7049
                        $item_id,
7050
                        $row['item_type']
7051
                    );
7052
                    $return .= $this->display_link_form('edit', $item_id, $row);
7053
                    break;
7054
                case TOOL_LP_FINAL_ITEM:
7055
                    Session::write('finalItem', true);
7056
                    $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
7057
                    $sql = "SELECT lp.*, doc.path as dir
7058
                            FROM $tbl_lp_item as lp
7059
                            LEFT JOIN $tbl_doc as doc
7060
                            ON (doc.iid = lp.path AND lp.c_id = doc.c_id)
7061
                            WHERE
7062
                                doc.c_id = $course_id AND
7063
                                lp.iid = ".intval($item_id);
7064
                    $res_step = Database::query($sql);
7065
                    $row_step = Database::fetch_array($res_step, 'ASSOC');
7066
                    $return .= $this->display_manipulate(
7067
                        $item_id,
7068
                        $row['item_type']
7069
                    );
7070
                    $return .= $this->display_document_form(
7071
                        'edit',
7072
                        $item_id,
7073
                        $row_step
7074
                    );
7075
                    break;
7076
                case TOOL_QUIZ:
7077
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
7078
                    $return .= $this->display_quiz_form('edit', $item_id, $row);
7079
                    break;
7080
                case TOOL_HOTPOTATOES:
7081
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
7082
                    $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
7083
                    break;
7084
                case TOOL_STUDENTPUBLICATION:
7085
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
7086
                    $return .= $this->display_student_publication_form('edit', $item_id, $row);
7087
                    break;
7088
                case TOOL_FORUM:
7089
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
7090
                    $return .= $this->display_forum_form('edit', $item_id, $row);
7091
                    break;
7092
                case TOOL_THREAD:
7093
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
7094
                    $return .= $this->display_thread_form('edit', $item_id, $row);
7095
                    break;
7096
            }
7097
        }
7098
7099
        return $return;
7100
    }
7101
7102
    /**
7103
     * Function that displays a list with al the resources that
7104
     * could be added to the learning path
7105
     * @return bool
7106
     */
7107
    public function display_resources()
7108
    {
7109
        $course_code = api_get_course_id();
7110
7111
        // Get all the docs.
7112
        $documents = $this->get_documents(true);
7113
7114
        // Get all the exercises.
7115
        $exercises = $this->get_exercises();
7116
7117
        // Get all the links.
7118
        $links = $this->get_links();
7119
7120
        // Get all the student publications.
7121
        $works = $this->get_student_publications();
7122
7123
        // Get all the forums.
7124
        $forums = $this->get_forums(null, $course_code);
7125
7126
        // Get the final item form (see BT#11048) .
7127
        $finish = $this->getFinalItemForm();
7128
7129
        $headers = [
7130
            Display::return_icon('folder_document.png', get_lang('Documents'), [], ICON_SIZE_BIG),
7131
            Display::return_icon('quiz.png', get_lang('Quiz'), [], ICON_SIZE_BIG),
7132
            Display::return_icon('links.png', get_lang('Links'), [], ICON_SIZE_BIG),
7133
            Display::return_icon('works.png', get_lang('Works'), [], ICON_SIZE_BIG),
7134
            Display::return_icon('forum.png', get_lang('Forums'), [], ICON_SIZE_BIG),
7135
            Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), [], ICON_SIZE_BIG),
7136
            Display::return_icon('certificate.png', get_lang('Certificate'), [], ICON_SIZE_BIG),
7137
        ];
7138
7139
        echo Display::return_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'), 'normal');
7140
        $dir = $_SESSION['oLP']->display_item_form('dir', get_lang('EnterDataNewChapter'), 'add_item');
7141
        echo Display::tabs(
7142
            $headers,
7143
            [
7144
                $documents,
7145
                $exercises,
7146
                $links,
7147
                $works,
7148
                $forums,
7149
                $dir,
7150
                $finish,
7151
            ],
7152
            'resource_tab'
7153
        );
7154
7155
        return true;
7156
    }
7157
7158
    /**
7159
     * Returns the extension of a document
7160
     * @param string $filename
7161
     * @return string Extension (part after the last dot)
7162
     */
7163
    public function get_extension($filename)
7164
    {
7165
        $explode = explode('.', $filename);
7166
        return $explode[count($explode) - 1];
7167
    }
7168
7169
    /**
7170
     * Displays a document by id
7171
     *
7172
     * @param int $id
7173
     * @return string
7174
     */
7175
    public function display_document($id, $show_title = false, $iframe = true, $edit_link = false)
7176
    {
7177
        $_course = api_get_course_info();
7178
        $course_id = api_get_course_int_id();
7179
        $return = '';
7180
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
7181
        $sql_doc = "SELECT * FROM ".$tbl_doc."
7182
                    WHERE c_id = $course_id AND iid = $id";
7183
        $res_doc = Database::query($sql_doc);
7184
        $row_doc = Database::fetch_array($res_doc);
7185
7186
        // TODO: Add a path filter.
7187
        if ($iframe) {
7188
            $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>';
7189
        } else {
7190
            $return .= file_get_contents(api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'.$row_doc['path']);
7191
        }
7192
7193
        return $return;
7194
    }
7195
7196
    /**
7197
     * Return HTML form to add/edit a quiz
7198
     * @param	string	$action Action (add/edit)
7199
     * @param	integer	$id Item ID if already exists
7200
     * @param	mixed	$extra_info Extra information (quiz ID if integer)
7201
     * @return	string	HTML form
7202
     */
7203
    public function display_quiz_form($action = 'add', $id = 0, $extra_info = '')
7204
    {
7205
        $course_id = api_get_course_int_id();
7206
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7207
        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
7208
7209
        if ($id != 0 && is_array($extra_info)) {
7210
            $item_title = $extra_info['title'];
7211
            $item_description = $extra_info['description'];
7212
        } elseif (is_numeric($extra_info)) {
7213
            $sql = "SELECT title, description
7214
                    FROM $tbl_quiz
7215
                    WHERE c_id = $course_id AND iid = ".$extra_info;
7216
7217
            $result = Database::query($sql);
7218
            $row = Database::fetch_array($result);
7219
            $item_title = $row['title'];
7220
            $item_description = $row['description'];
7221
        } else {
7222
            $item_title = '';
7223
            $item_description = '';
7224
        }
7225
        $item_title = Security::remove_XSS($item_title);
7226
        $item_description = Security::remove_XSS($item_description);
7227
7228
        if ($id != 0 && is_array($extra_info)) {
7229
            $parent = $extra_info['parent_item_id'];
7230
        } else {
7231
            $parent = 0;
7232
        }
7233
7234
        $sql = "SELECT * FROM $tbl_lp_item 
7235
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
7236
7237
        $result = Database::query($sql);
7238
        $arrLP = [];
7239
        while ($row = Database::fetch_array($result)) {
7240
            $arrLP[] = [
7241
                'id' => $row['id'],
7242
                'item_type' => $row['item_type'],
7243
                'title' => $row['title'],
7244
                'path' => $row['path'],
7245
                'description' => $row['description'],
7246
                'parent_item_id' => $row['parent_item_id'],
7247
                'previous_item_id' => $row['previous_item_id'],
7248
                'next_item_id' => $row['next_item_id'],
7249
                'display_order' => $row['display_order'],
7250
                'max_score' => $row['max_score'],
7251
                'min_score' => $row['min_score'],
7252
                'mastery_score' => $row['mastery_score'],
7253
                'prerequisite' => $row['prerequisite'],
7254
                'max_time_allowed' => $row['max_time_allowed']
7255
            ];
7256
        }
7257
7258
        $this->tree_array($arrLP);
7259
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7260
        unset($this->arrMenu);
7261
7262
        $form = new FormValidator(
7263
            'quiz_form',
7264
            'POST',
7265
            $this->getCurrentBuildingModeURL()
7266
        );
7267
        $defaults = [];
7268
7269
        if ($action == 'add') {
7270
            $legend = get_lang('CreateTheExercise');
7271
        } elseif ($action == 'move') {
7272
            $legend = get_lang('MoveTheCurrentExercise');
7273
        } else {
7274
            $legend = get_lang('EditCurrentExecice');
7275
        }
7276
7277
        if (isset($_GET['edit']) && $_GET['edit'] == 'true') {
7278
            $legend .= Display::return_message(get_lang('Warning').' ! '.get_lang('WarningEditingDocument'));
7279
        }
7280
7281
        $form->addHeader($legend);
7282
7283
        if ($action != 'move') {
7284
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
7285
            $defaults['title'] = $item_title;
7286
        }
7287
7288
        // Select for Parent item, root or chapter
7289
        $selectParent = $form->addSelect(
7290
            'parent',
7291
            get_lang('Parent'),
7292
            [],
7293
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
7294
        );
7295
        $selectParent->addOption($this->name, 0);
7296
7297
        $arrHide = [
7298
            $id
7299
        ];
7300
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7301
            if ($action != 'add') {
7302
                if (
7303
                    ($arrLP[$i]['item_type'] == 'dir') &&
7304
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7305
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7306
                ) {
7307
                    $selectParent->addOption(
7308
                        $arrLP[$i]['title'],
7309
                        $arrLP[$i]['id'],
7310
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7311
                    );
7312
7313
                    if ($parent == $arrLP[$i]['id']) {
7314
                        $selectParent->setSelected($arrLP[$i]['id']);
7315
                    }
7316
                } else {
7317
                    $arrHide[] = $arrLP[$i]['id'];
7318
                }
7319
            } else {
7320
                if ($arrLP[$i]['item_type'] == 'dir') {
7321
                    $selectParent->addOption(
7322
                        $arrLP[$i]['title'],
7323
                        $arrLP[$i]['id'],
7324
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7325
                    );
7326
7327
                    if ($parent == $arrLP[$i]['id']) {
7328
                        $selectParent->setSelected($arrLP[$i]['id']);
7329
                    }
7330
                }
7331
            }
7332
        }
7333
        if (is_array($arrLP)) {
7334
            reset($arrLP);
7335
        }
7336
7337
        $selectPrevious = $form->addSelect(
7338
            'previous',
7339
            get_lang('Position'),
7340
            [],
7341
            ['id' => 'previous']
7342
        );
7343
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7344
7345
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7346
            if ($arrLP[$i]['parent_item_id'] == $parent &&
7347
                $arrLP[$i]['id'] != $id
7348
            ) {
7349
                $selectPrevious->addOption(
7350
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
7351
                    $arrLP[$i]['id']
7352
                );
7353
7354
                if (is_array($extra_info)) {
7355
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7356
                        $selectPrevious->setSelected($arrLP[$i]['id']);
7357
                    }
7358
                } elseif ($action == 'add') {
7359
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7360
                }
7361
            }
7362
        }
7363
7364
        if ($action != 'move') {
7365
            $id_prerequisite = 0;
7366
            if (is_array($arrLP)) {
7367
                foreach ($arrLP as $key => $value) {
7368
                    if ($value['id'] == $id) {
7369
                        $id_prerequisite = $value['prerequisite'];
7370
                        break;
7371
                    }
7372
                }
7373
            }
7374
            $arrHide = [];
7375
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7376
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7377
                    if (is_array($extra_info)) {
7378
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7379
                            $s_selected_position = $arrLP[$i]['id'];
7380
                        }
7381
                    } elseif ($action == 'add') {
7382
                        $s_selected_position = 0;
7383
                    }
7384
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7385
                }
7386
            }
7387
        }
7388
7389
        if ($action == 'add') {
7390
            $form->addButtonSave(get_lang('AddExercise'), 'submit_button');
7391
        } else {
7392
            $form->addButtonSave(get_lang('EditCurrentExecice'), 'submit_button');
7393
        }
7394
7395
        if ($action == 'move') {
7396
            $form->addHidden('title', $item_title);
7397
            $form->addHidden('description', $item_description);
7398
        }
7399
7400
        if (is_numeric($extra_info)) {
7401
            $form->addHidden('path', $extra_info);
7402
        } elseif (is_array($extra_info)) {
7403
            $form->addHidden('path', $extra_info['path']);
7404
        }
7405
7406
        $form->addHidden('type', TOOL_QUIZ);
7407
        $form->addHidden('post_time', time());
7408
        $form->setDefaults($defaults);
7409
7410
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
7411
    }
7412
7413
    /**
7414
     * Addition of Hotpotatoes tests
7415
     * @param	string	Action
7416
     * @param	integer	Internal ID of the item
7417
     * @param	mixed	Extra information - can be an array with title and description indexes
7418
     * @return  string	HTML structure to display the hotpotatoes addition formular
7419
     */
7420
    public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '')
7421
    {
7422
        $course_id = api_get_course_int_id();
7423
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
7424
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7425
7426
        if ($id != 0 && is_array($extra_info)) {
7427
            $item_title = stripslashes($extra_info['title']);
7428
            $item_description = stripslashes($extra_info['description']);
7429
        } elseif (is_numeric($extra_info)) {
7430
            $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
7431
7432
            $sql = "SELECT * FROM ".$TBL_DOCUMENT."
7433
                    WHERE
7434
                        c_id = ".$course_id." AND
7435
                        path LIKE '" . $uploadPath."/%/%htm%' AND
7436
                        iid = " . (int) $extra_info."
7437
                    ORDER BY iid ASC";
7438
7439
            $res_hot = Database::query($sql);
7440
            $row = Database::fetch_array($res_hot);
7441
7442
            $item_title = $row['title'];
7443
            $item_description = $row['description'];
7444
7445
            if (!empty($row['comment'])) {
7446
                $item_title = $row['comment'];
7447
            }
7448
        } else {
7449
            $item_title = '';
7450
            $item_description = '';
7451
        }
7452
7453
        if ($id != 0 && is_array($extra_info)) {
7454
            $parent = $extra_info['parent_item_id'];
7455
        } else {
7456
            $parent = 0;
7457
        }
7458
7459
        $sql = "SELECT * FROM $tbl_lp_item
7460
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
7461
        $result = Database::query($sql);
7462
        $arrLP = [];
7463
        while ($row = Database::fetch_array($result)) {
7464
            $arrLP[] = [
7465
                'id' => $row['id'],
7466
                'item_type' => $row['item_type'],
7467
                'title' => $row['title'],
7468
                'path' => $row['path'],
7469
                'description' => $row['description'],
7470
                'parent_item_id' => $row['parent_item_id'],
7471
                'previous_item_id' => $row['previous_item_id'],
7472
                'next_item_id' => $row['next_item_id'],
7473
                'display_order' => $row['display_order'],
7474
                'max_score' => $row['max_score'],
7475
                'min_score' => $row['min_score'],
7476
                'mastery_score' => $row['mastery_score'],
7477
                'prerequisite' => $row['prerequisite'],
7478
                'max_time_allowed' => $row['max_time_allowed']
7479
            ];
7480
        }
7481
7482
        $legend = '<legend>';
7483
        if ($action == 'add') {
7484
            $legend .= get_lang('CreateTheExercise');
7485
        } elseif ($action == 'move') {
7486
            $legend .= get_lang('MoveTheCurrentExercise');
7487
        } else {
7488
            $legend .= get_lang('EditCurrentExecice');
7489
        }
7490
        if (isset($_GET['edit']) && $_GET['edit'] == 'true') {
7491
            $legend .= Display:: return_message(
7492
                get_lang('Warning').' ! '.get_lang('WarningEditingDocument')
7493
            );
7494
        }
7495
        $legend .= '</legend>';
7496
7497
        $return = '<form method="POST">';
7498
        $return .= $legend;
7499
        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
7500
        $return .= '<tr>';
7501
        $return .= '<td class="label"><label for="idParent">'.get_lang('Parent').' :</label></td>';
7502
        $return .= '<td class="input">';
7503
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
7504
        $return .= '<option class="top" value="0">'.$this->name.'</option>';
7505
        $arrHide = [
7506
            $id
7507
        ];
7508
7509
        if (count($arrLP) > 0) {
7510
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7511
                if ($action != 'add') {
7512
                    if ($arrLP[$i]['item_type'] == 'dir' &&
7513
                        !in_array($arrLP[$i]['id'], $arrHide) &&
7514
                        !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7515
                    ) {
7516
                        $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>';
7517
                    } else {
7518
                        $arrHide[] = $arrLP[$i]['id'];
7519
                    }
7520
                } else {
7521
                    if ($arrLP[$i]['item_type'] == 'dir') {
7522
                        $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>';
7523
                    }
7524
                }
7525
            }
7526
            reset($arrLP);
7527
        }
7528
7529
        $return .= '</select>';
7530
        $return .= '</td>';
7531
        $return .= '</tr>';
7532
        $return .= '<tr>';
7533
        $return .= '<td class="label"><label for="previous">'.get_lang('Position').' :</label></td>';
7534
        $return .= '<td class="input">';
7535
        $return .= '<select id="previous" name="previous" size="1">';
7536
        $return .= '<option class="top" value="0">'.get_lang('FirstPosition').'</option>';
7537
7538
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7539
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7540
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7541
                    $selected = 'selected="selected" ';
7542
                } elseif ($action == 'add') {
7543
                    $selected = 'selected="selected" ';
7544
                } else {
7545
                    $selected = '';
7546
                }
7547
7548
                $return .= '<option '.$selected.'value="'.$arrLP[$i]['id'].'">'.get_lang('After').' "'.$arrLP[$i]['title'].'"</option>';
7549
            }
7550
        }
7551
7552
        $return .= '</select>';
7553
        $return .= '</td>';
7554
        $return .= '</tr>';
7555
7556
        if ($action != 'move') {
7557
            $return .= '<tr>';
7558
            $return .= '<td class="label"><label for="idTitle">'.get_lang('Title').' :</label></td>';
7559
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="'.$item_title.'" /></td>';
7560
            $return .= '</tr>';
7561
            $id_prerequisite = 0;
7562
            if (is_array($arrLP) && count($arrLP) > 0) {
7563
                foreach ($arrLP as $key => $value) {
7564
                    if ($value['id'] == $id) {
7565
                        $id_prerequisite = $value['prerequisite'];
7566
                        break;
7567
                    }
7568
                }
7569
7570
                $arrHide = [];
7571
                for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7572
                    if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7573
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7574
                            $s_selected_position = $arrLP[$i]['id'];
7575
                        } elseif ($action == 'add') {
7576
                            $s_selected_position = 0;
7577
                        }
7578
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7579
                    }
7580
                }
7581
            }
7582
        }
7583
7584
        $return .= '<tr>';
7585
        $return .= '<td>&nbsp; </td><td><button class="save" name="submit_button" action="edit" type="submit">'.get_lang('SaveHotpotatoes').'</button></td>';
7586
        $return .= '</tr>';
7587
        $return .= '</table>';
7588
7589
        if ($action == 'move') {
7590
            $return .= '<input name="title" type="hidden" value="'.$item_title.'" />';
7591
            $return .= '<input name="description" type="hidden" value="'.$item_description.'" />';
7592
        }
7593
7594
        if (is_numeric($extra_info)) {
7595
            $return .= '<input name="path" type="hidden" value="'.$extra_info.'" />';
7596
        } elseif (is_array($extra_info)) {
7597
            $return .= '<input name="path" type="hidden" value="'.$extra_info['path'].'" />';
7598
        }
7599
        $return .= '<input name="type" type="hidden" value="'.TOOL_HOTPOTATOES.'" />';
7600
        $return .= '<input name="post_time" type="hidden" value="'.time().'" />';
7601
        $return .= '</form>';
7602
7603
        return $return;
7604
    }
7605
7606
    /**
7607
     * Return the form to display the forum edit/add option
7608
     * @param	string	Action (add/edit)
7609
     * @param	integer	ID of the lp_item if already exists
7610
     * @param	mixed	Forum ID or title
7611
     * @return	string	HTML form
7612
     */
7613
    public function display_forum_form($action = 'add', $id = 0, $extra_info = '')
7614
    {
7615
        $course_id = api_get_course_int_id();
7616
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7617
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
7618
7619
        if ($id != 0 && is_array($extra_info)) {
7620
            $item_title = stripslashes($extra_info['title']);
7621
        } elseif (is_numeric($extra_info)) {
7622
            $sql = "SELECT forum_title as title, forum_comment as comment
7623
                    FROM " . $tbl_forum."
7624
                    WHERE c_id = ".$course_id." AND forum_id = ".$extra_info;
7625
7626
            $result = Database::query($sql);
7627
            $row = Database::fetch_array($result);
7628
7629
            $item_title = $row['title'];
7630
            $item_description = $row['comment'];
7631
        } else {
7632
            $item_title = '';
7633
            $item_description = '';
7634
        }
7635
7636
        if ($id != 0 && is_array($extra_info)) {
7637
            $parent = $extra_info['parent_item_id'];
7638
        } else {
7639
            $parent = 0;
7640
        }
7641
7642
        $sql = "SELECT * FROM $tbl_lp_item
7643
                WHERE
7644
                    c_id = $course_id AND
7645
                    lp_id = " . $this->lp_id;
7646
        $result = Database::query($sql);
7647
        $arrLP = [];
7648
        while ($row = Database::fetch_array($result)) {
7649
            $arrLP[] = [
7650
                'id' => $row['iid'],
7651
                'item_type' => $row['item_type'],
7652
                'title' => $row['title'],
7653
                'path' => $row['path'],
7654
                'description' => $row['description'],
7655
                'parent_item_id' => $row['parent_item_id'],
7656
                'previous_item_id' => $row['previous_item_id'],
7657
                'next_item_id' => $row['next_item_id'],
7658
                'display_order' => $row['display_order'],
7659
                'max_score' => $row['max_score'],
7660
                'min_score' => $row['min_score'],
7661
                'mastery_score' => $row['mastery_score'],
7662
                'prerequisite' => $row['prerequisite']
7663
            ];
7664
        }
7665
7666
        $this->tree_array($arrLP);
7667
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7668
        unset($this->arrMenu);
7669
7670
        if ($action == 'add') {
7671
            $legend = get_lang('CreateTheForum');
7672
        } elseif ($action == 'move') {
7673
            $legend = get_lang('MoveTheCurrentForum');
7674
        } else {
7675
            $legend = get_lang('EditCurrentForum');
7676
        }
7677
7678
        $form = new FormValidator(
7679
            'forum_form',
7680
            'POST',
7681
            $this->getCurrentBuildingModeURL()
7682
        );
7683
        $defaults = [];
7684
7685
        $form->addHeader($legend);
7686
7687
        if ($action != 'move') {
7688
            $form->addText(
7689
                'title',
7690
                get_lang('Title'),
7691
                true,
7692
                ['id' => 'idTitle', 'class' => 'learnpath_item_form']
7693
            );
7694
            $defaults['title'] = $item_title;
7695
        }
7696
7697
        $selectParent = $form->addSelect(
7698
            'parent',
7699
            get_lang('Parent'),
7700
            [],
7701
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
7702
        );
7703
        $selectParent->addOption($this->name, 0);
7704
        $arrHide = [
7705
            $id
7706
        ];
7707
7708
        //$parent_item_id = $_SESSION['parent_item_id'];
7709
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7710
            if ($action != 'add') {
7711
                if ($arrLP[$i]['item_type'] == 'dir' &&
7712
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7713
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7714
                ) {
7715
                    $selectParent->addOption(
7716
                        $arrLP[$i]['title'],
7717
                        $arrLP[$i]['id'],
7718
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7719
                    );
7720
7721
                    if ($parent == $arrLP[$i]['id']) {
7722
                        $selectParent->setSelected($arrLP[$i]['id']);
7723
                    }
7724
                } else {
7725
                    $arrHide[] = $arrLP[$i]['id'];
7726
                }
7727
            } else {
7728
                if ($arrLP[$i]['item_type'] == 'dir') {
7729
                    $selectParent->addOption(
7730
                        $arrLP[$i]['title'],
7731
                        $arrLP[$i]['id'],
7732
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7733
                    );
7734
7735
                    if ($parent == $arrLP[$i]['id']) {
7736
                        $selectParent->setSelected($arrLP[$i]['id']);
7737
                    }
7738
                }
7739
            }
7740
        }
7741
7742
        if (is_array($arrLP)) {
7743
            reset($arrLP);
7744
        }
7745
7746
        $selectPrevious = $form->addSelect(
7747
            'previous',
7748
            get_lang('Position'),
7749
            [],
7750
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7751
        );
7752
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7753
7754
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7755
            if ($arrLP[$i]['parent_item_id'] == $parent &&
7756
                $arrLP[$i]['id'] != $id
7757
            ) {
7758
                $selectPrevious->addOption(
7759
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
7760
                    $arrLP[$i]['id']
7761
                );
7762
7763
                if (isset($extra_info['previous_item_id']) &&
7764
                    $extra_info['previous_item_id'] == $arrLP[$i]['id']
7765
                ) {
7766
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7767
                } elseif ($action == 'add') {
7768
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7769
                }
7770
            }
7771
        }
7772
7773
        if ($action != 'move') {
7774
            $id_prerequisite = 0;
7775
            if (is_array($arrLP)) {
7776
                foreach ($arrLP as $key => $value) {
7777
                    if ($value['id'] == $id) {
7778
                        $id_prerequisite = $value['prerequisite'];
7779
                        break;
7780
                    }
7781
                }
7782
            }
7783
7784
            $arrHide = [];
7785
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7786
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7787
                    if (isset($extra_info['previous_item_id']) &&
7788
                        $extra_info['previous_item_id'] == $arrLP[$i]['id']
7789
                    ) {
7790
                        $s_selected_position = $arrLP[$i]['id'];
7791
                    } elseif ($action == 'add') {
7792
                        $s_selected_position = 0;
7793
                    }
7794
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7795
                }
7796
            }
7797
        }
7798
7799
        if ($action == 'add') {
7800
            $form->addButtonSave(get_lang('AddForumToCourse'), 'submit_button');
7801
        } else {
7802
            $form->addButtonSave(get_lang('EditCurrentForum'), 'submit_button');
7803
        }
7804
7805
        if ($action == 'move') {
7806
            $form->addHidden('title', $item_title);
7807
            $form->addHidden('description', $item_description);
7808
        }
7809
7810
        if (is_numeric($extra_info)) {
7811
            $form->addHidden('path', $extra_info);
7812
        } elseif (is_array($extra_info)) {
7813
            $form->addHidden('path', $extra_info['path']);
7814
        }
7815
        $form->addHidden('type', TOOL_FORUM);
7816
        $form->addHidden('post_time', time());
7817
        $form->setDefaults($defaults);
7818
7819
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
7820
    }
7821
7822
    /**
7823
     * Return HTML form to add/edit forum threads
7824
     * @param	string	Action (add/edit)
7825
     * @param	integer	Item ID if already exists in learning path
7826
     * @param	mixed	Extra information (thread ID if integer)
7827
     * @return 	string	HTML form
7828
     */
7829
    public function display_thread_form($action = 'add', $id = 0, $extra_info = '')
7830
    {
7831
        $course_id = api_get_course_int_id();
7832
        if (empty($course_id)) {
7833
            return null;
7834
        }
7835
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7836
        $tbl_forum = Database::get_course_table(TABLE_FORUM_THREAD);
7837
7838
        if ($id != 0 && is_array($extra_info)) {
7839
            $item_title = stripslashes($extra_info['title']);
7840
        } elseif (is_numeric($extra_info)) {
7841
            $sql = "SELECT thread_title as title FROM $tbl_forum
7842
                    WHERE c_id = $course_id AND thread_id = ".$extra_info;
7843
7844
            $result = Database::query($sql);
7845
            $row = Database::fetch_array($result);
7846
7847
            $item_title = $row['title'];
7848
            $item_description = '';
7849
        } else {
7850
            $item_title = '';
7851
            $item_description = '';
7852
        }
7853
7854
        if ($id != 0 && is_array($extra_info)) {
7855
            $parent = $extra_info['parent_item_id'];
7856
        } else {
7857
            $parent = 0;
7858
        }
7859
7860
        $sql = "SELECT * FROM $tbl_lp_item
7861
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
7862
        $result = Database::query($sql);
7863
7864
        $arrLP = [];
7865
        while ($row = Database::fetch_array($result)) {
7866
            $arrLP[] = [
7867
                'id' => $row['iid'],
7868
                'item_type' => $row['item_type'],
7869
                'title' => $row['title'],
7870
                'path' => $row['path'],
7871
                'description' => $row['description'],
7872
                'parent_item_id' => $row['parent_item_id'],
7873
                'previous_item_id' => $row['previous_item_id'],
7874
                'next_item_id' => $row['next_item_id'],
7875
                'display_order' => $row['display_order'],
7876
                'max_score' => $row['max_score'],
7877
                'min_score' => $row['min_score'],
7878
                'mastery_score' => $row['mastery_score'],
7879
                'prerequisite' => $row['prerequisite']
7880
            ];
7881
        }
7882
7883
        $this->tree_array($arrLP);
7884
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7885
        unset($this->arrMenu);
7886
7887
        $form = new FormValidator(
7888
            'thread_form',
7889
            'POST',
7890
            $this->getCurrentBuildingModeURL()
7891
        );
7892
        $defaults = [];
7893
7894
        if ($action == 'add') {
7895
            $legend = get_lang('CreateTheForum');
7896
        } elseif ($action == 'move') {
7897
            $legend = get_lang('MoveTheCurrentForum');
7898
        } else {
7899
            $legend = get_lang('EditCurrentForum');
7900
        }
7901
7902
        $form->addHeader($legend);
7903
        $selectParent = $form->addSelect(
7904
            'parent',
7905
            get_lang('Parent'),
7906
            [],
7907
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
7908
        );
7909
        $selectParent->addOption($this->name, 0);
7910
7911
        $arrHide = [
7912
            $id
7913
        ];
7914
7915
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7916
            if ($action != 'add') {
7917
                if (
7918
                    ($arrLP[$i]['item_type'] == 'dir') &&
7919
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7920
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7921
                ) {
7922
                    $selectParent->addOption(
7923
                        $arrLP[$i]['title'],
7924
                        $arrLP[$i]['id'],
7925
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7926
                    );
7927
7928
                    if ($parent == $arrLP[$i]['id']) {
7929
                        $selectParent->setSelected($arrLP[$i]['id']);
7930
                    }
7931
                } else {
7932
                    $arrHide[] = $arrLP[$i]['id'];
7933
                }
7934
            } else {
7935
                if ($arrLP[$i]['item_type'] == 'dir') {
7936
                    $selectParent->addOption(
7937
                        $arrLP[$i]['title'],
7938
                        $arrLP[$i]['id'],
7939
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7940
                    );
7941
7942
                    if ($parent == $arrLP[$i]['id']) {
7943
                        $selectParent->setSelected($arrLP[$i]['id']);
7944
                    }
7945
                }
7946
            }
7947
        }
7948
7949
        if ($arrLP != null) {
7950
            reset($arrLP);
7951
        }
7952
7953
        $selectPrevious = $form->addSelect(
7954
            'previous',
7955
            get_lang('Position'),
7956
            [],
7957
            ['id' => 'previous']
7958
        );
7959
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7960
7961
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7962
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7963
                $selectPrevious->addOption(
7964
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
7965
                    $arrLP[$i]['id']
7966
                );
7967
7968
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7969
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7970
                } elseif ($action == 'add') {
7971
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7972
                }
7973
            }
7974
        }
7975
7976
        if ($action != 'move') {
7977
            $form->addText(
7978
                'title',
7979
                get_lang('Title'),
7980
                true,
7981
                ['id' => 'idTitle']
7982
            );
7983
            $defaults['title'] = $item_title;
7984
7985
            $id_prerequisite = 0;
7986
            if ($arrLP != null) {
7987
                foreach ($arrLP as $key => $value) {
7988
                    if ($value['id'] == $id) {
7989
                        $id_prerequisite = $value['prerequisite'];
7990
                        break;
7991
                    }
7992
                }
7993
            }
7994
7995
            $arrHide = [];
7996
            $s_selected_position = 0;
7997
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
7998
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7999
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8000
                        $s_selected_position = $arrLP[$i]['id'];
8001
                    } elseif ($action == 'add') {
8002
                        $s_selected_position = 0;
8003
                    }
8004
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8005
                }
8006
            }
8007
8008
            $selectPrerequisites = $form->addSelect(
8009
                'prerequisites',
8010
                get_lang('LearnpathPrerequisites'),
8011
                [],
8012
                ['id' => 'prerequisites']
8013
            );
8014
            $selectPrerequisites->addOption(get_lang('NoPrerequisites'), 0);
8015
8016
            foreach ($arrHide as $key => $value) {
8017
                $selectPrerequisites->addOption($value['value'], $key);
8018
8019
                if ($key == $s_selected_position && $action == 'add') {
8020
                    $selectPrerequisites->setSelected($key);
8021
                } elseif ($key == $id_prerequisite && $action == 'edit') {
8022
                    $selectPrerequisites->setSelected($key);
8023
                }
8024
            }
8025
        }
8026
8027
        $form->addButtonSave(get_lang('Ok'), 'submit_button');
8028
8029
        if ($action == 'move') {
8030
            $form->addHidden('title', $item_title);
8031
            $form->addHidden('description', $item_description);
8032
        }
8033
8034
        if (is_numeric($extra_info)) {
8035
            $form->addHidden('path', $extra_info);
8036
        } elseif (is_array($extra_info)) {
8037
            $form->addHidden('path', $extra_info['path']);
8038
        }
8039
8040
        $form->addHidden('type', TOOL_THREAD);
8041
        $form->addHidden('post_time', time());
8042
        $form->setDefaults($defaults);
8043
8044
        return $form->returnForm();
8045
    }
8046
8047
    /**
8048
     * Return the HTML form to display an item (generally a dir item)
8049
     * @param	string	Item type (dir)
8050
     * @param	string	Title (optional, only when creating)
8051
     * @param	string	Action ('add'/'edit')
8052
     * @param	integer	lp_item ID
8053
     * @param	mixed	Extra info
8054
     * @return	string 	HTML form
8055
     */
8056
    public function display_item_form(
8057
        $item_type,
8058
        $title = '',
8059
        $action = 'add_item',
8060
        $id = 0,
8061
        $extra_info = 'new'
8062
    ) {
8063
        $course_id = api_get_course_int_id();
8064
        $_course = api_get_course_info();
8065
8066
        global $charset;
8067
8068
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8069
8070
        if ($id != 0 && is_array($extra_info)) {
8071
            $item_title = $extra_info['title'];
8072
            $item_description = $extra_info['description'];
8073
            $item_path = api_get_path(WEB_COURSE_PATH).$_course['path'].'/scorm/'.$this->path.'/'.stripslashes($extra_info['path']);
8074
            $item_path_fck = '/scorm/'.$this->path.'/'.stripslashes($extra_info['path']);
8075
        } else {
8076
            $item_title = '';
8077
            $item_description = '';
8078
            $item_path_fck = '';
8079
        }
8080
8081
        if ($id != 0 && is_array($extra_info)) {
8082
            $parent = $extra_info['parent_item_id'];
8083
        } else {
8084
            $parent = 0;
8085
        }
8086
8087
        $id  = intval($id);
8088
        $sql = "SELECT * FROM $tbl_lp_item
8089
                WHERE
8090
                    lp_id = " . $this->lp_id." AND
8091
                    iid != $id";
8092
8093
        if ($item_type == 'dir') {
8094
            $sql .= " AND parent_item_id = 0";
8095
        }
8096
8097
        $result = Database::query($sql);
8098
        $arrLP = [];
8099
8100
        while ($row = Database::fetch_array($result)) {
8101
            $arrLP[] = [
8102
                'id' => $row['iid'],
8103
                'item_type' => $row['item_type'],
8104
                'title' => $row['title'],
8105
                'path' => $row['path'],
8106
                'description' => $row['description'],
8107
                'parent_item_id' => $row['parent_item_id'],
8108
                'previous_item_id' => $row['previous_item_id'],
8109
                'next_item_id' => $row['next_item_id'],
8110
                'max_score' => $row['max_score'],
8111
                'min_score' => $row['min_score'],
8112
                'mastery_score' => $row['mastery_score'],
8113
                'prerequisite' => $row['prerequisite'],
8114
                'display_order' => $row['display_order']
8115
            ];
8116
        }
8117
8118
        $this->tree_array($arrLP);
8119
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8120
        unset($this->arrMenu);
8121
8122
        $url = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id;
8123
8124
        $form = new FormValidator('form', 'POST', $url);
8125
        $defaults['title'] = api_html_entity_decode(
8126
            $item_title,
8127
            ENT_QUOTES,
8128
            $charset
8129
        );
8130
        $defaults['description'] = $item_description;
8131
8132
        $form->addElement('header', $title);
8133
8134
        //$arrHide = array($id);
8135
        $arrHide[0]['value'] = Security::remove_XSS($this->name);
8136
        $arrHide[0]['padding'] = 20;
8137
        $charset = api_get_system_encoding();
8138
8139
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8140
            if ($action != 'add') {
8141
                if ($arrLP[$i]['item_type'] == 'dir' && !in_array($arrLP[$i]['id'], $arrHide) &&
8142
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8143
                ) {
8144
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8145
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8146
                    if ($parent == $arrLP[$i]['id']) {
8147
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8148
                    }
8149
                }
8150
            } else {
8151
                if ($arrLP[$i]['item_type'] == 'dir') {
8152
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8153
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8154
                    if ($parent == $arrLP[$i]['id']) {
8155
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8156
                    }
8157
                }
8158
            }
8159
        }
8160
8161
        if ($action != 'move') {
8162
            $form->addElement('text', 'title', get_lang('Title'));
8163
            $form->applyFilter('title', 'html_filter');
8164
            $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
8165
        } else {
8166
            $form->addElement('hidden', 'title');
8167
        }
8168
8169
        $parent_select = $form->addElement(
8170
            'select',
8171
            'parent',
8172
            get_lang('Parent'),
8173
            '',
8174
            [
8175
                'id' => 'idParent',
8176
                'onchange' => "javascript: load_cbo(this.value);",
8177
            ]
8178
        );
8179
8180
        foreach ($arrHide as $key => $value) {
8181
            $parent_select->addOption(
8182
                $value['value'],
8183
                $key,
8184
                'style="padding-left:'.$value['padding'].'px;"'
8185
            );
8186
        }
8187
        if (!empty($s_selected_parent)) {
8188
            $parent_select->setSelected($s_selected_parent);
8189
        }
8190
8191
        if (is_array($arrLP)) {
8192
            reset($arrLP);
8193
        }
8194
        $arrHide = [];
8195
        // POSITION
8196
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8197
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8198
                //this is the same!
8199
                if (isset($extra_info['previous_item_id']) &&
8200
                    $extra_info['previous_item_id'] == $arrLP[$i]['id']
8201
                ) {
8202
                    $s_selected_position = $arrLP[$i]['id'];
8203
                } elseif ($action == 'add') {
8204
                    $s_selected_position = $arrLP[$i]['id'];
8205
                }
8206
8207
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"';
8208
            }
8209
        }
8210
8211
        $position = $form->addElement(
8212
            'select',
8213
            'previous',
8214
            get_lang('Position'),
8215
            '',
8216
            ['id' => 'previous']
8217
        );
8218
        $padding = isset($value['padding']) ? $value['padding'] : 0;
8219
        $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:'.$padding.'px;"');
8220
8221
        foreach ($arrHide as $key => $value) {
8222
            $position->addOption($value['value'].'"', $key, 'style="padding-left:'.$padding.'px;"');
8223
        }
8224
8225
        if (!empty($s_selected_position)) {
8226
            $position->setSelected($s_selected_position);
8227
        }
8228
8229
        if (is_array($arrLP)) {
8230
            reset($arrLP);
8231
        }
8232
8233
        $form->addButtonSave(get_lang('SaveSection'), 'submit_button');
8234
8235
        //fix in order to use the tab
8236
        if ($item_type == 'dir') {
8237
            $form->addElement('hidden', 'type', 'dir');
8238
        }
8239
8240
        $extension = null;
8241
        if (!empty($item_path)) {
8242
            $extension = pathinfo($item_path, PATHINFO_EXTENSION);
8243
        }
8244
8245
        //assets can't be modified
8246
        //$item_type == 'asset' ||
8247
        if (($item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {
8248
            if ($item_type == 'sco') {
8249
                $form->addElement(
8250
                    'html',
8251
                    '<script>alert("'.get_lang('WarningWhenEditingScorm').'")</script>'
8252
                );
8253
            }
8254
            $renderer = $form->defaultRenderer();
8255
            $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
8256
8257
            $relative_prefix = '';
8258
8259
            $editor_config = [
8260
                'ToolbarSet' => 'LearningPathDocuments',
8261
                'Width' => '100%',
8262
                'Height' => '500',
8263
                'FullPage' => true,
8264
                'CreateDocumentDir' => $relative_prefix,
8265
                'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/scorm/',
8266
                'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().$item_path_fck
8267
            ];
8268
8269
            $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
8270
            $content_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck;
8271
            $defaults['content_lp'] = file_get_contents($content_path);
8272
        }
8273
8274
        $form->addElement('hidden', 'type', $item_type);
8275
        $form->addElement('hidden', 'post_time', time());
8276
        $form->setDefaults($defaults);
8277
        return $form->returnForm();
8278
    }
8279
8280
    /**
8281
     * @return string
8282
     */
8283
    public function getCurrentBuildingModeURL()
8284
    {
8285
        $pathItem = isset($_GET['path_item']) ? (int) $_GET['path_item'] : '';
8286
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
8287
        $id = isset($_GET['id']) ? (int) $_GET['id'] : '';
8288
        $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : '';
8289
8290
        $currentUrl = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&lp_id='.$this->lp_id.'&path_item='.$pathItem.'&view='.$view.'&id='.$id;
8291
8292
        return $currentUrl;
8293
    }
8294
8295
    /**
8296
     * Returns the form to update or create a document
8297
     * @param	string	$action (add/edit)
8298
     * @param	integer	$id ID of the lp_item (if already exists)
8299
     * @param	mixed	$extra_info Integer if document ID, string if info ('new')
8300
     * @return	string	HTML form
8301
     */
8302
    public function display_document_form($action = 'add', $id = 0, $extra_info = 'new')
8303
    {
8304
        $course_id = api_get_course_int_id();
8305
        $_course = api_get_course_info();
8306
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8307
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
8308
8309
        $no_display_edit_textarea = false;
8310
        $item_description = '';
8311
        //If action==edit document
8312
        //We don't display the document form if it's not an editable document (html or txt file)
8313
        if ($action == 'edit') {
8314
            if (is_array($extra_info)) {
8315
                $path_parts = pathinfo($extra_info['dir']);
8316
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
8317
                    $no_display_edit_textarea = true;
8318
                }
8319
            }
8320
        }
8321
        $no_display_add = false;
8322
8323
        // If action==add an existing document
8324
        // We don't display the document form if it's not an editable document (html or txt file).
8325
        if ($action == 'add') {
8326
            if (is_numeric($extra_info)) {
8327
                $sql_doc = "SELECT path FROM $tbl_doc 
8328
                            WHERE c_id = $course_id AND iid = ".intval($extra_info);
8329
                $result = Database::query($sql_doc);
8330
                $path_file = Database::result($result, 0, 0);
8331
                $path_parts = pathinfo($path_file);
8332
                if ($path_parts['extension'] != 'txt' && $path_parts['extension'] != 'html') {
8333
                    $no_display_add = true;
8334
                }
8335
            }
8336
        }
8337
        if ($id != 0 && is_array($extra_info)) {
8338
            $item_title = stripslashes($extra_info['title']);
8339
            $item_description = stripslashes($extra_info['description']);
8340
            $item_terms = stripslashes($extra_info['terms']);
8341
            if (empty($item_title)) {
8342
                $path_parts = pathinfo($extra_info['path']);
8343
                $item_title = stripslashes($path_parts['filename']);
8344
            }
8345
        } elseif (is_numeric($extra_info)) {
8346
            $sql = "SELECT path, title FROM $tbl_doc
8347
                    WHERE
8348
                        c_id = ".$course_id." AND
8349
                        iid = " . intval($extra_info);
8350
            $result = Database::query($sql);
8351
            $row = Database::fetch_array($result);
8352
            $item_title = $row['title'];
8353
            $item_title = str_replace('_', ' ', $item_title);
8354
            if (empty($item_title)) {
8355
                $path_parts = pathinfo($row['path']);
8356
                $item_title = stripslashes($path_parts['filename']);
8357
            }
8358
        } else {
8359
            $item_title = '';
8360
            $item_description = '';
8361
        }
8362
        $return = '<legend>';
8363
8364
        if ($id != 0 && is_array($extra_info)) {
8365
            $parent = $extra_info['parent_item_id'];
8366
        } else {
8367
            $parent = 0;
8368
        }
8369
8370
        $sql = "SELECT * FROM $tbl_lp_item
8371
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
8372
        $result = Database::query($sql);
8373
        $arrLP = [];
8374
8375
        while ($row = Database::fetch_array($result)) {
8376
            $arrLP[] = [
8377
                'id' => $row['id'],
8378
                'item_type' => $row['item_type'],
8379
                'title' => $row['title'],
8380
                'path' => $row['path'],
8381
                'description' => $row['description'],
8382
                'parent_item_id' => $row['parent_item_id'],
8383
                'previous_item_id' => $row['previous_item_id'],
8384
                'next_item_id' => $row['next_item_id'],
8385
                'display_order' => $row['display_order'],
8386
                'max_score' => $row['max_score'],
8387
                'min_score' => $row['min_score'],
8388
                'mastery_score' => $row['mastery_score'],
8389
                'prerequisite' => $row['prerequisite']
8390
            ];
8391
        }
8392
8393
        $this->tree_array($arrLP);
8394
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8395
        unset($this->arrMenu);
8396
8397
        if ($action == 'add') {
8398
            $return .= get_lang('CreateTheDocument');
8399
        } elseif ($action == 'move') {
8400
            $return .= get_lang('MoveTheCurrentDocument');
8401
        } else {
8402
            $return .= get_lang('EditTheCurrentDocument');
8403
        }
8404
        $return .= '</legend>';
8405
8406
        if (isset($_GET['edit']) && $_GET['edit'] == 'true') {
8407
            $return .= Display::return_message(
8408
                '<strong>'.get_lang('Warning').' !</strong><br />'.get_lang('WarningEditingDocument'),
8409
                false
8410
            );
8411
        }
8412
        $form = new FormValidator(
8413
            'form',
8414
            'POST',
8415
            $this->getCurrentBuildingModeURL(),
8416
            '',
8417
            ['enctype' => 'multipart/form-data']
8418
        );
8419
        $defaults['title'] = Security::remove_XSS($item_title);
8420
        if (empty($item_title)) {
8421
            $defaults['title'] = Security::remove_XSS($item_title);
8422
        }
8423
        $defaults['description'] = $item_description;
8424
        $form->addElement('html', $return);
8425
8426
        if ($action != 'move') {
8427
            $data = $this->generate_lp_folder($_course);
8428
            if ($action != 'edit') {
8429
                $folders = DocumentManager::get_all_document_folders(
8430
                    $_course,
8431
                    0,
8432
                    true
8433
                );
8434
                DocumentManager::build_directory_selector(
8435
                    $folders,
8436
                    '',
8437
                    [],
8438
                    true,
8439
                    $form,
8440
                    'directory_parent_id'
8441
                );
8442
            }
8443
8444
            if (isset($data['id'])) {
8445
                $defaults['directory_parent_id'] = $data['id'];
8446
            }
8447
8448
            $form->addElement(
8449
                'text',
8450
                'title',
8451
                get_lang('Title'),
8452
                ['id' => 'idTitle', 'class' => 'col-md-4']
8453
            );
8454
            $form->applyFilter('title', 'html_filter');
8455
        }
8456
8457
        $arrHide[0]['value'] = $this->name;
8458
        $arrHide[0]['padding'] = 20;
8459
8460
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8461
            if ($action != 'add') {
8462
                if ($arrLP[$i]['item_type'] == 'dir' &&
8463
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8464
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8465
                ) {
8466
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8467
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8468
                    if ($parent == $arrLP[$i]['id']) {
8469
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8470
                    }
8471
                }
8472
            } else {
8473
                if ($arrLP[$i]['item_type'] == 'dir') {
8474
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8475
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8476
                    if ($parent == $arrLP[$i]['id']) {
8477
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8478
                    }
8479
                }
8480
            }
8481
        }
8482
8483
        $parent_select = $form->addSelect(
8484
            'parent',
8485
            get_lang('Parent'),
8486
            [],
8487
            [
8488
                'id' => 'idParent',
8489
                'onchange' => 'javascript: load_cbo(this.value);',
8490
            ]
8491
        );
8492
8493
        $my_count = 0;
8494
        foreach ($arrHide as $key => $value) {
8495
            if ($my_count != 0) {
8496
                // The LP name is also the first section and is not in the same charset like the other sections.
8497
                $value['value'] = Security::remove_XSS($value['value']);
8498
                $parent_select->addOption(
8499
                    $value['value'],
8500
                    $key,
8501
                    'style="padding-left:'.$value['padding'].'px;"'
8502
                );
8503
            } else {
8504
                $value['value'] = Security::remove_XSS($value['value']);
8505
                $parent_select->addOption(
8506
                    $value['value'],
8507
                    $key,
8508
                    'style="padding-left:'.$value['padding'].'px;"'
8509
                );
8510
            }
8511
            $my_count++;
8512
        }
8513
8514
        if (!empty($id)) {
8515
            $parent_select->setSelected($parent);
8516
        } else {
8517
            $parent_item_id = Session::read('parent_item_id', 0);
8518
            $parent_select->setSelected($parent_item_id);
8519
        }
8520
8521
        if (is_array($arrLP)) {
8522
            reset($arrLP);
8523
        }
8524
8525
        $arrHide = [];
8526
        $s_selected_position = null;
8527
8528
        // POSITION
8529
        $lastPosition = null;
8530
8531
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8532
            if (($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) ||
8533
                $arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM
8534
            ) {
8535
                if ((isset($extra_info['previous_item_id']) &&
8536
                    $extra_info['previous_item_id'] == $arrLP[$i]['id']) || $action == 'add'
8537
                ) {
8538
                    $s_selected_position = $arrLP[$i]['id'];
8539
                }
8540
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"';
8541
            }
8542
            $lastPosition = $arrLP[$i]['id'];
8543
        }
8544
8545
        if (empty($s_selected_position)) {
8546
            $s_selected_position = $lastPosition;
8547
        }
8548
8549
        $position = $form->addSelect(
8550
            'previous',
8551
            get_lang('Position'),
8552
            [],
8553
            ['id' => 'previous']
8554
        );
8555
        $position->addOption(get_lang('FirstPosition'), 0);
8556
8557
        foreach ($arrHide as $key => $value) {
8558
            $padding = isset($value['padding']) ? $value['padding'] : 20;
8559
            $position->addOption(
8560
                $value['value'],
8561
                $key,
8562
                'style="padding-left:'.$padding.'px;"'
8563
            );
8564
        }
8565
        $position->setSelected($s_selected_position);
8566
8567
        if (is_array($arrLP)) {
8568
            reset($arrLP);
8569
        }
8570
8571
        if ($action != 'move') {
8572
            $id_prerequisite = 0;
8573
            if (is_array($arrLP)) {
8574
                foreach ($arrLP as $key => $value) {
8575
                    if ($value['id'] == $id) {
8576
                        $id_prerequisite = $value['prerequisite'];
8577
                        break;
8578
                    }
8579
                }
8580
            }
8581
8582
            $arrHide = [];
8583
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8584
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir' &&
8585
                    $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM
8586
                ) {
8587
                    if (isset($extra_info['previous_item_id']) &&
8588
                        $extra_info['previous_item_id'] == $arrLP[$i]['id']
8589
                    ) {
8590
                        $s_selected_position = $arrLP[$i]['id'];
8591
                    } elseif ($action == 'add') {
8592
                        $s_selected_position = $arrLP[$i]['id'];
8593
                    }
8594
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8595
                }
8596
            }
8597
8598
            if (!$no_display_add) {
8599
                $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null;
8600
                $edit = isset($_GET['edit']) ? $_GET['edit'] : null;
8601
                if ($extra_info == 'new' || $item_type == TOOL_DOCUMENT ||
8602
                    $item_type == TOOL_LP_FINAL_ITEM || $edit == 'true'
8603
                ) {
8604
                    if (isset($_POST['content'])) {
8605
                        $content = stripslashes($_POST['content']);
8606
                    } elseif (is_array($extra_info)) {
8607
                        //If it's an html document or a text file
8608
                        if (!$no_display_edit_textarea) {
8609
                            $content = $this->display_document(
8610
                                $extra_info['path'],
8611
                                false,
8612
                                false
8613
                            );
8614
                        }
8615
                    } elseif (is_numeric($extra_info)) {
8616
                        $content = $this->display_document(
8617
                            $extra_info,
8618
                            false,
8619
                            false
8620
                        );
8621
                    } else {
8622
                        $content = '';
8623
                    }
8624
8625
                    if (!$no_display_edit_textarea) {
8626
                        // We need to calculate here some specific settings for the online editor.
8627
                        // The calculated settings work for documents in the Documents tool
8628
                        // (on the root or in subfolders).
8629
                        // For documents in native scorm packages it is unclear whether the
8630
                        // online editor should be activated or not.
8631
8632
                        // A new document, it is in the root of the repository.
8633
                        $relative_path 	 = '';
8634
                        $relative_prefix = '';
8635
                        if (is_array($extra_info) && $extra_info != 'new') {
8636
                            // The document already exists. Whe have to determine its relative path towards the repository root.
8637
                            $relative_path = explode('/', $extra_info['dir']);
8638
                            $cnt = count($relative_path) - 2;
8639
                            if ($cnt < 0) {
8640
                                $cnt = 0;
8641
                            }
8642
                            $relative_prefix = str_repeat('../', $cnt);
8643
                            $relative_path 	 = array_slice($relative_path, 1, $cnt);
8644
                            $relative_path 	 = implode('/', $relative_path);
8645
                            if (strlen($relative_path) > 0) {
8646
                                $relative_path = $relative_path.'/';
8647
                            }
8648
                        } else {
8649
                            $result = $this->generate_lp_folder($_course);
8650
                            $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
8651
                            $relative_prefix = '../../';
8652
                        }
8653
8654
                        $editor_config = [
8655
                            'ToolbarSet' => 'LearningPathDocuments',
8656
                            'Width' => '100%',
8657
                            'Height' => '500',
8658
                            'FullPage' => true,
8659
                            'CreateDocumentDir' => $relative_prefix,
8660
                            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/',
8661
                            'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path
8662
                        ];
8663
8664
                        if ($_GET['action'] == 'add_item') {
8665
                            $class = 'add';
8666
                            $text = get_lang('LPCreateDocument');
8667
                        } else {
8668
                            if ($_GET['action'] == 'edit_item') {
8669
                                $class = 'save';
8670
                                $text = get_lang('SaveDocument');
8671
                            }
8672
                        }
8673
8674
                        $form->addButtonSave($text, 'submit_button');
8675
                        $renderer = $form->defaultRenderer();
8676
                        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp');
8677
                        $form->addElement('html', '<div class="editor-lp">');
8678
                        $form->addHtmlEditor('content_lp', null, null, true, $editor_config, true);
8679
                        $form->addElement('html', '</div>');
8680
                        $defaults['content_lp'] = $content;
8681
                    }
8682
                } elseif (is_numeric($extra_info)) {
8683
                    $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8684
8685
                    $return = $this->display_document($extra_info, true, true, true);
8686
                    $form->addElement('html', $return);
8687
                }
8688
            }
8689
        }
8690
        if (isset($extra_info['item_type']) &&
8691
            $extra_info['item_type'] == TOOL_LP_FINAL_ITEM
8692
        ) {
8693
            $parent_select->freeze();
8694
            $position->freeze();
8695
        }
8696
8697
        if ($action == 'move') {
8698
            $form->addElement('hidden', 'title', $item_title);
8699
            $form->addElement('hidden', 'description', $item_description);
8700
        }
8701
        if (is_numeric($extra_info)) {
8702
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8703
            $form->addElement('hidden', 'path', $extra_info);
8704
        } elseif (is_array($extra_info)) {
8705
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8706
            $form->addElement('hidden', 'path', $extra_info['path']);
8707
        }
8708
        $form->addElement('hidden', 'type', TOOL_DOCUMENT);
8709
        $form->addElement('hidden', 'post_time', time());
8710
        $form->setDefaults($defaults);
8711
8712
        return $form->returnForm();
8713
    }
8714
8715
    /**
8716
     * Return HTML form to add/edit a link item
8717
     * @param string	$action (add/edit)
8718
     * @param integer	$id Item ID if exists
8719
     * @param mixed		$extra_info
8720
     * @return	string	HTML form
8721
     */
8722
    public function display_link_form($action = 'add', $id = 0, $extra_info = '')
8723
    {
8724
        $course_id = api_get_course_int_id();
8725
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8726
        $tbl_link = Database::get_course_table(TABLE_LINK);
8727
8728
        if ($id != 0 && is_array($extra_info)) {
8729
            $item_title = stripslashes($extra_info['title']);
8730
            $item_description = stripslashes($extra_info['description']);
8731
            $item_url = stripslashes($extra_info['url']);
8732
        } elseif (is_numeric($extra_info)) {
8733
            $extra_info = intval($extra_info);
8734
            $sql = "SELECT title, description, url FROM ".$tbl_link."
8735
                    WHERE c_id = ".$course_id." AND id = ".$extra_info;
8736
            $result = Database::query($sql);
8737
            $row = Database::fetch_array($result);
8738
            $item_title       = $row['title'];
8739
            $item_description = $row['description'];
8740
            $item_url = $row['url'];
8741
        } else {
8742
            $item_title = '';
8743
            $item_description = '';
8744
            $item_url = '';
8745
        }
8746
8747
        $form = new FormValidator(
8748
            'edit_link',
8749
            'POST',
8750
            $this->getCurrentBuildingModeURL()
8751
        );
8752
        $defaults = [];
8753
        if ($id != 0 && is_array($extra_info)) {
8754
            $parent = $extra_info['parent_item_id'];
8755
        } else {
8756
            $parent = 0;
8757
        }
8758
8759
        $sql = "SELECT * FROM $tbl_lp_item
8760
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
8761
        $result = Database::query($sql);
8762
        $arrLP = [];
8763
8764
        while ($row = Database::fetch_array($result)) {
8765
            $arrLP[] = [
8766
                'id' => $row['id'],
8767
                'item_type' => $row['item_type'],
8768
                'title' => $row['title'],
8769
                'path' => $row['path'],
8770
                'description' => $row['description'],
8771
                'parent_item_id' => $row['parent_item_id'],
8772
                'previous_item_id' => $row['previous_item_id'],
8773
                'next_item_id' => $row['next_item_id'],
8774
                'display_order' => $row['display_order'],
8775
                'max_score' => $row['max_score'],
8776
                'min_score' => $row['min_score'],
8777
                'mastery_score' => $row['mastery_score'],
8778
                'prerequisite' => $row['prerequisite']
8779
            ];
8780
        }
8781
8782
        $this->tree_array($arrLP);
8783
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8784
        unset($this->arrMenu);
8785
8786
        if ($action == 'add') {
8787
            $legend = get_lang('CreateTheLink');
8788
        } elseif ($action == 'move') {
8789
            $legend = get_lang('MoveCurrentLink');
8790
        } else {
8791
            $legend = get_lang('EditCurrentLink');
8792
        }
8793
8794
        $form->addHeader($legend);
8795
8796
        if ($action != 'move') {
8797
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form']);
8798
            $defaults['title'] = $item_title;
8799
        }
8800
8801
        $selectParent = $form->addSelect(
8802
            'parent',
8803
            get_lang('Parent'),
8804
            [],
8805
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
8806
        );
8807
        $selectParent->addOption($this->name, 0);
8808
        $arrHide = [
8809
            $id
8810
        ];
8811
8812
        $parent_item_id = Session::read('parent_item_id', 0);
8813
8814
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8815
            if ($action != 'add') {
8816
                if (
8817
                    ($arrLP[$i]['item_type'] == 'dir') &&
8818
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8819
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8820
                ) {
8821
                    $selectParent->addOption(
8822
                        $arrLP[$i]['title'],
8823
                        $arrLP[$i]['id'],
8824
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px;']
8825
                    );
8826
8827
                    if ($parent == $arrLP[$i]['id']) {
8828
                        $selectParent->setSelected($arrLP[$i]['id']);
8829
                    }
8830
                } else {
8831
                    $arrHide[] = $arrLP[$i]['id'];
8832
                }
8833
            } else {
8834
                if ($arrLP[$i]['item_type'] == 'dir') {
8835
                    $selectParent->addOption(
8836
                        $arrLP[$i]['title'],
8837
                        $arrLP[$i]['id'],
8838
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
8839
                    );
8840
8841
                    if ($parent_item_id == $arrLP[$i]['id']) {
8842
                        $selectParent->setSelected($arrLP[$i]['id']);
8843
                    }
8844
                }
8845
            }
8846
        }
8847
8848
        if (is_array($arrLP)) {
8849
            reset($arrLP);
8850
        }
8851
8852
        $selectPrevious = $form->addSelect(
8853
            'previous',
8854
            get_lang('Position'),
8855
            [],
8856
            ['id' => 'previous', 'class' => 'learnpath_item_form']
8857
        );
8858
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
8859
8860
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8861
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8862
                $selectPrevious->addOption(
8863
                    $arrLP[$i]['title'],
8864
                    $arrLP[$i]['id']
8865
                );
8866
8867
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8868
                    $selectPrevious->setSelected($arrLP[$i]['id']);
8869
                } elseif ($action == 'add') {
8870
                    $selectPrevious->setSelected($arrLP[$i]['id']);
8871
                }
8872
            }
8873
        }
8874
8875
        if ($action != 'move') {
8876
            $urlAttributes = ['class' => 'learnpath_item_form'];
8877
8878
            if (is_numeric($extra_info)) {
8879
                $urlAttributes['disabled'] = 'disabled';
8880
            }
8881
8882
            $form->addElement('url', 'url', get_lang('Url'), $urlAttributes);
8883
            $defaults['url'] = $item_url;
8884
8885
            $id_prerequisite = 0;
8886
            if (is_array($arrLP)) {
8887
                foreach ($arrLP as $key => $value) {
8888
                    if ($value['id'] == $id) {
8889
                        $id_prerequisite = $value['prerequisite'];
8890
                        break;
8891
                    }
8892
                }
8893
            }
8894
8895
            $arrHide = [];
8896
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
8897
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
8898
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8899
                        $s_selected_position = $arrLP[$i]['id'];
8900
                    } elseif ($action == 'add') {
8901
                        $s_selected_position = 0;
8902
                    }
8903
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8904
                }
8905
            }
8906
        }
8907
8908
        if ($action == 'add') {
8909
            $form->addButtonSave(get_lang('AddLinkToCourse'), 'submit_button');
8910
        } else {
8911
            $form->addButtonSave(get_lang('EditCurrentLink'), 'submit_button');
8912
        }
8913
8914
        if ($action == 'move') {
8915
            $form->addHidden('title', $item_title);
8916
            $form->addHidden('description', $item_description);
8917
        }
8918
8919
        if (is_numeric($extra_info)) {
8920
            $form->addHidden('path', $extra_info);
8921
        } elseif (is_array($extra_info)) {
8922
            $form->addHidden('path', $extra_info['path']);
8923
        }
8924
        $form->addHidden('type', TOOL_LINK);
8925
        $form->addHidden('post_time', time());
8926
        $form->setDefaults($defaults);
8927
8928
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
8929
    }
8930
8931
    /**
8932
     * Return HTML form to add/edit a student publication (work)
8933
     * @param	string	Action (add/edit)
8934
     * @param	integer	Item ID if already exists
8935
     * @param	mixed	Extra info (work ID if integer)
8936
     * @return	string	HTML form
8937
     */
8938
    public function display_student_publication_form(
8939
        $action = 'add',
8940
        $id = 0,
8941
        $extra_info = ''
8942
    ) {
8943
        $course_id = api_get_course_int_id();
8944
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8945
        $tbl_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
8946
8947
        if ($id != 0 && is_array($extra_info)) {
8948
            $item_title = stripslashes($extra_info['title']);
8949
            $item_description = stripslashes($extra_info['description']);
8950
        } elseif (is_numeric($extra_info)) {
8951
            $extra_info = intval($extra_info);
8952
            $sql = "SELECT title, description
8953
                    FROM $tbl_publication
8954
                    WHERE c_id = $course_id AND id = ".$extra_info;
8955
8956
            $result = Database::query($sql);
8957
            $row = Database::fetch_array($result);
8958
8959
            $item_title = $row['title'];
8960
        } else {
8961
            $item_title = get_lang('Student_publication');
8962
        }
8963
8964
        if ($id != 0 && is_array($extra_info)) {
8965
            $parent = $extra_info['parent_item_id'];
8966
        } else {
8967
            $parent = 0;
8968
        }
8969
8970
        $sql = "SELECT * FROM $tbl_lp_item 
8971
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
8972
        $result = Database::query($sql);
8973
        $arrLP = [];
8974
8975
        while ($row = Database::fetch_array($result)) {
8976
            $arrLP[] = [
8977
                'id' => $row['iid'],
8978
                'item_type' => $row['item_type'],
8979
                'title' => $row['title'],
8980
                'path' => $row['path'],
8981
                'description' => $row['description'],
8982
                'parent_item_id' => $row['parent_item_id'],
8983
                'previous_item_id' => $row['previous_item_id'],
8984
                'next_item_id' => $row['next_item_id'],
8985
                'display_order' => $row['display_order'],
8986
                'max_score' => $row['max_score'],
8987
                'min_score' => $row['min_score'],
8988
                'mastery_score' => $row['mastery_score'],
8989
                'prerequisite' => $row['prerequisite']
8990
            ];
8991
        }
8992
8993
        $this->tree_array($arrLP);
8994
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8995
        unset($this->arrMenu);
8996
8997
        $form = new FormValidator('frm_student_publication', 'post', '#');
8998
8999
        if ($action == 'add') {
9000
            $form->addHeader(get_lang('Student_publication'));
9001
        } elseif ($action == 'move') {
9002
            $form->addHeader(get_lang('MoveCurrentStudentPublication'));
9003
        } else {
9004
            $form->addHeader(get_lang('EditCurrentStudentPublication'));
9005
        }
9006
9007
        if ($action != 'move') {
9008
            $form->addText(
9009
                'title',
9010
                get_lang('Title'),
9011
                true,
9012
                ['class' => 'learnpath_item_form', 'id' => 'idTitle']
9013
            );
9014
        }
9015
9016
        $parentSelect = $form->addSelect(
9017
            'parent',
9018
            get_lang('Parent'),
9019
            ['0' => $this->name],
9020
            [
9021
                'onchange' => 'javascript: load_cbo(this.value);',
9022
                'class' => 'learnpath_item_form',
9023
                'id' => 'idParent'
9024
            ]
9025
        );
9026
9027
        $arrHide = [$id];
9028
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
9029
            if ($action != 'add') {
9030
                if (
9031
                    ($arrLP[$i]['item_type'] == 'dir') &&
9032
                    !in_array($arrLP[$i]['id'], $arrHide) &&
9033
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
9034
                ) {
9035
                    $parentSelect->addOption(
9036
                        $arrLP[$i]['title'],
9037
                        $arrLP[$i]['id'],
9038
                        ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;']
9039
                    );
9040
9041
                    if ($parent == $arrLP[$i]['id']) {
9042
                        $parentSelect->setSelected($arrLP[$i]['id']);
9043
                    }
9044
                } else {
9045
                    $arrHide[] = $arrLP[$i]['id'];
9046
                }
9047
            } else {
9048
                if ($arrLP[$i]['item_type'] == 'dir') {
9049
                    $parentSelect->addOption(
9050
                        $arrLP[$i]['title'],
9051
                        $arrLP[$i]['id'],
9052
                        ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;']
9053
                    );
9054
9055
                    if ($parent == $arrLP[$i]['id']) {
9056
                        $parentSelect->setSelected($arrLP[$i]['id']);
9057
                    }
9058
                }
9059
            }
9060
        }
9061
9062
        if (is_array($arrLP)) {
9063
            reset($arrLP);
9064
        }
9065
9066
        $previousSelect = $form->addSelect(
9067
            'previous',
9068
            get_lang('Position'),
9069
            ['0' => get_lang('FirstPosition')],
9070
            ['id' => 'previous', 'class' => 'learnpath_item_form']
9071
        );
9072
9073
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
9074
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
9075
                $previousSelect->addOption(
9076
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
9077
                    $arrLP[$i]['id']
9078
                );
9079
9080
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
9081
                    $previousSelect->setSelected($arrLP[$i]['id']);
9082
                } elseif ($action == 'add') {
9083
                    $previousSelect->setSelected($arrLP[$i]['id']);
9084
                }
9085
            }
9086
        }
9087
9088
        if ($action != 'move') {
9089
            $id_prerequisite = 0;
9090
            if (is_array($arrLP)) {
9091
                foreach ($arrLP as $key => $value) {
9092
                    if ($value['id'] == $id) {
9093
                        $id_prerequisite = $value['prerequisite'];
9094
                        break;
9095
                    }
9096
                }
9097
            }
9098
            $arrHide = [];
9099
            for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
9100
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
9101
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
9102
                        $s_selected_position = $arrLP[$i]['id'];
9103
                    } elseif ($action == 'add') {
9104
                        $s_selected_position = 0;
9105
                    }
9106
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
9107
                }
9108
            }
9109
        }
9110
9111
        if ($action == 'add') {
9112
            $form->addButtonCreate(get_lang('AddAssignmentToCourse'), 'submit_button');
9113
        } else {
9114
            $form->addButtonCreate(get_lang('EditCurrentStudentPublication'), 'submit_button');
9115
        }
9116
9117
        if ($action == 'move') {
9118
            $form->addHidden('title', $item_title);
9119
            $form->addHidden('description', $item_description);
9120
        }
9121
9122
        if (is_numeric($extra_info)) {
9123
            $form->addHidden('path', $extra_info);
9124
        } elseif (is_array($extra_info)) {
9125
            $form->addHidden('path', $extra_info['path']);
9126
        }
9127
9128
        $form->addHidden('type', TOOL_STUDENTPUBLICATION);
9129
        $form->addHidden('post_time', time());
9130
        $form->setDefaults(['title' => $item_title]);
9131
9132
        $return = '<div class="sectioncomment">';
9133
        $return .= $form->returnForm();
9134
        $return .= '</div>';
9135
9136
        return $return;
9137
    }
9138
9139
    /**
9140
     * Displays the menu for manipulating a step
9141
     *
9142
     * @param $item_id
9143
     * @param string $item_type
9144
     * @return string
9145
     */
9146
    public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT)
9147
    {
9148
        $_course = api_get_course_info();
9149
        $course_id = api_get_course_int_id();
9150
        $course_code = api_get_course_id();
9151
        $return = '<div class="actions">';
9152
        switch ($item_type) {
9153
            case 'dir':
9154
                // Commented the message cause should not show it.
9155
                //$lang = get_lang('TitleManipulateChapter');
9156
                break;
9157
            case TOOL_LP_FINAL_ITEM:
9158
            case TOOL_DOCUMENT:
9159
                // Commented the message cause should not show it.
9160
                //$lang = get_lang('TitleManipulateDocument');
9161
                break;
9162
            case TOOL_LINK:
9163
            case 'link':
9164
                // Commented the message cause should not show it.
9165
                //$lang = get_lang('TitleManipulateLink');
9166
                break;
9167
            case TOOL_QUIZ:
9168
                // Commented the message cause should not show it.
9169
                //$lang = get_lang('TitleManipulateQuiz');
9170
                break;
9171
            case TOOL_STUDENTPUBLICATION:
9172
                // Commented the message cause should not show it.
9173
                //$lang = get_lang('TitleManipulateStudentPublication');
9174
                break;
9175
        }
9176
9177
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
9178
        $item_id = intval($item_id);
9179
        $sql = "SELECT * FROM $tbl_lp_item 
9180
                WHERE iid = ".$item_id;
9181
        $result = Database::query($sql);
9182
        $row = Database::fetch_assoc($result);
9183
9184
        $audio_player = null;
9185
        // We display an audio player if needed.
9186
        if (!empty($row['audio'])) {
9187
            $audio_player .= '<div class="lp_mediaplayer" id="container">
9188
                              <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.
9189
                              </div>';
9190
            $audio_player .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
9191
            $audio_player .= '<script>
9192
                var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
9193
                s1.addParam("allowscriptaccess","always");
9194
                s1.addParam("flashvars","file=../..'.api_get_path(REL_COURSE_PATH).$_course['path'].'/document/audio/'.$row['audio'].'&autostart=true");
9195
                s1.write("container");
9196
            </script>';
9197
        }
9198
9199
        $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$item_id.'&lp_id='.$this->lp_id;
9200
9201
        if ($item_type != TOOL_LP_FINAL_ITEM) {
9202
            $return .= Display::url(
9203
                Display::return_icon(
9204
                    'edit.png',
9205
                    get_lang('Edit'),
9206
                    [],
9207
                    ICON_SIZE_SMALL
9208
                ),
9209
                $url.'&action=edit_item&path_item='.$row['path']
9210
            );
9211
9212
            $return .= Display::url(
9213
                Display::return_icon(
9214
                    'move.png',
9215
                    get_lang('Move'),
9216
                    [],
9217
                    ICON_SIZE_SMALL
9218
                ),
9219
                $url.'&action=move_item'
9220
            );
9221
        }
9222
9223
        // Commented for now as prerequisites cannot be added to chapters.
9224
        if ($item_type != 'dir') {
9225
            $return .= Display::url(
9226
                Display::return_icon(
9227
                    'accept.png',
9228
                    get_lang('LearnpathPrerequisites'),
9229
                    [],
9230
                    ICON_SIZE_SMALL
9231
                ),
9232
                $url.'&action=edit_item_prereq'
9233
            );
9234
        }
9235
        $return .= Display::url(
9236
            Display::return_icon(
9237
                'delete.png',
9238
                get_lang('Delete'),
9239
                [],
9240
                ICON_SIZE_SMALL
9241
            ),
9242
            $url.'&action=delete_item'
9243
        );
9244
9245
        if ($item_type == TOOL_HOTPOTATOES) {
9246
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
9247
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
9248
        }
9249
9250
        if ($item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM) {
9251
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
9252
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
9253
        }
9254
9255
        $return .= '</div>';
9256
9257
        if (!empty($audio_player)) {
9258
            $return .= '<br />'.$audio_player;
9259
        }
9260
9261
        return $return;
9262
    }
9263
9264
    /**
9265
     * Creates the javascript needed for filling up the checkboxes without page reload
9266
     * @return string
9267
     */
9268
    public function get_js_dropdown_array()
9269
    {
9270
        $course_id = api_get_course_int_id();
9271
        $return = 'var child_name = new Array();'."\n";
9272
        $return .= 'var child_value = new Array();'."\n\n";
9273
        $return .= 'child_name[0] = new Array();'."\n";
9274
        $return .= 'child_value[0] = new Array();'."\n\n";
9275
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
9276
        $sql = "SELECT * FROM ".$tbl_lp_item."
9277
                WHERE 
9278
                    c_id = $course_id AND 
9279
                    lp_id = ".$this->lp_id." AND 
9280
                    parent_item_id = 0
9281
                ORDER BY display_order ASC";
9282
        $res_zero = Database::query($sql);
9283
        $i = 0;
9284
9285
        while ($row_zero = Database::fetch_array($res_zero)) {
9286
            if ($row_zero['item_type'] !== TOOL_LP_FINAL_ITEM) {
9287
                if ($row_zero['item_type'] == TOOL_QUIZ) {
9288
                    $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']);
9289
                }
9290
                $js_var = json_encode(get_lang('After').' '.$row_zero['title']);
9291
                $return .= 'child_name[0]['.$i.'] = '.$js_var.' ;'."\n";
9292
                $return .= 'child_value[0]['.$i++.'] = "'.$row_zero['iid'].'";'."\n";
9293
            }
9294
        }
9295
        $return .= "\n";
9296
        $sql = "SELECT * FROM $tbl_lp_item
9297
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
9298
        $res = Database::query($sql);
9299
        while ($row = Database::fetch_array($res)) {
9300
            $sql_parent = "SELECT * FROM ".$tbl_lp_item."
9301
                           WHERE
9302
                                c_id = ".$course_id." AND
9303
                                parent_item_id = " . $row['iid']."
9304
                           ORDER BY display_order ASC";
9305
            $res_parent = Database::query($sql_parent);
9306
            $i = 0;
9307
            $return .= 'child_name['.$row['iid'].'] = new Array();'."\n";
9308
            $return .= 'child_value['.$row['iid'].'] = new Array();'."\n\n";
9309
9310
            while ($row_parent = Database::fetch_array($res_parent)) {
9311
                $js_var = json_encode(get_lang('After').' '.$row_parent['title']);
9312
                $return .= 'child_name['.$row['iid'].']['.$i.'] =   '.$js_var.' ;'."\n";
9313
                $return .= 'child_value['.$row['iid'].']['.$i++.'] = "'.$row_parent['iid'].'";'."\n";
9314
            }
9315
            $return .= "\n";
9316
        }
9317
9318
        return $return;
9319
    }
9320
9321
    /**
9322
     * Display the form to allow moving an item
9323
     * @param	integer $item_id		Item ID
9324
     * @return	string		HTML form
9325
     */
9326
    public function display_move_item($item_id)
9327
    {
9328
        $course_id = api_get_course_int_id();
9329
        $return = '';
9330
9331
        if (is_numeric($item_id)) {
9332
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
9333
9334
            $sql = "SELECT * FROM $tbl_lp_item
9335
                    WHERE iid = $item_id";
9336
            $res = Database::query($sql);
9337
            $row = Database::fetch_array($res);
9338
9339
            switch ($row['item_type']) {
9340
                case 'dir':
9341
                case 'asset':
9342
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9343
                    $return .= $this->display_item_form(
9344
                        $row['item_type'],
9345
                        get_lang('MoveCurrentChapter'),
9346
                        'move',
9347
                        $item_id,
9348
                        $row
9349
                    );
9350
                    break;
9351
                case TOOL_DOCUMENT:
9352
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9353
                    $return .= $this->display_document_form('move', $item_id, $row);
9354
                    break;
9355
                case TOOL_LINK:
9356
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9357
                    $return .= $this->display_link_form('move', $item_id, $row);
9358
                    break;
9359
                case TOOL_HOTPOTATOES:
9360
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9361
                    $return .= $this->display_link_form('move', $item_id, $row);
9362
                    break;
9363
                case TOOL_QUIZ:
9364
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9365
                    $return .= $this->display_quiz_form('move', $item_id, $row);
9366
                    break;
9367
                case TOOL_STUDENTPUBLICATION:
9368
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9369
                    $return .= $this->display_student_publication_form('move', $item_id, $row);
9370
                    break;
9371
                case TOOL_FORUM:
9372
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9373
                    $return .= $this->display_forum_form('move', $item_id, $row);
9374
                    break;
9375
                case TOOL_THREAD:
9376
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
9377
                    $return .= $this->display_forum_form('move', $item_id, $row);
9378
                    break;
9379
            }
9380
        }
9381
9382
        return $return;
9383
    }
9384
9385
    /**
9386
     * Displays a basic form on the overview page for changing the item title and the item description.
9387
     * @param string $item_type
9388
     * @param string $title
9389
     * @param array $data
9390
     * @return string
9391
     */
9392
    public function display_item_small_form($item_type, $title = '', $data = [])
9393
    {
9394
        $url = api_get_self().'?'.api_get_cidreq().'&action=edit_item&lp_id='.$this->lp_id;
9395
        $form = new FormValidator('small_form', 'post', $url);
9396
        $form->addElement('header', $title);
9397
        $form->addElement('text', 'title', get_lang('Title'));
9398
        $form->addButtonSave(get_lang('Save'), 'submit_button');
9399
        $form->addElement('hidden', 'id', $data['id']);
9400
        $form->addElement('hidden', 'parent', $data['parent_item_id']);
9401
        $form->addElement('hidden', 'previous', $data['previous_item_id']);
9402
        $form->setDefaults(['title' => $data['title']]);
9403
9404
        return $form->toHtml();
9405
    }
9406
9407
    /**
9408
     * Return HTML form to allow prerequisites selection
9409
     * @todo use FormValidator
9410
     * @param	integer Item ID
9411
     * @return	string	HTML form
9412
     */
9413
    public function display_item_prerequisites_form($item_id)
9414
    {
9415
        $course_id = api_get_course_int_id();
9416
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
9417
        $item_id = intval($item_id);
9418
        /* Current prerequisite */
9419
        $sql = "SELECT * FROM $tbl_lp_item
9420
                WHERE iid = $item_id";
9421
        $result = Database::query($sql);
9422
        $row    = Database::fetch_array($result);
9423
        $prerequisiteId = $row['prerequisite'];
9424
        $return = '<legend>';
9425
        $return .= get_lang('AddEditPrerequisites');
9426
        $return .= '</legend>';
9427
        $return .= '<form method="POST">';
9428
        $return .= '<div class="table-responsive">';
9429
        $return .= '<table class="table table-hover">';
9430
        $return .= '<thead>';
9431
        $return .= '<tr>';
9432
        $return .= '<th>'.get_lang('LearnpathPrerequisites').'</th>';
9433
        $return .= '<th width="140">'.get_lang('Minimum').'</th>';
9434
        $return .= '<th width="140">'.get_lang('Maximum').'</th>';
9435
        $return .= '</tr>';
9436
        $return .= '</thead>';
9437
9438
        // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146
9439
        $return .= '<tbody>';
9440
        $return .= '<tr>';
9441
        $return .= '<td colspan="3">';
9442
        $return .= '<div class="radio learnpath"><label for="idNone">';
9443
        $return .= '<input checked="checked" id="idNone" name="prerequisites" type="radio" />';
9444
        $return .= get_lang('None').'</label>';
9445
        $return .= '</div>';
9446
        $return .= '</tr>';
9447
9448
        $sql = "SELECT * FROM $tbl_lp_item
9449
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
9450
        $result = Database::query($sql);
9451
        $arrLP = [];
9452
9453
        $selectedMinScore = [];
9454
        $selectedMaxScore = [];
9455
        while ($row = Database::fetch_array($result)) {
9456
            if ($row['id'] == $item_id) {
9457
                $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score'];
9458
                $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score'];
9459
            }
9460
            $arrLP[] = [
9461
                'id' => $row['iid'],
9462
                'item_type' => $row['item_type'],
9463
                'title' => $row['title'],
9464
                'ref' => $row['ref'],
9465
                'description' => $row['description'],
9466
                'parent_item_id' => $row['parent_item_id'],
9467
                'previous_item_id' => $row['previous_item_id'],
9468
                'next_item_id' => $row['next_item_id'],
9469
                'max_score' => $row['max_score'],
9470
                'min_score' => $row['min_score'],
9471
                'mastery_score' => $row['mastery_score'],
9472
                'prerequisite' => $row['prerequisite'],
9473
                'next_item_id' => $row['next_item_id'],
9474
                'display_order' => $row['display_order'],
9475
                'prerequisite_min_score' => $row['prerequisite_min_score'],
9476
                'prerequisite_max_score' => $row['prerequisite_max_score'],
9477
            ];
9478
        }
9479
9480
        $this->tree_array($arrLP);
9481
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
9482
        unset($this->arrMenu);
9483
9484
        for ($i = 0; $i < count($arrLP); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
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...
9485
            $item = $arrLP[$i];
9486
9487
            if ($item['id'] == $item_id) {
9488
                break;
9489
            }
9490
9491
            $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score'];
9492
            $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']] : 0;
9493
9494
            $return .= '<tr>';
9495
            $return .= '<td '.(($item['item_type'] != TOOL_QUIZ && $item['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '').'>';
9496
            $return .= '<div style="margin-left:'.($item['depth'] * 20).'px;" class="radio learnpath">';
9497
            $return .= '<label for="id'.$item['id'].'">';
9498
            $return .= '<input'.(in_array($prerequisiteId, [$item['id'], $item['ref']]) ? ' checked="checked" ' : '').($item['item_type'] == 'dir' ? ' disabled="disabled" ' : ' ').'id="id'.$item['id'].'" name="prerequisites"  type="radio" value="'.$item['id'].'" />';
9499
9500
            $icon_name = str_replace(' ', '', $item['item_type']);
9501
9502
            if (file_exists('../img/lp_'.$icon_name.'.png')) {
9503
                $return .= Display::return_icon('lp_'.$icon_name.'.png');
9504
            } else {
9505
                if (file_exists('../img/lp_'.$icon_name.'.png')) {
9506
                    $return .= Display::return_icon('lp_'.$icon_name.'.png');
9507
                } else {
9508
                    $return .= Display::return_icon('folder_document.png');
9509
                }
9510
            }
9511
9512
            $return .= $item['title'].'</label>';
9513
            $return .= '</div>';
9514
            $return .= '</td>';
9515
9516
            if ($item['item_type'] == TOOL_QUIZ) {
9517
                // lets update max_score Quiz information depending of the Quiz Advanced properties
9518
                $tmp_obj_lp_item = new LpItem($course_id, $item['id']);
9519
                $tmp_obj_exercice = new Exercise($course_id);
9520
                $tmp_obj_exercice->read($tmp_obj_lp_item->path);
9521
                $tmp_obj_lp_item->max_score = $tmp_obj_exercice->get_max_score();
9522
                $tmp_obj_lp_item->update();
9523
                $item['max_score'] = $tmp_obj_lp_item->max_score;
9524
9525
                $return .= '<td>';
9526
                $return .= '<input class="form-control" size="4" maxlength="3" name="min_'.$item['id'].'" type="number" min="0" step="1" max="'.$item['max_score'].'" value="'.$selectedMinScoreValue.'" />';
9527
                $return .= '</td>';
9528
                $return .= '<td>';
9529
                $return .= '<input class="form-control" size="4" maxlength="3" readonly name="max_'.$item['id'].'" type="number" min="0" step="1" max="'.$item['max_score'].'" value="'.$selectedMaxScoreValue.'" />';
9530
                $return .= '</td>';
9531
            }
9532
9533
            if ($item['item_type'] == TOOL_HOTPOTATOES) {
9534
                $return .= '<td>';
9535
                $return .= '<input size="4" maxlength="3" name="min_'.$item['id'].'" type="number" min="0" step="1" max="'.$item['max_score'].'" value="'.$selectedMinScoreValue.'" />';
9536
                $return .= '</td>';
9537
                $return .= '<td>';
9538
                $return .= '<input size="4" maxlength="3" name="max_'.$item['id'].'" readonly type="number" min="0" step="1" max="'.$item['max_score'].'"  value="'.$selectedMaxScoreValue.'" />';
9539
                $return .= '</td>';
9540
            }
9541
            $return .= '</tr>';
9542
        }
9543
        $return .= '<tr>';
9544
        $return .= '</tr>';
9545
        $return .= '</tbody>';
9546
        $return .= '</table>';
9547
        $return .= '</div>';
9548
        $return .= '<div class="form-group">';
9549
        $return .= '<button class="btn btn-primary" name="submit_button" type="submit">'.get_lang('ModifyPrerequisites').'</button>';
9550
        $return .= '</form>';
9551
9552
        return $return;
9553
    }
9554
9555
    /**
9556
     * Return HTML list to allow prerequisites selection for lp
9557
     * @param	integer Item ID
9558
     * @return	string	HTML form
9559
     */
9560
    public function display_lp_prerequisites_list()
9561
    {
9562
        $course_id = api_get_course_int_id();
9563
        $lp_id = $this->lp_id;
9564
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
9565
9566
        // get current prerequisite
9567
        $sql = "SELECT * FROM $tbl_lp WHERE iid = $lp_id ";
9568
        $result = Database::query($sql);
9569
        $row = Database::fetch_array($result);
9570
        $prerequisiteId = $row['prerequisite'];
9571
        $session_id = api_get_session_id();
9572
        $session_condition = api_get_session_condition($session_id, true, true);
9573
        $sql = "SELECT * FROM $tbl_lp
9574
                WHERE c_id = $course_id $session_condition
9575
                ORDER BY display_order ";
9576
        $rs = Database::query($sql);
9577
        $return = '';
9578
        $return .= '<select name="prerequisites" class="form-control">';
9579
        $return .= '<option value="0">'.get_lang('None').'</option>';
9580
        if (Database::num_rows($rs) > 0) {
9581
            while ($row = Database::fetch_array($rs)) {
9582
                if ($row['id'] == $lp_id) {
9583
                    continue;
9584
                }
9585
                $return .= '<option value="'.$row['id'].'" '.(($row['id'] == $prerequisiteId) ? ' selected ' : '').'>'.$row['name'].'</option>';
9586
            }
9587
        }
9588
        $return .= '</select>';
9589
9590
        return $return;
9591
    }
9592
9593
    /**
9594
     * Creates a list with all the documents in it
9595
     * @param bool $showInvisibleFiles
9596
     * @return string
9597
     */
9598
    public function get_documents($showInvisibleFiles = false)
9599
    {
9600
        $course_info = api_get_course_info();
9601
        $sessionId = api_get_session_id();
9602
        $documentTree = DocumentManager::get_document_preview(
9603
            $course_info,
9604
            $this->lp_id,
9605
            null,
9606
            $sessionId,
9607
            true,
9608
            null,
9609
            null,
9610
            $showInvisibleFiles,
9611
            true
9612
        );
9613
9614
        $headers = [
9615
            get_lang('Files'),
9616
            get_lang('CreateTheDocument'),
9617
            get_lang('Upload')
9618
        ];
9619
9620
        $form = new FormValidator(
9621
            'form_upload',
9622
            'POST',
9623
            $this->getCurrentBuildingModeURL(),
9624
            '',
9625
            ['enctype' => 'multipart/form-data']
9626
        );
9627
9628
        $folders = DocumentManager::get_all_document_folders(
9629
            api_get_course_info(),
9630
            0,
9631
            true
9632
        );
9633
9634
        DocumentManager::build_directory_selector(
9635
            $folders,
9636
            '',
9637
            [],
9638
            true,
9639
            $form,
9640
            'directory_parent_id'
9641
        );
9642
9643
        $group = [
9644
            $form->createElement(
9645
                'radio',
9646
                'if_exists',
9647
                get_lang("UplWhatIfFileExists"),
9648
                get_lang('UplDoNothing'),
9649
                'nothing'
9650
            ),
9651
            $form->createElement(
9652
                'radio',
9653
                'if_exists',
9654
                null,
9655
                get_lang('UplOverwriteLong'),
9656
                'overwrite'
9657
            ),
9658
            $form->createElement(
9659
                'radio',
9660
                'if_exists',
9661
                null,
9662
                get_lang('UplRenameLong'),
9663
                'rename'
9664
            )
9665
        ];
9666
        $form->addGroup($group, null, get_lang('UplWhatIfFileExists'));
9667
        $form->setDefaults(['if_exists' => 'rename']);
9668
9669
        // Check box options
9670
        $form->addElement(
9671
            'checkbox',
9672
            'unzip',
9673
            get_lang('Options'),
9674
            get_lang('Uncompress')
9675
        );
9676
9677
        $url = api_get_path(WEB_AJAX_PATH).'document.ajax.php?'.api_get_cidreq().'&a=upload_file&curdirpath=';
9678
        $form->addMultipleUpload($url);
9679
        $new = $this->display_document_form('add', 0);
9680
        $tabs = Display::tabs(
9681
            $headers,
9682
            [$documentTree, $new, $form->returnForm()],
9683
            'subtab'
9684
        );
9685
9686
        return $tabs;
9687
    }
9688
9689
    /**
9690
     * Creates a list with all the exercises (quiz) in it
9691
     * @return string
9692
     */
9693
    public function get_exercises()
9694
    {
9695
        $course_id = api_get_course_int_id();
9696
        $session_id = api_get_session_id();
9697
        $userInfo = api_get_user_info();
9698
9699
        // New for hotpotatoes.
9700
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
9701
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
9702
        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
9703
        $condition_session = api_get_session_condition($session_id, true, true);
9704
        $setting = api_get_configuration_value('show_invisible_exercise_in_lp_list');
9705
9706
        $activeCondition = ' active <> -1 ';
9707
        if ($setting) {
9708
            $activeCondition = ' active = 1 ';
9709
        }
9710
9711
        $sql_quiz = "SELECT * FROM $tbl_quiz
9712
                     WHERE c_id = $course_id AND $activeCondition $condition_session
9713
                     ORDER BY title ASC";
9714
9715
        $sql_hot = "SELECT * FROM $tbl_doc
9716
                     WHERE c_id = $course_id AND path LIKE '".$uploadPath."/%/%htm%'  $condition_session
9717
                     ORDER BY id ASC";
9718
9719
        $res_quiz = Database::query($sql_quiz);
9720
        $res_hot  = Database::query($sql_hot);
9721
9722
        $return = '<ul class="lp_resource">';
9723
        $return .= '<li class="lp_resource_element">';
9724
        $return .= Display::return_icon('new_exercice.png');
9725
        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise_admin.php?'.api_get_cidreq().'&lp_id='.$this->lp_id.'">'.
9726
            get_lang('NewExercise').'</a>';
9727
        $return .= '</li>';
9728
9729
        $previewIcon = Display::return_icon(
9730
            'preview_view.png',
9731
            get_lang('Preview')
9732
        );
9733
        $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/showinframes.php?'.api_get_cidreq();
9734
9735
        // Display hotpotatoes
9736
        while ($row_hot = Database::fetch_array($res_hot)) {
9737
            $link = Display::url(
9738
                $previewIcon,
9739
                $exerciseUrl.'&file='.$row_hot['path'],
9740
                ['target' => '_blank']
9741
            );
9742
            $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >';
9743
            $return .= '<a class="moved" href="#">';
9744
            $return .= Display::return_icon(
9745
                'move_everywhere.png',
9746
                get_lang('Move'),
9747
                [],
9748
                ICON_SIZE_TINY
9749
            );
9750
            $return .= '</a> ';
9751
            $return .= Display::return_icon('hotpotatoes_s.png');
9752
            $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_HOTPOTATOES.'&file='.$row_hot['id'].'&lp_id='.$this->lp_id.'">'.
9753
                ((!empty($row_hot['comment'])) ? $row_hot['comment'] : Security::remove_XSS($row_hot['title'])).$link.'</a>';
9754
            $return .= '</li>';
9755
        }
9756
9757
        $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq();
9758
        while ($row_quiz = Database::fetch_array($res_quiz)) {
9759
            $title = strip_tags(
9760
                api_html_entity_decode($row_quiz['title'])
9761
            );
9762
9763
            $link = Display::url(
9764
                $previewIcon,
9765
                $exerciseUrl.'&exerciseId='.$row_quiz['id'],
9766
                ['target' => '_blank']
9767
            );
9768
            $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['id'].'" data_type="quiz" title="'.$title.'" >';
9769
            $return .= '<a class="moved" href="#">';
9770
            $return .= Display::return_icon(
9771
                'move_everywhere.png',
9772
                get_lang('Move'),
9773
                [],
9774
                ICON_SIZE_TINY
9775
            );
9776
            $return .= '</a> ';
9777
            $return .= Display::return_icon(
9778
                'quizz_small.gif',
9779
                '',
9780
                [],
9781
                ICON_SIZE_TINY
9782
            );
9783
            $sessionStar = api_get_session_image(
9784
                $row_quiz['session_id'],
9785
                $userInfo['status']
9786
            );
9787
            $return .= '<a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_QUIZ.'&file='.$row_quiz['id'].'&lp_id='.$this->lp_id.'">'.
9788
                Security::remove_XSS(cut($title, 80)).$link.$sessionStar.
9789
                '</a>';
9790
9791
            $return .= '</li>';
9792
        }
9793
9794
        $return .= '</ul>';
9795
        return $return;
9796
    }
9797
9798
    /**
9799
     * Creates a list with all the links in it
9800
     * @return string
9801
     */
9802
    public function get_links()
9803
    {
9804
        $selfUrl = api_get_self();
9805
        $courseIdReq = api_get_cidreq();
9806
        $course = api_get_course_info();
9807
        $userInfo = api_get_user_info();
9808
9809
        $course_id = $course['real_id'];
9810
        $tbl_link = Database::get_course_table(TABLE_LINK);
9811
        $linkCategoryTable = Database::get_course_table(TABLE_LINK_CATEGORY);
9812
        $moveEverywhereIcon = Display::return_icon(
9813
            'move_everywhere.png',
9814
            get_lang('Move'),
9815
            [],
9816
            ICON_SIZE_TINY
9817
        );
9818
9819
        $session_id = api_get_session_id();
9820
        $condition_session = api_get_session_condition(
9821
            $session_id,
9822
            true,
9823
            true,
9824
            "link.session_id"
9825
        );
9826
9827
        $sql = "SELECT 
9828
                    link.id as link_id,
9829
                    link.title as link_title,
9830
                    link.session_id as link_session_id,
9831
                    link.category_id as category_id,
9832
                    link_category.category_title as category_title
9833
                FROM $tbl_link as link
9834
                LEFT JOIN $linkCategoryTable as link_category
9835
                ON (link.category_id = link_category.id AND link.c_id = link_category.c_id)
9836
                WHERE link.c_id = ".$course_id." $condition_session
9837
                ORDER BY link_category.category_title ASC, link.title ASC";
9838
        $result = Database::query($sql);
9839
        $categorizedLinks = [];
9840
        $categories = [];
9841
9842
        while ($link = Database::fetch_array($result)) {
9843
            if (!$link['category_id']) {
9844
                $link['category_title'] = get_lang('Uncategorized');
9845
            }
9846
            $categories[$link['category_id']] = $link['category_title'];
9847
            $categorizedLinks[$link['category_id']][$link['link_id']] = $link;
9848
        }
9849
9850
        $linksHtmlCode =
9851
            '<script>
9852
            function toggle_tool(tool, id) {
9853
                if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){
9854
                    document.getElementById(tool+"_"+id+"_content").style.display = "block";
9855
                    document.getElementById(tool+"_"+id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
9856
                } else {
9857
                    document.getElementById(tool+"_"+id+"_content").style.display = "none";
9858
                    document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.gif').'";
9859
                }
9860
            }
9861
        </script>
9862
9863
        <ul class="lp_resource">
9864
            <li class="lp_resource_element">
9865
                '.Display::return_icon('linksnew.gif').'
9866
                <a href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" title="'.get_lang('LinkAdd').'">'.
9867
                get_lang('LinkAdd').'
9868
                </a>
9869
            </li>';
9870
9871
        foreach ($categorizedLinks as $categoryId => $links) {
9872
            $linkNodes = null;
9873
            foreach ($links as $key => $linkInfo) {
9874
                $title = $linkInfo['link_title'];
9875
                $linkSessionId = $linkInfo['link_session_id'];
9876
9877
                $link = Display::url(
9878
                    Display::return_icon('preview_view.png', get_lang('Preview')),
9879
                    api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$key,
9880
                    ['target' => '_blank']
9881
                );
9882
9883
                if (api_get_item_visibility($course, TOOL_LINK, $key, $session_id) != 2) {
9884
                    $sessionStar = api_get_session_image($linkSessionId, $userInfo['status']);
9885
                    $linkNodes .=
9886
                        '<li class="lp_resource_element" data_id="'.$key.'" data_type="'.TOOL_LINK.'" title="'.$title.'" >
9887
                        <a class="moved" href="#">'.
9888
                            $moveEverywhereIcon.
9889
                        '</a>
9890
                        '.Display::return_icon('lp_link.png').'
9891
                        <a class="moved" href="'.$selfUrl.'?'.$courseIdReq.'&action=add_item&type='.
9892
                        TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id.'">'.
9893
                        Security::remove_XSS($title).$sessionStar.$link.
9894
                        '</a>
9895
                    </li>';
9896
                }
9897
            }
9898
            $linksHtmlCode .=
9899
                '<li>
9900
                <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle">
9901
                    <img src="'.Display::returnIconPath('add.gif').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener"
9902
                    align="absbottom" />
9903
                </a>
9904
                <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span>
9905
            </li>
9906
            <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>';
9907
        }
9908
        $linksHtmlCode .= '</ul>';
9909
9910
        return $linksHtmlCode;
9911
    }
9912
9913
    /**
9914
     * Creates a list with all the student publications in it
9915
     * @return string
9916
     */
9917
    public function get_student_publications()
9918
    {
9919
        $return = '<ul class="lp_resource">';
9920
        $return .= '<li class="lp_resource_element">';
9921
        $return .= Display::return_icon('works_new.gif');
9922
        $return .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&lp_id='.$this->lp_id.'">'.
9923
            get_lang('AddAssignmentPage').'</a>';
9924
        $return .= '</li>';
9925
        $sessionId = api_get_session_id();
9926
9927
        if (empty($sessionId)) {
9928
            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
9929
            $works = getWorkListTeacher(0, 100, null, null, null);
9930
            if (!empty($works)) {
9931
                foreach ($works as $work) {
9932
                    $link = Display::url(
9933
                        Display::return_icon('preview_view.png', get_lang('Preview')),
9934
                        api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$work['iid'],
9935
                        ['target' => '_blank']
9936
                    );
9937
9938
                    $return .= '<li class="lp_resource_element" data_id="'.$work['iid'].'" data_type="'.TOOL_STUDENTPUBLICATION.'" title="'.Security::remove_XSS(cut(strip_tags($work['title']), 80)).'">';
9939
                    $return .= '<a class="moved" href="#">';
9940
                    $return .= Display::return_icon(
9941
                        'move_everywhere.png',
9942
                        get_lang('Move'),
9943
                        [],
9944
                        ICON_SIZE_TINY
9945
                    );
9946
                    $return .= '</a> ';
9947
9948
                    $return .= Display::return_icon('works.gif');
9949
                    $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.'">'.
9950
                        Security::remove_XSS(cut(strip_tags($work['title']), 80)).' '.$link.'
9951
                    </a>';
9952
9953
                    $return .= '</li>';
9954
                }
9955
            }
9956
        }
9957
9958
        $return .= '</ul>';
9959
9960
        return $return;
9961
    }
9962
9963
    /**
9964
     * Creates a list with all the forums in it
9965
     * @return string
9966
     */
9967
    public function get_forums()
9968
    {
9969
        require_once '../forum/forumfunction.inc.php';
9970
        require_once '../forum/forumconfig.inc.php';
9971
9972
        $forumCategories = get_forum_categories();
9973
        $forumsInNoCategory = get_forums_in_category(0);
9974
        if (!empty($forumsInNoCategory)) {
9975
            $forumCategories = array_merge(
9976
                $forumCategories,
9977
                [
9978
                    [
9979
                        'cat_id' => 0,
9980
                        'session_id' => 0,
9981
                        'visibility' => 1,
9982
                        'cat_comment' => null,
9983
                    ],
9984
                ]
9985
            );
9986
        }
9987
9988
        $forumList = get_forums();
9989
        $a_forums = [];
9990
        foreach ($forumCategories as $forumCategory) {
9991
            // The forums in this category.
9992
            $forumsInCategory = get_forums_in_category($forumCategory['cat_id']);
9993
            if (!empty($forumsInCategory)) {
9994
                foreach ($forumList as $forum) {
9995
                    if (isset($forum['forum_category']) &&
9996
                        $forum['forum_category'] == $forumCategory['cat_id']
9997
                    ) {
9998
                        $a_forums[] = $forum;
9999
                    }
10000
                }
10001
            }
10002
        }
10003
10004
        $return = '<ul class="lp_resource">';
10005
10006
        // First add link
10007
        $return .= '<li class="lp_resource_element">';
10008
        $return .= Display::return_icon('new_forum.png');
10009
        $return .= Display::url(
10010
            get_lang('CreateANewForum'),
10011
            api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq().'&'.http_build_query([
10012
                'action' => 'add',
10013
                'content' => 'forum',
10014
                'lp_id' => $this->lp_id
10015
            ]),
10016
            ['title' => get_lang('CreateANewForum')]
10017
        );
10018
        $return .= '</li>';
10019
10020
        $return .= '<script>
10021
            function toggle_forum(forum_id) {
10022
                if (document.getElementById("forum_"+forum_id+"_content").style.display == "none") {
10023
                    document.getElementById("forum_"+forum_id+"_content").style.display = "block";
10024
                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
10025
                } else {
10026
                    document.getElementById("forum_"+forum_id+"_content").style.display = "none";
10027
                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
10028
                }
10029
            }
10030
        </script>';
10031
10032
        foreach ($a_forums as $forum) {
10033
            if (!empty($forum['forum_id'])) {
10034
                $link = Display::url(
10035
                    Display::return_icon('preview_view.png', get_lang('Preview')),
10036
                    api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$forum['forum_id'],
10037
                    ['target' => '_blank']
10038
                );
10039
10040
                $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';
10041
                $return .= '<a class="moved" href="#">';
10042
                $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
10043
                $return .= ' </a>';
10044
                $return .= Display::return_icon('lp_forum.png', '', [], ICON_SIZE_TINY);
10045
                $return .= '<a onclick="javascript:toggle_forum('.$forum['forum_id'].');" style="cursor:hand; vertical-align:middle">
10046
                                <img src="' . Display::returnIconPath('add.gif').'" id="forum_'.$forum['forum_id'].'_opener" align="absbottom" />
10047
                            </a>
10048
                            <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">'.
10049
                    Security::remove_XSS($forum['forum_title']).' '.$link.'</a>';
10050
10051
                $return .= '</li>';
10052
10053
                $return .= '<div style="display:none" id="forum_'.$forum['forum_id'].'_content">';
10054
                $a_threads = get_threads($forum['forum_id']);
10055
                if (is_array($a_threads)) {
10056
                    foreach ($a_threads as $thread) {
10057
                        $link = Display::url(
10058
                            Display::return_icon('preview_view.png', get_lang('Preview')),
10059
                            api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forum['forum_id'].'&thread='.$thread['thread_id'],
10060
                            ['target' => '_blank']
10061
                        );
10062
10063
                        $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >';
10064
                        $return .= '&nbsp;<a class="moved" href="#">';
10065
                        $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
10066
                        $return .= ' </a>';
10067
                        $return .= Display::return_icon('forumthread.png', get_lang('Thread'), [], ICON_SIZE_TINY);
10068
                        $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.'">'.
10069
                            Security::remove_XSS($thread['thread_title']).' '.$link.'</a>';
10070
                        $return .= '</li>';
10071
                    }
10072
                }
10073
                $return .= '</div>';
10074
            }
10075
        }
10076
        $return .= '</ul>';
10077
10078
        return $return;
10079
    }
10080
10081
    /**
10082
     * // TODO: The output encoding should be equal to the system encoding.
10083
     *
10084
     * Exports the learning path as a SCORM package. This is the main function that
10085
     * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
10086
     * whole thing and returns the zip.
10087
     *
10088
     * This method needs to be called in PHP5, as it will fail with non-adequate
10089
     * XML package (like the ones for PHP4), and it is *not* a static method, so
10090
     * you need to call it on a learnpath object.
10091
     * @TODO The method might be redefined later on in the scorm class itself to avoid
10092
     * creating a SCORM structure if there is one already. However, if the initial SCORM
10093
     * path has been modified, it should use the generic method here below.
10094
     * @param	string	Optional name of zip file. If none, title of learnpath is
10095
     * 					domesticated and trailed with ".zip"
10096
     * @return	string	Returns the zip package string, or null if error
10097
     */
10098
    public function scorm_export()
10099
    {
10100
        $_course = api_get_course_info();
10101
        $course_id = $_course['real_id'];
10102
10103
        // Remove memory and time limits as much as possible as this might be a long process...
10104
        if (function_exists('ini_set')) {
10105
            api_set_memory_limit('256M');
10106
            ini_set('max_execution_time', 600);
10107
        }
10108
10109
        // Create the zip handler (this will remain available throughout the method).
10110
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
10111
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
10112
        $temp_dir_short = uniqid();
10113
        $temp_zip_dir = $archive_path.'/'.$temp_dir_short;
10114
        $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip';
10115
        $zip_folder = new PclZip($temp_zip_file);
10116
        $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
10117
        $root_path = $main_path = api_get_path(SYS_PATH);
10118
        $files_cleanup = [];
10119
10120
        // Place to temporarily stash the zip file.
10121
        // create the temp dir if it doesn't exist
10122
        // or do a cleanup before creating the zip file.
10123
        if (!is_dir($temp_zip_dir)) {
10124
            mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
10125
        } else {
10126
            // Cleanup: Check the temp dir for old files and delete them.
10127
            $handle = opendir($temp_zip_dir);
10128
            while (false !== ($file = readdir($handle))) {
10129
                if ($file != '.' && $file != '..') {
10130
                    unlink("$temp_zip_dir/$file");
10131
                }
10132
            }
10133
            closedir($handle);
10134
        }
10135
        $zip_files = $zip_files_abs = $zip_files_dist = [];
10136
        if (is_dir($current_course_path.'/scorm/'.$this->path) &&
10137
            is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml')
10138
        ) {
10139
            // Remove the possible . at the end of the path.
10140
            $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
10141
            $dest_path_to_scorm_folder = str_replace('//', '/', $temp_zip_dir.'/scorm/'.$dest_path_to_lp);
10142
            mkdir(
10143
                $dest_path_to_scorm_folder,
10144
                api_get_permissions_for_new_directories(),
10145
                true
10146
            );
10147
            copyr(
10148
                $current_course_path.'/scorm/'.$this->path,
10149
                $dest_path_to_scorm_folder,
10150
                ['imsmanifest'],
10151
                $zip_files
10152
            );
10153
        }
10154
10155
        // Build a dummy imsmanifest structure.
10156
        // Do not add to the zip yet (we still need it).
10157
        // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
10158
        // Aggregation Model official document, section "2.3 Content Packaging".
10159
        // We are going to build a UTF-8 encoded manifest. Later we will recode it to the desired (and supported) encoding.
10160
        $xmldoc = new DOMDocument('1.0');
10161
        $root = $xmldoc->createElement('manifest');
10162
        $root->setAttribute('identifier', 'SingleCourseManifest');
10163
        $root->setAttribute('version', '1.1');
10164
        $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2');
10165
        $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2');
10166
        $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
10167
        $root->setAttribute('xsi:schemaLocation', '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');
10168
        // Build mandatory sub-root container elements.
10169
        $metadata = $xmldoc->createElement('metadata');
10170
        $md_schema = $xmldoc->createElement('schema', 'ADL SCORM');
10171
        $metadata->appendChild($md_schema);
10172
        $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2');
10173
        $metadata->appendChild($md_schemaversion);
10174
        $root->appendChild($metadata);
10175
10176
        $organizations = $xmldoc->createElement('organizations');
10177
        $resources = $xmldoc->createElement('resources');
10178
10179
        // Build the only organization we will use in building our learnpaths.
10180
        $organizations->setAttribute('default', 'chamilo_scorm_export');
10181
        $organization = $xmldoc->createElement('organization');
10182
        $organization->setAttribute('identifier', 'chamilo_scorm_export');
10183
        // To set the title of the SCORM entity (=organization), we take the name given
10184
        // in Chamilo and convert it to HTML entities using the Chamilo charset (not the
10185
        // learning path charset) as it is the encoding that defines how it is stored
10186
        // in the database. Then we convert it to HTML entities again as the "&" character
10187
        // alone is not authorized in XML (must be &amp;).
10188
        // The title is then decoded twice when extracting (see scorm::parse_manifest).
10189
        $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name()));
10190
        $organization->appendChild($org_title);
10191
10192
        $folder_name = 'document';
10193
10194
        // Removes the learning_path/scorm_folder path when exporting see #4841
10195
        $path_to_remove = null;
10196
        $result = $this->generate_lp_folder($_course);
10197
10198
        if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) {
10199
            $path_to_remove = 'document'.$result['dir'];
10200
            $path_to_replace = $folder_name.'/';
10201
        }
10202
10203
        // Fixes chamilo scorm exports
10204
        if ($this->ref === 'chamilo_scorm_export') {
10205
            $path_to_remove = 'scorm/'.$this->path.'/document/';
10206
        }
10207
10208
        // For each element, add it to the imsmanifest structure, then add it to the zip.
10209
        // Always call the learnpathItem->scorm_export() method to change it to the SCORM format.
10210
        $link_updates = [];
10211
        $links_to_create = [];
10212
        //foreach ($this->items as $index => $item) {
10213
        foreach ($this->ordered_items as $index => $itemId) {
10214
            $item = $this->items[$itemId];
10215
            if (!in_array($item->type, [TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION])) {
10216
                // Get included documents from this item.
10217
                if ($item->type === 'sco') {
10218
                    $inc_docs = $item->get_resources_from_source(
10219
                        null,
10220
                        api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.'scorm/'.$this->path.'/'.$item->get_path()
10221
                    );
10222
                } else {
10223
                    $inc_docs = $item->get_resources_from_source();
10224
                }
10225
                // Give a child element <item> to the <organization> element.
10226
                $my_item_id = $item->get_id();
10227
                $my_item = $xmldoc->createElement('item');
10228
                $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id);
10229
                $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id);
10230
                $my_item->setAttribute('isvisible', 'true');
10231
                // Give a child element <title> to the <item> element.
10232
                $my_title = $xmldoc->createElement(
10233
                    'title',
10234
                    htmlspecialchars(
10235
                        api_utf8_encode($item->get_title()),
10236
                        ENT_QUOTES,
10237
                        'UTF-8'
10238
                    )
10239
                );
10240
                $my_item->appendChild($my_title);
10241
                // Give a child element <adlcp:prerequisites> to the <item> element.
10242
                $my_prereqs = $xmldoc->createElement(
10243
                    'adlcp:prerequisites',
10244
                    $this->get_scorm_prereq_string($my_item_id)
10245
                );
10246
                $my_prereqs->setAttribute('type', 'aicc_script');
10247
                $my_item->appendChild($my_prereqs);
10248
                // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
10249
                //$xmldoc->createElement('adlcp:maxtimeallowed','');
10250
                // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
10251
                //$xmldoc->createElement('adlcp:timelimitaction','');
10252
                // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
10253
                //$xmldoc->createElement('adlcp:datafromlms','');
10254
                // Give a child element <adlcp:masteryscore> to the <item> element.
10255
                $my_masteryscore = $xmldoc->createElement(
10256
                    'adlcp:masteryscore',
10257
                    $item->get_mastery_score()
10258
                );
10259
                $my_item->appendChild($my_masteryscore);
10260
10261
                // Attach this item to the organization element or hits parent if there is one.
10262
                if (!empty($item->parent) && $item->parent != 0) {
10263
                    $children = $organization->childNodes;
10264
                    $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
10265
                    if (is_object($possible_parent)) {
10266
                        $possible_parent->appendChild($my_item);
10267
                    } else {
10268
                        if ($this->debug > 0) {
10269
                            error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found');
10270
                        }
10271
                    }
10272
                } else {
10273
                    if ($this->debug > 0) {
10274
                        error_log('No parent');
10275
                    }
10276
                    $organization->appendChild($my_item);
10277
                }
10278
10279
                // Get the path of the file(s) from the course directory root.
10280
                $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
10281
10282
                if (!empty($path_to_remove)) {
10283
                    //From docs
10284
                    $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path);
10285
10286
                    //From quiz
10287
                    if ($this->ref === 'chamilo_scorm_export') {
10288
                        $path_to_remove = 'scorm/'.$this->path.'/';
10289
                        $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path);
10290
                    }
10291
                } else {
10292
                    $my_xml_file_path = $my_file_path;
10293
                }
10294
10295
                $my_sub_dir = dirname($my_file_path);
10296
                $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10297
                $my_xml_sub_dir = $my_sub_dir;
10298
                // Give a <resource> child to the <resources> element
10299
                $my_resource = $xmldoc->createElement('resource');
10300
                $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10301
                $my_resource->setAttribute('type', 'webcontent');
10302
                $my_resource->setAttribute('href', $my_xml_file_path);
10303
                // adlcp:scormtype can be either 'sco' or 'asset'.
10304
                if ($item->type === 'sco') {
10305
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
10306
                } else {
10307
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
10308
                }
10309
                // xml:base is the base directory to find the files declared in this resource.
10310
                $my_resource->setAttribute('xml:base', '');
10311
                // Give a <file> child to the <resource> element.
10312
                $my_file = $xmldoc->createElement('file');
10313
                $my_file->setAttribute('href', $my_xml_file_path);
10314
                $my_resource->appendChild($my_file);
10315
10316
                // Dependency to other files - not yet supported.
10317
                $i = 1;
10318
                if ($inc_docs) {
10319
                    foreach ($inc_docs as $doc_info) {
10320
                        if (count($doc_info) < 1 || empty($doc_info[0])) {
10321
                            continue;
10322
                        }
10323
                        $my_dep = $xmldoc->createElement('resource');
10324
                        $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
10325
                        $my_dep->setAttribute('identifier', $res_id);
10326
                        $my_dep->setAttribute('type', 'webcontent');
10327
                        $my_dep->setAttribute('adlcp:scormtype', 'asset');
10328
                        $my_dep_file = $xmldoc->createElement('file');
10329
                        // Check type of URL.
10330
                        if ($doc_info[1] == 'remote') {
10331
                            // Remote file. Save url as is.
10332
                            $my_dep_file->setAttribute('href', $doc_info[0]);
10333
                            $my_dep->setAttribute('xml:base', '');
10334
                        } elseif ($doc_info[1] === 'local') {
10335
                            switch ($doc_info[2]) {
10336
                                case 'url': // Local URL - save path as url for now, don't zip file.
10337
                                    $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
10338
                                    $current_dir = dirname($abs_path);
10339
                                    $current_dir = str_replace('\\', '/', $current_dir);
10340
                                    $file_path = realpath($abs_path);
10341
                                    $file_path = str_replace('\\', '/', $file_path);
10342
                                    $my_dep_file->setAttribute('href', $file_path);
10343
                                    $my_dep->setAttribute('xml:base', '');
10344
                                    if (strstr($file_path, $main_path) !== false) {
10345
                                        // The calculated real path is really inside Chamilo's root path.
10346
                                        // Reduce file path to what's under the DocumentRoot.
10347
                                        $file_path = substr($file_path, strlen($root_path) - 1);
10348
                                        //echo $file_path;echo '<br /><br />';
10349
                                        //error_log(__LINE__.'Reduced url path: '.$file_path, 0);
10350
                                        $zip_files_abs[] = $file_path;
10351
                                        $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10352
                                        $my_dep_file->setAttribute('href', $file_path);
10353
                                        $my_dep->setAttribute('xml:base', '');
10354
                                    } elseif (empty($file_path)) {
10355
                                        /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
10356
                                        if (strpos($document_root, -1) == '/') {
10357
                                            $document_root = substr(0, -1, $document_root);
10358
                                        }*/
10359
                                        $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
10360
                                        $file_path = str_replace('//', '/', $file_path);
10361
                                        if (file_exists($file_path)) {
10362
                                            $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10363
                                            $zip_files[] = $my_sub_dir.'/'.$file_path;
10364
                                            $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10365
                                            $my_dep_file->setAttribute('href', $file_path);
10366
                                            $my_dep->setAttribute('xml:base', '');
10367
                                        }
10368
                                    }
10369
                                    break;
10370
                                case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
10371
                                    $my_dep_file->setAttribute('href', $doc_info[0]);
10372
                                    $my_dep->setAttribute('xml:base', '');
10373
10374
                                    // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas
10375
                                    // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
10376
                                    $abs_img_path_without_subdir = $doc_info[0];
10377
                                    $relp = api_get_path(REL_PATH); // The url-append config param.
10378
                                    $pos = strpos($abs_img_path_without_subdir, $relp);
10379
                                    if ($pos === 0) {
10380
                                        $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir, strlen($relp));
10381
                                    }
10382
10383
                                    //$file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
10384
                                    $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir);
10385
10386
                                    $file_path = str_replace('\\', '/', $file_path);
10387
                                    $file_path = str_replace('//', '/', $file_path);
10388
10389
                                    // Prepare the current directory path (until just under 'document') with a trailing slash.
10390
                                    $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/';
10391
                                    // Check if the current document is in that path.
10392
                                    if (strstr($file_path, $cur_path) !== false) {
10393
                                        // The document is in that path, now get the relative path
10394
                                        // to the containing document.
10395
                                        $orig_file_path = dirname($cur_path.$my_file_path).'/';
10396
                                        $orig_file_path = str_replace('\\', '/', $orig_file_path);
10397
                                        $relative_path = '';
10398
                                        if (strstr($file_path, $cur_path) !== false) {
10399
                                            //$relative_path = substr($file_path, strlen($orig_file_path));
10400
                                            $relative_path = str_replace($cur_path, '', $file_path);
10401
                                            $file_path = substr($file_path, strlen($cur_path));
10402
                                        } else {
10403
                                            // This case is still a problem as it's difficult to calculate a relative path easily
10404
                                            // might still generate wrong links.
10405
                                            //$file_path = substr($file_path,strlen($cur_path));
10406
                                            // Calculate the directory path to the current file (without trailing slash).
10407
                                            $my_relative_path = dirname($file_path);
10408
                                            $my_relative_path = str_replace('\\', '/', $my_relative_path);
10409
                                            $my_relative_file = basename($file_path);
10410
                                            // Calculate the directory path to the containing file (without trailing slash).
10411
                                            $my_orig_file_path = substr($orig_file_path, 0, -1);
10412
                                            $dotdot = '';
10413
                                            $subdir = '';
10414
                                            while (strstr($my_relative_path, $my_orig_file_path) === false && (strlen($my_orig_file_path) > 1) && (strlen($my_relative_path) > 1)) {
10415
                                                $my_relative_path2 = dirname($my_relative_path);
10416
                                                $my_relative_path2 = str_replace('\\', '/', $my_relative_path2);
10417
                                                $my_orig_file_path = dirname($my_orig_file_path);
10418
                                                $my_orig_file_path = str_replace('\\', '/', $my_orig_file_path);
10419
                                                $subdir = substr($my_relative_path, strlen($my_relative_path2) + 1).'/'.$subdir;
10420
                                                $dotdot += '../';
10421
                                                $my_relative_path = $my_relative_path2;
10422
                                            }
10423
                                            $relative_path = $dotdot.$subdir.$my_relative_file;
10424
                                        }
10425
                                        // Put the current document in the zip (this array is the array
10426
                                        // that will manage documents already in the course folder - relative).
10427
                                        $zip_files[] = $file_path;
10428
                                        // Update the links to the current document in the containing document (make them relative).
10429
                                        $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $relative_path];
10430
                                        $my_dep_file->setAttribute('href', $file_path);
10431
                                        $my_dep->setAttribute('xml:base', '');
10432
                                    } elseif (strstr($file_path, $main_path) !== false) {
10433
                                        // The calculated real path is really inside Chamilo's root path.
10434
                                        // Reduce file path to what's under the DocumentRoot.
10435
                                        $file_path = substr($file_path, strlen($root_path));
10436
                                        //echo $file_path;echo '<br /><br />';
10437
                                        //error_log('Reduced path: '.$file_path, 0);
10438
                                        $zip_files_abs[] = $file_path;
10439
                                        $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10440
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
10441
                                        $my_dep->setAttribute('xml:base', '');
10442
                                    } elseif (empty($file_path)) {
10443
                                        /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
10444
                                        if(strpos($document_root,-1) == '/') {
10445
                                            $document_root = substr(0, -1, $document_root);
10446
                                        }*/
10447
                                        $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
10448
                                        $file_path = str_replace('//', '/', $file_path);
10449
                                        $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
10450
                                        $current_dir = dirname($abs_path);
10451
                                        $current_dir = str_replace('\\', '/', $current_dir);
10452
10453
                                        if (file_exists($file_path)) {
10454
                                            $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10455
                                            $zip_files[] = $my_sub_dir.'/'.$file_path;
10456
                                            $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10457
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10458
                                            $my_dep->setAttribute('xml:base', '');
10459
                                        }
10460
                                    }
10461
                                    break;
10462
                                case 'rel':
10463
                                    // Path relative to the current document.
10464
                                    // Save xml:base as current document's directory and save file in zip as subdir.file_path
10465
                                    if (substr($doc_info[0], 0, 2) == '..') {
10466
                                        // Relative path going up.
10467
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
10468
                                        $current_dir = str_replace('\\', '/', $current_dir);
10469
                                        $file_path = realpath($current_dir.$doc_info[0]);
10470
                                        $file_path = str_replace('\\', '/', $file_path);
10471
                                        if (strstr($file_path, $main_path) !== false) {
10472
                                            // The calculated real path is really inside Chamilo's root path.
10473
                                            // Reduce file path to what's under the DocumentRoot.
10474
                                            $file_path = substr($file_path, strlen($root_path));
10475
                                            $zip_files_abs[] = $file_path;
10476
                                            $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10477
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10478
                                            $my_dep->setAttribute('xml:base', '');
10479
                                        }
10480
                                    } else {
10481
                                        $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
10482
                                        $my_dep_file->setAttribute('href', $doc_info[0]);
10483
                                        $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
10484
                                    }
10485
                                    break;
10486
                                default:
10487
                                    $my_dep_file->setAttribute('href', $doc_info[0]);
10488
                                    $my_dep->setAttribute('xml:base', '');
10489
                                    break;
10490
                            }
10491
                        }
10492
                        $my_dep->appendChild($my_dep_file);
10493
                        $resources->appendChild($my_dep);
10494
                        $dependency = $xmldoc->createElement('dependency');
10495
                        $dependency->setAttribute('identifierref', $res_id);
10496
                        $my_resource->appendChild($dependency);
10497
                        $i++;
10498
                    }
10499
                }
10500
                $resources->appendChild($my_resource);
10501
                $zip_files[] = $my_file_path;
10502
            } else {
10503
                // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it.
10504
                switch ($item->type) {
10505
                    case TOOL_LINK:
10506
                        $my_item = $xmldoc->createElement('item');
10507
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
10508
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
10509
                        $my_item->setAttribute('isvisible', 'true');
10510
                        // Give a child element <title> to the <item> element.
10511
                        $my_title = $xmldoc->createElement(
10512
                            'title',
10513
                            htmlspecialchars(
10514
                                api_utf8_encode($item->get_title()),
10515
                                ENT_QUOTES,
10516
                                'UTF-8'
10517
                            )
10518
                        );
10519
                        $my_item->appendChild($my_title);
10520
                        // Give a child element <adlcp:prerequisites> to the <item> element.
10521
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
10522
                        $my_prereqs->setAttribute('type', 'aicc_script');
10523
                        $my_item->appendChild($my_prereqs);
10524
                        // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
10525
                        //$xmldoc->createElement('adlcp:maxtimeallowed', '');
10526
                        // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
10527
                        //$xmldoc->createElement('adlcp:timelimitaction', '');
10528
                        // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
10529
                        //$xmldoc->createElement('adlcp:datafromlms', '');
10530
                        // Give a child element <adlcp:masteryscore> to the <item> element.
10531
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
10532
                        $my_item->appendChild($my_masteryscore);
10533
10534
                        // Attach this item to the organization element or its parent if there is one.
10535
                        if (!empty($item->parent) && $item->parent != 0) {
10536
                            $children = $organization->childNodes;
10537
                            for ($i = 0; $i < $children->length; $i++) {
10538
                                $item_temp = $children->item($i);
10539
                                if ($item_temp -> nodeName == 'item') {
10540
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10541
                                        $item_temp -> appendChild($my_item);
10542
                                    }
10543
                                }
10544
                            }
10545
                        } else {
10546
                            $organization->appendChild($my_item);
10547
                        }
10548
10549
                        $my_file_path = 'link_'.$item->get_id().'.html';
10550
                        $sql = 'SELECT url, title FROM '.Database::get_course_table(TABLE_LINK).'
10551
                                WHERE c_id = '.$course_id.' AND id='.$item->path;
10552
                        $rs = Database::query($sql);
10553
                        if ($link = Database::fetch_array($rs)) {
10554
                            $url = $link['url'];
10555
                            $title = stripslashes($link['title']);
10556
                            $links_to_create[$my_file_path] = ['title' => $title, 'url' => $url];
10557
                            $my_xml_file_path = $my_file_path;
10558
                            $my_sub_dir = dirname($my_file_path);
10559
                            $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10560
                            $my_xml_sub_dir = $my_sub_dir;
10561
                            // Give a <resource> child to the <resources> element.
10562
                            $my_resource = $xmldoc->createElement('resource');
10563
                            $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10564
                            $my_resource->setAttribute('type', 'webcontent');
10565
                            $my_resource->setAttribute('href', $my_xml_file_path);
10566
                            // adlcp:scormtype can be either 'sco' or 'asset'.
10567
                            $my_resource->setAttribute('adlcp:scormtype', 'asset');
10568
                            // xml:base is the base directory to find the files declared in this resource.
10569
                            $my_resource->setAttribute('xml:base', '');
10570
                            // give a <file> child to the <resource> element.
10571
                            $my_file = $xmldoc->createElement('file');
10572
                            $my_file->setAttribute('href', $my_xml_file_path);
10573
                            $my_resource->appendChild($my_file);
10574
                            $resources->appendChild($my_resource);
10575
                        }
10576
                        break;
10577
                    case TOOL_QUIZ:
10578
                        $exe_id = $item->path; // Should be using ref when everything will be cleaned up in this regard.
10579
                        $exe = new Exercise();
10580
                        $exe->read($exe_id);
10581
                        $my_item = $xmldoc->createElement('item');
10582
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
10583
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
10584
                        $my_item->setAttribute('isvisible', 'true');
10585
                        // Give a child element <title> to the <item> element.
10586
                        $my_title = $xmldoc->createElement(
10587
                            'title',
10588
                            htmlspecialchars(
10589
                                api_utf8_encode($item->get_title()),
10590
                                ENT_QUOTES,
10591
                                'UTF-8'
10592
                            )
10593
                        );
10594
                        $my_item->appendChild($my_title);
10595
                        $my_max_score = $xmldoc->createElement('max_score', $item->get_max());
10596
                        //$my_item->appendChild($my_max_score);
10597
                        // Give a child element <adlcp:prerequisites> to the <item> element.
10598
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
10599
                        $my_prereqs->setAttribute('type', 'aicc_script');
10600
                        $my_item->appendChild($my_prereqs);
10601
                        // Give a child element <adlcp:masteryscore> to the <item> element.
10602
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
10603
                        $my_item->appendChild($my_masteryscore);
10604
10605
                        // Attach this item to the organization element or hits parent if there is one.
10606
10607
                        if (!empty($item->parent) && $item->parent != 0) {
10608
                            $children = $organization->childNodes;
10609
                            /*for ($i = 0; $i < $children->length; $i++) {
10610
                                $item_temp = $children->item($i);
10611
                                if ($exe_id == 81) {
10612
                                error_log($item_temp->nodeName );
10613
                                    error_log($item_temp->getAttribute('identifier'));
10614
                                }
10615
                                if ($item_temp->nodeName == 'item') {
10616
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10617
                                        $item_temp->appendChild($my_item);
10618
                                    }
10619
                                }
10620
                            }*/
10621
                            $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
10622
                            if ($possible_parent) {
10623
                                if ($possible_parent->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10624
                                    $possible_parent->appendChild($my_item);
10625
                                }
10626
                            }
10627
                        } else {
10628
                            $organization->appendChild($my_item);
10629
                        }
10630
10631
                        // Get the path of the file(s) from the course directory root
10632
                        //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
10633
                        $my_file_path = 'quiz_'.$item->get_id().'.html';
10634
                        // Write the contents of the exported exercise into a (big) html file
10635
                        // to later pack it into the exported SCORM. The file will be removed afterwards.
10636
                        $contents = ScormSection::export_exercise_to_scorm(
10637
                            $exe,
10638
                            true
10639
                        );
10640
10641
                        $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
10642
                        $res = file_put_contents($tmp_file_path, $contents);
10643
                        if ($res === false) {
10644
                            error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0);
10645
                        }
10646
                        $files_cleanup[] = $tmp_file_path;
10647
                        $my_xml_file_path = $my_file_path;
10648
                        $my_sub_dir = dirname($my_file_path);
10649
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10650
                        $my_xml_sub_dir = $my_sub_dir;
10651
                        // Give a <resource> child to the <resources> element.
10652
                        $my_resource = $xmldoc->createElement('resource');
10653
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10654
                        $my_resource->setAttribute('type', 'webcontent');
10655
                        $my_resource->setAttribute('href', $my_xml_file_path);
10656
10657
                        // adlcp:scormtype can be either 'sco' or 'asset'.
10658
                        $my_resource->setAttribute('adlcp:scormtype', 'sco');
10659
                        // xml:base is the base directory to find the files declared in this resource.
10660
                        $my_resource->setAttribute('xml:base', '');
10661
                        // Give a <file> child to the <resource> element.
10662
                        $my_file = $xmldoc->createElement('file');
10663
                        $my_file->setAttribute('href', $my_xml_file_path);
10664
                        $my_resource->appendChild($my_file);
10665
10666
                        // Get included docs.
10667
                        $inc_docs = $item->get_resources_from_source(null, $tmp_file_path);
10668
                        // Dependency to other files - not yet supported.
10669
                        $i = 1;
10670
                        foreach ($inc_docs as $doc_info) {
10671
                            if (count($doc_info) < 1 || empty($doc_info[0])) {
10672
                                continue;
10673
                            }
10674
                            $my_dep = $xmldoc->createElement('resource');
10675
                            $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
10676
                            $my_dep->setAttribute('identifier', $res_id);
10677
                            $my_dep->setAttribute('type', 'webcontent');
10678
                            $my_dep->setAttribute('adlcp:scormtype', 'asset');
10679
                            $my_dep_file = $xmldoc->createElement('file');
10680
                            // Check type of URL.
10681
                            if ($doc_info[1] == 'remote') {
10682
                                // Remote file. Save url as is.
10683
                                $my_dep_file->setAttribute('href', $doc_info[0]);
10684
                                $my_dep->setAttribute('xml:base', '');
10685
                            } elseif ($doc_info[1] == 'local') {
10686
                                switch ($doc_info[2]) {
10687
                                    case 'url': // Local URL - save path as url for now, don't zip file.
10688
                                        // Save file but as local file (retrieve from URL).
10689
                                        $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
10690
                                        $current_dir = dirname($abs_path);
10691
                                        $current_dir = str_replace('\\', '/', $current_dir);
10692
                                        $file_path = realpath($abs_path);
10693
                                        $file_path = str_replace('\\', '/', $file_path);
10694
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
10695
                                        $my_dep->setAttribute('xml:base', '');
10696
                                        if (strstr($file_path, $main_path) !== false) {
10697
                                            // The calculated real path is really inside the chamilo root path.
10698
                                            // Reduce file path to what's under the DocumentRoot.
10699
                                            $file_path = substr($file_path, strlen($root_path));
10700
                                            $zip_files_abs[] = $file_path;
10701
                                            $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => 'document/'.$file_path];
10702
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10703
                                            $my_dep->setAttribute('xml:base', '');
10704
                                        } elseif (empty($file_path)) {
10705
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
10706
                                            if (strpos($document_root,-1) == '/') {
10707
                                                $document_root = substr(0, -1, $document_root);
10708
                                            }*/
10709
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
10710
                                            $file_path = str_replace('//', '/', $file_path);
10711
                                            if (file_exists($file_path)) {
10712
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10713
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
10714
                                                $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => 'document/'.$file_path];
10715
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10716
                                                $my_dep->setAttribute('xml:base', '');
10717
                                            }
10718
                                        }
10719
                                        break;
10720
                                    case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
10721
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
10722
                                        $current_dir = str_replace('\\', '/', $current_dir);
10723
                                        $file_path = realpath($doc_info[0]);
10724
                                        $file_path = str_replace('\\', '/', $file_path);
10725
                                        $my_dep_file->setAttribute('href', $file_path);
10726
                                        $my_dep->setAttribute('xml:base', '');
10727
10728
                                        if (strstr($file_path, $main_path) !== false) {
10729
                                            // The calculated real path is really inside the chamilo root path.
10730
                                            // Reduce file path to what's under the DocumentRoot.
10731
                                            $file_path = substr($file_path, strlen($root_path));
10732
                                            //echo $file_path;echo '<br /><br />';
10733
                                            //error_log('Reduced path: '.$file_path, 0);
10734
                                            $zip_files_abs[] = $file_path;
10735
                                            $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10736
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10737
                                            $my_dep->setAttribute('xml:base', '');
10738
                                        } elseif (empty($file_path)) {
10739
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
10740
                                            if (strpos($document_root,-1) == '/') {
10741
                                                $document_root = substr(0, -1, $document_root);
10742
                                            }*/
10743
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
10744
                                            $file_path = str_replace('//', '/', $file_path);
10745
                                            if (file_exists($file_path)) {
10746
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10747
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
10748
                                                $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path];
10749
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10750
                                                $my_dep->setAttribute('xml:base', '');
10751
                                            }
10752
                                        }
10753
                                        break;
10754
                                    case 'rel': // Path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
10755
                                        if (substr($doc_info[0], 0, 2) == '..') {
10756
                                            // Relative path going up.
10757
                                            $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
10758
                                            $current_dir = str_replace('\\', '/', $current_dir);
10759
                                            $file_path = realpath($current_dir.$doc_info[0]);
10760
                                            $file_path = str_replace('\\', '/', $file_path);
10761
                                            //error_log($file_path.' <-> '.$main_path, 0);
10762
                                            if (strstr($file_path, $main_path) !== false) {
10763
                                                // The calculated real path is really inside Chamilo's root path.
10764
                                                // Reduce file path to what's under the DocumentRoot.
10765
10766
                                                $file_path = substr($file_path, strlen($root_path));
10767
                                                $file_path_dest = $file_path;
10768
10769
                                                // File path is courses/CHAMILO/document/....
10770
                                                $info_file_path = explode('/', $file_path);
10771
                                                if ($info_file_path[0] == 'courses') { // Add character "/" in file path.
10772
                                                    $file_path_dest = 'document/'.$file_path;
10773
                                                }
10774
10775
                                                //error_log('Reduced path: '.$file_path, 0);
10776
                                                $zip_files_abs[] = $file_path;
10777
10778
                                                $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path_dest];
10779
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10780
                                                $my_dep->setAttribute('xml:base', '');
10781
                                            }
10782
                                        } else {
10783
                                            $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
10784
                                            $my_dep_file->setAttribute('href', $doc_info[0]);
10785
                                            $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
10786
                                        }
10787
                                        break;
10788
                                    default:
10789
                                        $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/
10790
                                        $my_dep->setAttribute('xml:base', '');
10791
                                        break;
10792
                                }
10793
                            }
10794
                            $my_dep->appendChild($my_dep_file);
10795
                            $resources->appendChild($my_dep);
10796
                            $dependency = $xmldoc->createElement('dependency');
10797
                            $dependency->setAttribute('identifierref', $res_id);
10798
                            $my_resource->appendChild($dependency);
10799
                            $i++;
10800
                        }
10801
                        $resources->appendChild($my_resource);
10802
                        $zip_files[] = $my_file_path;
10803
                        break;
10804
                    default:
10805
                        // Get the path of the file(s) from the course directory root
10806
                        $my_file_path = 'non_exportable.html';
10807
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8');
10808
                        $my_xml_file_path = $my_file_path;
10809
                        $my_sub_dir = dirname($my_file_path);
10810
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10811
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8');
10812
                        $my_xml_sub_dir = $my_sub_dir;
10813
                        // Give a <resource> child to the <resources> element.
10814
                        $my_resource = $xmldoc->createElement('resource');
10815
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10816
                        $my_resource->setAttribute('type', 'webcontent');
10817
                        $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path);
10818
                        // adlcp:scormtype can be either 'sco' or 'asset'.
10819
                        $my_resource->setAttribute('adlcp:scormtype', 'asset');
10820
                        // xml:base is the base directory to find the files declared in this resource.
10821
                        $my_resource->setAttribute('xml:base', '');
10822
                        // Give a <file> child to the <resource> element.
10823
                        $my_file = $xmldoc->createElement('file');
10824
                        $my_file->setAttribute('href', 'document/'.$my_xml_file_path);
10825
                        $my_resource->appendChild($my_file);
10826
                        $resources->appendChild($my_resource);
10827
10828
                        break;
10829
                }
10830
            }
10831
        }
10832
        $organizations->appendChild($organization);
10833
        $root->appendChild($organizations);
10834
        $root->appendChild($resources);
10835
        $xmldoc->appendChild($root);
10836
10837
        $copyAll = api_get_configuration_value('add_all_files_in_lp_export');
10838
10839
        // TODO: Add a readme file here, with a short description and a link to the Reload player
10840
        // then add the file to the zip, then destroy the file (this is done automatically).
10841
        // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138
10842
        foreach ($zip_files as $file_path) {
10843
            if (empty($file_path)) {
10844
                continue;
10845
            }
10846
10847
            $filePath = $sys_course_path.$_course['path'].'/'.$file_path;
10848
            $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
10849
10850
            if (!empty($path_to_remove) && !empty($path_to_replace)) {
10851
                $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file);
10852
            }
10853
            $this->create_path($dest_file);
10854
            @copy($filePath, $dest_file);
10855
10856
            // Check if the file needs a link update.
10857
            if (in_array($file_path, array_keys($link_updates))) {
10858
                $string = file_get_contents($dest_file);
10859
                unlink($dest_file);
10860
                foreach ($link_updates[$file_path] as $old_new) {
10861
                    // This is an ugly hack that allows .flv files to be found by the flv player that
10862
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
10863
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
10864
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
10865
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
10866
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
10867
                    } elseif (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 6) == 'video/') {
10868
                        $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']);
10869
                    }
10870
                    //Fix to avoid problems with default_course_document
10871
                    if (strpos("main/default_course_document", $old_new['dest'] === false)) {
10872
                        //$newDestination = str_replace('document/', $mult.'document/', $old_new['dest']);
10873
                        $newDestination = $old_new['dest'];
10874
                    } else {
10875
                        $newDestination = str_replace('document/', '', $old_new['dest']);
10876
                    }
10877
                    $string = str_replace($old_new['orig'], $newDestination, $string);
10878
10879
                    // Add files inside the HTMLs
10880
                    $new_path = str_replace(api_get_path(REL_COURSE_PATH), '', $old_new['orig']);
10881
                    $destinationFile = $archive_path.$temp_dir_short.'/'.$old_new['dest'];
10882
                    if (file_exists($sys_course_path.$new_path)) {
10883
                        copy($sys_course_path.$new_path, $destinationFile);
10884
                    }
10885
                }
10886
                file_put_contents($dest_file, $string);
10887
            }
10888
10889
            if (file_exists($filePath) && $copyAll) {
10890
                $extension = $this->get_extension($filePath);
10891
                if (in_array($extension, ['html', 'html'])) {
10892
                    $containerOrigin = dirname($filePath);
10893
                    $containerDestination = dirname($dest_file);
10894
10895
                    $finder = new Finder();
10896
                    $finder->files()->in($containerOrigin)
10897
                        ->notName('*_DELETED_*')
10898
                        ->exclude('share_folder')
10899
                        ->exclude('chat_files')
10900
                        ->exclude('certificates')
10901
                    ;
10902
10903
                    if (is_dir($containerOrigin) &&
10904
                        is_dir($containerDestination)
10905
                    ) {
10906
                        $fs = new Filesystem();
10907
                        $fs->mirror(
10908
                            $containerOrigin,
10909
                            $containerDestination,
10910
                            $finder
10911
                        );
10912
                    }
10913
                }
10914
            }
10915
        }
10916
10917
        foreach ($zip_files_abs as $file_path) {
10918
            if (empty($file_path)) {
10919
                continue;
10920
            }
10921
            if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) {
10922
                continue;
10923
            }
10924
10925
            $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
10926
            $this->create_path($dest_file);
10927
            copy($main_path.$file_path, $dest_file);
10928
            // Check if the file needs a link update.
10929
            if (in_array($file_path, array_keys($link_updates))) {
10930
                $string = file_get_contents($dest_file);
10931
                unlink($dest_file);
10932
                foreach ($link_updates[$file_path] as $old_new) {
10933
                    // This is an ugly hack that allows .flv files to be found by the flv player that
10934
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
10935
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
10936
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
10937
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
10938
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
10939
                    }
10940
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
10941
                }
10942
                file_put_contents($dest_file, $string);
10943
            }
10944
        }
10945
10946
        if (is_array($links_to_create)) {
10947
            foreach ($links_to_create as $file => $link) {
10948
                $file_content = '<!DOCTYPE html><head>
10949
                                <meta charset="'.api_get_language_isocode().'" />
10950
                                <title>'.$link['title'].'</title>
10951
                                </head>
10952
                                <body dir="'.api_get_text_direction().'">
10953
                                <div style="text-align:center">
10954
                                <a href="'.$link['url'].'">'.$link['title'].'</a></div>
10955
                                </body>
10956
                                </html>';
10957
                file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
10958
            }
10959
        }
10960
10961
        // Add non exportable message explanation.
10962
        $lang_not_exportable = get_lang('ThisItemIsNotExportable');
10963
        $file_content = '<!DOCTYPE html><head>
10964
                        <meta charset="'.api_get_language_isocode().'" />
10965
                        <title>'.$lang_not_exportable.'</title>
10966
                        <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" />
10967
                        </head>
10968
                        <body dir="'.api_get_text_direction().'">';
10969
        $file_content .=
10970
            <<<EOD
10971
                    <style>
10972
            .error-message {
10973
                font-family: arial, verdana, helvetica, sans-serif;
10974
                border-width: 1px;
10975
                border-style: solid;
10976
                left: 50%;
10977
                margin: 10px auto;
10978
                min-height: 30px;
10979
                padding: 5px;
10980
                right: 50%;
10981
                width: 500px;
10982
                background-color: #FFD1D1;
10983
                border-color: #FF0000;
10984
                color: #000;
10985
            }
10986
        </style>
10987
    <body>
10988
        <div class="error-message">
10989
            $lang_not_exportable
10990
        </div>
10991
    </body>
10992
</html>
10993
EOD;
10994
        if (!is_dir($archive_path.$temp_dir_short.'/document')) {
10995
            @mkdir($archive_path.$temp_dir_short.'/document', api_get_permissions_for_new_directories());
10996
        }
10997
        file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);
10998
10999
        // Add the extra files that go along with a SCORM package.
11000
        $main_code_path = api_get_path(SYS_CODE_PATH).'lp/packaging/';
11001
11002
        $fs = new Filesystem();
11003
        $fs->mirror($main_code_path, $archive_path.$temp_dir_short);
11004
11005
        // Finalize the imsmanifest structure, add to the zip, then return the zip.
11006
        $manifest = @$xmldoc->saveXML();
11007
        $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now.
11008
        file_put_contents($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest);
11009
        $zip_folder->add(
11010
            $archive_path.'/'.$temp_dir_short,
11011
            PCLZIP_OPT_REMOVE_PATH,
11012
            $archive_path.'/'.$temp_dir_short.'/'
11013
        );
11014
11015
        // Clean possible temporary files.
11016
        foreach ($files_cleanup as $file) {
11017
            $res = unlink($file);
11018
            if ($res === false) {
11019
                error_log(
11020
                    'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__,
11021
                    0
11022
                );
11023
            }
11024
        }
11025
        $name = api_replace_dangerous_char($this->get_name()).'.zip';
11026
        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
11027
    }
11028
11029
    /**
11030
     * @param int $lp_id
11031
     * @return bool
11032
     */
11033
    public function scorm_export_to_pdf($lp_id)
11034
    {
11035
        $lp_id = intval($lp_id);
11036
        $files_to_export = [];
11037
        $course_data = api_get_course_info($this->cc);
11038
        if (!empty($course_data)) {
11039
            $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path;
11040
            $list = self::get_flat_ordered_items_list($lp_id);
11041
            if (!empty($list)) {
11042
                foreach ($list as $item_id) {
11043
                    $item = $this->items[$item_id];
11044
                    switch ($item->type) {
11045
                        case 'document':
11046
                            //Getting documents from a LP with chamilo documents
11047
                            $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc);
11048
                            // Try loading document from the base course.
11049
                            if (empty($file_data) && !empty($sessionId)) {
11050
                                $file_data = DocumentManager::get_document_data_by_id(
11051
                                    $item->path,
11052
                                    $this->cc,
11053
                                    false,
11054
                                    0
11055
                                );
11056
                            }
11057
                            $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path'];
11058
                            if (file_exists($file_path)) {
11059
                                $files_to_export[] = [
11060
                                    'title' => $item->get_title(),
11061
                                    'path' => $file_path
11062
                                ];
11063
                            }
11064
                            break;
11065
                        case 'asset': //commes from a scorm package generated by chamilo
11066
                        case 'sco':
11067
                            $file_path = $scorm_path.'/'.$item->path;
11068
                            if (file_exists($file_path)) {
11069
                                $files_to_export[] = [
11070
                                    'title' => $item->get_title(),
11071
                                    'path' => $file_path
11072
                                ];
11073
                            }
11074
                            break;
11075
                        case 'dir':
11076
                            $files_to_export[] = [
11077
                                'title' => $item->get_title(),
11078
                                'path' => null
11079
                            ];
11080
                            break;
11081
                    }
11082
                }
11083
            }
11084
            $pdf = new PDF();
11085
            $result = $pdf->html_to_pdf(
11086
                $files_to_export,
11087
                $this->name,
11088
                $this->cc,
11089
                true
11090
            );
11091
            return $result;
11092
        }
11093
11094
        return false;
11095
    }
11096
11097
    /**
11098
     * Temp function to be moved in main_api or the best place around for this.
11099
     * Creates a file path if it doesn't exist
11100
     * @param string $path
11101
     */
11102
    public function create_path($path)
11103
    {
11104
        $path_bits = explode('/', dirname($path));
11105
11106
        // IS_WINDOWS_OS has been defined in main_api.lib.php
11107
        $path_built = IS_WINDOWS_OS ? '' : '/';
11108
        foreach ($path_bits as $bit) {
11109
            if (!empty($bit)) {
11110
                $new_path = $path_built.$bit;
11111
                if (is_dir($new_path)) {
11112
                    $path_built = $new_path.'/';
11113
                } else {
11114
                    mkdir($new_path, api_get_permissions_for_new_directories());
11115
                    $path_built = $new_path.'/';
11116
                }
11117
            }
11118
        }
11119
    }
11120
11121
    /**
11122
     * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
11123
     * @return	boolean	The results of the unlink function, or false if there was no image to start with
11124
     */
11125
    public function delete_lp_image()
11126
    {
11127
        $img = $this->get_preview_image();
11128
        if ($img != '') {
11129
            $del_file = $this->get_preview_image_path(null, 'sys');
11130
            if (isset($del_file) && file_exists($del_file)) {
11131
                $del_file_2 = $this->get_preview_image_path(64, 'sys');
11132
                if (file_exists($del_file_2)) {
11133
                    unlink($del_file_2);
11134
                }
11135
                $this->set_preview_image('');
11136
                return @unlink($del_file);
11137
            }
11138
        }
11139
        return false;
11140
    }
11141
11142
    /**
11143
     * Uploads an author image to the upload/learning_path/images directory
11144
     * @param	array	The image array, coming from the $_FILES superglobal
11145
     * @return	boolean	True on success, false on error
11146
     */
11147
    public function upload_image($image_array)
11148
    {
11149
        if (!empty($image_array['name'])) {
11150
            $upload_ok = process_uploaded_file($image_array);
11151
            $has_attachment = true;
11152
        }
11153
11154
        if ($upload_ok && $has_attachment) {
11155
            $courseDir = api_get_course_path().'/upload/learning_path/images';
11156
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
11157
            $updir = $sys_course_path.$courseDir;
11158
            // Try to add an extension to the file if it hasn't one.
11159
            $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);
11160
11161
            if (filter_extension($new_file_name)) {
11162
                $file_extension = explode('.', $image_array['name']);
11163
                $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
11164
                $filename = uniqid('');
11165
                $new_file_name = $filename.'.'.$file_extension;
11166
                $new_path = $updir.'/'.$new_file_name;
11167
11168
                // Resize the image.
11169
                $temp = new Image($image_array['tmp_name']);
11170
                $temp->resize(104);
11171
                $result = $temp->send_image($new_path);
11172
11173
                // Storing the image filename.
11174
                if ($result) {
11175
                    $this->set_preview_image($new_file_name);
11176
11177
                    //Resize to 64px to use on course homepage
11178
                    $temp->resize(64);
11179
                    $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension);
11180
                    return true;
11181
                }
11182
            }
11183
        }
11184
11185
        return false;
11186
    }
11187
11188
    /**
11189
     * @param int $lp_id
11190
     * @param string $status
11191
     */
11192
    public function set_autolaunch($lp_id, $status)
11193
    {
11194
        $course_id = api_get_course_int_id();
11195
        $lp_id = intval($lp_id);
11196
        $status = intval($status);
11197
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11198
11199
        // Setting everything to autolaunch = 0
11200
        $attributes['autolaunch'] = 0;
11201
        $where = [
11202
            'session_id = ? AND c_id = ? ' => [
11203
                api_get_session_id(),
11204
                $course_id
11205
            ]
11206
        ];
11207
        Database::update($lp_table, $attributes, $where);
11208
        if ($status == 1) {
11209
            //Setting my lp_id to autolaunch = 1
11210
            $attributes['autolaunch'] = 1;
11211
            $where = [
11212
                'iid = ? AND session_id = ? AND c_id = ?' => [
11213
                    $lp_id,
11214
                    api_get_session_id(),
11215
                    $course_id
11216
                ]
11217
            ];
11218
            Database::update($lp_table, $attributes, $where);
11219
        }
11220
    }
11221
11222
    /**
11223
     * Gets previous_item_id for the next element of the lp_item table
11224
     * @author Isaac flores paz
11225
     * @return	integer	Previous item ID
11226
     */
11227
    public function select_previous_item_id()
11228
    {
11229
        $course_id = api_get_course_int_id();
11230
        if ($this->debug > 0) {
11231
            error_log('New LP - In learnpath::select_previous_item_id()', 0);
11232
        }
11233
        $table_lp_item = Database::get_course_table(TABLE_LP_ITEM);
11234
11235
        // Get the max order of the items
11236
        $sql = "SELECT max(display_order) AS display_order FROM $table_lp_item
11237
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
11238
        $rs_max_order = Database::query($sql);
11239
        $row_max_order = Database::fetch_object($rs_max_order);
11240
        $max_order = $row_max_order->display_order;
11241
        // Get the previous item ID
11242
        $sql = "SELECT iid as previous FROM $table_lp_item
11243
                WHERE 
11244
                    c_id = $course_id AND 
11245
                    lp_id = ".$this->lp_id." AND 
11246
                    display_order = '$max_order' ";
11247
        $rs_max = Database::query($sql);
11248
        $row_max = Database::fetch_object($rs_max);
11249
11250
        // Return the previous item ID
11251
        return $row_max->previous;
11252
    }
11253
11254
    /**
11255
     * Copies an LP
11256
     */
11257
    public function copy()
11258
    {
11259
        // Course builder
11260
        $cb = new CourseBuilder();
11261
11262
        //Setting tools that will be copied
11263
        $cb->set_tools_to_build(['learnpaths']);
11264
11265
        //Setting elements that will be copied
11266
        $cb->set_tools_specific_id_list(
11267
            ['learnpaths' => [$this->lp_id]]
11268
        );
11269
11270
        $course = $cb->build();
11271
11272
        //Course restorer
11273
        $course_restorer = new CourseRestorer($course);
11274
        $course_restorer->set_add_text_in_items(true);
11275
        $course_restorer->set_tool_copy_settings(
11276
            ['learnpaths' => ['reset_dates' => true]]
11277
        );
11278
        $course_restorer->restore(
11279
            api_get_course_id(),
11280
            api_get_session_id(),
11281
            false,
11282
            false
11283
        );
11284
    }
11285
11286
    /**
11287
     * Verify document size
11288
     * @param string $s
11289
     * @return bool
11290
     */
11291
    public static function verify_document_size($s)
11292
    {
11293
        $post_max = ini_get('post_max_size');
11294
        if (substr($post_max, -1, 1) == 'M') {
11295
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024;
11296
        } elseif (substr($post_max, -1, 1) == 'G') {
11297
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024;
11298
        }
11299
        $upl_max = ini_get('upload_max_filesize');
11300
        if (substr($upl_max, -1, 1) == 'M') {
11301
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024;
11302
        } elseif (substr($upl_max, -1, 1) == 'G') {
11303
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024;
11304
        }
11305
        $documents_total_space = DocumentManager::documents_total_space();
11306
        $course_max_space = DocumentManager::get_course_quota();
11307
        $total_size = filesize($s) + $documents_total_space;
11308
        if (filesize($s) > $post_max || filesize($s) > $upl_max || $total_size > $course_max_space) {
11309
            return true;
11310
        } else {
11311
            return false;
11312
        }
11313
    }
11314
11315
    /**
11316
     * Clear LP prerequisites
11317
     */
11318
    public function clear_prerequisites()
11319
    {
11320
        $course_id = $this->get_course_int_id();
11321
        if ($this->debug > 0) {
11322
            error_log('New LP - In learnpath::clear_prerequisites()', 0);
11323
        }
11324
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
11325
        $lp_id = $this->get_id();
11326
        //Cleaning prerequisites
11327
        $sql = "UPDATE $tbl_lp_item SET prerequisite = ''
11328
                WHERE c_id = $course_id AND lp_id = $lp_id";
11329
        Database::query($sql);
11330
11331
        //Cleaning mastery score for exercises
11332
        $sql = "UPDATE $tbl_lp_item SET mastery_score = ''
11333
                WHERE c_id = $course_id AND lp_id = $lp_id AND item_type = 'quiz'";
11334
        Database::query($sql);
11335
    }
11336
11337
    public function set_previous_step_as_prerequisite_for_all_items()
11338
    {
11339
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
11340
        $course_id = $this->get_course_int_id();
11341
        $lp_id = $this->get_id();
11342
11343
        if (!empty($this->items)) {
11344
            $previous_item_id = null;
11345
            $previous_item_max = 0;
11346
            $previous_item_type = null;
11347
            $last_item_not_dir = null;
11348
            $last_item_not_dir_type = null;
11349
            $last_item_not_dir_max = null;
11350
11351
            foreach ($this->ordered_items as $itemId) {
11352
                $item = $this->getItem($itemId);
11353
                // if there was a previous item... (otherwise jump to set it)
11354
                if (!empty($previous_item_id)) {
11355
                    $current_item_id = $item->get_id(); //save current id
11356
                    if ($item->get_type() != 'dir') {
11357
                        // Current item is not a folder, so it qualifies to get a prerequisites
11358
                        if ($last_item_not_dir_type == 'quiz') {
11359
                            // if previous is quiz, mark its max score as default score to be achieved
11360
                            $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_dir_max'
11361
                                    WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $last_item_not_dir";
11362
                            Database::query($sql);
11363
                        }
11364
                        // now simply update the prerequisite to set it to the last non-chapter item
11365
                        $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_dir'
11366
                                WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $current_item_id";
11367
                        Database::query($sql);
11368
                        // record item as 'non-chapter' reference
11369
                        $last_item_not_dir = $item->get_id();
11370
                        $last_item_not_dir_type = $item->get_type();
11371
                        $last_item_not_dir_max = $item->get_max();
11372
                    }
11373
                } else {
11374
                    if ($item->get_type() != 'dir') {
11375
                        // Current item is not a folder (but it is the first item) so record as last "non-chapter" item
11376
                        $last_item_not_dir = $item->get_id();
11377
                        $last_item_not_dir_type = $item->get_type();
11378
                        $last_item_not_dir_max = $item->get_max();
11379
                    }
11380
                }
11381
                // Saving the item as "previous item" for the next loop
11382
                $previous_item_id = $item->get_id();
11383
                $previous_item_max = $item->get_max();
11384
                $previous_item_type = $item->get_type();
11385
            }
11386
        }
11387
    }
11388
11389
    /**
11390
     * @param array $params
11391
     */
11392
    public static function createCategory($params)
11393
    {
11394
        $em = Database::getManager();
11395
        $item = new CLpCategory();
11396
        $item->setName($params['name']);
11397
        $item->setCId($params['c_id']);
11398
        $em->persist($item);
11399
        $em->flush();
11400
    }
11401
    /**
11402
     * @param array $params
11403
     */
11404
    public static function updateCategory($params)
11405
    {
11406
        $em = Database::getManager();
11407
        /** @var CLpCategory $item */
11408
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $params['id']);
11409
        if ($item) {
11410
            $item->setName($params['name']);
11411
            $em->merge($item);
11412
            $em->flush();
11413
        }
11414
    }
11415
11416
    /**
11417
     * @param int $id
11418
     */
11419
    public static function moveUpCategory($id)
11420
    {
11421
        $id = (int) $id;
11422
        $em = Database::getManager();
11423
        /** @var CLpCategory $item */
11424
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
11425
        if ($item) {
11426
            $position = $item->getPosition() - 1;
11427
            $item->setPosition($position);
11428
            $em->persist($item);
11429
            $em->flush();
11430
        }
11431
    }
11432
11433
    /**
11434
     * @param int $id
11435
     */
11436
    public static function moveDownCategory($id)
11437
    {
11438
        $id = (int) $id;
11439
        $em = Database::getManager();
11440
        /** @var CLpCategory $item */
11441
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
11442
        if ($item) {
11443
            $position = $item->getPosition() + 1;
11444
            $item->setPosition($position);
11445
            $em->persist($item);
11446
            $em->flush();
11447
        }
11448
    }
11449
11450
    /**
11451
     * @param int $courseId
11452
     * @return int|mixed
11453
     */
11454
    public static function getCountCategories($courseId)
11455
    {
11456
        if (empty($course_id)) {
11457
            return 0;
11458
        }
11459
        $em = Database::getManager();
11460
        $query = $em->createQuery('SELECT COUNT(u.id) FROM ChamiloCourseBundle:CLpCategory u WHERE u.cId = :id');
11461
        $query->setParameter('id', $courseId);
11462
11463
        return $query->getSingleScalarResult();
11464
    }
11465
11466
    /**
11467
     * @param int $courseId
11468
     *
11469
     * @return mixed
11470
     */
11471
    public static function getCategories($courseId)
11472
    {
11473
        $em = Database::getManager();
11474
        //Default behaviour
11475
        /*$items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
11476
            array('cId' => $course_id),
11477
            array('name' => 'ASC')
11478
        );*/
11479
11480
        // Using doctrine extensions
11481
        /** @var SortableRepository $repo */
11482
        $repo = $em->getRepository('ChamiloCourseBundle:CLpCategory');
11483
        $items = $repo
11484
            ->getBySortableGroupsQuery(['cId' => $courseId])
11485
            ->getResult();
11486
11487
        return $items;
11488
    }
11489
11490
    /**
11491
     * @param int $id
11492
     *
11493
     * @return CLpCategory
11494
     */
11495
    public static function getCategory($id)
11496
    {
11497
        $id = (int) $id;
11498
        $em = Database::getManager();
11499
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
11500
11501
        return $item;
11502
    }
11503
11504
    /**
11505
     * @param int $courseId
11506
     * @return array
11507
     */
11508
    public static function getCategoryByCourse($courseId)
11509
    {
11510
        $em = Database::getManager();
11511
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
11512
            ['cId' => $courseId]
11513
        );
11514
11515
        return $items;
11516
    }
11517
11518
    /**
11519
     * @param int $id
11520
     *
11521
     * @return mixed
11522
     */
11523
    public static function deleteCategory($id)
11524
    {
11525
        $em = Database::getManager();
11526
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
11527
        if ($item) {
11528
            $courseId = $item->getCId();
11529
            $query = $em->createQuery('SELECT u FROM ChamiloCourseBundle:CLp u WHERE u.cId = :id AND u.categoryId = :catId');
11530
            $query->setParameter('id', $courseId);
11531
            $query->setParameter('catId', $item->getId());
11532
            $lps = $query->getResult();
11533
11534
            // Setting category = 0.
11535
            if ($lps) {
11536
                foreach ($lps as $lpItem) {
11537
                    $lpItem->setCategoryId(0);
11538
                }
11539
            }
11540
11541
            // Removing category.
11542
            $em->remove($item);
11543
            $em->flush();
11544
11545
            $courseInfo = api_get_course_info_by_id($courseId);
11546
            $sessionId = api_get_session_id();
11547
11548
            // Delete link tool
11549
            $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
11550
            $link = 'lp/lp_controller.php?cidReq='.$courseInfo['code'].'&id_session='.$sessionId.'&gidReq=0&gradebook=0&origin=&action=view_category&id='.$id;
11551
            // Delete tools
11552
            $sql = "DELETE FROM $tbl_tool
11553
                    WHERE c_id = ".$courseId." AND (link LIKE '$link%' AND image='lp_category.gif')";
11554
            Database::query($sql);
11555
        }
11556
    }
11557
11558
    /**
11559
     * @param int $courseId
11560
     * @param bool $addSelectOption
11561
     *
11562
     * @return mixed
11563
     */
11564
    public static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false)
11565
    {
11566
        $items = self::getCategoryByCourse($courseId);
11567
        $cats = [];
11568
        if ($addSelectOption) {
11569
            $cats = [get_lang('SelectACategory')];
11570
        }
11571
11572
        if (!empty($items)) {
11573
            foreach ($items as $cat) {
11574
                $cats[$cat->getId()] = $cat->getName();
11575
            }
11576
        }
11577
11578
        return $cats;
11579
    }
11580
11581
    /**
11582
     * Return the scorm item type object with spaces replaced with _
11583
     * The return result is use to build a css classname like scorm_type_$return
11584
     * @param $in_type
11585
     * @return mixed
11586
     */
11587
    private static function format_scorm_type_item($in_type)
11588
    {
11589
        return str_replace(' ', '_', $in_type);
11590
    }
11591
11592
    /**
11593
     * @param string $courseCode
11594
     * @param int $lp_id
11595
     * @param int $user_id
11596
     *
11597
     * @return learnpath
11598
     */
11599
    public static function getLpFromSession($courseCode, $lp_id, $user_id)
11600
    {
11601
        $learnPath = null;
11602
        $lpObject = Session::read('lpobject');
11603
        if ($lpObject !== null) {
11604
            $learnPath = unserialize($lpObject);
11605
        }
11606
11607
        if (!is_object($learnPath)) {
11608
            $learnPath = new learnpath($courseCode, $lp_id, $user_id);
11609
        }
11610
11611
        return $learnPath;
11612
    }
11613
11614
    /**
11615
     * @param int $itemId
11616
     * @return learnpathItem|false
11617
     */
11618
    public function getItem($itemId)
11619
    {
11620
        if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) {
11621
            return $this->items[$itemId];
11622
        }
11623
11624
        return false;
11625
    }
11626
11627
    /**
11628
     * @return int
11629
     */
11630
    public function getCategoryId()
11631
    {
11632
        return $this->categoryId;
11633
    }
11634
11635
    /**
11636
     * @param int $categoryId
11637
     * @return bool
11638
     */
11639
    public function setCategoryId($categoryId)
11640
    {
11641
        $this->categoryId = intval($categoryId);
11642
        $courseId = api_get_course_int_id();
11643
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11644
        $lp_id = $this->get_id();
11645
        $sql = "UPDATE $lp_table SET category_id = ".$this->categoryId."
11646
                WHERE iid = $lp_id";
11647
        Database::query($sql);
11648
11649
        return true;
11650
    }
11651
11652
    /**
11653
     * Get whether this is a learning path with the possibility to subscribe
11654
     * users or not
11655
     * @return int
11656
     */
11657
    public function getSubscribeUsers()
11658
    {
11659
        return $this->subscribeUsers;
11660
    }
11661
11662
    /**
11663
     * Set whether this is a learning path with the possibility to subscribe
11664
     * users or not
11665
     * @param int $value (0 = false, 1 = true)
11666
     * @return bool
11667
     */
11668
    public function setSubscribeUsers($value)
11669
    {
11670
        if ($this->debug > 0) {
11671
            error_log('New LP - In learnpath::set_subscribe_users()', 0);
11672
        }
11673
        $this->subscribeUsers = (int) $value;
11674
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11675
        $lp_id = $this->get_id();
11676
        $sql = "UPDATE $lp_table SET subscribe_users = ".$this->subscribeUsers."
11677
                WHERE iid = $lp_id";
11678
        Database::query($sql);
11679
11680
        return true;
11681
    }
11682
11683
    /**
11684
     * Calculate the count of stars for a user in this LP
11685
     * This calculation is based on the following rules:
11686
     * - the student gets one star when he gets to 50% of the learning path
11687
     * - the student gets a second star when the average score of all tests inside the learning path >= 50%
11688
     * - the student gets a third star when the average score of all tests inside the learning path >= 80%
11689
     * - the student gets the final star when the score for the *last* test is >= 80%
11690
     * @param int $sessionId Optional. The session ID
11691
     * @return int The count of stars
11692
     */
11693
    public function getCalculateStars($sessionId = 0)
11694
    {
11695
        $stars = 0;
11696
        $progress = self::getProgress(
11697
            $this->lp_id,
11698
            $this->user_id,
11699
            $this->course_int_id,
11700
            $sessionId
11701
        );
11702
11703
        if ($progress >= 50) {
11704
            $stars++;
11705
        }
11706
11707
        // Calculate stars chapters evaluation
11708
        $exercisesItems = $this->getExercisesItems();
11709
11710
        if (!empty($exercisesItems)) {
11711
            $totalResult = 0;
11712
11713
            foreach ($exercisesItems as $exerciseItem) {
11714
                $exerciseResultInfo = Event::getExerciseResultsByUser(
11715
                    $this->user_id,
11716
                    $exerciseItem->path,
11717
                    $this->course_int_id,
11718
                    $sessionId,
11719
                    $this->lp_id,
11720
                    $exerciseItem->db_id
11721
                );
11722
11723
                $exerciseResultInfo = end($exerciseResultInfo);
11724
11725
                if (!$exerciseResultInfo) {
11726
                    continue;
11727
                }
11728
11729
                if (!empty($exerciseResultInfo['exe_weighting'])) {
11730
                    $exerciseResult = $exerciseResultInfo['exe_result'] * 100 / $exerciseResultInfo['exe_weighting'];
11731
                } else {
11732
                    $exerciseResult = 0;
11733
                }
11734
                $totalResult += $exerciseResult;
11735
            }
11736
11737
            $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1);
11738
11739
            if ($totalExerciseAverage >= 50) {
11740
                $stars++;
11741
            }
11742
11743
            if ($totalExerciseAverage >= 80) {
11744
                $stars++;
11745
            }
11746
        }
11747
11748
        // Calculate star for final evaluation
11749
        $finalEvaluationItem = $this->getFinalEvaluationItem();
11750
11751
        if (!empty($finalEvaluationItem)) {
11752
            $evaluationResultInfo = Event::getExerciseResultsByUser(
11753
                $this->user_id,
11754
                $finalEvaluationItem->path,
11755
                $this->course_int_id,
11756
                $sessionId,
11757
                $this->lp_id,
11758
                $finalEvaluationItem->db_id
11759
            );
11760
11761
            $evaluationResultInfo = end($evaluationResultInfo);
11762
11763
            if ($evaluationResultInfo) {
11764
                $evaluationResult = $evaluationResultInfo['exe_result'] * 100 / $evaluationResultInfo['exe_weighting'];
11765
11766
                if ($evaluationResult >= 80) {
11767
                    $stars++;
11768
                }
11769
            }
11770
        }
11771
11772
        return $stars;
11773
    }
11774
11775
    /**
11776
     * Get the items of exercise type
11777
     * @return array The items. Otherwise return false
11778
     */
11779
    public function getExercisesItems()
11780
    {
11781
        $exercises = [];
11782
        foreach ($this->items as $item) {
11783
            if ($item->type != 'quiz') {
11784
                continue;
11785
            }
11786
            $exercises[] = $item;
11787
        }
11788
11789
        array_pop($exercises);
11790
11791
        return $exercises;
11792
    }
11793
11794
    /**
11795
     * Get the item of exercise type (evaluation type)
11796
     * @return array The final evaluation. Otherwise return false
11797
     */
11798
    public function getFinalEvaluationItem()
11799
    {
11800
        $exercises = [];
11801
        foreach ($this->items as $item) {
11802
            if ($item->type != 'quiz') {
11803
                continue;
11804
            }
11805
11806
            $exercises[] = $item;
11807
        }
11808
11809
        return array_pop($exercises);
11810
    }
11811
11812
    /**
11813
     * Calculate the total points achieved for the current user in this learning path
11814
     * @param int $sessionId Optional. The session Id
11815
     * @return int
11816
     */
11817
    public function getCalculateScore($sessionId = 0)
11818
    {
11819
        // Calculate stars chapters evaluation
11820
        $exercisesItems = $this->getExercisesItems();
11821
        $finalEvaluationItem = $this->getFinalEvaluationItem();
11822
        $totalExercisesResult = 0;
11823
        $totalEvaluationResult = 0;
11824
11825
        if ($exercisesItems !== false) {
11826
            foreach ($exercisesItems as $exerciseItem) {
11827
                $exerciseResultInfo = Event::getExerciseResultsByUser(
11828
                    $this->user_id,
11829
                    $exerciseItem->path,
11830
                    $this->course_int_id,
11831
                    $sessionId,
11832
                    $this->lp_id,
11833
                    $exerciseItem->db_id
11834
                );
11835
11836
                $exerciseResultInfo = end($exerciseResultInfo);
11837
11838
                if (!$exerciseResultInfo) {
11839
                    continue;
11840
                }
11841
11842
                $totalExercisesResult += $exerciseResultInfo['exe_result'];
11843
            }
11844
        }
11845
11846
        if (!empty($finalEvaluationItem)) {
11847
            $evaluationResultInfo = Event::getExerciseResultsByUser(
11848
                $this->user_id,
11849
                $finalEvaluationItem->path,
11850
                $this->course_int_id,
11851
                $sessionId,
11852
                $this->lp_id,
11853
                $finalEvaluationItem->db_id
11854
            );
11855
11856
            $evaluationResultInfo = end($evaluationResultInfo);
11857
11858
            if ($evaluationResultInfo) {
11859
                $totalEvaluationResult += $evaluationResultInfo['exe_result'];
11860
            }
11861
        }
11862
11863
        return $totalExercisesResult + $totalEvaluationResult;
11864
    }
11865
11866
    /**
11867
     * Check if URL is not allowed to be show in a iframe
11868
     * @param string $src
11869
     *
11870
     * @return string
11871
     */
11872
    public function fixBlockedLinks($src)
11873
    {
11874
        $urlInfo = parse_url($src);
11875
        $platformProtocol = 'https';
11876
        if (strpos(api_get_path(WEB_CODE_PATH), 'https') === false) {
11877
            $platformProtocol = 'http';
11878
        }
11879
11880
        $protocolFixApplied = false;
11881
        //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme
11882
        $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null;
11883
        $host = isset($urlInfo['host']) ? $urlInfo['host'] : null;
11884
11885
        if ($platformProtocol != $scheme) {
11886
            Session::write('x_frame_source', $src);
11887
            $src = 'blank.php?error=x_frames_options';
11888
            $protocolFixApplied = true;
11889
        }
11890
11891
        if ($protocolFixApplied == false) {
11892
            if (strpos(api_get_path(WEB_PATH), $host) === false) {
11893
                // Check X-Frame-Options
11894
                $ch = curl_init();
11895
11896
                $options = [
11897
                    CURLOPT_URL => $src,
11898
                    CURLOPT_RETURNTRANSFER => true,
11899
                    CURLOPT_HEADER => true,
11900
                    CURLOPT_FOLLOWLOCATION => true,
11901
                    CURLOPT_ENCODING => "",
11902
                    CURLOPT_AUTOREFERER => true,
11903
                    CURLOPT_CONNECTTIMEOUT => 120,
11904
                    CURLOPT_TIMEOUT => 120,
11905
                    CURLOPT_MAXREDIRS => 10,
11906
                ];
11907
                curl_setopt_array($ch, $options);
11908
                $response = curl_exec($ch);
11909
                $httpCode = curl_getinfo($ch);
11910
                $headers = substr($response, 0, $httpCode['header_size']);
11911
11912
                $error = false;
11913
                if (stripos($headers, 'X-Frame-Options: DENY') > -1
11914
                    //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1
11915
                ) {
11916
                    $error = true;
11917
                }
11918
11919
                if ($error) {
11920
                    Session::write('x_frame_source', $src);
11921
                    $src = 'blank.php?error=x_frames_options';
11922
                }
11923
            }
11924
        }
11925
11926
        return $src;
11927
    }
11928
11929
    /**
11930
     * Check if this LP has a created forum in the basis course
11931
     * @return boolean
11932
     */
11933
    public function lpHasForum()
11934
    {
11935
        $forumTable = Database::get_course_table(TABLE_FORUM);
11936
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
11937
11938
        $fakeFrom = "
11939
            $forumTable f
11940
            INNER JOIN $itemProperty ip
11941
            ON (f.forum_id = ip.ref AND f.c_id = ip.c_id)
11942
        ";
11943
11944
        $resultData = Database::select(
11945
            'COUNT(f.iid) AS qty',
11946
            $fakeFrom,
11947
            [
11948
                'where' => [
11949
                    'ip.visibility != ? AND ' => 2,
11950
                    'ip.tool = ? AND ' => TOOL_FORUM,
11951
                    'f.c_id = ? AND ' => intval($this->course_int_id),
11952
                    'f.lp_id = ?' => intval($this->lp_id)
11953
                ]
11954
            ],
11955
            'first'
11956
        );
11957
11958
        return $resultData['qty'] > 0;
11959
    }
11960
11961
    /**
11962
     * Get the forum for this learning path
11963
     * @param int $sessionId
11964
     * @return boolean
11965
     */
11966
    public function getForum($sessionId = 0)
11967
    {
11968
        $forumTable = Database::get_course_table(TABLE_FORUM);
11969
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
11970
11971
        $fakeFrom = "$forumTable f
11972
            INNER JOIN $itemProperty ip ";
11973
11974
        if ($this->lp_session_id == 0) {
11975
            $fakeFrom .= "
11976
                ON (
11977
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND (
11978
                        f.session_id = ip.session_id OR ip.session_id IS NULL
11979
                    )
11980
                )
11981
            ";
11982
        } else {
11983
            $fakeFrom .= "
11984
                ON (
11985
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND f.session_id = ip.session_id
11986
                )
11987
            ";
11988
        }
11989
11990
        $resultData = Database::select(
11991
            'f.*',
11992
            $fakeFrom,
11993
            [
11994
                'where' => [
11995
                    'ip.visibility != ? AND ' => 2,
11996
                    'ip.tool = ? AND ' => TOOL_FORUM,
11997
                    'f.session_id = ? AND ' => $sessionId,
11998
                    'f.c_id = ? AND ' => intval($this->course_int_id),
11999
                    'f.lp_id = ?' => intval($this->lp_id)
12000
                ]
12001
            ],
12002
            'first'
12003
        );
12004
12005
        if (empty($resultData)) {
12006
            return false;
12007
        }
12008
12009
        return $resultData;
12010
    }
12011
12012
    /**
12013
     * Create a forum for this learning path
12014
     * @param int $forumCategoryId
12015
     * @return int The forum ID if was created. Otherwise return false
12016
     */
12017
    public function createForum($forumCategoryId)
12018
    {
12019
        require_once api_get_path(SYS_CODE_PATH).'/forum/forumfunction.inc.php';
12020
12021
        $forumId = store_forum(
12022
            [
12023
                'lp_id' => $this->lp_id,
12024
                'forum_title' => $this->name,
12025
                'forum_comment' => null,
12026
                'forum_category' => intval($forumCategoryId),
12027
                'students_can_edit_group' => ['students_can_edit' => 0],
12028
                'allow_new_threads_group' => ['allow_new_threads' => 0],
12029
                'default_view_type_group' => ['default_view_type' => 'flat'],
12030
                'group_forum' => 0,
12031
                'public_private_group_forum_group' => ['public_private_group_forum' => 'public']
12032
            ],
12033
            [],
12034
            true
12035
        );
12036
12037
        return $forumId;
12038
    }
12039
12040
    /**
12041
     * Check and obtain the lp final item if exist
12042
     *
12043
     * @return learnpathItem
12044
     */
12045
    private function getFinalItem()
12046
    {
12047
        if (empty($this->items)) {
12048
            return null;
12049
        }
12050
12051
        foreach ($this->items as $item) {
12052
            if ($item->type !== 'final_item') {
12053
                continue;
12054
            }
12055
12056
            return $item;
12057
        }
12058
    }
12059
12060
    /**
12061
     * Get the LP Final Item Template
12062
     *
12063
     * @return string
12064
     */
12065
    private function getFinalItemTemplate()
12066
    {
12067
        return file_get_contents(api_get_path(SYS_CODE_PATH).'lp/final_item_template/template.html');
12068
    }
12069
12070
    /**
12071
     * Get the LP Final Item Url
12072
     *
12073
     * @return string
12074
     */
12075
    private function getSavedFinalItem()
12076
    {
12077
        $finalItem = $this->getFinalItem();
12078
        $doc = DocumentManager::get_document_data_by_id(
12079
            $finalItem->path,
12080
            $this->cc
12081
        );
12082
        if ($doc && file_exists($doc['absolute_path'])) {
12083
            return file_get_contents($doc['absolute_path']);
12084
        }
12085
12086
        return '';
12087
    }
12088
12089
    /**
12090
     * Get the LP Final Item form
12091
     *
12092
     * @return string
12093
     */
12094
    public function getFinalItemForm()
12095
    {
12096
        $finalItem = $this->getFinalItem();
12097
        $title = '';
12098
12099
        if ($finalItem) {
12100
            $title = $finalItem->get_title();
12101
            $buttonText = get_lang('Save');
12102
            $content = $this->getSavedFinalItem();
12103
        } else {
12104
            $buttonText = get_lang('LPCreateDocument');
12105
            $content = $this->getFinalItemTemplate();
12106
        }
12107
12108
        $courseInfo = api_get_course_info();
12109
        $result = $this->generate_lp_folder($courseInfo);
12110
        $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
12111
        $relative_prefix = '../../';
12112
12113
        $editorConfig = [
12114
            'ToolbarSet' => 'LearningPathDocuments',
12115
            'Width' => '100%',
12116
            'Height' => '500',
12117
            'FullPage' => true,
12118
            'CreateDocumentDir' => $relative_prefix,
12119
            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/',
12120
            'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path
12121
        ];
12122
12123
        $url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
12124
            'type' => 'document',
12125
            'lp_id' => $this->lp_id
12126
        ]);
12127
12128
        $form = new FormValidator('final_item', 'POST', $url);
12129
        $form->addText('title', get_lang('Title'));
12130
        $form->addButtonSave($buttonText);
12131
        $form->addHtml(
12132
            Display::return_message(
12133
                'Variables :</br></br> <b>((certificate))</b> </br> <b>((skill))</b>',
12134
                'normal',
12135
                false
12136
            )
12137
        );
12138
12139
        $renderer = $form->defaultRenderer();
12140
        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp_certificate');
12141
12142
        $form->addHtmlEditor(
12143
            'content_lp_certificate',
12144
            null,
12145
            true,
12146
            false,
12147
            $editorConfig,
12148
            true
12149
        );
12150
        $form->addHidden('action', 'add_final_item');
12151
        $form->addHidden('path', Session::read('pathItem'));
12152
        $form->addHidden('previous', $this->get_last());
12153
        $form->setDefaults(
12154
            ['title' => $title, 'content_lp_certificate' => $content]
12155
        );
12156
12157
        if ($form->validate()) {
12158
            $values = $form->exportValues();
12159
            $lastItemId = $this->get_last();
12160
12161
            if (!$finalItem) {
12162
                $documentId = $this->create_document(
12163
                    $this->course_info,
12164
                    $values['content_lp_certificate'],
12165
                    $values['title']
12166
                );
12167
                $this->add_item(
12168
                    0,
12169
                    $lastItemId,
12170
                    'final_item',
12171
                    $documentId,
12172
                    $values['title'],
12173
                    ''
12174
                );
12175
12176
                Display::addFlash(
12177
                    Display::return_message(get_lang('Added'))
12178
                );
12179
            } else {
12180
                $this->edit_document($this->course_info);
12181
            }
12182
        }
12183
12184
        return $form->returnForm();
12185
    }
12186
12187
    /**
12188
     * Check if the current lp item is first, both, last or none from lp list
12189
     *
12190
     * @param int $currentItemId
12191
     * @return string
12192
     */
12193
    public function isFirstOrLastItem($currentItemId)
12194
    {
12195
        if ($this->debug > 0) {
12196
            error_log('New LP - In learnpath::isFirstOrLastItem('.$currentItemId.')', 0);
12197
        }
12198
12199
        $lpItemId = [];
12200
        $typeListNotToVerify = self::getChapterTypes();
12201
12202
        // Using get_toc() function instead $this->items because returns the correct order of the items
12203
        foreach ($this->get_toc() as $item) {
12204
            if (!in_array($item['type'], $typeListNotToVerify)) {
12205
                $lpItemId[] = $item['id'];
12206
            }
12207
        }
12208
12209
        $lastLpItemIndex = count($lpItemId) - 1;
12210
        $position = array_search($currentItemId, $lpItemId);
12211
12212
        switch ($position) {
12213
            case 0:
12214
                if (!$lastLpItemIndex) {
12215
                    $answer = 'both';
12216
                    break;
12217
                }
12218
12219
                $answer = 'first';
12220
                break;
12221
            case $lastLpItemIndex:
12222
                $answer = 'last';
12223
                break;
12224
            default:
12225
                $answer = 'none';
12226
        }
12227
12228
        return $answer;
12229
    }
12230
12231
    /**
12232
     * Get whether this is a learning path with the accumulated SCORM time or not
12233
     * @return int
12234
     */
12235
    public function getAccumulateScormTime()
12236
    {
12237
        return $this->accumulateScormTime;
12238
    }
12239
12240
    /**
12241
     * Set whether this is a learning path with the accumulated SCORM time or not
12242
     * @param int $value (0 = false, 1 = true)
12243
     * @return bool Always returns true
12244
     */
12245
    public function setAccumulateScormTime($value)
12246
    {
12247
        if ($this->debug > 0) {
12248
            error_log('New LP - In learnpath::setAccumulateScormTime()', 0);
12249
        }
12250
        $this->accumulateScormTime = intval($value);
12251
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
12252
        $lp_id = $this->get_id();
12253
        $sql = "UPDATE $lp_table 
12254
                SET accumulate_scorm_time = ".$this->accumulateScormTime."
12255
                WHERE iid = $lp_id";
12256
        Database::query($sql);
12257
12258
        return true;
12259
    }
12260
12261
    /**
12262
     * Returns an HTML-formatted link to a resource, to incorporate directly into
12263
     * the new learning path tool.
12264
     *
12265
     * The function is a big switch on tool type.
12266
     * In each case, we query the corresponding table for information and build the link
12267
     * with that information.
12268
     * @author Yannick Warnier <[email protected]> - rebranding based on
12269
     * previous work (display_addedresource_link_in_learnpath())
12270
     * @param int	$course_id Course code
12271
     * @param int $learningPathId The learning path ID (in lp table)
12272
     * @param int $id_in_path the unique index in the items table
12273
     * @param int $lpViewId
12274
     * @param string $origin
12275
     * @return string
12276
     */
12277
    public static function rl_get_resource_link_for_learnpath(
12278
        $course_id,
12279
        $learningPathId,
12280
        $id_in_path,
12281
        $lpViewId,
12282
        $origin = 'learnpath'
12283
    ) {
12284
        $session_id = api_get_session_id();
12285
        $course_info = api_get_course_info_by_id($course_id);
12286
12287
        $learningPathId = intval($learningPathId);
12288
        $id_in_path = intval($id_in_path);
12289
        $lpViewId = intval($lpViewId);
12290
12291
        $em = Database::getManager();
12292
        $lpItemRepo = $em->getRepository('ChamiloCourseBundle:CLpItem');
12293
        /** @var CLpItem $rowItem */
12294
        $rowItem = $lpItemRepo->findOneBy([
12295
            'cId' => $course_id,
12296
            'lpId' => $learningPathId,
12297
            'id' => $id_in_path
12298
        ]);
12299
12300
        if (!$rowItem) {
12301
            return -1;
12302
        }
12303
12304
        $course_code = $course_info['code'];
12305
        $type = $rowItem->getItemType();
12306
        $id = empty($rowItem->getPath()) ? '0' : $rowItem->getPath();
12307
        $main_dir_path = api_get_path(WEB_CODE_PATH);
12308
        $main_course_path = api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/';
12309
        $link = '';
12310
        $extraParams = api_get_cidreq(true, true, 'learnpath').'&session_id='.$session_id;
12311
        switch ($type) {
12312
            case 'dir':
12313
                return $main_dir_path.'lp/blank.php';
12314
            case TOOL_CALENDAR_EVENT:
12315
                return $main_dir_path.'calendar/agenda.php?agenda_id='.$id.'&'.$extraParams;
12316
            case TOOL_ANNOUNCEMENT:
12317
                return $main_dir_path.'announcements/announcements.php?ann_id='.$id.'&'.$extraParams;
12318
            case TOOL_LINK:
12319
                $linkInfo = Link::getLinkInfo($id);
12320
                if (isset($linkInfo['url'])) {
12321
                    return $linkInfo['url'];
12322
                }
12323
                return '';
12324
            case TOOL_QUIZ:
12325
                if (empty($id)) {
12326
                    return '';
12327
                }
12328
12329
                // Get the lp_item_view with the highest view_count.
12330
                $learnpathItemViewResult = $em
12331
                    ->getRepository('ChamiloCourseBundle:CLpItemView')
12332
                    ->findBy(
12333
                        ['cId' => $course_id, 'lpItemId' => $rowItem->getId(), 'lpViewId' => $lpViewId],
12334
                        ['viewCount' => 'DESC'],
12335
                        1
12336
                    );
12337
                /** @var CLpItemView $learnpathItemViewData */
12338
                $learnpathItemViewData = current($learnpathItemViewResult);
12339
                $learnpathItemViewId = $learnpathItemViewData ? $learnpathItemViewData->getId() : 0;
12340
12341
                return $main_dir_path.'exercise/overview.php?'.$extraParams.'&'
12342
                    .http_build_query([
12343
                        'lp_init' => 1,
12344
                        'learnpath_item_view_id' => $learnpathItemViewId,
12345
                        'learnpath_id' => $learningPathId,
12346
                        'learnpath_item_id' => $id_in_path,
12347
                        'exerciseId' => $id
12348
                    ]);
12349
            case TOOL_HOTPOTATOES: //lowercase because of strtolower above
12350
                $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
12351
                $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND iid=$id");
12352
                $myrow = Database::fetch_array($result);
12353
                $path = $myrow['path'];
12354
12355
                return $main_dir_path.'exercise/showinframes.php?file='.$path.'&cid='.$course_code.'&uid='
12356
                    .api_get_user_id().'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path
12357
                    .'&lp_view_id='.$lpViewId.'&'.$extraParams;
12358
            case TOOL_FORUM:
12359
                return $main_dir_path.'forum/viewforum.php?forum='.$id.'&lp=true&'.$extraParams;
12360
            case TOOL_THREAD:  //forum post
12361
                $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD);
12362
                if (empty($id)) {
12363
                    return '';
12364
                }
12365
                $sql = "SELECT * FROM $tbl_topics WHERE c_id = $course_id AND thread_id=$id";
12366
                $result = Database::query($sql);
12367
                $myrow = Database::fetch_array($result);
12368
12369
                return $main_dir_path.'forum/viewthread.php?thread='.$id.'&forum='.$myrow['forum_id'].'&lp=true&'
12370
                    .$extraParams;
12371
            case TOOL_POST:
12372
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
12373
                $result = Database::query("SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=$id");
12374
                $myrow = Database::fetch_array($result);
12375
12376
                return $main_dir_path.'forum/viewthread.php?post='.$id.'&thread='.$myrow['thread_id'].'&forum='
12377
                    .$myrow['forum_id'].'&lp=true&'.$extraParams;
12378
            case TOOL_DOCUMENT:
12379
                $document = $em
12380
                    ->getRepository('ChamiloCourseBundle:CDocument')
12381
                    ->findOneBy(['cId' => $course_id, 'iid' => $id]);
12382
12383
                if (!$document) {
12384
                    return '';
12385
                }
12386
12387
                $documentPathInfo = pathinfo($document->getPath());
12388
                $jplayer_supported_files = ['mp4', 'ogv', 'flv', 'm4v'];
12389
                $extension = isset($documentPathInfo['extension']) ? $documentPathInfo['extension'] : '';
12390
                $showDirectUrl = !in_array($extension, $jplayer_supported_files);
12391
12392
                $openmethod = 2;
12393
                $officedoc = false;
12394
                Session::write('openmethod', $openmethod);
12395
                Session::write('officedoc', $officedoc);
12396
12397
                if ($showDirectUrl) {
12398
                    return $main_course_path.'document'.$document->getPath().'?'.$extraParams;
12399
                }
12400
12401
                return api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.'&'.$extraParams;
12402
            case TOOL_LP_FINAL_ITEM:
12403
                return api_get_path(WEB_CODE_PATH).'lp/lp_final_item.php?&id='.$id.'&lp_id='.$learningPathId.'&'
12404
                    .$extraParams;
12405
            case 'assignments':
12406
                return $main_dir_path.'work/work.php?'.$extraParams;
12407
            case TOOL_DROPBOX:
12408
                return $main_dir_path.'dropbox/index.php?'.$extraParams;
12409
            case 'introduction_text': //DEPRECATED
12410
                return '';
12411
            case TOOL_COURSE_DESCRIPTION:
12412
                return $main_dir_path.'course_description?'.$extraParams;
12413
            case TOOL_GROUP:
12414
                return $main_dir_path.'group/group.php?'.$extraParams;
12415
            case TOOL_USER:
12416
                return $main_dir_path.'user/user.php?'.$extraParams;
12417
            case TOOL_STUDENTPUBLICATION:
12418
                if (!empty($rowItem->getPath())) {
12419
                    return $main_dir_path.'work/work_list.php?id='.$rowItem->getPath().'&'.$extraParams;
12420
                }
12421
12422
                return $main_dir_path.'work/work.php?'.api_get_cidreq().'&id='.$rowItem->getPath().'&'.$extraParams;
12423
        } //end switch
12424
12425
        return $link;
12426
    }
12427
12428
    /**
12429
     * Gets the name of a resource (generally used in learnpath when no name is provided)
12430
     *
12431
     * @author Yannick Warnier <[email protected]>
12432
     * @param string    Course code
12433
     * @param string    The tool type (using constants declared in main_api.lib.php)
12434
     * @param integer    The resource ID
12435
     * @return string
12436
     */
12437
    public static function rl_get_resource_name($course_code, $learningPathId, $id_in_path)
12438
    {
12439
        $_course = api_get_course_info($course_code);
12440
        $course_id = $_course['real_id'];
12441
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
12442
        $learningPathId = intval($learningPathId);
12443
        $id_in_path = intval($id_in_path);
12444
12445
        $sql_item = "SELECT item_type, title, ref FROM $tbl_lp_item
12446
                     WHERE c_id = $course_id AND lp_id = $learningPathId AND iid = $id_in_path";
12447
        $res_item = Database::query($sql_item);
12448
12449
        if (Database::num_rows($res_item) < 1) {
12450
            return '';
12451
        }
12452
        $row_item = Database::fetch_array($res_item);
12453
        $type = strtolower($row_item['item_type']);
12454
        $id = $row_item['ref'];
12455
        $output = '';
12456
12457
        switch ($type) {
12458
            case TOOL_CALENDAR_EVENT:
12459
                $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
12460
                $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
12461
                $myrow = Database::fetch_array($result);
12462
                $output = $myrow['title'];
12463
                break;
12464
            case TOOL_ANNOUNCEMENT:
12465
                $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
12466
                $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
12467
                $myrow = Database::fetch_array($result);
12468
                $output = $myrow['title'];
12469
                break;
12470
            case TOOL_LINK:
12471
                // Doesn't take $target into account.
12472
                $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
12473
                $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
12474
                $myrow = Database::fetch_array($result);
12475
                $output = $myrow['title'];
12476
                break;
12477
            case TOOL_QUIZ:
12478
                $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
12479
                $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
12480
                $myrow = Database::fetch_array($result);
12481
                $output = $myrow['title'];
12482
                break;
12483
            case TOOL_FORUM:
12484
                $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
12485
                $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
12486
                $myrow = Database::fetch_array($result);
12487
                $output = $myrow['forum_name'];
12488
                break;
12489
            case TOOL_THREAD:  //=topics
12490
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
12491
                // Grabbing the title of the post.
12492
                $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id;
12493
                $result_title = Database::query($sql_title);
12494
                $myrow_title = Database::fetch_array($result_title);
12495
                $output = $myrow_title['post_title'];
12496
                break;
12497
            case TOOL_POST:
12498
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
12499
                //$tbl_post_text = Database::get_course_table(FORUM_POST_TEXT_TABLE);
12500
                $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id";
12501
                $result = Database::query($sql);
12502
                $post = Database::fetch_array($result);
12503
                $output = $post['post_title'];
12504
                break;
12505
            case 'dir':
12506
                $title = $row_item['title'];
12507
                if (!empty($title)) {
12508
                    $output = $title;
12509
                } else {
12510
                    $output = '-';
12511
                }
12512
                break;
12513
            case TOOL_DOCUMENT:
12514
                $title = $row_item['title'];
12515
                if (!empty($title)) {
12516
                    $output = $title;
12517
                } else {
12518
                    $output = '-';
12519
                }
12520
                break;
12521
            case 'hotpotatoes':
12522
                $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
12523
                $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND iid = $id");
12524
                $myrow = Database::fetch_array($result);
12525
                $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
12526
                $last = count($pathname) - 1; // Making a correct name for the link.
12527
                $filename = $pathname[$last]; // Making a correct name for the link.
12528
                $ext = explode('.', $filename);
12529
                $ext = strtolower($ext[sizeof($ext) - 1]);
12530
                $myrow['path'] = rawurlencode($myrow['path']);
12531
                $output = $filename;
12532
                break;
12533
        }
12534
        return stripslashes($output);
12535
    }
12536
12537
    /**
12538
     * Get the parent names for the current item
12539
     * @param int $newItemId Optional. The item ID
12540
     * @return array
12541
     */
12542
    public function getCurrentItemParentNames($newItemId = 0)
12543
    {
12544
        $newItemId = $newItemId ?: $this->get_current_item_id();
12545
        $return = [];
12546
        $item = $this->getItem($newItemId);
12547
        $parent = $this->getItem($item->get_parent());
12548
12549
        while ($parent) {
12550
            $return[] = $parent->get_title();
12551
12552
            $parent = $this->getItem($parent->get_parent());
12553
        }
12554
12555
        return array_reverse($return);
12556
    }
12557
12558
    /**
12559
     * Reads and process "lp_subscription_settings" setting
12560
     * @return array
12561
     */
12562
    public static function getSubscriptionSettings()
12563
    {
12564
        $subscriptionSettings = api_get_configuration_value('lp_subscription_settings');
12565
        if (empty($subscriptionSettings)) {
12566
            // By default allow both settings
12567
            $subscriptionSettings = [
12568
                'allow_add_users_to_lp' => true,
12569
                'allow_add_users_to_lp_category' => true,
12570
            ];
12571
        } else {
12572
            $subscriptionSettings = $subscriptionSettings['options'];
12573
        }
12574
12575
        return $subscriptionSettings;
12576
    }
12577
}
12578