| Total Complexity | 1370 |
| Total Lines | 11329 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
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 |
||
| 39 | class learnpath |
||
| 40 | { |
||
| 41 | public const MAX_LP_ITEM_TITLE_LENGTH = 32; |
||
| 42 | public const STATUS_CSS_CLASS_NAME = [ |
||
| 43 | 'not attempted' => 'scorm_not_attempted', |
||
| 44 | 'incomplete' => 'scorm_not_attempted', |
||
| 45 | 'failed' => 'scorm_failed', |
||
| 46 | 'completed' => 'scorm_completed', |
||
| 47 | 'passed' => 'scorm_completed', |
||
| 48 | 'succeeded' => 'scorm_completed', |
||
| 49 | 'browsed' => 'scorm_completed', |
||
| 50 | ]; |
||
| 51 | |||
| 52 | public $attempt = 0; // The number for the current ID view. |
||
| 53 | public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ... |
||
| 54 | public $current; // Id of the current item the user is viewing. |
||
| 55 | public $current_score; // The score of the current item. |
||
| 56 | public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet). |
||
| 57 | public $current_time_stop; // The time the user closed this resource. |
||
| 58 | public $default_status = 'not attempted'; |
||
| 59 | public $encoding = 'UTF-8'; |
||
| 60 | public $error = ''; |
||
| 61 | public $force_commit = false; // For SCORM only- if true will send a scorm LMSCommit() request on each LMSSetValue() |
||
| 62 | public $index; // The index of the active learnpath_item in $ordered_items array. |
||
| 63 | /** @var learnpathItem[] */ |
||
| 64 | public $items = []; |
||
| 65 | public $last; // item_id of last item viewed in the learning path. |
||
| 66 | public $last_item_seen = 0; // In case we have already come in this lp, reuse the last item seen if authorized. |
||
| 67 | public $license; // Which license this course has been given - not used yet on 20060522. |
||
| 68 | public $lp_id; // DB iid for this learnpath. |
||
| 69 | public $lp_view_id; // DB ID for lp_view |
||
| 70 | public $maker; // Which maker has conceived the content (ENI, Articulate, ...). |
||
| 71 | public $message = ''; |
||
| 72 | public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded). |
||
| 73 | public $name; // Learnpath name (they generally have one). |
||
| 74 | public $ordered_items = []; // List of the learnpath items in the order they are to be read. |
||
| 75 | public $path = ''; // Path inside the scorm directory (if scorm). |
||
| 76 | public $theme; // The current theme of the learning path. |
||
| 77 | public $accumulateScormTime; // Flag to decide whether to accumulate SCORM time or not |
||
| 78 | public $accumulateWorkTime; // The min time of learnpath |
||
| 79 | |||
| 80 | // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1). |
||
| 81 | public $prevent_reinit = 1; |
||
| 82 | |||
| 83 | // Describes the mode of progress bar display. |
||
| 84 | public $seriousgame_mode = 0; |
||
| 85 | public $progress_bar_mode = '%'; |
||
| 86 | |||
| 87 | // Percentage progress as saved in the db. |
||
| 88 | public $progress_db = 0; |
||
| 89 | public $proximity; // Wether the content is distant or local or unknown. |
||
| 90 | public $refs_list = []; //list of items by ref => db_id. Used only for prerequisites match. |
||
| 91 | // !!!This array (refs_list) is built differently depending on the nature of the LP. |
||
| 92 | // If SCORM, uses ref, if Chamilo, uses id to keep a unique value. |
||
| 93 | public $type; //type of learnpath. Could be 'chamilo', 'scorm', 'scorm2004', 'aicc', ... |
||
| 94 | // TODO: Check if this type variable is useful here (instead of just in the controller script). |
||
| 95 | public $user_id; //ID of the user that is viewing/using the course |
||
| 96 | public $update_queue = []; |
||
| 97 | public $scorm_debug = 0; |
||
| 98 | public $arrMenu = []; // Array for the menu items. |
||
| 99 | public $debug = 0; // Logging level. |
||
| 100 | public $lp_session_id = 0; |
||
| 101 | public $lp_view_session_id = 0; // The specific view might be bound to a session. |
||
| 102 | public $prerequisite = 0; |
||
| 103 | public $use_max_score = 1; // 1 or 0 |
||
| 104 | public $subscribeUsers = 0; // Subscribe users or not |
||
| 105 | public $created_on = ''; |
||
| 106 | public $modified_on = ''; |
||
| 107 | public $publicated_on = ''; |
||
| 108 | public $expired_on = ''; |
||
| 109 | public $ref; |
||
| 110 | public $course_int_id; |
||
| 111 | public $course_info; |
||
| 112 | public $categoryId; |
||
| 113 | public $scormUrl; |
||
| 114 | public $entity; |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Constructor. |
||
| 118 | * Needs a database handler, a course code and a learnpath id from the database. |
||
| 119 | * Also builds the list of items into $this->items. |
||
| 120 | */ |
||
| 121 | public function __construct(CLp $entity, $course_info, $user_id) |
||
| 122 | { |
||
| 123 | $debug = $this->debug; |
||
| 124 | $this->encoding = api_get_system_encoding(); |
||
| 125 | $lp_id = (int) $entity->getIid(); |
||
| 126 | $course_info = empty($course_info) ? api_get_course_info() : $course_info; |
||
| 127 | $course_id = (int) $course_info['real_id']; |
||
| 128 | $this->course_info = $course_info; |
||
| 129 | $this->set_course_int_id($course_id); |
||
| 130 | if (empty($lp_id) || empty($course_id)) { |
||
| 131 | $this->error = "Parameter is empty: LpId:'$lp_id', courseId: '$lp_id'"; |
||
| 132 | } else { |
||
| 133 | $this->entity = $entity; |
||
| 134 | $this->lp_id = $lp_id; |
||
| 135 | $this->type = $entity->getLpType(); |
||
| 136 | $this->name = stripslashes($entity->getName()); |
||
| 137 | $this->proximity = $entity->getContentLocal(); |
||
| 138 | $this->theme = $entity->getTheme(); |
||
| 139 | $this->maker = $entity->getContentLocal(); |
||
| 140 | $this->prevent_reinit = $entity->getPreventReinit(); |
||
| 141 | $this->seriousgame_mode = $entity->getSeriousgameMode(); |
||
| 142 | $this->license = $entity->getContentLicense(); |
||
| 143 | $this->scorm_debug = $entity->getDebug(); |
||
| 144 | $this->js_lib = $entity->getJsLib(); |
||
| 145 | $this->path = $entity->getPath(); |
||
| 146 | $this->author = $entity->getAuthor(); |
||
| 147 | $this->hide_toc_frame = $entity->getHideTocFrame(); |
||
| 148 | //$this->lp_session_id = $entity->getSessionId(); |
||
| 149 | $this->use_max_score = $entity->getUseMaxScore(); |
||
| 150 | $this->subscribeUsers = $entity->getSubscribeUsers(); |
||
| 151 | $this->created_on = $entity->getCreatedOn()->format('Y-m-d H:i:s'); |
||
| 152 | $this->modified_on = $entity->getModifiedOn()->format('Y-m-d H:i:s'); |
||
| 153 | $this->ref = $entity->getRef(); |
||
| 154 | $this->categoryId = 0; |
||
| 155 | if ($entity->getCategory()) { |
||
| 156 | $this->categoryId = $entity->getCategory()->getIid(); |
||
| 157 | } |
||
| 158 | |||
| 159 | if ($entity->hasAsset()) { |
||
| 160 | $asset = $entity->getAsset(); |
||
| 161 | $this->scormUrl = Container::getAssetRepository()->getAssetUrl($asset).'/'; |
||
| 162 | } |
||
| 163 | |||
| 164 | $this->accumulateScormTime = $entity->getAccumulateWorkTime(); |
||
| 165 | |||
| 166 | if (!empty($entity->getPublicatedOn())) { |
||
| 167 | $this->publicated_on = $entity->getPublicatedOn()->format('Y-m-d H:i:s'); |
||
| 168 | } |
||
| 169 | |||
| 170 | if (!empty($entity->getExpiredOn())) { |
||
| 171 | $this->expired_on = $entity->getExpiredOn()->format('Y-m-d H:i:s'); |
||
| 172 | } |
||
| 173 | if (2 == $this->type) { |
||
| 174 | if (1 == $entity->getForceCommit()) { |
||
| 175 | $this->force_commit = true; |
||
| 176 | } |
||
| 177 | } |
||
| 178 | $this->mode = $entity->getDefaultViewMod(); |
||
| 179 | |||
| 180 | // Check user ID. |
||
| 181 | if (empty($user_id)) { |
||
| 182 | $this->error = 'User ID is empty'; |
||
| 183 | } else { |
||
| 184 | $userInfo = api_get_user_info($user_id); |
||
| 185 | if (!empty($userInfo)) { |
||
| 186 | $this->user_id = $userInfo['user_id']; |
||
| 187 | } else { |
||
| 188 | $this->error = 'User ID does not exist in database #'.$user_id; |
||
| 189 | } |
||
| 190 | } |
||
| 191 | |||
| 192 | // End of variables checking. |
||
| 193 | $session_id = api_get_session_id(); |
||
| 194 | // Get the session condition for learning paths of the base + session. |
||
| 195 | $session = api_get_session_condition($session_id); |
||
| 196 | // Now get the latest attempt from this user on this LP, if available, otherwise create a new one. |
||
| 197 | $lp_table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 198 | |||
| 199 | // Selecting by view_count descending allows to get the highest view_count first. |
||
| 200 | $sql = "SELECT * FROM $lp_table |
||
| 201 | WHERE |
||
| 202 | c_id = $course_id AND |
||
| 203 | lp_id = $lp_id AND |
||
| 204 | user_id = $user_id |
||
| 205 | $session |
||
| 206 | ORDER BY view_count DESC"; |
||
| 207 | $res = Database::query($sql); |
||
| 208 | |||
| 209 | if (Database::num_rows($res) > 0) { |
||
| 210 | $row = Database::fetch_array($res); |
||
| 211 | $this->attempt = $row['view_count']; |
||
| 212 | $this->lp_view_id = $row['iid']; |
||
| 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 | $this->attempt = 1; |
||
| 218 | $params = [ |
||
| 219 | 'c_id' => $course_id, |
||
| 220 | 'lp_id' => $lp_id, |
||
| 221 | 'user_id' => $user_id, |
||
| 222 | 'view_count' => 1, |
||
| 223 | //'session_id' => $session_id, |
||
| 224 | 'last_item' => 0, |
||
| 225 | ]; |
||
| 226 | if (!empty($session_id)) { |
||
| 227 | $params['session_id'] = $session_id; |
||
| 228 | } |
||
| 229 | $this->last_item_seen = 0; |
||
| 230 | $this->lp_view_session_id = $session_id; |
||
| 231 | $this->lp_view_id = Database::insert($lp_table, $params); |
||
| 232 | } |
||
| 233 | |||
| 234 | $criteria = Criteria::create() |
||
| 235 | ->orderBy( |
||
| 236 | [ |
||
| 237 | 'parent' => Criteria::ASC, |
||
| 238 | 'displayOrder' => Criteria::ASC, |
||
| 239 | ] |
||
| 240 | ); |
||
| 241 | |||
| 242 | $items = $this->entity->getItems()->matching($criteria); |
||
| 243 | |||
| 244 | // Initialise items. |
||
| 245 | /*$lp_item_table = Database::get_course_table(TABLE_LP_ITEM); |
||
| 246 | $sql = "SELECT * FROM $lp_item_table |
||
| 247 | WHERE lp_id = '".$this->lp_id."' |
||
| 248 | ORDER BY parent_item_id, display_order"; |
||
| 249 | $res = Database::query($sql);*/ |
||
| 250 | |||
| 251 | $lp_item_id_list = []; |
||
| 252 | //while ($row = Database::fetch_array($res)) { |
||
| 253 | foreach ($items as $item) { |
||
| 254 | $itemId = $item->getIid(); |
||
| 255 | $lp_item_id_list[] = $itemId; |
||
| 256 | switch ($this->type) { |
||
| 257 | case 3: //aicc |
||
| 258 | $oItem = new aiccItem('db', $itemId, $course_id); |
||
| 259 | if (is_object($oItem)) { |
||
| 260 | $my_item_id = $oItem->get_id(); |
||
| 261 | $oItem->set_lp_view($this->lp_view_id, $course_id); |
||
| 262 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
| 263 | // Don't use reference here as the next loop will make the pointed object change. |
||
| 264 | $this->items[$my_item_id] = $oItem; |
||
| 265 | $this->refs_list[$oItem->ref] = $my_item_id; |
||
| 266 | } |
||
| 267 | break; |
||
| 268 | case 2: |
||
| 269 | $oItem = new scormItem('db', $itemId, $course_id); |
||
| 270 | if (is_object($oItem)) { |
||
| 271 | $my_item_id = $oItem->get_id(); |
||
| 272 | $oItem->set_lp_view($this->lp_view_id, $course_id); |
||
| 273 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
| 274 | // Don't use reference here as the next loop will make the pointed object change. |
||
| 275 | $this->items[$my_item_id] = $oItem; |
||
| 276 | $this->refs_list[$oItem->ref] = $my_item_id; |
||
| 277 | } |
||
| 278 | break; |
||
| 279 | case 1: |
||
| 280 | default: |
||
| 281 | $oItem = new learnpathItem($itemId, $user_id, $course_id, $item); |
||
| 282 | if (is_object($oItem)) { |
||
| 283 | $my_item_id = $oItem->get_id(); |
||
| 284 | // Moved down to when we are sure the item_view exists. |
||
| 285 | //$oItem->set_lp_view($this->lp_view_id); |
||
| 286 | $oItem->set_prevent_reinit($this->prevent_reinit); |
||
| 287 | // Don't use reference here as the next loop will make the pointed object change. |
||
| 288 | $this->items[$my_item_id] = $oItem; |
||
| 289 | $this->refs_list[$my_item_id] = $my_item_id; |
||
| 290 | } |
||
| 291 | break; |
||
| 292 | } |
||
| 293 | |||
| 294 | // Setting the object level with variable $this->items[$i][parent] |
||
| 295 | foreach ($this->items as $itemLPObject) { |
||
| 296 | $level = self::get_level_for_item( |
||
| 297 | $this->items, |
||
| 298 | $itemLPObject->db_id |
||
| 299 | ); |
||
| 300 | $itemLPObject->level = $level; |
||
| 301 | } |
||
| 302 | |||
| 303 | // Setting the view in the item object. |
||
| 304 | if (is_object($this->items[$itemId])) { |
||
| 305 | $this->items[$itemId]->set_lp_view($this->lp_view_id, $course_id); |
||
| 306 | if (TOOL_HOTPOTATOES == $this->items[$itemId]->get_type()) { |
||
| 307 | $this->items[$itemId]->current_start_time = 0; |
||
| 308 | $this->items[$itemId]->current_stop_time = 0; |
||
| 309 | } |
||
| 310 | } |
||
| 311 | } |
||
| 312 | |||
| 313 | if (!empty($lp_item_id_list)) { |
||
| 314 | $lp_item_id_list_to_string = implode("','", $lp_item_id_list); |
||
| 315 | if (!empty($lp_item_id_list_to_string)) { |
||
| 316 | // Get last viewing vars. |
||
| 317 | $itemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
| 318 | // This query should only return one or zero result. |
||
| 319 | $sql = "SELECT lp_item_id, status |
||
| 320 | FROM $itemViewTable |
||
| 321 | WHERE |
||
| 322 | lp_view_id = ".$this->get_view_id()." AND |
||
| 323 | lp_item_id IN ('".$lp_item_id_list_to_string."') |
||
| 324 | ORDER BY view_count DESC "; |
||
| 325 | $status_list = []; |
||
| 326 | $res = Database::query($sql); |
||
| 327 | while ($row = Database:: fetch_array($res)) { |
||
| 328 | $status_list[$row['lp_item_id']] = $row['status']; |
||
| 329 | } |
||
| 330 | |||
| 331 | foreach ($lp_item_id_list as $item_id) { |
||
| 332 | if (isset($status_list[$item_id])) { |
||
| 333 | $status = $status_list[$item_id]; |
||
| 334 | if (is_object($this->items[$item_id])) { |
||
| 335 | $this->items[$item_id]->set_status($status); |
||
| 336 | if (empty($status)) { |
||
| 337 | $this->items[$item_id]->set_status( |
||
| 338 | $this->default_status |
||
| 339 | ); |
||
| 340 | } |
||
| 341 | } |
||
| 342 | } else { |
||
| 343 | if (!api_is_invitee()) { |
||
| 344 | if (is_object($this->items[$item_id])) { |
||
| 345 | $this->items[$item_id]->set_status( |
||
| 346 | $this->default_status |
||
| 347 | ); |
||
| 348 | } |
||
| 349 | |||
| 350 | if (!empty($this->lp_view_id)) { |
||
| 351 | // Add that row to the lp_item_view table so that |
||
| 352 | // we have something to show in the stats page. |
||
| 353 | $params = [ |
||
| 354 | //'c_id' => $course_id, |
||
| 355 | 'lp_item_id' => $item_id, |
||
| 356 | 'lp_view_id' => $this->lp_view_id, |
||
| 357 | 'view_count' => 1, |
||
| 358 | 'status' => 'not attempted', |
||
| 359 | 'start_time' => time(), |
||
| 360 | 'total_time' => 0, |
||
| 361 | 'score' => 0, |
||
| 362 | ]; |
||
| 363 | Database::insert($itemViewTable, $params); |
||
| 364 | |||
| 365 | $this->items[$item_id]->set_lp_view( |
||
| 366 | $this->lp_view_id, |
||
| 367 | $course_id |
||
| 368 | ); |
||
| 369 | } |
||
| 370 | } |
||
| 371 | } |
||
| 372 | } |
||
| 373 | } |
||
| 374 | } |
||
| 375 | |||
| 376 | $this->ordered_items = self::get_flat_ordered_items_list($this->entity, null); |
||
| 377 | $this->max_ordered_items = 0; |
||
| 378 | foreach ($this->ordered_items as $index => $dummy) { |
||
| 379 | if ($index > $this->max_ordered_items && !empty($dummy)) { |
||
| 380 | $this->max_ordered_items = $index; |
||
| 381 | } |
||
| 382 | } |
||
| 383 | // TODO: Define the current item better. |
||
| 384 | $this->first(); |
||
| 385 | if ($debug) { |
||
| 386 | error_log('lp_view_session_id '.$this->lp_view_session_id); |
||
| 387 | error_log('End of learnpath constructor for learnpath '.$this->get_id()); |
||
| 388 | } |
||
| 389 | } |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * @return string |
||
| 394 | */ |
||
| 395 | public function getCourseCode() |
||
| 396 | { |
||
| 397 | return $this->cc; |
||
| 398 | } |
||
| 399 | |||
| 400 | /** |
||
| 401 | * @return int |
||
| 402 | */ |
||
| 403 | public function get_course_int_id() |
||
| 404 | { |
||
| 405 | return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id(); |
||
| 406 | } |
||
| 407 | |||
| 408 | /** |
||
| 409 | * @param $course_id |
||
| 410 | * |
||
| 411 | * @return int |
||
| 412 | */ |
||
| 413 | public function set_course_int_id($course_id) |
||
| 414 | { |
||
| 415 | return $this->course_int_id = (int) $course_id; |
||
| 416 | } |
||
| 417 | |||
| 418 | /** |
||
| 419 | * Function rewritten based on old_add_item() from Yannick Warnier. |
||
| 420 | * Due the fact that users can decide where the item should come, I had to overlook this function and |
||
| 421 | * I found it better to rewrite it. Old function is still available. |
||
| 422 | * Added also the possibility to add a description. |
||
| 423 | * |
||
| 424 | * @param CLpItem $parent |
||
| 425 | * @param int $previous |
||
| 426 | * @param string $type |
||
| 427 | * @param int $id resource ID (ref) |
||
| 428 | * @param string $title |
||
| 429 | * @param string $description |
||
| 430 | * @param int $prerequisites |
||
| 431 | * @param int $max_time_allowed |
||
| 432 | * @param int $userId |
||
| 433 | * |
||
| 434 | * @return int |
||
| 435 | */ |
||
| 436 | public function add_item( |
||
| 437 | ?CLpItem $parent, |
||
| 438 | $previous, |
||
| 439 | $type, |
||
| 440 | $id, |
||
| 441 | $title, |
||
| 442 | $description = '', |
||
| 443 | $prerequisites = 0, |
||
| 444 | $max_time_allowed = 0, |
||
| 445 | $userId = 0 |
||
| 446 | ) { |
||
| 447 | $type = empty($type) ? 'dir' : $type; |
||
| 448 | $course_id = $this->course_info['real_id']; |
||
| 449 | if (empty($course_id)) { |
||
| 450 | // Sometimes Oogie doesn't catch the course info but sets $this->cc |
||
| 451 | $this->course_info = api_get_course_info($this->cc); |
||
| 452 | $course_id = $this->course_info['real_id']; |
||
| 453 | } |
||
| 454 | $userId = empty($userId) ? api_get_user_id() : $userId; |
||
| 455 | $sessionId = api_get_session_id(); |
||
| 456 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 457 | |||
| 458 | $previous = (int) $previous; |
||
| 459 | $id = (int) $id; |
||
| 460 | $max_time_allowed = (int) $max_time_allowed; |
||
| 461 | if (empty($max_time_allowed)) { |
||
| 462 | $max_time_allowed = 0; |
||
| 463 | } |
||
| 464 | $lpId = $this->get_id(); |
||
| 465 | $parentCondition = ' AND parent_item_id IS NULL '; |
||
| 466 | $parentId = 0; |
||
| 467 | if (!empty($parent)) { |
||
| 468 | $parentId = $parent->getIid(); |
||
| 469 | $parentCondition = " AND parent_item_id = $parentId "; |
||
| 470 | } |
||
| 471 | |||
| 472 | $sql = "SELECT COUNT(iid) AS num |
||
| 473 | FROM $tbl_lp_item |
||
| 474 | WHERE |
||
| 475 | lp_id = $lpId |
||
| 476 | $parentCondition "; |
||
| 477 | |||
| 478 | $res_count = Database::query($sql); |
||
| 479 | $row = Database::fetch_array($res_count); |
||
| 480 | $num = $row['num']; |
||
| 481 | |||
| 482 | $tmp_previous = 0; |
||
| 483 | $display_order = 0; |
||
| 484 | $next = 0; |
||
| 485 | if ($num > 0) { |
||
| 486 | if (empty($previous)) { |
||
| 487 | $sql = "SELECT iid, next_item_id, display_order |
||
| 488 | FROM $tbl_lp_item |
||
| 489 | WHERE |
||
| 490 | lp_id = $lpId AND |
||
| 491 | (previous_item_id = 0 OR |
||
| 492 | previous_item_id = $parentId) |
||
| 493 | $parentCondition"; |
||
| 494 | $result = Database::query($sql); |
||
| 495 | $row = Database::fetch_array($result); |
||
| 496 | if ($row) { |
||
| 497 | $next = $row['iid']; |
||
| 498 | } |
||
| 499 | } else { |
||
| 500 | $previous = (int) $previous; |
||
| 501 | $sql = "SELECT iid, previous_item_id, next_item_id, display_order |
||
| 502 | FROM $tbl_lp_item |
||
| 503 | WHERE |
||
| 504 | lp_id = $lpId AND |
||
| 505 | iid = $previous"; |
||
| 506 | $result = Database::query($sql); |
||
| 507 | $row = Database::fetch_array($result); |
||
| 508 | if ($row) { |
||
| 509 | $tmp_previous = $row['iid']; |
||
| 510 | $next = $row['next_item_id']; |
||
| 511 | $display_order = $row['display_order']; |
||
| 512 | } |
||
| 513 | } |
||
| 514 | } |
||
| 515 | |||
| 516 | $id = (int) $id; |
||
| 517 | $max_score = 100; |
||
| 518 | if ('quiz' === $type && $id) { |
||
| 519 | $sql = 'SELECT SUM(ponderation) |
||
| 520 | FROM '.Database::get_course_table(TABLE_QUIZ_QUESTION).' as q |
||
| 521 | INNER JOIN '.Database::get_course_table(TABLE_QUIZ_TEST_QUESTION).' as quiz_rel_question |
||
| 522 | ON |
||
| 523 | q.iid = quiz_rel_question.question_id |
||
| 524 | WHERE |
||
| 525 | quiz_rel_question.quiz_id = '.$id; |
||
| 526 | $rsQuiz = Database::query($sql); |
||
| 527 | $max_score = Database::result($rsQuiz, 0, 0); |
||
| 528 | |||
| 529 | // Disabling the exercise if we add it inside a LP |
||
| 530 | $exercise = new Exercise($course_id); |
||
| 531 | $exercise->read($id); |
||
| 532 | $exercise->disable(); |
||
| 533 | $exercise->save(); |
||
| 534 | $title = $exercise->get_formated_title(); |
||
| 535 | } |
||
| 536 | |||
| 537 | $lpItem = new CLpItem(); |
||
| 538 | $lpItem |
||
| 539 | ->setTitle($title) |
||
| 540 | ->setDescription($description) |
||
| 541 | ->setPath($id) |
||
| 542 | ->setLp($this->entity) |
||
| 543 | ->setItemType($type) |
||
| 544 | ->setMaxScore($max_score) |
||
| 545 | ->setMaxTimeAllowed($max_time_allowed) |
||
| 546 | ->setPrerequisite($prerequisites) |
||
| 547 | ->setDisplayOrder($display_order + 1) |
||
| 548 | ->setNextItemId((int) $next) |
||
| 549 | ->setParent($parent) |
||
| 550 | ->setPreviousItemId($previous) |
||
| 551 | ; |
||
| 552 | $em = Database::getManager(); |
||
| 553 | $em->persist($lpItem); |
||
| 554 | $em->flush(); |
||
| 555 | |||
| 556 | $new_item_id = $lpItem->getIid(); |
||
| 557 | if ($new_item_id) { |
||
| 558 | if (!empty($next)) { |
||
| 559 | $sql = "UPDATE $tbl_lp_item |
||
| 560 | SET previous_item_id = $new_item_id |
||
| 561 | WHERE iid = $next AND item_type != '".TOOL_LP_FINAL_ITEM."'"; |
||
| 562 | Database::query($sql); |
||
| 563 | } |
||
| 564 | |||
| 565 | // Update the item that should be before the new item. |
||
| 566 | if (!empty($tmp_previous)) { |
||
| 567 | $sql = "UPDATE $tbl_lp_item |
||
| 568 | SET next_item_id = $new_item_id |
||
| 569 | WHERE iid = $tmp_previous"; |
||
| 570 | Database::query($sql); |
||
| 571 | } |
||
| 572 | |||
| 573 | $parentCondition = ' AND parent_item_id IS NULL '; |
||
| 574 | if (!empty($lpItem->getParentItemId())) { |
||
| 575 | $parentId = $lpItem->getParentItemId(); |
||
| 576 | $parentCondition = " AND parent_item_id = $parentId "; |
||
| 577 | } |
||
| 578 | |||
| 579 | // Update all the items after the new item. |
||
| 580 | $sql = "UPDATE $tbl_lp_item |
||
| 581 | SET display_order = display_order + 1 |
||
| 582 | WHERE |
||
| 583 | lp_id = $lpId AND |
||
| 584 | iid <> $new_item_id AND |
||
| 585 | display_order > $display_order |
||
| 586 | $parentCondition |
||
| 587 | "; |
||
| 588 | Database::query($sql); |
||
| 589 | |||
| 590 | // Update the item that should come after the new item. |
||
| 591 | $sql = "UPDATE $tbl_lp_item |
||
| 592 | SET ref = $new_item_id |
||
| 593 | WHERE iid = $new_item_id"; |
||
| 594 | Database::query($sql); |
||
| 595 | |||
| 596 | $sql = "UPDATE $tbl_lp_item |
||
| 597 | SET previous_item_id = ".$this->getLastInFirstLevel()." |
||
| 598 | WHERE lp_id = $lpId AND item_type = '".TOOL_LP_FINAL_ITEM."'"; |
||
| 599 | Database::query($sql); |
||
| 600 | |||
| 601 | // Upload audio. |
||
| 602 | if (!empty($_FILES['mp3']['name'])) { |
||
| 603 | // Create the audio folder if it does not exist yet. |
||
| 604 | /*$filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||
| 605 | if (!is_dir($filepath.'audio')) { |
||
| 606 | mkdir( |
||
| 607 | $filepath.'audio', |
||
| 608 | api_get_permissions_for_new_directories() |
||
| 609 | ); |
||
| 610 | DocumentManager::addDocument( |
||
| 611 | $_course, |
||
| 612 | '/audio', |
||
| 613 | 'folder', |
||
| 614 | 0, |
||
| 615 | 'audio', |
||
| 616 | '', |
||
| 617 | 0, |
||
| 618 | true, |
||
| 619 | null, |
||
| 620 | $sessionId, |
||
| 621 | $userId |
||
| 622 | ); |
||
| 623 | } |
||
| 624 | |||
| 625 | $file_path = handle_uploaded_document( |
||
| 626 | $_course, |
||
| 627 | $_FILES['mp3'], |
||
| 628 | api_get_path(SYS_COURSE_PATH).$_course['path'].'/document', |
||
| 629 | '/audio', |
||
| 630 | $userId, |
||
| 631 | '', |
||
| 632 | '', |
||
| 633 | '', |
||
| 634 | '', |
||
| 635 | false |
||
| 636 | ); |
||
| 637 | |||
| 638 | // Getting the filename only. |
||
| 639 | $file_components = explode('/', $file_path); |
||
| 640 | $file = $file_components[count($file_components) - 1]; |
||
| 641 | |||
| 642 | // Store the mp3 file in the lp_item table. |
||
| 643 | $sql = "UPDATE $tbl_lp_item SET |
||
| 644 | audio = '".Database::escape_string($file)."' |
||
| 645 | WHERE iid = '".intval($new_item_id)."'"; |
||
| 646 | Database::query($sql);*/ |
||
| 647 | } |
||
| 648 | } |
||
| 649 | |||
| 650 | return $new_item_id; |
||
| 651 | } |
||
| 652 | |||
| 653 | /** |
||
| 654 | * Static admin function allowing addition of a learnpath to a course. |
||
| 655 | * |
||
| 656 | * @param string $courseCode |
||
| 657 | * @param string $name |
||
| 658 | * @param string $description |
||
| 659 | * @param string $learnpath |
||
| 660 | * @param string $origin |
||
| 661 | * @param string $zipname Zip file containing the learnpath or directory containing the learnpath |
||
| 662 | * @param string $publicated_on |
||
| 663 | * @param string $expired_on |
||
| 664 | * @param int $categoryId |
||
| 665 | * @param int $userId |
||
| 666 | * |
||
| 667 | * @return CLp |
||
| 668 | */ |
||
| 669 | public static function add_lp( |
||
| 670 | $courseCode, |
||
| 671 | $name, |
||
| 672 | $description = '', |
||
| 673 | $learnpath = 'guess', |
||
| 674 | $origin = 'zip', |
||
| 675 | $zipname = '', |
||
| 676 | $publicated_on = '', |
||
| 677 | $expired_on = '', |
||
| 678 | $categoryId = 0, |
||
| 679 | $userId = 0 |
||
| 680 | ) { |
||
| 681 | global $charset; |
||
| 682 | |||
| 683 | if (!empty($courseCode)) { |
||
| 684 | $courseInfo = api_get_course_info($courseCode); |
||
| 685 | $course_id = $courseInfo['real_id']; |
||
| 686 | } else { |
||
| 687 | $course_id = api_get_course_int_id(); |
||
| 688 | $courseInfo = api_get_course_info(); |
||
| 689 | } |
||
| 690 | |||
| 691 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 692 | // Check course code exists. |
||
| 693 | // Check lp_name doesn't exist, otherwise append something. |
||
| 694 | $i = 0; |
||
| 695 | $categoryId = (int) $categoryId; |
||
| 696 | // Session id. |
||
| 697 | $session_id = api_get_session_id(); |
||
| 698 | $userId = empty($userId) ? api_get_user_id() : $userId; |
||
| 699 | |||
| 700 | if (empty($publicated_on)) { |
||
| 701 | $publicated_on = null; |
||
| 702 | } else { |
||
| 703 | $publicated_on = api_get_utc_datetime($publicated_on, true, true); |
||
| 704 | } |
||
| 705 | |||
| 706 | if (empty($expired_on)) { |
||
| 707 | $expired_on = null; |
||
| 708 | } else { |
||
| 709 | $expired_on = api_get_utc_datetime($expired_on, true, true); |
||
| 710 | } |
||
| 711 | |||
| 712 | /*$check_name = "SELECT * FROM $tbl_lp |
||
| 713 | WHERE c_id = $course_id AND name = '".Database::escape_string($name)."'"; |
||
| 714 | $res_name = Database::query($check_name); |
||
| 715 | |||
| 716 | while (Database::num_rows($res_name)) { |
||
| 717 | // There is already one such name, update the current one a bit. |
||
| 718 | $i++; |
||
| 719 | $name = $name.' - '.$i; |
||
| 720 | $check_name = "SELECT * FROM $tbl_lp |
||
| 721 | WHERE c_id = $course_id AND name = '".Database::escape_string($name)."' "; |
||
| 722 | $res_name = Database::query($check_name); |
||
| 723 | }*/ |
||
| 724 | // New name does not exist yet; keep it. |
||
| 725 | // Escape description. |
||
| 726 | // Kevin: added htmlentities(). |
||
| 727 | $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset)); |
||
| 728 | $type = 1; |
||
| 729 | switch ($learnpath) { |
||
| 730 | case 'guess': |
||
| 731 | case 'aicc': |
||
| 732 | break; |
||
| 733 | case 'dokeos': |
||
| 734 | case 'chamilo': |
||
| 735 | $type = 1; |
||
| 736 | break; |
||
| 737 | } |
||
| 738 | |||
| 739 | $id = null; |
||
| 740 | $sessionEntity = api_get_session_entity(); |
||
| 741 | $courseEntity = api_get_course_entity($courseInfo['real_id']); |
||
| 742 | $lp = null; |
||
| 743 | switch ($origin) { |
||
| 744 | case 'zip': |
||
| 745 | // Check zip name string. If empty, we are currently creating a new Chamilo learnpath. |
||
| 746 | break; |
||
| 747 | case 'manual': |
||
| 748 | default: |
||
| 749 | /*$get_max = "SELECT MAX(display_order) |
||
| 750 | FROM $tbl_lp WHERE c_id = $course_id"; |
||
| 751 | $res_max = Database::query($get_max); |
||
| 752 | if (Database::num_rows($res_max) < 1) { |
||
| 753 | $dsp = 1; |
||
| 754 | } else { |
||
| 755 | $row = Database::fetch_array($res_max); |
||
| 756 | $dsp = $row[0] + 1; |
||
| 757 | }*/ |
||
| 758 | |||
| 759 | $dsp = 1; |
||
| 760 | $category = null; |
||
| 761 | if (!empty($categoryId)) { |
||
| 762 | $category = Container::getLpCategoryRepository()->find($categoryId); |
||
| 763 | } |
||
| 764 | |||
| 765 | $lp = new CLp(); |
||
| 766 | $lp |
||
| 767 | ->setLpType($type) |
||
| 768 | ->setName($name) |
||
| 769 | ->setDescription($description) |
||
| 770 | ->setDisplayOrder($dsp) |
||
| 771 | ->setCategory($category) |
||
| 772 | ->setPublicatedOn($publicated_on) |
||
| 773 | ->setExpiredOn($expired_on) |
||
| 774 | ->setParent($courseEntity) |
||
| 775 | ->addCourseLink($courseEntity, $sessionEntity) |
||
| 776 | ; |
||
| 777 | |||
| 778 | $em = Database::getManager(); |
||
| 779 | $em->persist($lp); |
||
| 780 | $em->flush(); |
||
| 781 | |||
| 782 | // Insert into item_property. |
||
| 783 | /*api_item_property_update( |
||
| 784 | $courseInfo, |
||
| 785 | TOOL_LEARNPATH, |
||
| 786 | $id, |
||
| 787 | 'LearnpathAdded', |
||
| 788 | $userId |
||
| 789 | ); |
||
| 790 | api_set_default_visibility( |
||
| 791 | $id, |
||
| 792 | TOOL_LEARNPATH, |
||
| 793 | 0, |
||
| 794 | $courseInfo, |
||
| 795 | $session_id, |
||
| 796 | $userId |
||
| 797 | );*/ |
||
| 798 | |||
| 799 | break; |
||
| 800 | } |
||
| 801 | |||
| 802 | return $lp; |
||
| 803 | } |
||
| 804 | |||
| 805 | /** |
||
| 806 | * Auto completes the parents of an item in case it's been completed or passed. |
||
| 807 | * |
||
| 808 | * @param int $item Optional ID of the item from which to look for parents |
||
| 809 | */ |
||
| 810 | public function autocomplete_parents($item) |
||
| 811 | { |
||
| 812 | $debug = $this->debug; |
||
| 813 | |||
| 814 | if (empty($item)) { |
||
| 815 | $item = $this->current; |
||
| 816 | } |
||
| 817 | |||
| 818 | $currentItem = $this->getItem($item); |
||
| 819 | if ($currentItem) { |
||
| 820 | $parent_id = $currentItem->get_parent(); |
||
| 821 | $parent = $this->getItem($parent_id); |
||
| 822 | if ($parent) { |
||
| 823 | // if $item points to an object and there is a parent. |
||
| 824 | if ($debug) { |
||
| 825 | error_log( |
||
| 826 | 'Autocompleting parent of item '.$item.' '. |
||
| 827 | $currentItem->get_title().'" (item '.$parent_id.' "'.$parent->get_title().'") ', |
||
| 828 | 0 |
||
| 829 | ); |
||
| 830 | } |
||
| 831 | |||
| 832 | // New experiment including failed and browsed in completed status. |
||
| 833 | //$current_status = $currentItem->get_status(); |
||
| 834 | //if ($currentItem->is_done() || $current_status == 'browsed' || $current_status == 'failed') { |
||
| 835 | // Fixes chapter auto complete |
||
| 836 | if (true) { |
||
| 837 | // If the current item is completed or passes or succeeded. |
||
| 838 | $updateParentStatus = true; |
||
| 839 | if ($debug) { |
||
| 840 | error_log('Status of current item is alright'); |
||
| 841 | } |
||
| 842 | |||
| 843 | foreach ($parent->get_children() as $childItemId) { |
||
| 844 | $childItem = $this->getItem($childItemId); |
||
| 845 | |||
| 846 | // If children was not set try to get the info |
||
| 847 | if (empty($childItem->db_item_view_id)) { |
||
| 848 | $childItem->set_lp_view($this->lp_view_id, $this->course_int_id); |
||
| 849 | } |
||
| 850 | |||
| 851 | // Check all his brothers (parent's children) for completion status. |
||
| 852 | if ($childItemId != $item) { |
||
| 853 | if ($debug) { |
||
| 854 | error_log( |
||
| 855 | 'Looking at brother #'.$childItemId.' "'.$childItem->get_title().'", status is '.$childItem->get_status(), |
||
| 856 | 0 |
||
| 857 | ); |
||
| 858 | } |
||
| 859 | // Trying completing parents of failed and browsed items as well. |
||
| 860 | if ($childItem->status_is( |
||
| 861 | [ |
||
| 862 | 'completed', |
||
| 863 | 'passed', |
||
| 864 | 'succeeded', |
||
| 865 | 'browsed', |
||
| 866 | 'failed', |
||
| 867 | ] |
||
| 868 | ) |
||
| 869 | ) { |
||
| 870 | // Keep completion status to true. |
||
| 871 | continue; |
||
| 872 | } else { |
||
| 873 | if ($debug > 2) { |
||
| 874 | error_log( |
||
| 875 | '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, |
||
| 876 | 0 |
||
| 877 | ); |
||
| 878 | } |
||
| 879 | $updateParentStatus = false; |
||
| 880 | break; |
||
| 881 | } |
||
| 882 | } |
||
| 883 | } |
||
| 884 | |||
| 885 | if ($updateParentStatus) { |
||
| 886 | // If all the children were completed: |
||
| 887 | $parent->set_status('completed'); |
||
| 888 | $parent->save(false, $this->prerequisites_match($parent->get_id())); |
||
| 889 | // Force the status to "completed" |
||
| 890 | //$this->update_queue[$parent->get_id()] = $parent->get_status(); |
||
| 891 | $this->update_queue[$parent->get_id()] = 'completed'; |
||
| 892 | if ($debug) { |
||
| 893 | error_log( |
||
| 894 | 'Added parent #'.$parent->get_id().' "'.$parent->get_title().'" to update queue status: completed '. |
||
| 895 | print_r($this->update_queue, 1), |
||
| 896 | 0 |
||
| 897 | ); |
||
| 898 | } |
||
| 899 | // Recursive call. |
||
| 900 | $this->autocomplete_parents($parent->get_id()); |
||
| 901 | } |
||
| 902 | } |
||
| 903 | } else { |
||
| 904 | if ($debug) { |
||
| 905 | error_log("Parent #$parent_id does not exists"); |
||
| 906 | } |
||
| 907 | } |
||
| 908 | } else { |
||
| 909 | if ($debug) { |
||
| 910 | error_log("#$item is an item that doesn't have parents"); |
||
| 911 | } |
||
| 912 | } |
||
| 913 | } |
||
| 914 | |||
| 915 | /** |
||
| 916 | * Closes the current resource. |
||
| 917 | * |
||
| 918 | * Stops the timer |
||
| 919 | * Saves into the database if required |
||
| 920 | * Clears the current resource data from this object |
||
| 921 | * |
||
| 922 | * @return bool True on success, false on failure |
||
| 923 | */ |
||
| 924 | public function close() |
||
| 925 | { |
||
| 926 | if (empty($this->lp_id)) { |
||
| 927 | $this->error = 'Trying to close this learnpath but no ID is set'; |
||
| 928 | |||
| 929 | return false; |
||
| 930 | } |
||
| 931 | $this->current_time_stop = time(); |
||
| 932 | $this->ordered_items = []; |
||
| 933 | $this->index = 0; |
||
| 934 | unset($this->lp_id); |
||
| 935 | //unset other stuff |
||
| 936 | return true; |
||
| 937 | } |
||
| 938 | |||
| 939 | /** |
||
| 940 | * Static admin function allowing removal of a learnpath. |
||
| 941 | * |
||
| 942 | * @param array $courseInfo |
||
| 943 | * @param int $id Learnpath ID |
||
| 944 | * @param string $delete Whether to delete data or keep it (default: 'keep', others: 'remove') |
||
| 945 | * |
||
| 946 | * @return bool True on success, false on failure (might change that to return number of elements deleted) |
||
| 947 | */ |
||
| 948 | public function delete($courseInfo = null, $id = null, $delete = 'keep') |
||
| 949 | { |
||
| 950 | $course_id = api_get_course_int_id(); |
||
| 951 | if (!empty($courseInfo)) { |
||
| 952 | $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id; |
||
| 953 | } |
||
| 954 | |||
| 955 | // TODO: Implement a way of getting this to work when the current object is not set. |
||
| 956 | // In clear: implement this in the item class as well (abstract class) and use the given ID in queries. |
||
| 957 | // If an ID is specifically given and the current LP is not the same, prevent delete. |
||
| 958 | if (!empty($id) && ($id != $this->lp_id)) { |
||
| 959 | return false; |
||
| 960 | } |
||
| 961 | |||
| 962 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 963 | $lp_view = Database::get_course_table(TABLE_LP_VIEW); |
||
| 964 | $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
| 965 | |||
| 966 | // Delete lp item id. |
||
| 967 | foreach ($this->items as $lpItemId => $dummy) { |
||
| 968 | $sql = "DELETE FROM $lp_item_view |
||
| 969 | WHERE lp_item_id = '".$lpItemId."'"; |
||
| 970 | Database::query($sql); |
||
| 971 | } |
||
| 972 | |||
| 973 | // Proposed by Christophe (nickname: clefevre) |
||
| 974 | $sql = "DELETE FROM $lp_item |
||
| 975 | WHERE lp_id = ".$this->lp_id; |
||
| 976 | Database::query($sql); |
||
| 977 | |||
| 978 | $sql = "DELETE FROM $lp_view |
||
| 979 | WHERE lp_id = ".$this->lp_id; |
||
| 980 | Database::query($sql); |
||
| 981 | |||
| 982 | //self::toggleVisibility($this->lp_id, 0); |
||
| 983 | |||
| 984 | /*if (2 == $this->type || 3 == $this->type) { |
||
| 985 | // This is a scorm learning path, delete the files as well. |
||
| 986 | $sql = "SELECT path FROM $lp |
||
| 987 | WHERE iid = ".$this->lp_id; |
||
| 988 | $res = Database::query($sql); |
||
| 989 | if (Database::num_rows($res) > 0) { |
||
| 990 | $row = Database::fetch_array($res); |
||
| 991 | $path = $row['path']; |
||
| 992 | $sql = "SELECT iid FROM $lp |
||
| 993 | WHERE |
||
| 994 | c_id = $course_id AND |
||
| 995 | path = '$path' AND |
||
| 996 | iid != ".$this->lp_id; |
||
| 997 | $res = Database::query($sql); |
||
| 998 | if (Database::num_rows($res) > 0) { |
||
| 999 | // Another learning path uses this directory, so don't delete it. |
||
| 1000 | if ($this->debug > 2) { |
||
| 1001 | error_log('In learnpath::delete(), found other LP using path '.$path.', keeping directory', 0); |
||
| 1002 | } |
||
| 1003 | } else { |
||
| 1004 | // No other LP uses that directory, delete it. |
||
| 1005 | $course_rel_dir = api_get_course_path().'/scorm/'; // scorm dir web path starting from /courses |
||
| 1006 | // The absolute system path for this course. |
||
| 1007 | $course_scorm_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; |
||
| 1008 | if ('remove' == $delete && is_dir($course_scorm_dir.$path) && !empty($course_scorm_dir)) { |
||
| 1009 | if ($this->debug > 2) { |
||
| 1010 | error_log('In learnpath::delete(), found SCORM, deleting directory: '.$course_scorm_dir.$path, 0); |
||
| 1011 | } |
||
| 1012 | // Proposed by Christophe (clefevre). |
||
| 1013 | if (0 == strcmp(substr($path, -2), "/.")) { |
||
| 1014 | $path = substr($path, 0, -1); // Remove "." at the end. |
||
| 1015 | } |
||
| 1016 | //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way. |
||
| 1017 | rmdirr($course_scorm_dir.$path); |
||
| 1018 | } |
||
| 1019 | } |
||
| 1020 | } |
||
| 1021 | }*/ |
||
| 1022 | |||
| 1023 | $table = Database::get_course_table(TABLE_LP_REL_USERGROUP); |
||
| 1024 | $sql = "DELETE FROM $table |
||
| 1025 | WHERE |
||
| 1026 | lp_id = {$this->lp_id}"; |
||
| 1027 | Database::query($sql); |
||
| 1028 | |||
| 1029 | /*$tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
| 1030 | $link = 'lp/lp_controller.php?action=view&lp_id='.$this->lp_id; |
||
| 1031 | // Delete tools |
||
| 1032 | $sql = "DELETE FROM $tbl_tool |
||
| 1033 | WHERE c_id = $course_id AND (link LIKE '$link%' AND image='scormbuilder.gif')"; |
||
| 1034 | Database::query($sql);*/ |
||
| 1035 | |||
| 1036 | $repo = Container::getLpRepository(); |
||
| 1037 | $lp = $repo->find($this->lp_id); |
||
| 1038 | Database::getManager()->remove($lp); |
||
| 1039 | Database::getManager()->flush(); |
||
| 1040 | |||
| 1041 | // Updates the display order of all lps. |
||
| 1042 | $this->update_display_order(); |
||
| 1043 | |||
| 1044 | $link_info = GradebookUtils::isResourceInCourseGradebook( |
||
| 1045 | api_get_course_id(), |
||
| 1046 | 4, |
||
| 1047 | $id, |
||
| 1048 | api_get_session_id() |
||
| 1049 | ); |
||
| 1050 | |||
| 1051 | if (false !== $link_info) { |
||
| 1052 | GradebookUtils::remove_resource_from_course_gradebook($link_info['id']); |
||
| 1053 | } |
||
| 1054 | |||
| 1055 | if ('true' === api_get_setting('search_enabled')) { |
||
| 1056 | delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id); |
||
| 1057 | } |
||
| 1058 | } |
||
| 1059 | |||
| 1060 | /** |
||
| 1061 | * Removes all the children of one item - dangerous! |
||
| 1062 | * |
||
| 1063 | * @param int $id Element ID of which children have to be removed |
||
| 1064 | * |
||
| 1065 | * @return int Total number of children removed |
||
| 1066 | */ |
||
| 1067 | public function delete_children_items($id) |
||
| 1068 | { |
||
| 1069 | $course_id = $this->course_info['real_id']; |
||
| 1070 | |||
| 1071 | $num = 0; |
||
| 1072 | $id = (int) $id; |
||
| 1073 | if (empty($id) || empty($course_id)) { |
||
| 1074 | return false; |
||
| 1075 | } |
||
| 1076 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 1077 | $sql = "SELECT * FROM $lp_item |
||
| 1078 | WHERE parent_item_id = $id"; |
||
| 1079 | $res = Database::query($sql); |
||
| 1080 | while ($row = Database::fetch_array($res)) { |
||
| 1081 | $num += $this->delete_children_items($row['iid']); |
||
| 1082 | $sql = "DELETE FROM $lp_item |
||
| 1083 | WHERE iid = ".$row['iid']; |
||
| 1084 | Database::query($sql); |
||
| 1085 | $num++; |
||
| 1086 | } |
||
| 1087 | |||
| 1088 | return $num; |
||
| 1089 | } |
||
| 1090 | |||
| 1091 | /** |
||
| 1092 | * Removes an item from the current learnpath. |
||
| 1093 | * |
||
| 1094 | * @param int $id Elem ID (0 if first) |
||
| 1095 | * |
||
| 1096 | * @return int Number of elements moved |
||
| 1097 | * |
||
| 1098 | * @todo implement resource removal |
||
| 1099 | */ |
||
| 1100 | public function delete_item($id) |
||
| 1101 | { |
||
| 1102 | $course_id = api_get_course_int_id(); |
||
| 1103 | $id = (int) $id; |
||
| 1104 | // TODO: Implement the resource removal. |
||
| 1105 | if (empty($id) || empty($course_id)) { |
||
| 1106 | return false; |
||
| 1107 | } |
||
| 1108 | // First select item to get previous, next, and display order. |
||
| 1109 | $lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 1110 | $sql_sel = "SELECT * FROM $lp_item WHERE iid = $id"; |
||
| 1111 | $res_sel = Database::query($sql_sel); |
||
| 1112 | if (Database::num_rows($res_sel) < 1) { |
||
| 1113 | return false; |
||
| 1114 | } |
||
| 1115 | $row = Database::fetch_array($res_sel); |
||
| 1116 | $previous = $row['previous_item_id']; |
||
| 1117 | $next = $row['next_item_id']; |
||
| 1118 | $display = $row['display_order']; |
||
| 1119 | $parent = $row['parent_item_id']; |
||
| 1120 | $lp = $row['lp_id']; |
||
| 1121 | // Delete children items. |
||
| 1122 | $this->delete_children_items($id); |
||
| 1123 | // Now delete the item. |
||
| 1124 | $sql_del = "DELETE FROM $lp_item WHERE iid = $id"; |
||
| 1125 | Database::query($sql_del); |
||
| 1126 | // Now update surrounding items. |
||
| 1127 | $sql_upd = "UPDATE $lp_item SET next_item_id = $next |
||
| 1128 | WHERE iid = $previous"; |
||
| 1129 | Database::query($sql_upd); |
||
| 1130 | $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous |
||
| 1131 | WHERE iid = $next AND item_type != '".TOOL_LP_FINAL_ITEM."'"; |
||
| 1132 | Database::query($sql_upd); |
||
| 1133 | |||
| 1134 | $parentCondition = ' AND parent_item_id IS NULL '; |
||
| 1135 | if (!empty($parent)) { |
||
| 1136 | $parent = (int) $parent; |
||
| 1137 | $parentCondition = " AND parent_item_id = $parent "; |
||
| 1138 | } |
||
| 1139 | |||
| 1140 | // Now update all following items with new display order. |
||
| 1141 | $sql_all = "UPDATE $lp_item SET display_order = display_order-1 |
||
| 1142 | WHERE |
||
| 1143 | lp_id = $lp AND |
||
| 1144 | display_order > $display |
||
| 1145 | $parentCondition |
||
| 1146 | "; |
||
| 1147 | Database::query($sql_all); |
||
| 1148 | |||
| 1149 | //Removing prerequisites since the item will not longer exist |
||
| 1150 | $sql_all = "UPDATE $lp_item SET prerequisite = '' |
||
| 1151 | WHERE prerequisite = '$id'"; |
||
| 1152 | Database::query($sql_all); |
||
| 1153 | |||
| 1154 | $sql = "UPDATE $lp_item |
||
| 1155 | SET previous_item_id = ".$this->getLastInFirstLevel()." |
||
| 1156 | WHERE lp_id = {$this->lp_id} AND item_type = '".TOOL_LP_FINAL_ITEM."'"; |
||
| 1157 | Database::query($sql); |
||
| 1158 | |||
| 1159 | // Remove from search engine if enabled. |
||
| 1160 | if ('true' === api_get_setting('search_enabled')) { |
||
| 1161 | $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
||
| 1162 | $sql = 'SELECT * FROM %s |
||
| 1163 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
| 1164 | LIMIT 1'; |
||
| 1165 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id); |
||
| 1166 | $res = Database::query($sql); |
||
| 1167 | if (Database::num_rows($res) > 0) { |
||
| 1168 | $row2 = Database::fetch_array($res); |
||
| 1169 | $di = new ChamiloIndexer(); |
||
| 1170 | $di->remove_document($row2['search_did']); |
||
| 1171 | } |
||
| 1172 | $sql = 'DELETE FROM %s |
||
| 1173 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
| 1174 | LIMIT 1'; |
||
| 1175 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id); |
||
| 1176 | Database::query($sql); |
||
| 1177 | } |
||
| 1178 | } |
||
| 1179 | |||
| 1180 | /** |
||
| 1181 | * Updates an item's content in place. |
||
| 1182 | * |
||
| 1183 | * @param int $id Element ID |
||
| 1184 | * @param int $parent Parent item ID |
||
| 1185 | * @param int $previous Previous item ID |
||
| 1186 | * @param string $title Item title |
||
| 1187 | * @param string $description Item description |
||
| 1188 | * @param string $prerequisites Prerequisites (optional) |
||
| 1189 | * @param array $audio The array resulting of the $_FILES[mp3] element |
||
| 1190 | * @param int $max_time_allowed |
||
| 1191 | * @param string $url |
||
| 1192 | * |
||
| 1193 | * @return bool True on success, false on error |
||
| 1194 | */ |
||
| 1195 | public function edit_item( |
||
| 1196 | $id, |
||
| 1197 | $parent, |
||
| 1198 | $previous, |
||
| 1199 | $title, |
||
| 1200 | $description, |
||
| 1201 | $prerequisites = '0', |
||
| 1202 | $audio = [], |
||
| 1203 | $max_time_allowed = 0, |
||
| 1204 | $url = '' |
||
| 1205 | ) { |
||
| 1206 | $course_id = api_get_course_int_id(); |
||
| 1207 | $_course = api_get_course_info(); |
||
| 1208 | $id = (int) $id; |
||
| 1209 | |||
| 1210 | if (empty($max_time_allowed)) { |
||
| 1211 | $max_time_allowed = 0; |
||
| 1212 | } |
||
| 1213 | |||
| 1214 | if (empty($id) || empty($_course)) { |
||
| 1215 | return false; |
||
| 1216 | } |
||
| 1217 | |||
| 1218 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 1219 | $sql = "SELECT * FROM $tbl_lp_item |
||
| 1220 | WHERE iid = $id"; |
||
| 1221 | $res_select = Database::query($sql); |
||
| 1222 | $row_select = Database::fetch_array($res_select); |
||
| 1223 | $audio_update_sql = ''; |
||
| 1224 | if (is_array($audio) && !empty($audio['tmp_name']) && 0 === $audio['error']) { |
||
| 1225 | // Create the audio folder if it does not exist yet. |
||
| 1226 | $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||
| 1227 | if (!is_dir($filepath.'audio')) { |
||
| 1228 | mkdir($filepath.'audio', api_get_permissions_for_new_directories()); |
||
| 1229 | $audio_id = DocumentManager::addDocument( |
||
| 1230 | $_course, |
||
| 1231 | '/audio', |
||
| 1232 | 'folder', |
||
| 1233 | 0, |
||
| 1234 | 'audio' |
||
| 1235 | ); |
||
| 1236 | } |
||
| 1237 | |||
| 1238 | // Upload file in documents. |
||
| 1239 | $pi = pathinfo($audio['name']); |
||
| 1240 | if ('mp3' === $pi['extension']) { |
||
| 1241 | $c_det = api_get_course_info($this->cc); |
||
| 1242 | $bp = api_get_path(SYS_COURSE_PATH).$c_det['path'].'/document'; |
||
| 1243 | $path = handle_uploaded_document( |
||
| 1244 | $c_det, |
||
| 1245 | $audio, |
||
| 1246 | $bp, |
||
| 1247 | '/audio', |
||
| 1248 | api_get_user_id(), |
||
| 1249 | 0, |
||
| 1250 | null, |
||
| 1251 | 0, |
||
| 1252 | 'rename', |
||
| 1253 | false, |
||
| 1254 | 0 |
||
| 1255 | ); |
||
| 1256 | $path = substr($path, 7); |
||
| 1257 | // Update reference in lp_item - audio path is the path from inside de document/audio/ dir. |
||
| 1258 | $audio_update_sql = ", audio = '".Database::escape_string($path)."' "; |
||
| 1259 | } |
||
| 1260 | } |
||
| 1261 | |||
| 1262 | $same_parent = $row_select['parent_item_id'] == $parent ? true : false; |
||
| 1263 | $same_previous = $row_select['previous_item_id'] == $previous ? true : false; |
||
| 1264 | |||
| 1265 | // TODO: htmlspecialchars to be checked for encoding related problems. |
||
| 1266 | if ($same_parent && $same_previous) { |
||
| 1267 | // Only update title and description. |
||
| 1268 | $sql = "UPDATE $tbl_lp_item |
||
| 1269 | SET title = '".Database::escape_string($title)."', |
||
| 1270 | prerequisite = '".$prerequisites."', |
||
| 1271 | description = '".Database::escape_string($description)."' |
||
| 1272 | ".$audio_update_sql.", |
||
| 1273 | max_time_allowed = '".Database::escape_string($max_time_allowed)."' |
||
| 1274 | WHERE iid = $id"; |
||
| 1275 | Database::query($sql); |
||
| 1276 | } else { |
||
| 1277 | $old_parent = $row_select['parent_item_id']; |
||
| 1278 | $old_previous = $row_select['previous_item_id']; |
||
| 1279 | $old_next = $row_select['next_item_id']; |
||
| 1280 | $old_order = $row_select['display_order']; |
||
| 1281 | $old_prerequisite = $row_select['prerequisite']; |
||
| 1282 | $old_max_time_allowed = $row_select['max_time_allowed']; |
||
| 1283 | |||
| 1284 | /* BEGIN -- virtually remove the current item id */ |
||
| 1285 | /* for the next and previous item it is like the current item doesn't exist anymore */ |
||
| 1286 | if (0 != $old_previous) { |
||
| 1287 | // Next |
||
| 1288 | $sql = "UPDATE $tbl_lp_item |
||
| 1289 | SET next_item_id = $old_next |
||
| 1290 | WHERE iid = $old_previous"; |
||
| 1291 | Database::query($sql); |
||
| 1292 | } |
||
| 1293 | |||
| 1294 | if (!empty($old_next)) { |
||
| 1295 | // Previous |
||
| 1296 | $sql = "UPDATE $tbl_lp_item |
||
| 1297 | SET previous_item_id = $old_previous |
||
| 1298 | WHERE iid = $old_next"; |
||
| 1299 | Database::query($sql); |
||
| 1300 | } |
||
| 1301 | |||
| 1302 | // display_order - 1 for every item with a display_order |
||
| 1303 | // bigger then the display_order of the current item. |
||
| 1304 | $sql = "UPDATE $tbl_lp_item |
||
| 1305 | SET display_order = display_order - 1 |
||
| 1306 | WHERE |
||
| 1307 | c_id = $course_id AND |
||
| 1308 | display_order > $old_order AND |
||
| 1309 | lp_id = ".$this->lp_id." AND |
||
| 1310 | parent_item_id = $old_parent"; |
||
| 1311 | Database::query($sql); |
||
| 1312 | /* END -- virtually remove the current item id */ |
||
| 1313 | |||
| 1314 | /* BEGIN -- update the current item id to his new location */ |
||
| 1315 | if (0 == $previous) { |
||
| 1316 | // Select the data of the item that should come after the current item. |
||
| 1317 | $sql = "SELECT id, display_order |
||
| 1318 | FROM $tbl_lp_item |
||
| 1319 | WHERE |
||
| 1320 | c_id = $course_id AND |
||
| 1321 | lp_id = ".$this->lp_id." AND |
||
| 1322 | parent_item_id = $parent AND |
||
| 1323 | previous_item_id = $previous"; |
||
| 1324 | $res_select_old = Database::query($sql); |
||
| 1325 | $row_select_old = Database::fetch_array($res_select_old); |
||
| 1326 | |||
| 1327 | // If the new parent didn't have children before. |
||
| 1328 | if (0 == Database::num_rows($res_select_old)) { |
||
| 1329 | $new_next = 0; |
||
| 1330 | $new_order = 1; |
||
| 1331 | } else { |
||
| 1332 | $new_next = $row_select_old['id']; |
||
| 1333 | $new_order = $row_select_old['display_order']; |
||
| 1334 | } |
||
| 1335 | } else { |
||
| 1336 | // Select the data of the item that should come before the current item. |
||
| 1337 | $sql = "SELECT next_item_id, display_order |
||
| 1338 | FROM $tbl_lp_item |
||
| 1339 | WHERE iid = $previous"; |
||
| 1340 | $res_select_old = Database::query($sql); |
||
| 1341 | $row_select_old = Database::fetch_array($res_select_old); |
||
| 1342 | $new_next = $row_select_old['next_item_id']; |
||
| 1343 | $new_order = $row_select_old['display_order'] + 1; |
||
| 1344 | } |
||
| 1345 | |||
| 1346 | // TODO: htmlspecialchars to be checked for encoding related problems. |
||
| 1347 | // Update the current item with the new data. |
||
| 1348 | $sql = "UPDATE $tbl_lp_item |
||
| 1349 | SET |
||
| 1350 | title = '".Database::escape_string($title)."', |
||
| 1351 | description = '".Database::escape_string($description)."', |
||
| 1352 | parent_item_id = $parent, |
||
| 1353 | previous_item_id = $previous, |
||
| 1354 | next_item_id = $new_next, |
||
| 1355 | display_order = $new_order |
||
| 1356 | $audio_update_sql |
||
| 1357 | WHERE iid = $id"; |
||
| 1358 | Database::query($sql); |
||
| 1359 | |||
| 1360 | if (0 != $previous) { |
||
| 1361 | // Update the previous item's next_item_id. |
||
| 1362 | $sql = "UPDATE $tbl_lp_item |
||
| 1363 | SET next_item_id = $id |
||
| 1364 | WHERE iid = $previous"; |
||
| 1365 | Database::query($sql); |
||
| 1366 | } |
||
| 1367 | |||
| 1368 | if (!empty($new_next)) { |
||
| 1369 | // Update the next item's previous_item_id. |
||
| 1370 | $sql = "UPDATE $tbl_lp_item |
||
| 1371 | SET previous_item_id = $id |
||
| 1372 | WHERE iid = $new_next"; |
||
| 1373 | Database::query($sql); |
||
| 1374 | } |
||
| 1375 | |||
| 1376 | if ($old_prerequisite != $prerequisites) { |
||
| 1377 | $sql = "UPDATE $tbl_lp_item |
||
| 1378 | SET prerequisite = '$prerequisites' |
||
| 1379 | WHERE iid = $id"; |
||
| 1380 | Database::query($sql); |
||
| 1381 | } |
||
| 1382 | |||
| 1383 | if ($old_max_time_allowed != $max_time_allowed) { |
||
| 1384 | // update max time allowed |
||
| 1385 | $sql = "UPDATE $tbl_lp_item |
||
| 1386 | SET max_time_allowed = $max_time_allowed |
||
| 1387 | WHERE iid = $id"; |
||
| 1388 | Database::query($sql); |
||
| 1389 | } |
||
| 1390 | |||
| 1391 | // Update all the items with the same or a bigger display_order than the current item. |
||
| 1392 | $sql = "UPDATE $tbl_lp_item |
||
| 1393 | SET display_order = display_order + 1 |
||
| 1394 | WHERE |
||
| 1395 | c_id = $course_id AND |
||
| 1396 | lp_id = ".$this->get_id()." AND |
||
| 1397 | iid <> $id AND |
||
| 1398 | parent_item_id = $parent AND |
||
| 1399 | display_order >= $new_order"; |
||
| 1400 | Database::query($sql); |
||
| 1401 | } |
||
| 1402 | |||
| 1403 | if ('link' == $row_select['item_type']) { |
||
| 1404 | $link = new Link(); |
||
| 1405 | $linkId = $row_select['path']; |
||
| 1406 | $link->updateLink($linkId, $url); |
||
| 1407 | } |
||
| 1408 | } |
||
| 1409 | |||
| 1410 | /** |
||
| 1411 | * Updates an item's prereq in place. |
||
| 1412 | * |
||
| 1413 | * @param int $id Element ID |
||
| 1414 | * @param string $prerequisite_id Prerequisite Element ID |
||
| 1415 | * @param int $minScore Prerequisite min score |
||
| 1416 | * @param int $maxScore Prerequisite max score |
||
| 1417 | * |
||
| 1418 | * @return bool True on success, false on error |
||
| 1419 | */ |
||
| 1420 | public function edit_item_prereq( |
||
| 1421 | $id, |
||
| 1422 | $prerequisite_id, |
||
| 1423 | $minScore = 0, |
||
| 1424 | $maxScore = 100 |
||
| 1425 | ) { |
||
| 1426 | $id = (int) $id; |
||
| 1427 | |||
| 1428 | if (empty($id)) { |
||
| 1429 | return false; |
||
| 1430 | } |
||
| 1431 | $prerequisite_id = (int) $prerequisite_id; |
||
| 1432 | |||
| 1433 | if (empty($minScore) || $minScore < 0) { |
||
| 1434 | $minScore = 0; |
||
| 1435 | } |
||
| 1436 | |||
| 1437 | if (empty($maxScore) || $maxScore < 0) { |
||
| 1438 | $maxScore = 100; |
||
| 1439 | } |
||
| 1440 | |||
| 1441 | $minScore = (float) $minScore; |
||
| 1442 | $maxScore = (float) $maxScore; |
||
| 1443 | |||
| 1444 | if (empty($prerequisite_id)) { |
||
| 1445 | $prerequisite_id = 'NULL'; |
||
| 1446 | $minScore = 0; |
||
| 1447 | $maxScore = 100; |
||
| 1448 | } |
||
| 1449 | |||
| 1450 | $table = Database::get_course_table(TABLE_LP_ITEM); |
||
| 1451 | $sql = " UPDATE $table |
||
| 1452 | SET |
||
| 1453 | prerequisite = $prerequisite_id , |
||
| 1454 | prerequisite_min_score = $minScore , |
||
| 1455 | prerequisite_max_score = $maxScore |
||
| 1456 | WHERE iid = $id"; |
||
| 1457 | Database::query($sql); |
||
| 1458 | |||
| 1459 | return true; |
||
| 1460 | } |
||
| 1461 | |||
| 1462 | /** |
||
| 1463 | * Get the specific prefix index terms of this learning path. |
||
| 1464 | * |
||
| 1465 | * @param string $prefix |
||
| 1466 | * |
||
| 1467 | * @return array Array of terms |
||
| 1468 | */ |
||
| 1469 | public function get_common_index_terms_by_prefix($prefix) |
||
| 1470 | { |
||
| 1471 | $terms = get_specific_field_values_list_by_prefix( |
||
| 1472 | $prefix, |
||
| 1473 | $this->cc, |
||
| 1474 | TOOL_LEARNPATH, |
||
| 1475 | $this->lp_id |
||
| 1476 | ); |
||
| 1477 | $prefix_terms = []; |
||
| 1478 | if (!empty($terms)) { |
||
| 1479 | foreach ($terms as $term) { |
||
| 1480 | $prefix_terms[] = $term['value']; |
||
| 1481 | } |
||
| 1482 | } |
||
| 1483 | |||
| 1484 | return $prefix_terms; |
||
| 1485 | } |
||
| 1486 | |||
| 1487 | /** |
||
| 1488 | * Gets the number of items currently completed. |
||
| 1489 | * |
||
| 1490 | * @param bool $failedStatusException flag to determine the failed status is not considered progressed |
||
| 1491 | * |
||
| 1492 | * @return int The number of items currently completed |
||
| 1493 | */ |
||
| 1494 | public function get_complete_items_count($failedStatusException = false) |
||
| 1495 | { |
||
| 1496 | $i = 0; |
||
| 1497 | $completedStatusList = [ |
||
| 1498 | 'completed', |
||
| 1499 | 'passed', |
||
| 1500 | 'succeeded', |
||
| 1501 | 'browsed', |
||
| 1502 | ]; |
||
| 1503 | |||
| 1504 | if (!$failedStatusException) { |
||
| 1505 | $completedStatusList[] = 'failed'; |
||
| 1506 | } |
||
| 1507 | |||
| 1508 | foreach ($this->items as $id => $dummy) { |
||
| 1509 | // Trying failed and browsed considered "progressed" as well. |
||
| 1510 | if ($this->items[$id]->status_is($completedStatusList) && |
||
| 1511 | 'dir' != $this->items[$id]->get_type() |
||
| 1512 | ) { |
||
| 1513 | $i++; |
||
| 1514 | } |
||
| 1515 | } |
||
| 1516 | |||
| 1517 | return $i; |
||
| 1518 | } |
||
| 1519 | |||
| 1520 | /** |
||
| 1521 | * Gets the current item ID. |
||
| 1522 | * |
||
| 1523 | * @return int The current learnpath item id |
||
| 1524 | */ |
||
| 1525 | public function get_current_item_id() |
||
| 1526 | { |
||
| 1527 | $current = 0; |
||
| 1528 | if (!empty($this->current)) { |
||
| 1529 | $current = (int) $this->current; |
||
| 1530 | } |
||
| 1531 | |||
| 1532 | return $current; |
||
| 1533 | } |
||
| 1534 | |||
| 1535 | /** |
||
| 1536 | * Force to get the first learnpath item id. |
||
| 1537 | * |
||
| 1538 | * @return int The current learnpath item id |
||
| 1539 | */ |
||
| 1540 | public function get_first_item_id() |
||
| 1541 | { |
||
| 1542 | $current = 0; |
||
| 1543 | if (is_array($this->ordered_items)) { |
||
| 1544 | $current = $this->ordered_items[0]; |
||
| 1545 | } |
||
| 1546 | |||
| 1547 | return $current; |
||
| 1548 | } |
||
| 1549 | |||
| 1550 | /** |
||
| 1551 | * Gets the total number of items available for viewing in this SCORM. |
||
| 1552 | * |
||
| 1553 | * @return int The total number of items |
||
| 1554 | */ |
||
| 1555 | public function get_total_items_count() |
||
| 1556 | { |
||
| 1557 | return count($this->items); |
||
| 1558 | } |
||
| 1559 | |||
| 1560 | /** |
||
| 1561 | * Gets the total number of items available for viewing in this SCORM but without chapters. |
||
| 1562 | * |
||
| 1563 | * @return int The total no-chapters number of items |
||
| 1564 | */ |
||
| 1565 | public function getTotalItemsCountWithoutDirs() |
||
| 1566 | { |
||
| 1567 | $total = 0; |
||
| 1568 | $typeListNotToCount = self::getChapterTypes(); |
||
| 1569 | foreach ($this->items as $temp2) { |
||
| 1570 | if (!in_array($temp2->get_type(), $typeListNotToCount)) { |
||
| 1571 | $total++; |
||
| 1572 | } |
||
| 1573 | } |
||
| 1574 | |||
| 1575 | return $total; |
||
| 1576 | } |
||
| 1577 | |||
| 1578 | /** |
||
| 1579 | * Sets the first element URL. |
||
| 1580 | */ |
||
| 1581 | public function first() |
||
| 1582 | { |
||
| 1583 | if ($this->debug > 0) { |
||
| 1584 | error_log('In learnpath::first()', 0); |
||
| 1585 | error_log('$this->last_item_seen '.$this->last_item_seen); |
||
| 1586 | } |
||
| 1587 | |||
| 1588 | // Test if the last_item_seen exists and is not a dir. |
||
| 1589 | if (0 == count($this->ordered_items)) { |
||
| 1590 | $this->index = 0; |
||
| 1591 | } |
||
| 1592 | |||
| 1593 | if (!empty($this->last_item_seen) && |
||
| 1594 | !empty($this->items[$this->last_item_seen]) && |
||
| 1595 | 'dir' != $this->items[$this->last_item_seen]->get_type() |
||
| 1596 | //with this change (below) the LP will NOT go to the next item, it will take lp item we left |
||
| 1597 | //&& !$this->items[$this->last_item_seen]->is_done() |
||
| 1598 | ) { |
||
| 1599 | if ($this->debug > 2) { |
||
| 1600 | error_log( |
||
| 1601 | 'In learnpath::first() - Last item seen is '.$this->last_item_seen.' of type '. |
||
| 1602 | $this->items[$this->last_item_seen]->get_type() |
||
| 1603 | ); |
||
| 1604 | } |
||
| 1605 | $index = -1; |
||
| 1606 | foreach ($this->ordered_items as $myindex => $item_id) { |
||
| 1607 | if ($item_id == $this->last_item_seen) { |
||
| 1608 | $index = $myindex; |
||
| 1609 | break; |
||
| 1610 | } |
||
| 1611 | } |
||
| 1612 | if (-1 == $index) { |
||
| 1613 | // Index hasn't changed, so item not found - panic (this shouldn't happen). |
||
| 1614 | if ($this->debug > 2) { |
||
| 1615 | error_log('Last item ('.$this->last_item_seen.') was found in items but not in ordered_items, panic!', 0); |
||
| 1616 | } |
||
| 1617 | |||
| 1618 | return false; |
||
| 1619 | } else { |
||
| 1620 | $this->last = $this->last_item_seen; |
||
| 1621 | $this->current = $this->last_item_seen; |
||
| 1622 | $this->index = $index; |
||
| 1623 | } |
||
| 1624 | } else { |
||
| 1625 | if ($this->debug > 2) { |
||
| 1626 | error_log('In learnpath::first() - No last item seen', 0); |
||
| 1627 | } |
||
| 1628 | $index = 0; |
||
| 1629 | // Loop through all ordered items and stop at the first item that is |
||
| 1630 | // not a directory *and* that has not been completed yet. |
||
| 1631 | while (!empty($this->ordered_items[$index]) && |
||
| 1632 | is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') && |
||
| 1633 | ( |
||
| 1634 | 'dir' == $this->items[$this->ordered_items[$index]]->get_type() || |
||
| 1635 | true === $this->items[$this->ordered_items[$index]]->is_done() |
||
| 1636 | ) && $index < $this->max_ordered_items) { |
||
| 1637 | $index++; |
||
| 1638 | } |
||
| 1639 | |||
| 1640 | $this->last = $this->current; |
||
| 1641 | // current is |
||
| 1642 | $this->current = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null; |
||
| 1643 | $this->index = $index; |
||
| 1644 | if ($this->debug > 2) { |
||
| 1645 | error_log('$index '.$index); |
||
| 1646 | error_log('In learnpath::first() - No last item seen'); |
||
| 1647 | error_log('New last = '.$this->last.'('.$this->ordered_items[$index].')'); |
||
| 1648 | } |
||
| 1649 | } |
||
| 1650 | if ($this->debug > 2) { |
||
| 1651 | error_log('In learnpath::first() - First item is '.$this->get_current_item_id()); |
||
| 1652 | } |
||
| 1653 | } |
||
| 1654 | |||
| 1655 | /** |
||
| 1656 | * Gets the js library from the database. |
||
| 1657 | * |
||
| 1658 | * @return string The name of the javascript library to be used |
||
| 1659 | */ |
||
| 1660 | public function get_js_lib() |
||
| 1661 | { |
||
| 1662 | $lib = ''; |
||
| 1663 | if (!empty($this->js_lib)) { |
||
| 1664 | $lib = $this->js_lib; |
||
| 1665 | } |
||
| 1666 | |||
| 1667 | return $lib; |
||
| 1668 | } |
||
| 1669 | |||
| 1670 | /** |
||
| 1671 | * Gets the learnpath database ID. |
||
| 1672 | * |
||
| 1673 | * @return int Learnpath ID in the lp table |
||
| 1674 | */ |
||
| 1675 | public function get_id() |
||
| 1676 | { |
||
| 1677 | if (!empty($this->lp_id)) { |
||
| 1678 | return (int) $this->lp_id; |
||
| 1679 | } |
||
| 1680 | |||
| 1681 | return 0; |
||
| 1682 | } |
||
| 1683 | |||
| 1684 | /** |
||
| 1685 | * Gets the last element URL. |
||
| 1686 | * |
||
| 1687 | * @return string URL to load into the viewer |
||
| 1688 | */ |
||
| 1689 | public function get_last() |
||
| 1690 | { |
||
| 1691 | // This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices" |
||
| 1692 | if (count($this->ordered_items) > 0) { |
||
| 1693 | $this->index = count($this->ordered_items) - 1; |
||
| 1694 | |||
| 1695 | return $this->ordered_items[$this->index]; |
||
| 1696 | } |
||
| 1697 | |||
| 1698 | return false; |
||
| 1699 | } |
||
| 1700 | |||
| 1701 | /** |
||
| 1702 | * Get the last element in the first level. |
||
| 1703 | * Unlike learnpath::get_last this function doesn't consider the subsection' elements. |
||
| 1704 | * |
||
| 1705 | * @return mixed |
||
| 1706 | */ |
||
| 1707 | public function getLastInFirstLevel() |
||
| 1708 | { |
||
| 1709 | try { |
||
| 1710 | $lastId = Database::getManager() |
||
| 1711 | ->createQuery('SELECT i.iid FROM ChamiloCourseBundle:CLpItem i |
||
| 1712 | WHERE i.lpId = :lp AND i.parent IS NULL AND i.itemType != :type ORDER BY i.displayOrder DESC') |
||
| 1713 | ->setMaxResults(1) |
||
| 1714 | ->setParameters(['lp' => $this->lp_id, 'type' => TOOL_LP_FINAL_ITEM]) |
||
| 1715 | ->getSingleScalarResult(); |
||
| 1716 | |||
| 1717 | return $lastId; |
||
| 1718 | } catch (Exception $exception) { |
||
| 1719 | return 0; |
||
| 1720 | } |
||
| 1721 | } |
||
| 1722 | |||
| 1723 | /** |
||
| 1724 | * Get the learning path name by id. |
||
| 1725 | * |
||
| 1726 | * @param int $lpId |
||
| 1727 | * |
||
| 1728 | * @return mixed |
||
| 1729 | */ |
||
| 1730 | public static function getLpNameById($lpId) |
||
| 1731 | { |
||
| 1732 | $em = Database::getManager(); |
||
| 1733 | |||
| 1734 | return $em->createQuery('SELECT clp.name FROM ChamiloCourseBundle:CLp clp |
||
| 1735 | WHERE clp.iid = :iid') |
||
| 1736 | ->setParameter('iid', $lpId) |
||
| 1737 | ->getSingleScalarResult(); |
||
| 1738 | } |
||
| 1739 | |||
| 1740 | /** |
||
| 1741 | * Gets the navigation bar for the learnpath display screen. |
||
| 1742 | * |
||
| 1743 | * @param string $barId |
||
| 1744 | * |
||
| 1745 | * @return string The HTML string to use as a navigation bar |
||
| 1746 | */ |
||
| 1747 | public function get_navigation_bar($barId = '') |
||
| 1748 | { |
||
| 1749 | if (empty($barId)) { |
||
| 1750 | $barId = 'control-top'; |
||
| 1751 | } |
||
| 1752 | $lpId = $this->lp_id; |
||
| 1753 | $mycurrentitemid = $this->get_current_item_id(); |
||
| 1754 | |||
| 1755 | $reportingText = get_lang('Reporting'); |
||
| 1756 | $previousText = get_lang('Previous'); |
||
| 1757 | $nextText = get_lang('Next'); |
||
| 1758 | $fullScreenText = get_lang('Back to normal screen'); |
||
| 1759 | |||
| 1760 | $settings = api_get_configuration_value('lp_view_settings'); |
||
| 1761 | $display = isset($settings['display']) ? $settings['display'] : false; |
||
| 1762 | $reportingIcon = ' |
||
| 1763 | <a class="icon-toolbar" |
||
| 1764 | id="stats_link" |
||
| 1765 | href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lpId.'" |
||
| 1766 | onclick="window.parent.API.save_asset(); return true;" |
||
| 1767 | target="content_name" title="'.$reportingText.'"> |
||
| 1768 | <span class="fa fa-info"></span><span class="sr-only">'.$reportingText.'</span> |
||
| 1769 | </a>'; |
||
| 1770 | |||
| 1771 | if (!empty($display)) { |
||
| 1772 | $showReporting = isset($display['show_reporting_icon']) ? $display['show_reporting_icon'] : true; |
||
| 1773 | if (false === $showReporting) { |
||
| 1774 | $reportingIcon = ''; |
||
| 1775 | } |
||
| 1776 | } |
||
| 1777 | |||
| 1778 | $hideArrows = false; |
||
| 1779 | if (isset($settings['display']) && isset($settings['display']['hide_lp_arrow_navigation'])) { |
||
| 1780 | $hideArrows = $settings['display']['hide_lp_arrow_navigation']; |
||
| 1781 | } |
||
| 1782 | |||
| 1783 | $previousIcon = ''; |
||
| 1784 | $nextIcon = ''; |
||
| 1785 | if (false === $hideArrows) { |
||
| 1786 | $previousIcon = ' |
||
| 1787 | <a class="icon-toolbar" id="scorm-previous" href="#" |
||
| 1788 | onclick="switch_item('.$mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'"> |
||
| 1789 | <span class="fa fa-chevron-left"></span><span class="sr-only">'.$previousText.'</span> |
||
| 1790 | </a>'; |
||
| 1791 | |||
| 1792 | $nextIcon = ' |
||
| 1793 | <a class="icon-toolbar" id="scorm-next" href="#" |
||
| 1794 | onclick="switch_item('.$mycurrentitemid.',\'next\');return false;" title="'.$nextText.'"> |
||
| 1795 | <span class="fa fa-chevron-right"></span><span class="sr-only">'.$nextText.'</span> |
||
| 1796 | </a>'; |
||
| 1797 | } |
||
| 1798 | |||
| 1799 | if ('fullscreen' === $this->mode) { |
||
| 1800 | $navbar = ' |
||
| 1801 | <span id="'.$barId.'" class="buttons"> |
||
| 1802 | '.$reportingIcon.' |
||
| 1803 | '.$previousIcon.' |
||
| 1804 | '.$nextIcon.' |
||
| 1805 | <a class="icon-toolbar" id="view-embedded" |
||
| 1806 | href="lp_controller.php?action=mode&mode=embedded" target="_top" title="'.$fullScreenText.'"> |
||
| 1807 | <span class="fa fa-columns"></span><span class="sr-only">'.$fullScreenText.'</span> |
||
| 1808 | </a> |
||
| 1809 | </span>'; |
||
| 1810 | } else { |
||
| 1811 | $navbar = ' |
||
| 1812 | <span id="'.$barId.'" class="buttons text-right"> |
||
| 1813 | '.$reportingIcon.' |
||
| 1814 | '.$previousIcon.' |
||
| 1815 | '.$nextIcon.' |
||
| 1816 | </span>'; |
||
| 1817 | } |
||
| 1818 | |||
| 1819 | return $navbar; |
||
| 1820 | } |
||
| 1821 | |||
| 1822 | /** |
||
| 1823 | * Gets the next resource in queue (url). |
||
| 1824 | * |
||
| 1825 | * @return string URL to load into the viewer |
||
| 1826 | */ |
||
| 1827 | public function get_next_index() |
||
| 1828 | { |
||
| 1829 | // TODO |
||
| 1830 | $index = $this->index; |
||
| 1831 | $index++; |
||
| 1832 | while ( |
||
| 1833 | !empty($this->ordered_items[$index]) && ('dir' == $this->items[$this->ordered_items[$index]]->get_type()) && |
||
| 1834 | $index < $this->max_ordered_items |
||
| 1835 | ) { |
||
| 1836 | $index++; |
||
| 1837 | if ($index == $this->max_ordered_items) { |
||
| 1838 | if ('dir' == $this->items[$this->ordered_items[$index]]->get_type()) { |
||
| 1839 | return $this->index; |
||
| 1840 | } |
||
| 1841 | |||
| 1842 | return $index; |
||
| 1843 | } |
||
| 1844 | } |
||
| 1845 | if (empty($this->ordered_items[$index])) { |
||
| 1846 | return $this->index; |
||
| 1847 | } |
||
| 1848 | |||
| 1849 | return $index; |
||
| 1850 | } |
||
| 1851 | |||
| 1852 | /** |
||
| 1853 | * Gets item_id for the next element. |
||
| 1854 | * |
||
| 1855 | * @return int Next item (DB) ID |
||
| 1856 | */ |
||
| 1857 | public function get_next_item_id() |
||
| 1858 | { |
||
| 1859 | $new_index = $this->get_next_index(); |
||
| 1860 | if (!empty($new_index)) { |
||
| 1861 | if (isset($this->ordered_items[$new_index])) { |
||
| 1862 | return $this->ordered_items[$new_index]; |
||
| 1863 | } |
||
| 1864 | } |
||
| 1865 | |||
| 1866 | return 0; |
||
| 1867 | } |
||
| 1868 | |||
| 1869 | /** |
||
| 1870 | * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...). |
||
| 1871 | * |
||
| 1872 | * Generally, the package provided is in the form of a zip file, so the function |
||
| 1873 | * has been written to test a zip file. If not a zip, the function will return the |
||
| 1874 | * default return value: '' |
||
| 1875 | * |
||
| 1876 | * @param string $file_path the path to the file |
||
| 1877 | * @param string $file_name the original name of the file |
||
| 1878 | * |
||
| 1879 | * @return string 'scorm','aicc','scorm2004','dokeos', 'error-empty-package' |
||
| 1880 | * if the package is empty, or '' if the package cannot be recognized |
||
| 1881 | */ |
||
| 1882 | public static function getPackageType($file_path, $file_name) |
||
| 1883 | { |
||
| 1884 | // Get name of the zip file without the extension. |
||
| 1885 | $file_info = pathinfo($file_name); |
||
| 1886 | $extension = $file_info['extension']; // Extension only. |
||
| 1887 | if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), [ |
||
| 1888 | 'dll', |
||
| 1889 | 'exe', |
||
| 1890 | ])) { |
||
| 1891 | return 'oogie'; |
||
| 1892 | } |
||
| 1893 | if (!empty($_POST['woogie']) && !in_array(strtolower($extension), [ |
||
| 1894 | 'dll', |
||
| 1895 | 'exe', |
||
| 1896 | ])) { |
||
| 1897 | return 'woogie'; |
||
| 1898 | } |
||
| 1899 | |||
| 1900 | $zipFile = new PclZip($file_path); |
||
| 1901 | // Check the zip content (real size and file extension). |
||
| 1902 | $zipContentArray = $zipFile->listContent(); |
||
| 1903 | $package_type = ''; |
||
| 1904 | $manifest = ''; |
||
| 1905 | $aicc_match_crs = 0; |
||
| 1906 | $aicc_match_au = 0; |
||
| 1907 | $aicc_match_des = 0; |
||
| 1908 | $aicc_match_cst = 0; |
||
| 1909 | $countItems = 0; |
||
| 1910 | |||
| 1911 | // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?). |
||
| 1912 | if (is_array($zipContentArray)) { |
||
| 1913 | $countItems = count($zipContentArray); |
||
| 1914 | if ($countItems > 0) { |
||
| 1915 | foreach ($zipContentArray as $thisContent) { |
||
| 1916 | if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) { |
||
| 1917 | // New behaviour: Don't do anything. These files will be removed in scorm::import_package. |
||
| 1918 | } elseif (false !== stristr($thisContent['filename'], 'imsmanifest.xml')) { |
||
| 1919 | $manifest = $thisContent['filename']; // Just the relative directory inside scorm/ |
||
| 1920 | $package_type = 'scorm'; |
||
| 1921 | break; // Exit the foreach loop. |
||
| 1922 | } elseif ( |
||
| 1923 | preg_match('/aicc\//i', $thisContent['filename']) || |
||
| 1924 | in_array( |
||
| 1925 | strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)), |
||
| 1926 | ['crs', 'au', 'des', 'cst'] |
||
| 1927 | ) |
||
| 1928 | ) { |
||
| 1929 | $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)); |
||
| 1930 | switch ($ext) { |
||
| 1931 | case 'crs': |
||
| 1932 | $aicc_match_crs = 1; |
||
| 1933 | break; |
||
| 1934 | case 'au': |
||
| 1935 | $aicc_match_au = 1; |
||
| 1936 | break; |
||
| 1937 | case 'des': |
||
| 1938 | $aicc_match_des = 1; |
||
| 1939 | break; |
||
| 1940 | case 'cst': |
||
| 1941 | $aicc_match_cst = 1; |
||
| 1942 | break; |
||
| 1943 | default: |
||
| 1944 | break; |
||
| 1945 | } |
||
| 1946 | //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC. |
||
| 1947 | } else { |
||
| 1948 | $package_type = ''; |
||
| 1949 | } |
||
| 1950 | } |
||
| 1951 | } |
||
| 1952 | } |
||
| 1953 | |||
| 1954 | if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) { |
||
| 1955 | // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)). |
||
| 1956 | $package_type = 'aicc'; |
||
| 1957 | } |
||
| 1958 | |||
| 1959 | // Try with chamilo course builder |
||
| 1960 | if (empty($package_type)) { |
||
| 1961 | // Sometimes users will try to upload an empty zip, or a zip with |
||
| 1962 | // only a folder. Catch that and make the calling function aware. |
||
| 1963 | // If the single file was the imsmanifest.xml, then $package_type |
||
| 1964 | // would be 'scorm' and we wouldn't be here. |
||
| 1965 | if ($countItems < 2) { |
||
| 1966 | return 'error-empty-package'; |
||
| 1967 | } |
||
| 1968 | $package_type = 'chamilo'; |
||
| 1969 | } |
||
| 1970 | |||
| 1971 | return $package_type; |
||
| 1972 | } |
||
| 1973 | |||
| 1974 | /** |
||
| 1975 | * Gets the previous resource in queue (url). Also initialises time values for this viewing. |
||
| 1976 | * |
||
| 1977 | * @return string URL to load into the viewer |
||
| 1978 | */ |
||
| 1979 | public function get_previous_index() |
||
| 1980 | { |
||
| 1981 | $index = $this->index; |
||
| 1982 | if (isset($this->ordered_items[$index - 1])) { |
||
| 1983 | $index--; |
||
| 1984 | while (isset($this->ordered_items[$index]) && |
||
| 1985 | ('dir' == $this->items[$this->ordered_items[$index]]->get_type()) |
||
| 1986 | ) { |
||
| 1987 | $index--; |
||
| 1988 | if ($index < 0) { |
||
| 1989 | return $this->index; |
||
| 1990 | } |
||
| 1991 | } |
||
| 1992 | } |
||
| 1993 | |||
| 1994 | return $index; |
||
| 1995 | } |
||
| 1996 | |||
| 1997 | /** |
||
| 1998 | * Gets item_id for the next element. |
||
| 1999 | * |
||
| 2000 | * @return int Previous item (DB) ID |
||
| 2001 | */ |
||
| 2002 | public function get_previous_item_id() |
||
| 2003 | { |
||
| 2004 | $index = $this->get_previous_index(); |
||
| 2005 | |||
| 2006 | return $this->ordered_items[$index]; |
||
| 2007 | } |
||
| 2008 | |||
| 2009 | /** |
||
| 2010 | * Returns the HTML necessary to print a mediaplayer block inside a page. |
||
| 2011 | * |
||
| 2012 | * @param int $lpItemId |
||
| 2013 | * @param string $autostart |
||
| 2014 | * |
||
| 2015 | * @return string The mediaplayer HTML |
||
| 2016 | */ |
||
| 2017 | public function get_mediaplayer($lpItemId, $autostart = 'true') |
||
| 2018 | { |
||
| 2019 | $course_id = api_get_course_int_id(); |
||
| 2020 | $courseInfo = api_get_course_info(); |
||
| 2021 | $lpItemId = (int) $lpItemId; |
||
| 2022 | |||
| 2023 | if (empty($courseInfo) || empty($lpItemId)) { |
||
| 2024 | return ''; |
||
| 2025 | } |
||
| 2026 | $item = isset($this->items[$lpItemId]) ? $this->items[$lpItemId] : null; |
||
| 2027 | |||
| 2028 | if (empty($item)) { |
||
| 2029 | return ''; |
||
| 2030 | } |
||
| 2031 | |||
| 2032 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 2033 | $tbl_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
| 2034 | $itemViewId = (int) $item->db_item_view_id; |
||
| 2035 | |||
| 2036 | // Getting all the information about the item. |
||
| 2037 | $sql = "SELECT lp_view.status |
||
| 2038 | FROM $tbl_lp_item as lpi |
||
| 2039 | INNER JOIN $tbl_lp_item_view as lp_view |
||
| 2040 | ON (lpi.iid = lp_view.lp_item_id) |
||
| 2041 | WHERE |
||
| 2042 | lp_view.iid = $itemViewId AND |
||
| 2043 | lpi.iid = $lpItemId |
||
| 2044 | "; |
||
| 2045 | $result = Database::query($sql); |
||
| 2046 | $row = Database::fetch_assoc($result); |
||
| 2047 | $output = ''; |
||
| 2048 | $audio = $item->audio; |
||
| 2049 | |||
| 2050 | if (!empty($audio)) { |
||
| 2051 | $list = $_SESSION['oLP']->get_toc(); |
||
| 2052 | |||
| 2053 | switch ($item->get_type()) { |
||
| 2054 | case 'quiz': |
||
| 2055 | $type_quiz = false; |
||
| 2056 | foreach ($list as $toc) { |
||
| 2057 | if ($toc['id'] == $_SESSION['oLP']->current) { |
||
| 2058 | $type_quiz = true; |
||
| 2059 | } |
||
| 2060 | } |
||
| 2061 | |||
| 2062 | if ($type_quiz) { |
||
| 2063 | if (1 == $_SESSION['oLP']->prevent_reinit) { |
||
| 2064 | $autostart_audio = 'completed' === $row['status'] ? 'false' : 'true'; |
||
| 2065 | } else { |
||
| 2066 | $autostart_audio = $autostart; |
||
| 2067 | } |
||
| 2068 | } |
||
| 2069 | break; |
||
| 2070 | case TOOL_READOUT_TEXT: |
||
| 2071 | $autostart_audio = 'false'; |
||
| 2072 | break; |
||
| 2073 | default: |
||
| 2074 | $autostart_audio = 'true'; |
||
| 2075 | } |
||
| 2076 | |||
| 2077 | $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'.$audio; |
||
| 2078 | $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document'.$audio.'?'.api_get_cidreq(); |
||
| 2079 | |||
| 2080 | $player = Display::getMediaPlayer( |
||
| 2081 | $file, |
||
| 2082 | [ |
||
| 2083 | 'id' => 'lp_audio_media_player', |
||
| 2084 | 'url' => $url, |
||
| 2085 | 'autoplay' => $autostart_audio, |
||
| 2086 | 'width' => '100%', |
||
| 2087 | ] |
||
| 2088 | ); |
||
| 2089 | |||
| 2090 | // The mp3 player. |
||
| 2091 | $output = '<div id="container">'; |
||
| 2092 | $output .= $player; |
||
| 2093 | $output .= '</div>'; |
||
| 2094 | } |
||
| 2095 | |||
| 2096 | return $output; |
||
| 2097 | } |
||
| 2098 | |||
| 2099 | /** |
||
| 2100 | * @param int $studentId |
||
| 2101 | * @param int $prerequisite |
||
| 2102 | * @param Course $course |
||
| 2103 | * @param int $sessionId |
||
| 2104 | * |
||
| 2105 | * @return bool |
||
| 2106 | */ |
||
| 2107 | public static function isBlockedByPrerequisite( |
||
| 2108 | $studentId, |
||
| 2109 | $prerequisite, |
||
| 2110 | Course $course, |
||
| 2111 | $sessionId |
||
| 2112 | ) { |
||
| 2113 | if (null === $course) { |
||
| 2114 | return false; |
||
| 2115 | } |
||
| 2116 | |||
| 2117 | $courseId = $course->getId(); |
||
| 2118 | |||
| 2119 | $allow = api_get_configuration_value('allow_teachers_to_access_blocked_lp_by_prerequisite'); |
||
| 2120 | if ($allow) { |
||
| 2121 | if (api_is_allowed_to_edit() || |
||
| 2122 | api_is_platform_admin(true) || |
||
| 2123 | api_is_drh() || |
||
| 2124 | api_is_coach($sessionId, $courseId, false) |
||
| 2125 | ) { |
||
| 2126 | return false; |
||
| 2127 | } |
||
| 2128 | } |
||
| 2129 | |||
| 2130 | $isBlocked = false; |
||
| 2131 | if (!empty($prerequisite)) { |
||
| 2132 | $progress = self::getProgress( |
||
| 2133 | $prerequisite, |
||
| 2134 | $studentId, |
||
| 2135 | $courseId, |
||
| 2136 | $sessionId |
||
| 2137 | ); |
||
| 2138 | if ($progress < 100) { |
||
| 2139 | $isBlocked = true; |
||
| 2140 | } |
||
| 2141 | |||
| 2142 | if (Tracking::minimumTimeAvailable($sessionId, $courseId)) { |
||
| 2143 | // Block if it does not exceed minimum time |
||
| 2144 | // Minimum time (in minutes) to pass the learning path |
||
| 2145 | $accumulateWorkTime = self::getAccumulateWorkTimePrerequisite($prerequisite, $courseId); |
||
| 2146 | |||
| 2147 | if ($accumulateWorkTime > 0) { |
||
| 2148 | // Total time in course (sum of times in learning paths from course) |
||
| 2149 | $accumulateWorkTimeTotal = self::getAccumulateWorkTimeTotal($courseId); |
||
| 2150 | |||
| 2151 | // Connect with the plugin_licences_course_session table |
||
| 2152 | // which indicates what percentage of the time applies |
||
| 2153 | // Minimum connection percentage |
||
| 2154 | $perc = 100; |
||
| 2155 | // Time from the course |
||
| 2156 | $tc = $accumulateWorkTimeTotal; |
||
| 2157 | |||
| 2158 | // Percentage of the learning paths |
||
| 2159 | $pl = $accumulateWorkTime / $accumulateWorkTimeTotal; |
||
| 2160 | // Minimum time for each learning path |
||
| 2161 | $accumulateWorkTime = ($pl * $tc * $perc / 100); |
||
| 2162 | |||
| 2163 | // Spent time (in seconds) so far in the learning path |
||
| 2164 | $lpTimeList = Tracking::getCalculateTime($studentId, $courseId, $sessionId); |
||
| 2165 | $lpTime = isset($lpTimeList[TOOL_LEARNPATH][$prerequisite]) ? $lpTimeList[TOOL_LEARNPATH][$prerequisite] : 0; |
||
| 2166 | |||
| 2167 | if ($lpTime < ($accumulateWorkTime * 60)) { |
||
| 2168 | $isBlocked = true; |
||
| 2169 | } |
||
| 2170 | } |
||
| 2171 | } |
||
| 2172 | } |
||
| 2173 | |||
| 2174 | return $isBlocked; |
||
| 2175 | } |
||
| 2176 | |||
| 2177 | /** |
||
| 2178 | * Checks if the learning path is visible for student after the progress |
||
| 2179 | * of its prerequisite is completed, considering the time availability and |
||
| 2180 | * the LP visibility. |
||
| 2181 | */ |
||
| 2182 | public static function is_lp_visible_for_student(CLp $lp, $student_id, Course $course, \Chamilo\CoreBundle\Entity\Session $session = null): bool |
||
| 2183 | { |
||
| 2184 | if (null === $course) { |
||
| 2185 | return false; |
||
| 2186 | } |
||
| 2187 | |||
| 2188 | $sessionId = $session ? $session->getId() : 0; |
||
| 2189 | $courseId = $course->getId(); |
||
| 2190 | |||
| 2191 | /*$itemInfo = api_get_item_property_info( |
||
| 2192 | $courseId, |
||
| 2193 | TOOL_LEARNPATH, |
||
| 2194 | $lp_id, |
||
| 2195 | $sessionId |
||
| 2196 | );*/ |
||
| 2197 | |||
| 2198 | $visibility = $lp->isVisible($course, $session); |
||
| 2199 | |||
| 2200 | // If the item was deleted. |
||
| 2201 | if (false === $visibility) { |
||
| 2202 | return false; |
||
| 2203 | } |
||
| 2204 | |||
| 2205 | $now = time(); |
||
| 2206 | if ($lp->hasCategory()) { |
||
| 2207 | $category = $lp->getCategory(); |
||
| 2208 | |||
| 2209 | if (false === self::categoryIsVisibleForStudent( |
||
| 2210 | $category, |
||
| 2211 | api_get_user_entity($student_id), |
||
| 2212 | $course, |
||
| 2213 | $session |
||
| 2214 | )) { |
||
| 2215 | return false; |
||
| 2216 | } |
||
| 2217 | |||
| 2218 | $prerequisite = $lp->getPrerequisite(); |
||
| 2219 | $is_visible = true; |
||
| 2220 | |||
| 2221 | $isBlocked = self::isBlockedByPrerequisite( |
||
| 2222 | $student_id, |
||
| 2223 | $prerequisite, |
||
| 2224 | $course, |
||
| 2225 | $sessionId |
||
| 2226 | ); |
||
| 2227 | |||
| 2228 | if ($isBlocked) { |
||
| 2229 | $is_visible = false; |
||
| 2230 | } |
||
| 2231 | |||
| 2232 | // Also check the time availability of the LP |
||
| 2233 | if ($is_visible) { |
||
| 2234 | // Adding visibility restrictions |
||
| 2235 | if (null !== $lp->getPublicatedOn()) { |
||
| 2236 | if ($now < $lp->getPublicatedOn()->getTimestamp()) { |
||
| 2237 | $is_visible = false; |
||
| 2238 | } |
||
| 2239 | } |
||
| 2240 | // Blocking empty start times see BT#2800 |
||
| 2241 | global $_custom; |
||
| 2242 | if (isset($_custom['lps_hidden_when_no_start_date']) && |
||
| 2243 | $_custom['lps_hidden_when_no_start_date'] |
||
| 2244 | ) { |
||
| 2245 | if (null !== $lp->getPublicatedOn()) { |
||
| 2246 | $is_visible = false; |
||
| 2247 | } |
||
| 2248 | } |
||
| 2249 | |||
| 2250 | if (null !== $lp->getExpiredOn()) { |
||
| 2251 | if ($now > $lp->getExpiredOn()->getTimestamp()) { |
||
| 2252 | $is_visible = false; |
||
| 2253 | } |
||
| 2254 | } |
||
| 2255 | } |
||
| 2256 | |||
| 2257 | if ($is_visible) { |
||
| 2258 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
| 2259 | |||
| 2260 | // Check if the subscription users/group to a LP is ON |
||
| 2261 | if (1 == $lp->getSubscribeUsers() && |
||
| 2262 | true === $subscriptionSettings['allow_add_users_to_lp'] |
||
| 2263 | ) { |
||
| 2264 | // Try group |
||
| 2265 | $is_visible = false; |
||
| 2266 | // Checking only the user visibility |
||
| 2267 | // @todo fix visibility |
||
| 2268 | $userVisibility = 1; |
||
| 2269 | /*$userVisibility = api_get_item_visibility( |
||
| 2270 | $courseInfo, |
||
| 2271 | 'learnpath', |
||
| 2272 | $row['id'], |
||
| 2273 | $sessionId, |
||
| 2274 | $student_id, |
||
| 2275 | 'LearnpathSubscription' |
||
| 2276 | );*/ |
||
| 2277 | |||
| 2278 | if (1 == $userVisibility) { |
||
| 2279 | $is_visible = true; |
||
| 2280 | } else { |
||
| 2281 | $userGroups = GroupManager::getAllGroupPerUserSubscription($student_id, $courseId); |
||
| 2282 | if (!empty($userGroups)) { |
||
| 2283 | foreach ($userGroups as $groupInfo) { |
||
| 2284 | $groupId = $groupInfo['iid']; |
||
| 2285 | // @todo fix visibility. |
||
| 2286 | $userVisibility = 1; |
||
| 2287 | /*$userVisibility = api_get_item_visibility( |
||
| 2288 | $courseInfo, |
||
| 2289 | 'learnpath', |
||
| 2290 | $row['id'], |
||
| 2291 | $sessionId, |
||
| 2292 | null, |
||
| 2293 | 'LearnpathSubscription', |
||
| 2294 | $groupId |
||
| 2295 | );*/ |
||
| 2296 | |||
| 2297 | if (1 == $userVisibility) { |
||
| 2298 | $is_visible = true; |
||
| 2299 | break; |
||
| 2300 | } |
||
| 2301 | } |
||
| 2302 | } |
||
| 2303 | } |
||
| 2304 | } |
||
| 2305 | } |
||
| 2306 | |||
| 2307 | return $is_visible; |
||
| 2308 | } |
||
| 2309 | |||
| 2310 | return true; |
||
| 2311 | } |
||
| 2312 | |||
| 2313 | /** |
||
| 2314 | * @param int $lpId |
||
| 2315 | * @param int $userId |
||
| 2316 | * @param int $courseId |
||
| 2317 | * @param int $sessionId |
||
| 2318 | * |
||
| 2319 | * @return int |
||
| 2320 | */ |
||
| 2321 | public static function getProgress($lpId, $userId, $courseId, $sessionId = 0) |
||
| 2322 | { |
||
| 2323 | $lpId = (int) $lpId; |
||
| 2324 | $userId = (int) $userId; |
||
| 2325 | $courseId = (int) $courseId; |
||
| 2326 | $sessionId = (int) $sessionId; |
||
| 2327 | |||
| 2328 | $sessionCondition = api_get_session_condition($sessionId); |
||
| 2329 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 2330 | $sql = "SELECT progress FROM $table |
||
| 2331 | WHERE |
||
| 2332 | c_id = $courseId AND |
||
| 2333 | lp_id = $lpId AND |
||
| 2334 | user_id = $userId $sessionCondition "; |
||
| 2335 | $res = Database::query($sql); |
||
| 2336 | |||
| 2337 | $progress = 0; |
||
| 2338 | if (Database::num_rows($res) > 0) { |
||
| 2339 | $row = Database::fetch_array($res); |
||
| 2340 | $progress = (int) $row['progress']; |
||
| 2341 | } |
||
| 2342 | |||
| 2343 | return $progress; |
||
| 2344 | } |
||
| 2345 | |||
| 2346 | /** |
||
| 2347 | * @param array $lpList |
||
| 2348 | * @param int $userId |
||
| 2349 | * @param int $courseId |
||
| 2350 | * @param int $sessionId |
||
| 2351 | * |
||
| 2352 | * @return array |
||
| 2353 | */ |
||
| 2354 | public static function getProgressFromLpList($lpList, $userId, $courseId, $sessionId = 0) |
||
| 2355 | { |
||
| 2356 | $lpList = array_map('intval', $lpList); |
||
| 2357 | if (empty($lpList)) { |
||
| 2358 | return []; |
||
| 2359 | } |
||
| 2360 | |||
| 2361 | $lpList = implode("','", $lpList); |
||
| 2362 | |||
| 2363 | $userId = (int) $userId; |
||
| 2364 | $courseId = (int) $courseId; |
||
| 2365 | $sessionId = (int) $sessionId; |
||
| 2366 | |||
| 2367 | $sessionCondition = api_get_session_condition($sessionId); |
||
| 2368 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 2369 | $sql = "SELECT lp_id, progress FROM $table |
||
| 2370 | WHERE |
||
| 2371 | c_id = $courseId AND |
||
| 2372 | lp_id IN ('".$lpList."') AND |
||
| 2373 | user_id = $userId $sessionCondition "; |
||
| 2374 | $res = Database::query($sql); |
||
| 2375 | |||
| 2376 | if (Database::num_rows($res) > 0) { |
||
| 2377 | $list = []; |
||
| 2378 | while ($row = Database::fetch_array($res)) { |
||
| 2379 | $list[$row['lp_id']] = $row['progress']; |
||
| 2380 | } |
||
| 2381 | |||
| 2382 | return $list; |
||
| 2383 | } |
||
| 2384 | |||
| 2385 | return []; |
||
| 2386 | } |
||
| 2387 | |||
| 2388 | /** |
||
| 2389 | * Displays a progress bar |
||
| 2390 | * completed so far. |
||
| 2391 | * |
||
| 2392 | * @param int $percentage Progress value to display |
||
| 2393 | * @param string $text_add Text to display near the progress value |
||
| 2394 | * |
||
| 2395 | * @return string HTML string containing the progress bar |
||
| 2396 | */ |
||
| 2397 | public static function get_progress_bar($percentage = -1, $text_add = '') |
||
| 2398 | { |
||
| 2399 | $text = $percentage.$text_add; |
||
| 2400 | $output = '<div class="progress"> |
||
| 2401 | <div id="progress_bar_value" |
||
| 2402 | class="progress-bar progress-bar-success" role="progressbar" |
||
| 2403 | aria-valuenow="'.$percentage.'" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';"> |
||
| 2404 | '.$text.' |
||
| 2405 | </div> |
||
| 2406 | </div>'; |
||
| 2407 | |||
| 2408 | return $output; |
||
| 2409 | } |
||
| 2410 | |||
| 2411 | /** |
||
| 2412 | * @param string $mode can be '%' or 'abs' |
||
| 2413 | * otherwise this value will be used $this->progress_bar_mode |
||
| 2414 | * |
||
| 2415 | * @return string |
||
| 2416 | */ |
||
| 2417 | public function getProgressBar($mode = null) |
||
| 2418 | { |
||
| 2419 | [$percentage, $text_add] = $this->get_progress_bar_text($mode); |
||
| 2420 | |||
| 2421 | return self::get_progress_bar($percentage, $text_add); |
||
| 2422 | } |
||
| 2423 | |||
| 2424 | /** |
||
| 2425 | * Gets the progress bar info to display inside the progress bar. |
||
| 2426 | * Also used by scorm_api.php. |
||
| 2427 | * |
||
| 2428 | * @param string $mode Mode of display (can be '%' or 'abs').abs means |
||
| 2429 | * we display a number of completed elements per total elements |
||
| 2430 | * @param int $add Additional steps to fake as completed |
||
| 2431 | * |
||
| 2432 | * @return array Percentage or number and symbol (% or /xx) |
||
| 2433 | */ |
||
| 2434 | public function get_progress_bar_text($mode = '', $add = 0) |
||
| 2435 | { |
||
| 2436 | if (empty($mode)) { |
||
| 2437 | $mode = $this->progress_bar_mode; |
||
| 2438 | } |
||
| 2439 | $text = ''; |
||
| 2440 | $percentage = 0; |
||
| 2441 | // If the option to use the score as progress is set for this learning |
||
| 2442 | // path, then the rules are completely different: we assume only one |
||
| 2443 | // item exists and the progress of the LP depends on the score |
||
| 2444 | $scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable'); |
||
| 2445 | if (true === $scoreAsProgressSetting) { |
||
| 2446 | $scoreAsProgress = $this->getUseScoreAsProgress(); |
||
| 2447 | if ($scoreAsProgress) { |
||
| 2448 | // Get single item's score |
||
| 2449 | $itemId = $this->get_current_item_id(); |
||
| 2450 | $item = $this->getItem($itemId); |
||
| 2451 | $score = $item->get_score(); |
||
| 2452 | $maxScore = $item->get_max(); |
||
| 2453 | if ($mode = '%') { |
||
| 2454 | if (!empty($maxScore)) { |
||
| 2455 | $percentage = ((float) $score / (float) $maxScore) * 100; |
||
| 2456 | } |
||
| 2457 | $percentage = number_format($percentage, 0); |
||
| 2458 | $text = '%'; |
||
| 2459 | } else { |
||
| 2460 | $percentage = $score; |
||
| 2461 | $text = '/'.$maxScore; |
||
| 2462 | } |
||
| 2463 | |||
| 2464 | return [$percentage, $text]; |
||
| 2465 | } |
||
| 2466 | } |
||
| 2467 | // otherwise just continue the normal processing of progress |
||
| 2468 | $total_items = $this->getTotalItemsCountWithoutDirs(); |
||
| 2469 | $completeItems = $this->get_complete_items_count(); |
||
| 2470 | if (0 != $add) { |
||
| 2471 | $completeItems += $add; |
||
| 2472 | } |
||
| 2473 | if ($completeItems > $total_items) { |
||
| 2474 | $completeItems = $total_items; |
||
| 2475 | } |
||
| 2476 | if ('%' == $mode) { |
||
| 2477 | if ($total_items > 0) { |
||
| 2478 | $percentage = ((float) $completeItems / (float) $total_items) * 100; |
||
| 2479 | } |
||
| 2480 | $percentage = number_format($percentage, 0); |
||
| 2481 | $text = '%'; |
||
| 2482 | } elseif ('abs' === $mode) { |
||
| 2483 | $percentage = $completeItems; |
||
| 2484 | $text = '/'.$total_items; |
||
| 2485 | } |
||
| 2486 | |||
| 2487 | return [ |
||
| 2488 | $percentage, |
||
| 2489 | $text, |
||
| 2490 | ]; |
||
| 2491 | } |
||
| 2492 | |||
| 2493 | /** |
||
| 2494 | * Gets the progress bar mode. |
||
| 2495 | * |
||
| 2496 | * @return string The progress bar mode attribute |
||
| 2497 | */ |
||
| 2498 | public function get_progress_bar_mode() |
||
| 2499 | { |
||
| 2500 | if (!empty($this->progress_bar_mode)) { |
||
| 2501 | return $this->progress_bar_mode; |
||
| 2502 | } |
||
| 2503 | |||
| 2504 | return '%'; |
||
| 2505 | } |
||
| 2506 | |||
| 2507 | /** |
||
| 2508 | * Gets the learnpath theme (remote or local). |
||
| 2509 | * |
||
| 2510 | * @return string Learnpath theme |
||
| 2511 | */ |
||
| 2512 | public function get_theme() |
||
| 2513 | { |
||
| 2514 | if (!empty($this->theme)) { |
||
| 2515 | return $this->theme; |
||
| 2516 | } |
||
| 2517 | |||
| 2518 | return ''; |
||
| 2519 | } |
||
| 2520 | |||
| 2521 | /** |
||
| 2522 | * Gets the learnpath session id. |
||
| 2523 | * |
||
| 2524 | * @return int |
||
| 2525 | */ |
||
| 2526 | public function get_lp_session_id() |
||
| 2527 | { |
||
| 2528 | if (!empty($this->lp_session_id)) { |
||
| 2529 | return (int) $this->lp_session_id; |
||
| 2530 | } |
||
| 2531 | |||
| 2532 | return 0; |
||
| 2533 | } |
||
| 2534 | |||
| 2535 | /** |
||
| 2536 | * Gets the learnpath author. |
||
| 2537 | * |
||
| 2538 | * @return string LP's author |
||
| 2539 | */ |
||
| 2540 | public function get_author() |
||
| 2541 | { |
||
| 2542 | if (!empty($this->author)) { |
||
| 2543 | return $this->author; |
||
| 2544 | } |
||
| 2545 | |||
| 2546 | return ''; |
||
| 2547 | } |
||
| 2548 | |||
| 2549 | /** |
||
| 2550 | * Gets hide table of contents. |
||
| 2551 | * |
||
| 2552 | * @return int |
||
| 2553 | */ |
||
| 2554 | public function getHideTableOfContents() |
||
| 2555 | { |
||
| 2556 | return (int) $this->hide_toc_frame; |
||
| 2557 | } |
||
| 2558 | |||
| 2559 | /** |
||
| 2560 | * Generate a new prerequisites string for a given item. If this item was a sco and |
||
| 2561 | * its prerequisites were strings (instead of IDs), then transform those strings into |
||
| 2562 | * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table. |
||
| 2563 | * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the |
||
| 2564 | * same rule as the scormExport() method. |
||
| 2565 | * |
||
| 2566 | * @param int $item_id Item ID |
||
| 2567 | * |
||
| 2568 | * @return string Prerequisites string ready for the export as SCORM |
||
| 2569 | */ |
||
| 2570 | public function get_scorm_prereq_string($item_id) |
||
| 2571 | { |
||
| 2572 | if ($this->debug > 0) { |
||
| 2573 | error_log('In learnpath::get_scorm_prereq_string()'); |
||
| 2574 | } |
||
| 2575 | if (!is_object($this->items[$item_id])) { |
||
| 2576 | return false; |
||
| 2577 | } |
||
| 2578 | /** @var learnpathItem $oItem */ |
||
| 2579 | $oItem = $this->items[$item_id]; |
||
| 2580 | $prereq = $oItem->get_prereq_string(); |
||
| 2581 | |||
| 2582 | if (empty($prereq)) { |
||
| 2583 | return ''; |
||
| 2584 | } |
||
| 2585 | if (preg_match('/^\d+$/', $prereq) && |
||
| 2586 | isset($this->items[$prereq]) && |
||
| 2587 | is_object($this->items[$prereq]) |
||
| 2588 | ) { |
||
| 2589 | // If the prerequisite is a simple integer ID and this ID exists as an item ID, |
||
| 2590 | // then simply return it (with the ITEM_ prefix). |
||
| 2591 | //return 'ITEM_' . $prereq; |
||
| 2592 | return $this->items[$prereq]->ref; |
||
| 2593 | } else { |
||
| 2594 | if (isset($this->refs_list[$prereq])) { |
||
| 2595 | // It's a simple string item from which the ID can be found in the refs list, |
||
| 2596 | // so we can transform it directly to an ID for export. |
||
| 2597 | return $this->items[$this->refs_list[$prereq]]->ref; |
||
| 2598 | } elseif (isset($this->refs_list['ITEM_'.$prereq])) { |
||
| 2599 | return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref; |
||
| 2600 | } else { |
||
| 2601 | // The last case, if it's a complex form, then find all the IDs (SCORM strings) |
||
| 2602 | // and replace them, one by one, by the internal IDs (chamilo db) |
||
| 2603 | // TODO: Modify the '*' replacement to replace the multiplier in front of it |
||
| 2604 | // by a space as well. |
||
| 2605 | $find = [ |
||
| 2606 | '&', |
||
| 2607 | '|', |
||
| 2608 | '~', |
||
| 2609 | '=', |
||
| 2610 | '<>', |
||
| 2611 | '{', |
||
| 2612 | '}', |
||
| 2613 | '*', |
||
| 2614 | '(', |
||
| 2615 | ')', |
||
| 2616 | ]; |
||
| 2617 | $replace = [ |
||
| 2618 | ' ', |
||
| 2619 | ' ', |
||
| 2620 | ' ', |
||
| 2621 | ' ', |
||
| 2622 | ' ', |
||
| 2623 | ' ', |
||
| 2624 | ' ', |
||
| 2625 | ' ', |
||
| 2626 | ' ', |
||
| 2627 | ' ', |
||
| 2628 | ]; |
||
| 2629 | $prereq_mod = str_replace($find, $replace, $prereq); |
||
| 2630 | $ids = explode(' ', $prereq_mod); |
||
| 2631 | foreach ($ids as $id) { |
||
| 2632 | $id = trim($id); |
||
| 2633 | if (isset($this->refs_list[$id])) { |
||
| 2634 | $prereq = preg_replace( |
||
| 2635 | '/[^a-zA-Z_0-9]('.$id.')[^a-zA-Z_0-9]/', |
||
| 2636 | 'ITEM_'.$this->refs_list[$id], |
||
| 2637 | $prereq |
||
| 2638 | ); |
||
| 2639 | } |
||
| 2640 | } |
||
| 2641 | |||
| 2642 | return $prereq; |
||
| 2643 | } |
||
| 2644 | } |
||
| 2645 | } |
||
| 2646 | |||
| 2647 | /** |
||
| 2648 | * Returns the XML DOM document's node. |
||
| 2649 | * |
||
| 2650 | * @param resource $children Reference to a list of objects to search for the given ITEM_* |
||
| 2651 | * @param string $id The identifier to look for |
||
| 2652 | * |
||
| 2653 | * @return mixed The reference to the element found with that identifier. False if not found |
||
| 2654 | */ |
||
| 2655 | public function get_scorm_xml_node(&$children, $id) |
||
| 2656 | { |
||
| 2657 | for ($i = 0; $i < $children->length; $i++) { |
||
| 2658 | $item_temp = $children->item($i); |
||
| 2659 | if ('item' == $item_temp->nodeName) { |
||
| 2660 | if ($item_temp->getAttribute('identifier') == $id) { |
||
| 2661 | return $item_temp; |
||
| 2662 | } |
||
| 2663 | } |
||
| 2664 | $subchildren = $item_temp->childNodes; |
||
| 2665 | if ($subchildren && $subchildren->length > 0) { |
||
| 2666 | $val = $this->get_scorm_xml_node($subchildren, $id); |
||
| 2667 | if (is_object($val)) { |
||
| 2668 | return $val; |
||
| 2669 | } |
||
| 2670 | } |
||
| 2671 | } |
||
| 2672 | |||
| 2673 | return false; |
||
| 2674 | } |
||
| 2675 | |||
| 2676 | /** |
||
| 2677 | * Gets the status list for all LP's items. |
||
| 2678 | * |
||
| 2679 | * @return array Array of [index] => [item ID => current status] |
||
| 2680 | */ |
||
| 2681 | public function get_items_status_list() |
||
| 2682 | { |
||
| 2683 | $list = []; |
||
| 2684 | foreach ($this->ordered_items as $item_id) { |
||
| 2685 | $list[] = [ |
||
| 2686 | $item_id => $this->items[$item_id]->get_status(), |
||
| 2687 | ]; |
||
| 2688 | } |
||
| 2689 | |||
| 2690 | return $list; |
||
| 2691 | } |
||
| 2692 | |||
| 2693 | /** |
||
| 2694 | * Return the number of interactions for the given learnpath Item View ID. |
||
| 2695 | * This method can be used as static. |
||
| 2696 | * |
||
| 2697 | * @param int $lp_iv_id Item View ID |
||
| 2698 | * @param int $course_id course id |
||
| 2699 | * |
||
| 2700 | * @return int |
||
| 2701 | */ |
||
| 2702 | public static function get_interactions_count_from_db($lp_iv_id, $course_id) |
||
| 2703 | { |
||
| 2704 | $table = Database::get_course_table(TABLE_LP_IV_INTERACTION); |
||
| 2705 | $lp_iv_id = (int) $lp_iv_id; |
||
| 2706 | $course_id = (int) $course_id; |
||
| 2707 | |||
| 2708 | $sql = "SELECT count(*) FROM $table |
||
| 2709 | WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id"; |
||
| 2710 | $res = Database::query($sql); |
||
| 2711 | $num = 0; |
||
| 2712 | if (Database::num_rows($res)) { |
||
| 2713 | $row = Database::fetch_array($res); |
||
| 2714 | $num = $row[0]; |
||
| 2715 | } |
||
| 2716 | |||
| 2717 | return $num; |
||
| 2718 | } |
||
| 2719 | |||
| 2720 | /** |
||
| 2721 | * Return the interactions as an array for the given lp_iv_id. |
||
| 2722 | * This method can be used as static. |
||
| 2723 | * |
||
| 2724 | * @param int $lp_iv_id Learnpath Item View ID |
||
| 2725 | * |
||
| 2726 | * @return array |
||
| 2727 | * |
||
| 2728 | * @todo Transcode labels instead of switching to HTML (which requires to know the encoding of the LP) |
||
| 2729 | */ |
||
| 2730 | public static function get_iv_interactions_array($lp_iv_id, $course_id = 0) |
||
| 2731 | { |
||
| 2732 | $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id; |
||
| 2733 | $list = []; |
||
| 2734 | $table = Database::get_course_table(TABLE_LP_IV_INTERACTION); |
||
| 2735 | $lp_iv_id = (int) $lp_iv_id; |
||
| 2736 | |||
| 2737 | if (empty($lp_iv_id) || empty($course_id)) { |
||
| 2738 | return []; |
||
| 2739 | } |
||
| 2740 | |||
| 2741 | $sql = "SELECT * FROM $table |
||
| 2742 | WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id |
||
| 2743 | ORDER BY order_id ASC"; |
||
| 2744 | $res = Database::query($sql); |
||
| 2745 | $num = Database::num_rows($res); |
||
| 2746 | if ($num > 0) { |
||
| 2747 | $list[] = [ |
||
| 2748 | 'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES), |
||
| 2749 | 'id' => api_htmlentities(get_lang('Interaction ID'), ENT_QUOTES), |
||
| 2750 | 'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES), |
||
| 2751 | 'time' => api_htmlentities(get_lang('Time (finished at...)'), ENT_QUOTES), |
||
| 2752 | 'correct_responses' => api_htmlentities(get_lang('Correct answers'), ENT_QUOTES), |
||
| 2753 | 'student_response' => api_htmlentities(get_lang('Learner answers'), ENT_QUOTES), |
||
| 2754 | 'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES), |
||
| 2755 | 'latency' => api_htmlentities(get_lang('Time spent'), ENT_QUOTES), |
||
| 2756 | 'student_response_formatted' => '', |
||
| 2757 | ]; |
||
| 2758 | while ($row = Database::fetch_array($res)) { |
||
| 2759 | $studentResponseFormatted = urldecode($row['student_response']); |
||
| 2760 | $content_student_response = explode('__|', $studentResponseFormatted); |
||
| 2761 | if (count($content_student_response) > 0) { |
||
| 2762 | if (count($content_student_response) >= 3) { |
||
| 2763 | // Pop the element off the end of array. |
||
| 2764 | array_pop($content_student_response); |
||
| 2765 | } |
||
| 2766 | $studentResponseFormatted = implode(',', $content_student_response); |
||
| 2767 | } |
||
| 2768 | |||
| 2769 | $list[] = [ |
||
| 2770 | 'order_id' => $row['order_id'] + 1, |
||
| 2771 | 'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that |
||
| 2772 | 'type' => $row['interaction_type'], |
||
| 2773 | 'time' => $row['completion_time'], |
||
| 2774 | 'correct_responses' => '', // Hide correct responses from students. |
||
| 2775 | 'student_response' => $row['student_response'], |
||
| 2776 | 'result' => $row['result'], |
||
| 2777 | 'latency' => $row['latency'], |
||
| 2778 | 'student_response_formatted' => $studentResponseFormatted, |
||
| 2779 | ]; |
||
| 2780 | } |
||
| 2781 | } |
||
| 2782 | |||
| 2783 | return $list; |
||
| 2784 | } |
||
| 2785 | |||
| 2786 | /** |
||
| 2787 | * Return the number of objectives for the given learnpath Item View ID. |
||
| 2788 | * This method can be used as static. |
||
| 2789 | * |
||
| 2790 | * @param int $lp_iv_id Item View ID |
||
| 2791 | * @param int $course_id Course ID |
||
| 2792 | * |
||
| 2793 | * @return int Number of objectives |
||
| 2794 | */ |
||
| 2795 | public static function get_objectives_count_from_db($lp_iv_id, $course_id) |
||
| 2796 | { |
||
| 2797 | $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE); |
||
| 2798 | $course_id = (int) $course_id; |
||
| 2799 | $lp_iv_id = (int) $lp_iv_id; |
||
| 2800 | $sql = "SELECT count(*) FROM $table |
||
| 2801 | WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id"; |
||
| 2802 | //@todo seems that this always returns 0 |
||
| 2803 | $res = Database::query($sql); |
||
| 2804 | $num = 0; |
||
| 2805 | if (Database::num_rows($res)) { |
||
| 2806 | $row = Database::fetch_array($res); |
||
| 2807 | $num = $row[0]; |
||
| 2808 | } |
||
| 2809 | |||
| 2810 | return $num; |
||
| 2811 | } |
||
| 2812 | |||
| 2813 | /** |
||
| 2814 | * Return the objectives as an array for the given lp_iv_id. |
||
| 2815 | * This method can be used as static. |
||
| 2816 | * |
||
| 2817 | * @param int $lpItemViewId Learnpath Item View ID |
||
| 2818 | * @param int $course_id |
||
| 2819 | * |
||
| 2820 | * @return array |
||
| 2821 | * |
||
| 2822 | * @todo Translate labels |
||
| 2823 | */ |
||
| 2824 | public static function get_iv_objectives_array($lpItemViewId = 0, $course_id = 0) |
||
| 2825 | { |
||
| 2826 | $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id; |
||
| 2827 | $lpItemViewId = (int) $lpItemViewId; |
||
| 2828 | |||
| 2829 | if (empty($course_id) || empty($lpItemViewId)) { |
||
| 2830 | return []; |
||
| 2831 | } |
||
| 2832 | |||
| 2833 | $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE); |
||
| 2834 | $sql = "SELECT * FROM $table |
||
| 2835 | WHERE c_id = $course_id AND lp_iv_id = $lpItemViewId |
||
| 2836 | ORDER BY order_id ASC"; |
||
| 2837 | $res = Database::query($sql); |
||
| 2838 | $num = Database::num_rows($res); |
||
| 2839 | $list = []; |
||
| 2840 | if ($num > 0) { |
||
| 2841 | $list[] = [ |
||
| 2842 | 'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES), |
||
| 2843 | 'objective_id' => api_htmlentities(get_lang('Objective ID'), ENT_QUOTES), |
||
| 2844 | 'score_raw' => api_htmlentities(get_lang('Objective raw score'), ENT_QUOTES), |
||
| 2845 | 'score_max' => api_htmlentities(get_lang('Objective max score'), ENT_QUOTES), |
||
| 2846 | 'score_min' => api_htmlentities(get_lang('Objective min score'), ENT_QUOTES), |
||
| 2847 | 'status' => api_htmlentities(get_lang('Objective status'), ENT_QUOTES), |
||
| 2848 | ]; |
||
| 2849 | while ($row = Database::fetch_array($res)) { |
||
| 2850 | $list[] = [ |
||
| 2851 | 'order_id' => $row['order_id'] + 1, |
||
| 2852 | 'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F |
||
| 2853 | 'score_raw' => $row['score_raw'], |
||
| 2854 | 'score_max' => $row['score_max'], |
||
| 2855 | 'score_min' => $row['score_min'], |
||
| 2856 | 'status' => $row['status'], |
||
| 2857 | ]; |
||
| 2858 | } |
||
| 2859 | } |
||
| 2860 | |||
| 2861 | return $list; |
||
| 2862 | } |
||
| 2863 | |||
| 2864 | /** |
||
| 2865 | * Generate and return the table of contents for this learnpath. The (flat) table returned can be |
||
| 2866 | * used by get_html_toc() to be ready to display. |
||
| 2867 | * |
||
| 2868 | * @return array TOC as a table with 4 elements per row: title, link, status and level |
||
| 2869 | */ |
||
| 2870 | public function get_toc() |
||
| 2871 | { |
||
| 2872 | $toc = []; |
||
| 2873 | foreach ($this->ordered_items as $item_id) { |
||
| 2874 | // TODO: Change this link generation and use new function instead. |
||
| 2875 | $toc[] = [ |
||
| 2876 | 'id' => $item_id, |
||
| 2877 | 'title' => $this->items[$item_id]->get_title(), |
||
| 2878 | 'status' => $this->items[$item_id]->get_status(), |
||
| 2879 | 'level' => $this->items[$item_id]->get_level(), |
||
| 2880 | 'type' => $this->items[$item_id]->get_type(), |
||
| 2881 | 'description' => $this->items[$item_id]->get_description(), |
||
| 2882 | 'path' => $this->items[$item_id]->get_path(), |
||
| 2883 | 'parent' => $this->items[$item_id]->get_parent(), |
||
| 2884 | ]; |
||
| 2885 | } |
||
| 2886 | |||
| 2887 | return $toc; |
||
| 2888 | } |
||
| 2889 | |||
| 2890 | /** |
||
| 2891 | * Returns the CSS class name associated with a given item status. |
||
| 2892 | * |
||
| 2893 | * @param $status string an item status |
||
| 2894 | * |
||
| 2895 | * @return string CSS class name |
||
| 2896 | */ |
||
| 2897 | public static function getStatusCSSClassName($status) |
||
| 2898 | { |
||
| 2899 | if (array_key_exists($status, self::STATUS_CSS_CLASS_NAME)) { |
||
| 2900 | return self::STATUS_CSS_CLASS_NAME[$status]; |
||
| 2901 | } |
||
| 2902 | |||
| 2903 | return ''; |
||
| 2904 | } |
||
| 2905 | |||
| 2906 | /** |
||
| 2907 | * Generate the tree of contents for this learnpath as an associative array tree |
||
| 2908 | * with keys id, title, status, type, description, path, parent_id, children |
||
| 2909 | * (title and descriptions as secured) |
||
| 2910 | * and clues for CSS class composition: |
||
| 2911 | * - booleans is_current, is_parent_of_current, is_chapter |
||
| 2912 | * - string status_css_class_name. |
||
| 2913 | * |
||
| 2914 | * @param $parentId int restrict returned list to children of this parent |
||
| 2915 | * |
||
| 2916 | * @return array TOC as a table |
||
| 2917 | */ |
||
| 2918 | public function getTOCTree($parentId = 0) |
||
| 2919 | { |
||
| 2920 | $toc = []; |
||
| 2921 | $currentItemId = $this->get_current_item_id(); |
||
| 2922 | |||
| 2923 | foreach ($this->ordered_items as $itemId) { |
||
| 2924 | $item = $this->items[$itemId]; |
||
| 2925 | if ($item->get_parent() == $parentId) { |
||
| 2926 | $title = $item->get_title(); |
||
| 2927 | if (empty($title)) { |
||
| 2928 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $itemId); |
||
| 2929 | } |
||
| 2930 | |||
| 2931 | $itemData = [ |
||
| 2932 | 'id' => $itemId, |
||
| 2933 | 'title' => Security::remove_XSS($title), |
||
| 2934 | 'status' => $item->get_status(), |
||
| 2935 | 'level' => $item->get_level(), // FIXME should not be needed |
||
| 2936 | 'type' => $item->get_type(), |
||
| 2937 | 'description' => Security::remove_XSS($item->get_description()), |
||
| 2938 | 'path' => $item->get_path(), |
||
| 2939 | 'parent_id' => $item->get_parent(), |
||
| 2940 | 'children' => $this->getTOCTree($itemId), |
||
| 2941 | 'is_current' => ($itemId == $currentItemId), |
||
| 2942 | 'is_parent_of_current' => false, |
||
| 2943 | 'is_chapter' => in_array($item->get_type(), self::getChapterTypes()), |
||
| 2944 | 'status_css_class_name' => $this->getStatusCSSClassName($item->get_status()), |
||
| 2945 | 'current_id' => $currentItemId, // FIXME should not be needed, not a property of item |
||
| 2946 | ]; |
||
| 2947 | |||
| 2948 | if (!empty($itemData['children'])) { |
||
| 2949 | foreach ($itemData['children'] as $child) { |
||
| 2950 | if ($child['is_current'] || $child['is_parent_of_current']) { |
||
| 2951 | $itemData['is_parent_of_current'] = true; |
||
| 2952 | break; |
||
| 2953 | } |
||
| 2954 | } |
||
| 2955 | } |
||
| 2956 | |||
| 2957 | $toc[] = $itemData; |
||
| 2958 | } |
||
| 2959 | } |
||
| 2960 | |||
| 2961 | return $toc; |
||
| 2962 | } |
||
| 2963 | |||
| 2964 | /** |
||
| 2965 | * Generate and return the table of contents for this learnpath. The JS |
||
| 2966 | * table returned is used inside of scorm_api.php. |
||
| 2967 | * |
||
| 2968 | * @param string $varname |
||
| 2969 | * |
||
| 2970 | * @return string A JS array variable construction |
||
| 2971 | */ |
||
| 2972 | public function get_items_details_as_js($varname = 'olms.lms_item_types') |
||
| 2973 | { |
||
| 2974 | $toc = $varname.' = new Array();'; |
||
| 2975 | foreach ($this->ordered_items as $item_id) { |
||
| 2976 | $toc .= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';"; |
||
| 2977 | } |
||
| 2978 | |||
| 2979 | return $toc; |
||
| 2980 | } |
||
| 2981 | |||
| 2982 | /** |
||
| 2983 | * Gets the learning path type. |
||
| 2984 | * |
||
| 2985 | * @param bool $get_name Return the name? If false, return the ID. Default is false. |
||
| 2986 | * |
||
| 2987 | * @return mixed Type ID or name, depending on the parameter |
||
| 2988 | */ |
||
| 2989 | public function get_type($get_name = false) |
||
| 2990 | { |
||
| 2991 | $res = false; |
||
| 2992 | if (!empty($this->type) && (!$get_name)) { |
||
| 2993 | $res = $this->type; |
||
| 2994 | } |
||
| 2995 | |||
| 2996 | return $res; |
||
| 2997 | } |
||
| 2998 | |||
| 2999 | /** |
||
| 3000 | * Gets the learning path type as static method. |
||
| 3001 | * |
||
| 3002 | * @param int $lp_id |
||
| 3003 | * |
||
| 3004 | * @return mixed Type ID or name, depending on the parameter |
||
| 3005 | */ |
||
| 3006 | public static function get_type_static($lp_id = 0) |
||
| 3007 | { |
||
| 3008 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 3009 | $lp_id = (int) $lp_id; |
||
| 3010 | $sql = "SELECT lp_type FROM $tbl_lp |
||
| 3011 | WHERE iid = $lp_id"; |
||
| 3012 | $res = Database::query($sql); |
||
| 3013 | if (false === $res) { |
||
| 3014 | return null; |
||
| 3015 | } |
||
| 3016 | if (Database::num_rows($res) <= 0) { |
||
| 3017 | return null; |
||
| 3018 | } |
||
| 3019 | $row = Database::fetch_array($res); |
||
| 3020 | |||
| 3021 | return $row['lp_type']; |
||
| 3022 | } |
||
| 3023 | |||
| 3024 | /** |
||
| 3025 | * Gets a flat list of item IDs ordered for display (level by level ordered by order_display) |
||
| 3026 | * This method can be used as abstract and is recursive. |
||
| 3027 | * |
||
| 3028 | * @param CLp $lp |
||
| 3029 | * @param int $parent Parent ID of the items to look for |
||
| 3030 | * |
||
| 3031 | * @return array Ordered list of item IDs (empty array on error) |
||
| 3032 | */ |
||
| 3033 | public static function get_flat_ordered_items_list(CLp $lp, $parent = 0) |
||
| 3034 | { |
||
| 3035 | $parent = (int) $parent; |
||
| 3036 | |||
| 3037 | $criteria = Criteria::create() |
||
| 3038 | ->orderBy( |
||
| 3039 | [ |
||
| 3040 | 'displayOrder' => Criteria::ASC, |
||
| 3041 | ] |
||
| 3042 | ); |
||
| 3043 | $items = $lp->getItems()->matching($criteria); |
||
| 3044 | $items = $items->filter( |
||
| 3045 | function (CLpItem $element) use ($parent) { |
||
| 3046 | if (empty($parent)) { |
||
| 3047 | $parent = null; |
||
| 3048 | return $element->getParent() === $parent; |
||
| 3049 | } else { |
||
| 3050 | if (null !== $element->getParent()) { |
||
| 3051 | return $element->getParent()->getIid() === $parent; |
||
| 3052 | } |
||
| 3053 | return false; |
||
| 3054 | } |
||
| 3055 | } |
||
| 3056 | ); |
||
| 3057 | |||
| 3058 | /* |
||
| 3059 | $lp = (int) $lp; |
||
| 3060 | $parent = (int) $parent; |
||
| 3061 | |||
| 3062 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 3063 | $sql = "SELECT iid FROM $tbl_lp_item |
||
| 3064 | WHERE lp_id = $lp AND parent_item_id = $parent |
||
| 3065 | ORDER BY display_order"; |
||
| 3066 | |||
| 3067 | $res = Database::query($sql); |
||
| 3068 | while ($row = Database::fetch_array($res)) { |
||
| 3069 | $sublist = self::get_flat_ordered_items_list( |
||
| 3070 | $lp, |
||
| 3071 | $row['iid'], |
||
| 3072 | $course_id |
||
| 3073 | ); |
||
| 3074 | $list[] = $row['iid']; |
||
| 3075 | foreach ($sublist as $item) { |
||
| 3076 | $list[] = $item; |
||
| 3077 | } |
||
| 3078 | } |
||
| 3079 | */ |
||
| 3080 | |||
| 3081 | $list = []; |
||
| 3082 | foreach ($items as $item) { |
||
| 3083 | $itemId = $item->getIid(); |
||
| 3084 | $sublist = self::get_flat_ordered_items_list($lp, $itemId); |
||
| 3085 | $list[] = $itemId; |
||
| 3086 | foreach ($sublist as $subItem) { |
||
| 3087 | $list[] = $subItem; |
||
| 3088 | } |
||
| 3089 | } |
||
| 3090 | |||
| 3091 | return $list; |
||
| 3092 | } |
||
| 3093 | |||
| 3094 | /** |
||
| 3095 | * @return array |
||
| 3096 | */ |
||
| 3097 | public static function getChapterTypes() |
||
| 3098 | { |
||
| 3099 | return [ |
||
| 3100 | 'dir', |
||
| 3101 | ]; |
||
| 3102 | } |
||
| 3103 | |||
| 3104 | /** |
||
| 3105 | * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display. |
||
| 3106 | * |
||
| 3107 | * @param $tree |
||
| 3108 | * |
||
| 3109 | * @return array HTML TOC ready to display |
||
| 3110 | */ |
||
| 3111 | public function getParentToc($tree) |
||
| 3112 | { |
||
| 3113 | if (empty($tree)) { |
||
| 3114 | $tree = $this->get_toc(); |
||
| 3115 | } |
||
| 3116 | $dirTypes = self::getChapterTypes(); |
||
| 3117 | $myCurrentId = $this->get_current_item_id(); |
||
| 3118 | $listParent = []; |
||
| 3119 | $listChildren = []; |
||
| 3120 | $listNotParent = []; |
||
| 3121 | $list = []; |
||
| 3122 | foreach ($tree as $subtree) { |
||
| 3123 | if (in_array($subtree['type'], $dirTypes)) { |
||
| 3124 | $listChildren = $this->getChildrenToc($tree, $subtree['id']); |
||
| 3125 | $subtree['children'] = $listChildren; |
||
| 3126 | if (!empty($subtree['children'])) { |
||
| 3127 | foreach ($subtree['children'] as $subItem) { |
||
| 3128 | if ($subItem['id'] == $this->current) { |
||
| 3129 | $subtree['parent_current'] = 'in'; |
||
| 3130 | $subtree['current'] = 'on'; |
||
| 3131 | } |
||
| 3132 | } |
||
| 3133 | } |
||
| 3134 | $listParent[] = $subtree; |
||
| 3135 | } |
||
| 3136 | if (!in_array($subtree['type'], $dirTypes) && null == $subtree['parent']) { |
||
| 3137 | if (array_key_exists($subtree['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
| 3138 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$subtree['status']]; |
||
| 3139 | } |
||
| 3140 | |||
| 3141 | $title = Security::remove_XSS($subtree['title']); |
||
| 3142 | unset($subtree['title']); |
||
| 3143 | |||
| 3144 | if (empty($title)) { |
||
| 3145 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']); |
||
| 3146 | } |
||
| 3147 | $classStyle = null; |
||
| 3148 | if ($subtree['id'] == $this->current) { |
||
| 3149 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
| 3150 | } elseif (!in_array($subtree['type'], $dirTypes)) { |
||
| 3151 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
| 3152 | } |
||
| 3153 | $subtree['title'] = $title; |
||
| 3154 | $subtree['class'] = $classStyle.' '.$cssStatus; |
||
| 3155 | $subtree['url'] = $this->get_link('http', $subtree['id'], $tree); |
||
| 3156 | $subtree['current_id'] = $myCurrentId; |
||
| 3157 | $listNotParent[] = $subtree; |
||
| 3158 | } |
||
| 3159 | } |
||
| 3160 | |||
| 3161 | $list['are_parents'] = $listParent; |
||
| 3162 | $list['not_parents'] = $listNotParent; |
||
| 3163 | |||
| 3164 | return $list; |
||
| 3165 | } |
||
| 3166 | |||
| 3167 | /** |
||
| 3168 | * Uses the table generated by get_toc() and returns an HTML-formattedstring ready to display. |
||
| 3169 | * |
||
| 3170 | * @param array $tree |
||
| 3171 | * @param int $id |
||
| 3172 | * @param bool $parent |
||
| 3173 | * |
||
| 3174 | * @return array HTML TOC ready to display |
||
| 3175 | */ |
||
| 3176 | public function getChildrenToc($tree, $id, $parent = true) |
||
| 3177 | { |
||
| 3178 | if (empty($tree)) { |
||
| 3179 | $tree = $this->get_toc(); |
||
| 3180 | } |
||
| 3181 | |||
| 3182 | $dirTypes = self::getChapterTypes(); |
||
| 3183 | $currentItemId = $this->get_current_item_id(); |
||
| 3184 | $list = []; |
||
| 3185 | |||
| 3186 | foreach ($tree as $subtree) { |
||
| 3187 | $subtree['tree'] = null; |
||
| 3188 | |||
| 3189 | if (!in_array($subtree['type'], $dirTypes) && $subtree['parent'] == $id) { |
||
| 3190 | if ($subtree['id'] == $this->current) { |
||
| 3191 | $subtree['current'] = 'active'; |
||
| 3192 | } else { |
||
| 3193 | $subtree['current'] = null; |
||
| 3194 | } |
||
| 3195 | if (array_key_exists($subtree['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
| 3196 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$subtree['status']]; |
||
| 3197 | } |
||
| 3198 | |||
| 3199 | $title = Security::remove_XSS($subtree['title']); |
||
| 3200 | unset($subtree['title']); |
||
| 3201 | if (empty($title)) { |
||
| 3202 | $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $subtree['id']); |
||
| 3203 | } |
||
| 3204 | |||
| 3205 | $classStyle = null; |
||
| 3206 | if ($subtree['id'] == $this->current) { |
||
| 3207 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
| 3208 | } elseif (!in_array($subtree['type'], $dirTypes)) { |
||
| 3209 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
| 3210 | } |
||
| 3211 | |||
| 3212 | if (in_array($subtree['type'], $dirTypes)) { |
||
| 3213 | $subtree['title'] = stripslashes($title); |
||
| 3214 | } else { |
||
| 3215 | $subtree['title'] = $title; |
||
| 3216 | $subtree['class'] = $classStyle.' '.$cssStatus; |
||
| 3217 | $subtree['url'] = $this->get_link('http', $subtree['id'], $tree); |
||
| 3218 | $subtree['current_id'] = $currentItemId; |
||
| 3219 | } |
||
| 3220 | $list[] = $subtree; |
||
| 3221 | } |
||
| 3222 | } |
||
| 3223 | |||
| 3224 | return $list; |
||
| 3225 | } |
||
| 3226 | |||
| 3227 | /** |
||
| 3228 | * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display. |
||
| 3229 | * |
||
| 3230 | * @param array $toc_list |
||
| 3231 | * |
||
| 3232 | * @return array HTML TOC ready to display |
||
| 3233 | */ |
||
| 3234 | public function getListArrayToc($toc_list = []) |
||
| 3235 | { |
||
| 3236 | if (empty($toc_list)) { |
||
| 3237 | $toc_list = $this->get_toc(); |
||
| 3238 | } |
||
| 3239 | // Temporary variables. |
||
| 3240 | $currentItemId = $this->get_current_item_id(); |
||
| 3241 | $list = []; |
||
| 3242 | $arrayList = []; |
||
| 3243 | |||
| 3244 | foreach ($toc_list as $item) { |
||
| 3245 | $list['id'] = $item['id']; |
||
| 3246 | $list['status'] = $item['status']; |
||
| 3247 | $cssStatus = null; |
||
| 3248 | |||
| 3249 | if (array_key_exists($item['status'], self::STATUS_CSS_CLASS_NAME)) { |
||
| 3250 | $cssStatus = self::STATUS_CSS_CLASS_NAME[$item['status']]; |
||
| 3251 | } |
||
| 3252 | |||
| 3253 | $classStyle = ' '; |
||
| 3254 | $dirTypes = self::getChapterTypes(); |
||
| 3255 | |||
| 3256 | if (in_array($item['type'], $dirTypes)) { |
||
| 3257 | $classStyle = 'scorm_item_section '; |
||
| 3258 | } |
||
| 3259 | if ($item['id'] == $this->current) { |
||
| 3260 | $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight'; |
||
| 3261 | } elseif (!in_array($item['type'], $dirTypes)) { |
||
| 3262 | $classStyle = 'scorm_item_normal '.$classStyle.' '; |
||
| 3263 | } |
||
| 3264 | $title = $item['title']; |
||
| 3265 | if (empty($title)) { |
||
| 3266 | $title = self::rl_get_resource_name( |
||
| 3267 | api_get_course_id(), |
||
| 3268 | $this->get_id(), |
||
| 3269 | $item['id'] |
||
| 3270 | ); |
||
| 3271 | } |
||
| 3272 | $title = Security::remove_XSS($item['title']); |
||
| 3273 | |||
| 3274 | if (empty($item['description'])) { |
||
| 3275 | $list['description'] = $title; |
||
| 3276 | } else { |
||
| 3277 | $list['description'] = $item['description']; |
||
| 3278 | } |
||
| 3279 | |||
| 3280 | $list['class'] = $classStyle.' '.$cssStatus; |
||
| 3281 | $list['level'] = $item['level']; |
||
| 3282 | $list['type'] = $item['type']; |
||
| 3283 | |||
| 3284 | if (in_array($item['type'], $dirTypes)) { |
||
| 3285 | $list['css_level'] = 'level_'.$item['level']; |
||
| 3286 | } else { |
||
| 3287 | $list['css_level'] = 'level_'.$item['level'].' scorm_type_'.self::format_scorm_type_item($item['type']); |
||
| 3288 | } |
||
| 3289 | |||
| 3290 | if (in_array($item['type'], $dirTypes)) { |
||
| 3291 | $list['title'] = stripslashes($title); |
||
| 3292 | } else { |
||
| 3293 | $list['title'] = stripslashes($title); |
||
| 3294 | $list['url'] = $this->get_link('http', $item['id'], $toc_list); |
||
| 3295 | $list['current_id'] = $currentItemId; |
||
| 3296 | } |
||
| 3297 | $arrayList[] = $list; |
||
| 3298 | } |
||
| 3299 | |||
| 3300 | return $arrayList; |
||
| 3301 | } |
||
| 3302 | |||
| 3303 | /** |
||
| 3304 | * Returns an HTML-formatted string ready to display with teacher buttons |
||
| 3305 | * in LP view menu. |
||
| 3306 | * |
||
| 3307 | * @return string HTML TOC ready to display |
||
| 3308 | */ |
||
| 3309 | public function get_teacher_toc_buttons() |
||
| 3310 | { |
||
| 3311 | $isAllow = api_is_allowed_to_edit(null, true, false, false); |
||
| 3312 | $hideIcons = api_get_configuration_value('hide_teacher_icons_lp'); |
||
| 3313 | $html = ''; |
||
| 3314 | if ($isAllow && false == $hideIcons) { |
||
| 3315 | if ($this->get_lp_session_id() == api_get_session_id()) { |
||
| 3316 | $html .= '<div id="actions_lp" class="actions_lp"><hr>'; |
||
| 3317 | $html .= '<div class="btn-group">'; |
||
| 3318 | $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'>". |
||
| 3319 | Display::returnFontAwesomeIcon('street-view').get_lang('Overview')."</a>"; |
||
| 3320 | $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'>". |
||
| 3321 | Display::returnFontAwesomeIcon('pencil').get_lang('Edit')."</a>"; |
||
| 3322 | $html .= '<a class="btn btn-sm btn-default" href="lp_controller.php?'.api_get_cidreq()."&action=edit&lp_id=".$this->lp_id.'&isStudentView=false">'. |
||
| 3323 | Display::returnFontAwesomeIcon('cog').get_lang('Settings').'</a>'; |
||
| 3324 | $html .= '</div>'; |
||
| 3325 | $html .= '</div>'; |
||
| 3326 | } |
||
| 3327 | } |
||
| 3328 | |||
| 3329 | return $html; |
||
| 3330 | } |
||
| 3331 | |||
| 3332 | /** |
||
| 3333 | * Gets the learnpath maker name - generally the editor's name. |
||
| 3334 | * |
||
| 3335 | * @return string Learnpath maker name |
||
| 3336 | */ |
||
| 3337 | public function get_maker() |
||
| 3344 | } |
||
| 3345 | |||
| 3346 | /** |
||
| 3347 | * Gets the learnpath name/title. |
||
| 3348 | * |
||
| 3349 | * @return string Learnpath name/title |
||
| 3350 | */ |
||
| 3351 | public function get_name() |
||
| 3352 | { |
||
| 3353 | if (!empty($this->name)) { |
||
| 3354 | return $this->name; |
||
| 3355 | } |
||
| 3356 | |||
| 3357 | return 'N/A'; |
||
| 3358 | } |
||
| 3359 | |||
| 3360 | /** |
||
| 3361 | * @return string |
||
| 3362 | */ |
||
| 3363 | public function getNameNoTags() |
||
| 3366 | } |
||
| 3367 | |||
| 3368 | /** |
||
| 3369 | * Gets a link to the resource from the present location, depending on item ID. |
||
| 3370 | * |
||
| 3371 | * @param string $type Type of link expected |
||
| 3372 | * @param int $item_id Learnpath item ID |
||
| 3373 | * @param bool $provided_toc |
||
| 3374 | * |
||
| 3375 | * @return string $provided_toc Link to the lp_item resource |
||
| 3376 | */ |
||
| 3377 | public function get_link($type = 'http', $item_id = 0, $provided_toc = false) |
||
| 3378 | { |
||
| 3379 | $course_id = $this->get_course_int_id(); |
||
| 3380 | $item_id = (int) $item_id; |
||
| 3381 | |||
| 3382 | if (empty($item_id)) { |
||
| 3383 | $item_id = $this->get_current_item_id(); |
||
| 3384 | |||
| 3385 | if (empty($item_id)) { |
||
| 3386 | //still empty, this means there was no item_id given and we are not in an object context or |
||
| 3387 | //the object property is empty, return empty link |
||
| 3388 | $this->first(); |
||
| 3389 | |||
| 3390 | return ''; |
||
| 3391 | } |
||
| 3392 | } |
||
| 3393 | |||
| 3394 | $file = ''; |
||
| 3395 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 3396 | $lp_item_table = Database::get_course_table(TABLE_LP_ITEM); |
||
| 3397 | $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW); |
||
| 3398 | |||
| 3399 | $sql = "SELECT |
||
| 3400 | l.lp_type as ltype, |
||
| 3401 | l.path as lpath, |
||
| 3402 | li.item_type as litype, |
||
| 3403 | li.path as lipath, |
||
| 3404 | li.parameters as liparams |
||
| 3405 | FROM $lp_table l |
||
| 3406 | INNER JOIN $lp_item_table li |
||
| 3407 | ON (li.lp_id = l.iid) |
||
| 3408 | WHERE |
||
| 3409 | li.iid = $item_id |
||
| 3410 | "; |
||
| 3411 | $res = Database::query($sql); |
||
| 3412 | if (Database::num_rows($res) > 0) { |
||
| 3413 | $row = Database::fetch_array($res); |
||
| 3414 | $lp_type = $row['ltype']; |
||
| 3415 | $lp_path = $row['lpath']; |
||
| 3416 | $lp_item_type = $row['litype']; |
||
| 3417 | $lp_item_path = $row['lipath']; |
||
| 3418 | $lp_item_params = $row['liparams']; |
||
| 3419 | if (empty($lp_item_params) && false !== strpos($lp_item_path, '?')) { |
||
| 3420 | [$lp_item_path, $lp_item_params] = explode('?', $lp_item_path); |
||
| 3421 | } |
||
| 3422 | //$sys_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path(); |
||
| 3423 | if ('http' === $type) { |
||
| 3424 | //web path |
||
| 3425 | //$course_path = api_get_path(WEB_COURSE_PATH).api_get_course_path(); |
||
| 3426 | } else { |
||
| 3427 | //$course_path = $sys_course_path; //system path |
||
| 3428 | } |
||
| 3429 | |||
| 3430 | // Fixed issue BT#1272 - If the item type is a Chamilo Item (quiz, link, etc), |
||
| 3431 | // then change the lp type to thread it as a normal Chamilo LP not a SCO. |
||
| 3432 | if (in_array( |
||
| 3433 | $lp_item_type, |
||
| 3434 | ['quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication'] |
||
| 3435 | ) |
||
| 3436 | ) { |
||
| 3437 | $lp_type = CLp::LP_TYPE; |
||
| 3438 | } |
||
| 3439 | |||
| 3440 | // Now go through the specific cases to get the end of the path |
||
| 3441 | // @todo Use constants instead of int values. |
||
| 3442 | switch ($lp_type) { |
||
| 3443 | case CLp::LP_TYPE: |
||
| 3444 | $file = self::rl_get_resource_link_for_learnpath( |
||
| 3445 | $course_id, |
||
| 3446 | $this->get_id(), |
||
| 3447 | $item_id, |
||
| 3448 | $this->get_view_id() |
||
| 3449 | ); |
||
| 3450 | switch ($lp_item_type) { |
||
| 3451 | case 'document': |
||
| 3452 | // Shows a button to download the file instead of just downloading the file directly. |
||
| 3453 | $documentPathInfo = pathinfo($file); |
||
| 3454 | if (isset($documentPathInfo['extension'])) { |
||
| 3455 | $parsed = parse_url($documentPathInfo['extension']); |
||
| 3456 | if (isset($parsed['path'])) { |
||
| 3457 | $extension = $parsed['path']; |
||
| 3458 | $extensionsToDownload = [ |
||
| 3459 | 'zip', |
||
| 3460 | 'ppt', |
||
| 3461 | 'pptx', |
||
| 3462 | 'ods', |
||
| 3463 | 'xlsx', |
||
| 3464 | 'xls', |
||
| 3465 | 'csv', |
||
| 3466 | 'doc', |
||
| 3467 | 'docx', |
||
| 3468 | 'dot', |
||
| 3469 | ]; |
||
| 3470 | |||
| 3471 | if (in_array($extension, $extensionsToDownload)) { |
||
| 3472 | $file = api_get_path(WEB_CODE_PATH). |
||
| 3473 | 'lp/embed.php?type=download&source=file&lp_item_id='.$item_id.'&'.api_get_cidreq(); |
||
| 3474 | } |
||
| 3475 | } |
||
| 3476 | } |
||
| 3477 | break; |
||
| 3478 | case 'dir': |
||
| 3479 | $file = 'lp_content.php?type=dir'; |
||
| 3480 | break; |
||
| 3481 | case 'link': |
||
| 3482 | if (Link::is_youtube_link($file)) { |
||
| 3483 | $src = Link::get_youtube_video_id($file); |
||
| 3484 | $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=youtube&source='.$src; |
||
| 3485 | } elseif (Link::isVimeoLink($file)) { |
||
| 3486 | $src = Link::getVimeoLinkId($file); |
||
| 3487 | $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=vimeo&source='.$src; |
||
| 3488 | } else { |
||
| 3489 | // If the current site is HTTPS and the link is |
||
| 3490 | // HTTP, browsers will refuse opening the link |
||
| 3491 | $urlId = api_get_current_access_url_id(); |
||
| 3492 | $url = api_get_access_url($urlId, false); |
||
| 3493 | $protocol = substr($url['url'], 0, 5); |
||
| 3494 | if ('https' === $protocol) { |
||
| 3495 | $linkProtocol = substr($file, 0, 5); |
||
| 3496 | if ('http:' === $linkProtocol) { |
||
| 3497 | //this is the special intervention case |
||
| 3498 | $file = api_get_path(WEB_CODE_PATH). |
||
| 3499 | 'lp/embed.php?type=nonhttps&source='.urlencode($file); |
||
| 3500 | } |
||
| 3501 | } |
||
| 3502 | } |
||
| 3503 | break; |
||
| 3504 | case 'quiz': |
||
| 3505 | // Check how much attempts of a exercise exits in lp |
||
| 3506 | $lp_item_id = $this->get_current_item_id(); |
||
| 3507 | $lp_view_id = $this->get_view_id(); |
||
| 3508 | |||
| 3509 | $prevent_reinit = null; |
||
| 3510 | if (isset($this->items[$this->current])) { |
||
| 3511 | $prevent_reinit = $this->items[$this->current]->get_prevent_reinit(); |
||
| 3512 | } |
||
| 3513 | |||
| 3514 | if (empty($provided_toc)) { |
||
| 3515 | $list = $this->get_toc(); |
||
| 3516 | } else { |
||
| 3517 | $list = $provided_toc; |
||
| 3518 | } |
||
| 3519 | |||
| 3520 | $type_quiz = false; |
||
| 3521 | foreach ($list as $toc) { |
||
| 3522 | if ($toc['id'] == $lp_item_id && 'quiz' === $toc['type']) { |
||
| 3523 | $type_quiz = true; |
||
| 3524 | } |
||
| 3525 | } |
||
| 3526 | |||
| 3527 | if ($type_quiz) { |
||
| 3528 | $lp_item_id = (int) $lp_item_id; |
||
| 3529 | $lp_view_id = (int) $lp_view_id; |
||
| 3530 | $sql = "SELECT count(*) FROM $lp_item_view_table |
||
| 3531 | WHERE |
||
| 3532 | c_id = $course_id AND |
||
| 3533 | lp_item_id='".$lp_item_id."' AND |
||
| 3534 | lp_view_id ='".$lp_view_id."' AND |
||
| 3535 | status='completed'"; |
||
| 3536 | $result = Database::query($sql); |
||
| 3537 | $row_count = Database:: fetch_row($result); |
||
| 3538 | $count_item_view = (int) $row_count[0]; |
||
| 3539 | $not_multiple_attempt = 0; |
||
| 3540 | if (1 === $prevent_reinit && $count_item_view > 0) { |
||
| 3541 | $not_multiple_attempt = 1; |
||
| 3542 | } |
||
| 3543 | $file .= '¬_multiple_attempt='.$not_multiple_attempt; |
||
| 3544 | } |
||
| 3545 | break; |
||
| 3546 | } |
||
| 3547 | |||
| 3548 | $tmp_array = explode('/', $file); |
||
| 3549 | $document_name = $tmp_array[count($tmp_array) - 1]; |
||
| 3550 | if (strpos($document_name, '_DELETED_')) { |
||
| 3551 | $file = 'blank.php?error=document_deleted'; |
||
| 3552 | } |
||
| 3553 | break; |
||
| 3554 | case CLp::SCORM_TYPE: |
||
| 3555 | if ('dir' !== $lp_item_type) { |
||
| 3556 | // Quite complex here: |
||
| 3557 | // We want to make sure 'http://' (and similar) links can |
||
| 3558 | // be loaded as is (withouth the Chamilo path in front) but |
||
| 3559 | // some contents use this form: resource.htm?resource=http://blablabla |
||
| 3560 | // which means we have to find a protocol at the path's start, otherwise |
||
| 3561 | // it should not be considered as an external URL. |
||
| 3562 | // if ($this->prerequisites_match($item_id)) { |
||
| 3563 | if (0 != preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path)) { |
||
| 3564 | if ($this->debug > 2) { |
||
| 3565 | error_log('In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0); |
||
| 3566 | } |
||
| 3567 | // Distant url, return as is. |
||
| 3568 | $file = $lp_item_path; |
||
| 3569 | } else { |
||
| 3570 | if ($this->debug > 2) { |
||
| 3571 | error_log('In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path); |
||
| 3572 | } |
||
| 3573 | // Prevent getting untranslatable urls. |
||
| 3574 | $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path); |
||
| 3575 | $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path); |
||
| 3576 | |||
| 3577 | /*$asset = $this->getEntity()->getAsset(); |
||
| 3578 | $folder = Container::getAssetRepository()->getFolder($asset); |
||
| 3579 | $hasFile = Container::getAssetRepository()->getFileSystem()->has($folder.$lp_item_path); |
||
| 3580 | $file = null; |
||
| 3581 | if ($hasFile) { |
||
| 3582 | $file = Container::getAssetRepository()->getAssetUrl($asset).'/'.$lp_item_path; |
||
| 3583 | }*/ |
||
| 3584 | $file = $this->scormUrl.$lp_item_path; |
||
| 3585 | |||
| 3586 | // Prepare the path. |
||
| 3587 | /*$file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path; |
||
| 3588 | // TODO: Fix this for urls with protocol header. |
||
| 3589 | $file = str_replace('//', '/', $file); |
||
| 3590 | $file = str_replace(':/', '://', $file); |
||
| 3591 | if ('/' === substr($lp_path, -1)) { |
||
| 3592 | $lp_path = substr($lp_path, 0, -1); |
||
| 3593 | }*/ |
||
| 3594 | /*if (!$hasFile) { |
||
| 3595 | // if file not found. |
||
| 3596 | $decoded = html_entity_decode($lp_item_path); |
||
| 3597 | [$decoded] = explode('?', $decoded); |
||
| 3598 | if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$decoded))) { |
||
| 3599 | $file = self::rl_get_resource_link_for_learnpath( |
||
| 3600 | $course_id, |
||
| 3601 | $this->get_id(), |
||
| 3602 | $item_id, |
||
| 3603 | $this->get_view_id() |
||
| 3604 | ); |
||
| 3605 | if (empty($file)) { |
||
| 3606 | $file = 'blank.php?error=document_not_found'; |
||
| 3607 | } else { |
||
| 3608 | $tmp_array = explode('/', $file); |
||
| 3609 | $document_name = $tmp_array[count($tmp_array) - 1]; |
||
| 3610 | if (strpos($document_name, '_DELETED_')) { |
||
| 3611 | $file = 'blank.php?error=document_deleted'; |
||
| 3612 | } else { |
||
| 3613 | $file = 'blank.php?error=document_not_found'; |
||
| 3614 | } |
||
| 3615 | } |
||
| 3616 | } else { |
||
| 3617 | $file = $course_path.'/scorm/'.$lp_path.'/'.$decoded; |
||
| 3618 | } |
||
| 3619 | }*/ |
||
| 3620 | } |
||
| 3621 | |||
| 3622 | // We want to use parameters if they were defined in the imsmanifest |
||
| 3623 | if (false === strpos($file, 'blank.php')) { |
||
| 3624 | $lp_item_params = ltrim($lp_item_params, '?'); |
||
| 3625 | $file .= (false === strstr($file, '?') ? '?' : '').$lp_item_params; |
||
| 3626 | } |
||
| 3627 | } else { |
||
| 3628 | $file = 'lp_content.php?type=dir'; |
||
| 3629 | } |
||
| 3630 | break; |
||
| 3631 | case CLp::AICC_TYPE: |
||
| 3632 | // Formatting AICC HACP append URL. |
||
| 3633 | $aicc_append = '?aicc_sid='. |
||
| 3634 | urlencode(session_id()).'&aicc_url='.urlencode(api_get_path(WEB_CODE_PATH).'lp/aicc_hacp.php').'&'; |
||
| 3635 | if (!empty($lp_item_params)) { |
||
| 3636 | $aicc_append .= $lp_item_params.'&'; |
||
| 3637 | } |
||
| 3638 | if ('dir' !== $lp_item_type) { |
||
| 3639 | // Quite complex here: |
||
| 3640 | // We want to make sure 'http://' (and similar) links can |
||
| 3641 | // be loaded as is (withouth the Chamilo path in front) but |
||
| 3642 | // some contents use this form: resource.htm?resource=http://blablabla |
||
| 3643 | // which means we have to find a protocol at the path's start, otherwise |
||
| 3644 | // it should not be considered as an external URL. |
||
| 3645 | if (0 != preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path)) { |
||
| 3646 | if ($this->debug > 2) { |
||
| 3647 | error_log('In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0); |
||
| 3648 | } |
||
| 3649 | // Distant url, return as is. |
||
| 3650 | $file = $lp_item_path; |
||
| 3651 | // Enabled and modified by Ivan Tcholakov, 16-OCT-2008. |
||
| 3652 | /* |
||
| 3653 | if (stristr($file,'<servername>') !== false) { |
||
| 3654 | $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path); |
||
| 3655 | } |
||
| 3656 | */ |
||
| 3657 | if (false !== stripos($file, '<servername>')) { |
||
| 3658 | //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path); |
||
| 3659 | $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path)); |
||
| 3660 | $file = str_replace('<servername>', $web_course_path.'/scorm/'.$lp_path, $lp_item_path); |
||
| 3661 | } |
||
| 3662 | |||
| 3663 | $file .= $aicc_append; |
||
| 3664 | } else { |
||
| 3665 | if ($this->debug > 2) { |
||
| 3666 | error_log('In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0); |
||
| 3667 | } |
||
| 3668 | // Prevent getting untranslatable urls. |
||
| 3669 | $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path); |
||
| 3670 | $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path); |
||
| 3671 | // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name. |
||
| 3672 | $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path; |
||
| 3673 | // TODO: Fix this for urls with protocol header. |
||
| 3674 | $file = str_replace('//', '/', $file); |
||
| 3675 | $file = str_replace(':/', '://', $file); |
||
| 3676 | $file .= $aicc_append; |
||
| 3677 | } |
||
| 3678 | } else { |
||
| 3679 | $file = 'lp_content.php?type=dir'; |
||
| 3680 | } |
||
| 3681 | break; |
||
| 3682 | case 4: |
||
| 3683 | default: |
||
| 3684 | break; |
||
| 3685 | } |
||
| 3686 | // Replace & by & because & will break URL with params |
||
| 3687 | $file = !empty($file) ? str_replace('&', '&', $file) : ''; |
||
| 3688 | } |
||
| 3689 | if ($this->debug > 2) { |
||
| 3690 | error_log('In learnpath::get_link() - returning "'.$file.'" from get_link', 0); |
||
| 3691 | } |
||
| 3692 | |||
| 3693 | return $file; |
||
| 3694 | } |
||
| 3695 | |||
| 3696 | /** |
||
| 3697 | * Gets the latest usable view or generate a new one. |
||
| 3698 | * |
||
| 3699 | * @param int $attempt_num Optional attempt number. If none given, takes the highest from the lp_view table |
||
| 3700 | * @param int $userId The user ID, as $this->get_user_id() is not always available |
||
| 3701 | * |
||
| 3702 | * @return int DB lp_view id |
||
| 3703 | */ |
||
| 3704 | public function get_view($attempt_num = 0, $userId = null) |
||
| 3705 | { |
||
| 3706 | $search = ''; |
||
| 3707 | // Use $attempt_num to enable multi-views management (disabled so far). |
||
| 3708 | if (0 != $attempt_num && intval(strval($attempt_num)) == $attempt_num) { |
||
| 3709 | $search = 'AND view_count = '.$attempt_num; |
||
| 3710 | } |
||
| 3711 | // When missing $attempt_num, search for a unique lp_view record for this lp and user. |
||
| 3712 | $lp_view_table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 3713 | |||
| 3714 | $course_id = api_get_course_int_id(); |
||
| 3715 | $sessionId = api_get_session_id(); |
||
| 3716 | |||
| 3717 | // Check user ID. |
||
| 3718 | if (empty($userId)) { |
||
| 3719 | if (empty($this->get_user_id())) { |
||
| 3720 | $this->error = 'User ID is empty in learnpath::get_view()'; |
||
| 3721 | |||
| 3722 | return null; |
||
| 3723 | } else { |
||
| 3724 | $userId = $this->get_user_id(); |
||
| 3725 | } |
||
| 3726 | } |
||
| 3727 | |||
| 3728 | $sql = "SELECT iid, view_count FROM $lp_view_table |
||
| 3729 | WHERE |
||
| 3730 | c_id = $course_id AND |
||
| 3731 | lp_id = ".$this->get_id()." AND |
||
| 3732 | user_id = ".$userId." AND |
||
| 3733 | session_id = $sessionId |
||
| 3734 | $search |
||
| 3735 | ORDER BY view_count DESC"; |
||
| 3736 | $res = Database::query($sql); |
||
| 3737 | if (Database::num_rows($res) > 0) { |
||
| 3738 | $row = Database::fetch_array($res); |
||
| 3739 | $this->lp_view_id = $row['iid']; |
||
| 3740 | } elseif (!api_is_invitee()) { |
||
| 3741 | // There is no database record, create one. |
||
| 3742 | $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) VALUES |
||
| 3743 | ($course_id, ".$this->get_id().",".$this->get_user_id().", 1, $sessionId)"; |
||
| 3744 | Database::query($sql); |
||
| 3745 | $id = Database::insert_id(); |
||
| 3746 | $this->lp_view_id = $id; |
||
| 3747 | } |
||
| 3748 | |||
| 3749 | return $this->lp_view_id; |
||
| 3750 | } |
||
| 3751 | |||
| 3752 | /** |
||
| 3753 | * Gets the current view id. |
||
| 3754 | * |
||
| 3755 | * @return int View ID (from lp_view) |
||
| 3756 | */ |
||
| 3757 | public function get_view_id() |
||
| 3758 | { |
||
| 3759 | if (!empty($this->lp_view_id)) { |
||
| 3760 | return (int) $this->lp_view_id; |
||
| 3761 | } |
||
| 3762 | |||
| 3763 | return 0; |
||
| 3764 | } |
||
| 3765 | |||
| 3766 | /** |
||
| 3767 | * Gets the update queue. |
||
| 3768 | * |
||
| 3769 | * @return array Array containing IDs of items to be updated by JavaScript |
||
| 3770 | */ |
||
| 3771 | public function get_update_queue() |
||
| 3772 | { |
||
| 3773 | return $this->update_queue; |
||
| 3774 | } |
||
| 3775 | |||
| 3776 | /** |
||
| 3777 | * Gets the user ID. |
||
| 3778 | * |
||
| 3779 | * @return int User ID |
||
| 3780 | */ |
||
| 3781 | public function get_user_id() |
||
| 3782 | { |
||
| 3783 | if (!empty($this->user_id)) { |
||
| 3784 | return (int) $this->user_id; |
||
| 3785 | } |
||
| 3786 | |||
| 3787 | return false; |
||
| 3788 | } |
||
| 3789 | |||
| 3790 | /** |
||
| 3791 | * Checks if any of the items has an audio element attached. |
||
| 3792 | * |
||
| 3793 | * @return bool True or false |
||
| 3794 | */ |
||
| 3795 | public function has_audio() |
||
| 3796 | { |
||
| 3797 | $has = false; |
||
| 3798 | foreach ($this->items as $i => $item) { |
||
| 3799 | if (!empty($this->items[$i]->audio)) { |
||
| 3800 | $has = true; |
||
| 3801 | break; |
||
| 3802 | } |
||
| 3803 | } |
||
| 3804 | |||
| 3805 | return $has; |
||
| 3806 | } |
||
| 3807 | |||
| 3808 | /** |
||
| 3809 | * Moves an item up and down at its level. |
||
| 3810 | * |
||
| 3811 | * @param int $id Item to move up and down |
||
| 3812 | * @param string $direction Direction 'up' or 'down' |
||
| 3813 | * |
||
| 3814 | * @return bool|int |
||
| 3815 | */ |
||
| 3816 | public function move_item($id, $direction) |
||
| 3817 | { |
||
| 3818 | $course_id = api_get_course_int_id(); |
||
| 3819 | if (empty($id) || empty($direction)) { |
||
| 3820 | return false; |
||
| 3821 | } |
||
| 3822 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 3823 | $sql_sel = "SELECT * |
||
| 3824 | FROM $tbl_lp_item |
||
| 3825 | WHERE |
||
| 3826 | iid = $id |
||
| 3827 | "; |
||
| 3828 | $res_sel = Database::query($sql_sel); |
||
| 3829 | // Check if elem exists. |
||
| 3830 | if (Database::num_rows($res_sel) < 1) { |
||
| 3831 | return false; |
||
| 3832 | } |
||
| 3833 | // Gather data. |
||
| 3834 | $row = Database::fetch_array($res_sel); |
||
| 3835 | $previous = $row['previous_item_id']; |
||
| 3836 | $next = $row['next_item_id']; |
||
| 3837 | $display = $row['display_order']; |
||
| 3838 | $parent = $row['parent_item_id']; |
||
| 3839 | $lp = $row['lp_id']; |
||
| 3840 | // Update the item (switch with previous/next one). |
||
| 3841 | switch ($direction) { |
||
| 3842 | case 'up': |
||
| 3843 | if ($display > 1) { |
||
| 3844 | $sql_sel2 = "SELECT * FROM $tbl_lp_item |
||
| 3845 | WHERE iid = $previous"; |
||
| 3846 | $res_sel2 = Database::query($sql_sel2); |
||
| 3847 | if (Database::num_rows($res_sel2) < 1) { |
||
| 3848 | $previous_previous = 0; |
||
| 3849 | } |
||
| 3850 | // Gather data. |
||
| 3851 | $row2 = Database::fetch_array($res_sel2); |
||
| 3852 | $previous_previous = $row2['previous_item_id']; |
||
| 3853 | // Update previous_previous item (switch "next" with current). |
||
| 3854 | if (0 != $previous_previous) { |
||
| 3855 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3856 | next_item_id = $id |
||
| 3857 | WHERE iid = $previous_previous"; |
||
| 3858 | Database::query($sql_upd2); |
||
| 3859 | } |
||
| 3860 | // Update previous item (switch with current). |
||
| 3861 | if (0 != $previous) { |
||
| 3862 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3863 | next_item_id = $next, |
||
| 3864 | previous_item_id = $id, |
||
| 3865 | display_order = display_order +1 |
||
| 3866 | WHERE iid = $previous"; |
||
| 3867 | Database::query($sql_upd2); |
||
| 3868 | } |
||
| 3869 | |||
| 3870 | // Update current item (switch with previous). |
||
| 3871 | if (0 != $id) { |
||
| 3872 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3873 | next_item_id = $previous, |
||
| 3874 | previous_item_id = $previous_previous, |
||
| 3875 | display_order = display_order-1 |
||
| 3876 | WHERE c_id = ".$course_id." AND id = $id"; |
||
| 3877 | Database::query($sql_upd2); |
||
| 3878 | } |
||
| 3879 | // Update next item (new previous item). |
||
| 3880 | if (!empty($next)) { |
||
| 3881 | $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous |
||
| 3882 | WHERE iid = $next"; |
||
| 3883 | Database::query($sql_upd2); |
||
| 3884 | } |
||
| 3885 | $display = $display - 1; |
||
| 3886 | } |
||
| 3887 | break; |
||
| 3888 | case 'down': |
||
| 3889 | if (0 != $next) { |
||
| 3890 | $sql_sel2 = "SELECT * FROM $tbl_lp_item |
||
| 3891 | WHERE iid = $next"; |
||
| 3892 | $res_sel2 = Database::query($sql_sel2); |
||
| 3893 | if (Database::num_rows($res_sel2) < 1) { |
||
| 3894 | $next_next = 0; |
||
| 3895 | } |
||
| 3896 | // Gather data. |
||
| 3897 | $row2 = Database::fetch_array($res_sel2); |
||
| 3898 | $next_next = $row2['next_item_id']; |
||
| 3899 | // Update previous item (switch with current). |
||
| 3900 | if (0 != $previous) { |
||
| 3901 | $sql_upd2 = "UPDATE $tbl_lp_item |
||
| 3902 | SET next_item_id = $next |
||
| 3903 | WHERE iid = $previous"; |
||
| 3904 | Database::query($sql_upd2); |
||
| 3905 | } |
||
| 3906 | // Update current item (switch with previous). |
||
| 3907 | if (0 != $id) { |
||
| 3908 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3909 | previous_item_id = $next, |
||
| 3910 | next_item_id = $next_next, |
||
| 3911 | display_order = display_order + 1 |
||
| 3912 | WHERE iid = $id"; |
||
| 3913 | Database::query($sql_upd2); |
||
| 3914 | } |
||
| 3915 | |||
| 3916 | // Update next item (new previous item). |
||
| 3917 | if (0 != $next) { |
||
| 3918 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3919 | previous_item_id = $previous, |
||
| 3920 | next_item_id = $id, |
||
| 3921 | display_order = display_order-1 |
||
| 3922 | WHERE iid = $next"; |
||
| 3923 | Database::query($sql_upd2); |
||
| 3924 | } |
||
| 3925 | |||
| 3926 | // Update next_next item (switch "previous" with current). |
||
| 3927 | if (0 != $next_next) { |
||
| 3928 | $sql_upd2 = "UPDATE $tbl_lp_item SET |
||
| 3929 | previous_item_id = $id |
||
| 3930 | WHERE iid = $next_next"; |
||
| 3931 | Database::query($sql_upd2); |
||
| 3932 | } |
||
| 3933 | $display = $display + 1; |
||
| 3934 | } |
||
| 3935 | break; |
||
| 3936 | default: |
||
| 3937 | return false; |
||
| 3938 | } |
||
| 3939 | |||
| 3940 | return $display; |
||
| 3941 | } |
||
| 3942 | |||
| 3943 | /** |
||
| 3944 | * Move a LP up (display_order). |
||
| 3945 | * |
||
| 3946 | * @param int $lp_id Learnpath ID |
||
| 3947 | * @param int $categoryId Category ID |
||
| 3948 | * |
||
| 3949 | * @return bool |
||
| 3950 | */ |
||
| 3951 | public static function move_up($lp_id, $categoryId = 0) |
||
| 3952 | { |
||
| 3953 | $courseId = api_get_course_int_id(); |
||
| 3954 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 3955 | |||
| 3956 | $categoryCondition = ''; |
||
| 3957 | if (!empty($categoryId)) { |
||
| 3958 | $categoryId = (int) $categoryId; |
||
| 3959 | $categoryCondition = " AND category_id = $categoryId"; |
||
| 3960 | } |
||
| 3961 | $sql = "SELECT * FROM $lp_table |
||
| 3962 | WHERE c_id = $courseId |
||
| 3963 | $categoryCondition |
||
| 3964 | ORDER BY display_order"; |
||
| 3965 | $res = Database::query($sql); |
||
| 3966 | if (false === $res) { |
||
| 3967 | return false; |
||
| 3968 | } |
||
| 3969 | |||
| 3970 | $lps = []; |
||
| 3971 | $lp_order = []; |
||
| 3972 | $num = Database::num_rows($res); |
||
| 3973 | // First check the order is correct, globally (might be wrong because |
||
| 3974 | // of versions < 1.8.4) |
||
| 3975 | if ($num > 0) { |
||
| 3976 | $i = 1; |
||
| 3977 | while ($row = Database::fetch_array($res)) { |
||
| 3978 | if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it. |
||
| 3979 | $sql = "UPDATE $lp_table SET display_order = $i |
||
| 3980 | WHERE iid = ".$row['iid']; |
||
| 3981 | Database::query($sql); |
||
| 3982 | } |
||
| 3983 | $row['display_order'] = $i; |
||
| 3984 | $lps[$row['iid']] = $row; |
||
| 3985 | $lp_order[$i] = $row['iid']; |
||
| 3986 | $i++; |
||
| 3987 | } |
||
| 3988 | } |
||
| 3989 | if ($num > 1) { // If there's only one element, no need to sort. |
||
| 3990 | $order = $lps[$lp_id]['display_order']; |
||
| 3991 | if ($order > 1) { // If it's the first element, no need to move up. |
||
| 3992 | $sql = "UPDATE $lp_table SET display_order = $order |
||
| 3993 | WHERE iid = ".$lp_order[$order - 1]; |
||
| 3994 | Database::query($sql); |
||
| 3995 | $sql = "UPDATE $lp_table SET display_order = ".($order - 1)." |
||
| 3996 | WHERE iid = $lp_id"; |
||
| 3997 | Database::query($sql); |
||
| 3998 | } |
||
| 3999 | } |
||
| 4000 | |||
| 4001 | return true; |
||
| 4002 | } |
||
| 4003 | |||
| 4004 | /** |
||
| 4005 | * Move a learnpath down (display_order). |
||
| 4006 | * |
||
| 4007 | * @param int $lp_id Learnpath ID |
||
| 4008 | * @param int $categoryId Category ID |
||
| 4009 | * |
||
| 4010 | * @return bool |
||
| 4011 | */ |
||
| 4012 | public static function move_down($lp_id, $categoryId = 0) |
||
| 4013 | { |
||
| 4014 | $courseId = api_get_course_int_id(); |
||
| 4015 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 4016 | |||
| 4017 | $categoryCondition = ''; |
||
| 4018 | if (!empty($categoryId)) { |
||
| 4019 | $categoryId = (int) $categoryId; |
||
| 4020 | $categoryCondition = " AND category_id = $categoryId"; |
||
| 4021 | } |
||
| 4022 | |||
| 4023 | $sql = "SELECT * FROM $lp_table |
||
| 4024 | WHERE c_id = $courseId |
||
| 4025 | $categoryCondition |
||
| 4026 | ORDER BY display_order"; |
||
| 4027 | $res = Database::query($sql); |
||
| 4028 | if (false === $res) { |
||
| 4029 | return false; |
||
| 4030 | } |
||
| 4031 | $lps = []; |
||
| 4032 | $lp_order = []; |
||
| 4033 | $num = Database::num_rows($res); |
||
| 4034 | $max = 0; |
||
| 4035 | // First check the order is correct, globally (might be wrong because |
||
| 4036 | // of versions < 1.8.4). |
||
| 4037 | if ($num > 0) { |
||
| 4038 | $i = 1; |
||
| 4039 | while ($row = Database::fetch_array($res)) { |
||
| 4040 | $max = $i; |
||
| 4041 | if ($row['display_order'] != $i) { |
||
| 4042 | // If we find a gap in the order, we need to fix it. |
||
| 4043 | $sql = "UPDATE $lp_table SET display_order = $i |
||
| 4044 | WHERE iid = ".$row['iid']; |
||
| 4045 | Database::query($sql); |
||
| 4046 | } |
||
| 4047 | $row['display_order'] = $i; |
||
| 4048 | $lps[$row['iid']] = $row; |
||
| 4049 | $lp_order[$i] = $row['iid']; |
||
| 4050 | $i++; |
||
| 4051 | } |
||
| 4052 | } |
||
| 4053 | if ($num > 1) { // If there's only one element, no need to sort. |
||
| 4054 | $order = $lps[$lp_id]['display_order']; |
||
| 4055 | if ($order < $max) { // If it's the first element, no need to move up. |
||
| 4056 | $sql = "UPDATE $lp_table SET display_order = $order |
||
| 4057 | WHERE iid = ".$lp_order[$order + 1]; |
||
| 4058 | Database::query($sql); |
||
| 4059 | $sql = "UPDATE $lp_table SET display_order = ".($order + 1)." |
||
| 4060 | WHERE iid = $lp_id"; |
||
| 4061 | Database::query($sql); |
||
| 4062 | } |
||
| 4063 | } |
||
| 4064 | |||
| 4065 | return true; |
||
| 4066 | } |
||
| 4067 | |||
| 4068 | /** |
||
| 4069 | * Updates learnpath attributes to point to the next element |
||
| 4070 | * The last part is similar to set_current_item but processing the other way around. |
||
| 4071 | */ |
||
| 4072 | public function next() |
||
| 4073 | { |
||
| 4074 | if ($this->debug > 0) { |
||
| 4075 | error_log('In learnpath::next()', 0); |
||
| 4076 | } |
||
| 4077 | $this->last = $this->get_current_item_id(); |
||
| 4078 | $this->items[$this->last]->save( |
||
| 4079 | false, |
||
| 4080 | $this->prerequisites_match($this->last) |
||
| 4081 | ); |
||
| 4082 | $this->autocomplete_parents($this->last); |
||
| 4083 | $new_index = $this->get_next_index(); |
||
| 4084 | if ($this->debug > 2) { |
||
| 4085 | error_log('New index: '.$new_index, 0); |
||
| 4086 | } |
||
| 4087 | $this->index = $new_index; |
||
| 4088 | if ($this->debug > 2) { |
||
| 4089 | error_log('Now having orderedlist['.$new_index.'] = '.$this->ordered_items[$new_index], 0); |
||
| 4090 | } |
||
| 4091 | $this->current = $this->ordered_items[$new_index]; |
||
| 4092 | if ($this->debug > 2) { |
||
| 4093 | error_log('new item id is '.$this->current.'-'.$this->get_current_item_id(), 0); |
||
| 4094 | } |
||
| 4095 | } |
||
| 4096 | |||
| 4097 | /** |
||
| 4098 | * Open a resource = initialise all local variables relative to this resource. Depending on the child |
||
| 4099 | * class, this might be redefined to allow several behaviours depending on the document type. |
||
| 4100 | * |
||
| 4101 | * @param int $id Resource ID |
||
| 4102 | */ |
||
| 4103 | public function open($id) |
||
| 4104 | { |
||
| 4105 | // TODO: |
||
| 4106 | // set the current resource attribute to this resource |
||
| 4107 | // switch on element type (redefine in child class?) |
||
| 4108 | // set status for this item to "opened" |
||
| 4109 | // start timer |
||
| 4110 | // initialise score |
||
| 4111 | $this->index = 0; //or = the last item seen (see $this->last) |
||
| 4112 | } |
||
| 4113 | |||
| 4114 | /** |
||
| 4115 | * Check that all prerequisites are fulfilled. Returns true and an |
||
| 4116 | * empty string on success, returns false |
||
| 4117 | * and the prerequisite string on error. |
||
| 4118 | * This function is based on the rules for aicc_script language as |
||
| 4119 | * described in the SCORM 1.2 CAM documentation page 108. |
||
| 4120 | * |
||
| 4121 | * @param int $itemId Optional item ID. If none given, uses the current open item. |
||
| 4122 | * |
||
| 4123 | * @return bool true if prerequisites are matched, false otherwise - Empty string if true returned, prerequisites |
||
| 4124 | * string otherwise |
||
| 4125 | */ |
||
| 4126 | public function prerequisites_match($itemId = null) |
||
| 4127 | { |
||
| 4128 | $allow = api_get_configuration_value('allow_teachers_to_access_blocked_lp_by_prerequisite'); |
||
| 4129 | if ($allow) { |
||
| 4130 | if (api_is_allowed_to_edit() || |
||
| 4131 | api_is_platform_admin(true) || |
||
| 4132 | api_is_drh() || |
||
| 4133 | api_is_coach(api_get_session_id(), api_get_course_int_id()) |
||
| 4134 | ) { |
||
| 4135 | return true; |
||
| 4136 | } |
||
| 4137 | } |
||
| 4138 | |||
| 4139 | $debug = $this->debug; |
||
| 4140 | if ($debug > 0) { |
||
| 4141 | error_log('In learnpath::prerequisites_match()'); |
||
| 4142 | } |
||
| 4143 | |||
| 4144 | if (empty($itemId)) { |
||
| 4145 | $itemId = $this->current; |
||
| 4146 | } |
||
| 4147 | |||
| 4148 | $currentItem = $this->getItem($itemId); |
||
| 4149 | |||
| 4150 | if ($currentItem) { |
||
| 4151 | if (2 == $this->type) { |
||
| 4152 | // Getting prereq from scorm |
||
| 4153 | $prereq_string = $this->get_scorm_prereq_string($itemId); |
||
| 4154 | } else { |
||
| 4155 | $prereq_string = $currentItem->get_prereq_string(); |
||
| 4156 | } |
||
| 4157 | |||
| 4158 | if (empty($prereq_string)) { |
||
| 4159 | if ($debug > 0) { |
||
| 4160 | error_log('Found prereq_string is empty return true'); |
||
| 4161 | } |
||
| 4162 | |||
| 4163 | return true; |
||
| 4164 | } |
||
| 4165 | |||
| 4166 | // Clean spaces. |
||
| 4167 | $prereq_string = str_replace(' ', '', $prereq_string); |
||
| 4168 | if ($debug > 0) { |
||
| 4169 | error_log('Found prereq_string: '.$prereq_string, 0); |
||
| 4170 | } |
||
| 4171 | |||
| 4172 | // Now send to the parse_prereq() function that will check this component's prerequisites. |
||
| 4173 | $result = $currentItem->parse_prereq( |
||
| 4174 | $prereq_string, |
||
| 4175 | $this->items, |
||
| 4176 | $this->refs_list, |
||
| 4177 | $this->get_user_id() |
||
| 4178 | ); |
||
| 4179 | |||
| 4180 | if (false === $result) { |
||
| 4181 | $this->set_error_msg($currentItem->prereq_alert); |
||
| 4182 | } |
||
| 4183 | } else { |
||
| 4184 | $result = true; |
||
| 4185 | if ($debug > 1) { |
||
| 4186 | error_log('$this->items['.$itemId.'] was not an object', 0); |
||
| 4187 | } |
||
| 4188 | } |
||
| 4189 | |||
| 4190 | if ($debug > 1) { |
||
| 4191 | error_log('End of prerequisites_match(). Error message is now '.$this->error, 0); |
||
| 4192 | } |
||
| 4193 | |||
| 4194 | return $result; |
||
| 4195 | } |
||
| 4196 | |||
| 4197 | /** |
||
| 4198 | * Updates learnpath attributes to point to the previous element |
||
| 4199 | * The last part is similar to set_current_item but processing the other way around. |
||
| 4200 | */ |
||
| 4201 | public function previous() |
||
| 4202 | { |
||
| 4203 | $this->last = $this->get_current_item_id(); |
||
| 4204 | $this->items[$this->last]->save( |
||
| 4205 | false, |
||
| 4206 | $this->prerequisites_match($this->last) |
||
| 4207 | ); |
||
| 4208 | $this->autocomplete_parents($this->last); |
||
| 4209 | $new_index = $this->get_previous_index(); |
||
| 4210 | $this->index = $new_index; |
||
| 4211 | $this->current = $this->ordered_items[$new_index]; |
||
| 4212 | } |
||
| 4213 | |||
| 4214 | /** |
||
| 4215 | * Publishes a learnpath. This basically means show or hide the learnpath |
||
| 4216 | * to normal users. |
||
| 4217 | * Can be used as abstract. |
||
| 4218 | * |
||
| 4219 | * @param int $id Learnpath ID |
||
| 4220 | * @param int $visibility New visibility (1 = visible/published, 0= invisible/draft) |
||
| 4221 | * |
||
| 4222 | * @return bool |
||
| 4223 | */ |
||
| 4224 | public static function toggleVisibility($id, $visibility = 1) |
||
| 4225 | { |
||
| 4226 | $repo = Container::getLpRepository(); |
||
| 4227 | $lp = $repo->find($id); |
||
| 4228 | |||
| 4229 | if (!$lp) { |
||
| 4230 | return false; |
||
| 4231 | } |
||
| 4232 | |||
| 4233 | $visibility = (int) $visibility; |
||
| 4234 | |||
| 4235 | if (1 === $visibility) { |
||
| 4236 | $repo->setVisibilityPublished($lp); |
||
| 4237 | } else { |
||
| 4238 | $repo->setVisibilityDraft($lp); |
||
| 4239 | } |
||
| 4240 | |||
| 4241 | return true; |
||
| 4242 | |||
| 4243 | /*$action = 'visible'; |
||
| 4244 | if (1 != $set_visibility) { |
||
| 4245 | $action = 'invisible'; |
||
| 4246 | self::toggle_publish($lp_id, 'i'); |
||
| 4247 | } |
||
| 4248 | |||
| 4249 | return api_item_property_update( |
||
| 4250 | api_get_course_info(), |
||
| 4251 | TOOL_LEARNPATH, |
||
| 4252 | $lp_id, |
||
| 4253 | $action, |
||
| 4254 | api_get_user_id() |
||
| 4255 | );*/ |
||
| 4256 | } |
||
| 4257 | |||
| 4258 | /** |
||
| 4259 | * Publishes a learnpath category. |
||
| 4260 | * This basically means show or hide the learnpath category to normal users. |
||
| 4261 | * |
||
| 4262 | * @param int $id |
||
| 4263 | * @param int $visibility |
||
| 4264 | * |
||
| 4265 | * @return bool |
||
| 4266 | */ |
||
| 4267 | public static function toggleCategoryVisibility($id, $visibility = 1) |
||
| 4268 | { |
||
| 4269 | $repo = Container::getLpCategoryRepository(); |
||
| 4270 | $resource = $repo->find($id); |
||
| 4271 | |||
| 4272 | if (!$resource) { |
||
| 4273 | return false; |
||
| 4274 | } |
||
| 4275 | |||
| 4276 | $visibility = (int) $visibility; |
||
| 4277 | |||
| 4278 | if (1 === $visibility) { |
||
| 4279 | $repo->setVisibilityPublished($resource); |
||
| 4280 | } else { |
||
| 4281 | $repo->setVisibilityDraft($resource); |
||
| 4282 | self::toggleCategoryPublish($id, 0); |
||
| 4283 | } |
||
| 4284 | |||
| 4285 | return false; |
||
| 4286 | /* |
||
| 4287 | $action = 'visible'; |
||
| 4288 | if (1 != $visibility) { |
||
| 4289 | self::toggleCategoryPublish($id, 0); |
||
| 4290 | $action = 'invisible'; |
||
| 4291 | } |
||
| 4292 | |||
| 4293 | return api_item_property_update( |
||
| 4294 | api_get_course_info(), |
||
| 4295 | TOOL_LEARNPATH_CATEGORY, |
||
| 4296 | $id, |
||
| 4297 | $action, |
||
| 4298 | api_get_user_id() |
||
| 4299 | );*/ |
||
| 4300 | } |
||
| 4301 | |||
| 4302 | /** |
||
| 4303 | * Publishes a learnpath. This basically means show or hide the learnpath |
||
| 4304 | * on the course homepage. |
||
| 4305 | * |
||
| 4306 | * @param int $id Learnpath id |
||
| 4307 | * @param string $setVisibility New visibility (v/i - visible/invisible) |
||
| 4308 | * |
||
| 4309 | * @return bool |
||
| 4310 | */ |
||
| 4311 | public static function togglePublish($id, $setVisibility = 'v') |
||
| 4312 | { |
||
| 4313 | $addShortcut = false; |
||
| 4314 | if ('v' === $setVisibility) { |
||
| 4315 | $addShortcut = true; |
||
| 4316 | } |
||
| 4317 | $repo = Container::getLpRepository(); |
||
| 4318 | /** @var CLp $lp */ |
||
| 4319 | $lp = $repo->find($id); |
||
| 4320 | if (null === $lp) { |
||
| 4321 | return false; |
||
| 4322 | } |
||
| 4323 | $repoShortcut = Container::getShortcutRepository(); |
||
| 4324 | $courseEntity = api_get_course_entity(); |
||
| 4325 | |||
| 4326 | if ($addShortcut) { |
||
| 4327 | $repoShortcut->addShortCut($lp, $courseEntity, $courseEntity, api_get_session_entity()); |
||
| 4328 | } else { |
||
| 4329 | $repoShortcut->removeShortCut($lp); |
||
| 4330 | } |
||
| 4331 | |||
| 4332 | return true; |
||
| 4333 | |||
| 4334 | /* |
||
| 4335 | $course_id = api_get_course_int_id(); |
||
| 4336 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 4337 | $lp_id = (int) $lp_id; |
||
| 4338 | $sql = "SELECT * FROM $tbl_lp |
||
| 4339 | WHERE iid = $lp_id"; |
||
| 4340 | $result = Database::query($sql); |
||
| 4341 | |||
| 4342 | if (Database::num_rows($result)) { |
||
| 4343 | $row = Database::fetch_array($result); |
||
| 4344 | $name = Database::escape_string($row['name']); |
||
| 4345 | if ($set_visibility == 'i') { |
||
| 4346 | $v = 0; |
||
| 4347 | } |
||
| 4348 | if ($set_visibility == 'v') { |
||
| 4349 | $v = 1; |
||
| 4350 | } |
||
| 4351 | |||
| 4352 | $session_id = api_get_session_id(); |
||
| 4353 | $session_condition = api_get_session_condition($session_id); |
||
| 4354 | |||
| 4355 | $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST); |
||
| 4356 | $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id; |
||
| 4357 | $oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id; |
||
| 4358 | |||
| 4359 | $sql = "SELECT * FROM $tbl_tool |
||
| 4360 | WHERE |
||
| 4361 | c_id = $course_id AND |
||
| 4362 | (link = '$link' OR link = '$oldLink') AND |
||
| 4363 | image = 'scormbuilder.gif' AND |
||
| 4364 | ( |
||
| 4365 | link LIKE '$link%' OR |
||
| 4366 | link LIKE '$oldLink%' |
||
| 4367 | ) |
||
| 4368 | $session_condition |
||
| 4369 | "; |
||
| 4370 | |||
| 4371 | $result = Database::query($sql); |
||
| 4372 | $num = Database::num_rows($result); |
||
| 4373 | if ($set_visibility == 'i' && $num > 0) { |
||
| 4374 | $sql = "DELETE FROM $tbl_tool |
||
| 4375 | WHERE |
||
| 4376 | c_id = $course_id AND |
||
| 4377 | (link = '$link' OR link = '$oldLink') AND |
||
| 4378 | image='scormbuilder.gif' |
||
| 4379 | $session_condition"; |
||
| 4380 | Database::query($sql); |
||
| 4381 | } elseif ($set_visibility == 'v' && $num == 0) { |
||
| 4382 | $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES |
||
| 4383 | ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)"; |
||
| 4384 | Database::query($sql); |
||
| 4385 | $insertId = Database::insert_id(); |
||
| 4386 | if ($insertId) { |
||
| 4387 | $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId"; |
||
| 4388 | Database::query($sql); |
||
| 4389 | } |
||
| 4390 | } elseif ($set_visibility == 'v' && $num > 0) { |
||
| 4391 | $sql = "UPDATE $tbl_tool SET |
||
| 4392 | c_id = $course_id, |
||
| 4393 | name = '$name', |
||
| 4394 | link = '$link', |
||
| 4395 | image = 'scormbuilder.gif', |
||
| 4396 | visibility = '$v', |
||
| 4397 | admin = '0', |
||
| 4398 | address = 'pastillegris.gif', |
||
| 4399 | added_tool = 0, |
||
| 4400 | session_id = $session_id |
||
| 4401 | WHERE |
||
| 4402 | c_id = ".$course_id." AND |
||
| 4403 | (link = '$link' OR link = '$oldLink') AND |
||
| 4404 | image='scormbuilder.gif' |
||
| 4405 | $session_condition |
||
| 4406 | "; |
||
| 4407 | Database::query($sql); |
||
| 4408 | } else { |
||
| 4409 | // Parameter and database incompatible, do nothing, exit. |
||
| 4410 | return false; |
||
| 4411 | } |
||
| 4412 | } else { |
||
| 4413 | return false; |
||
| 4414 | }*/ |
||
| 4415 | } |
||
| 4416 | |||
| 4417 | /** |
||
| 4418 | * Show or hide the learnpath category on the course homepage. |
||
| 4419 | * |
||
| 4420 | * @param int $id |
||
| 4421 | * @param int $setVisibility |
||
| 4422 | * |
||
| 4423 | * @return bool |
||
| 4424 | */ |
||
| 4425 | public static function toggleCategoryPublish($id, $setVisibility = 1) |
||
| 4426 | { |
||
| 4427 | $setVisibility = (int) $setVisibility; |
||
| 4428 | $addShortcut = false; |
||
| 4429 | if (1 === $setVisibility) { |
||
| 4430 | $addShortcut = true; |
||
| 4431 | } |
||
| 4432 | |||
| 4433 | $repo = Container::getLpCategoryRepository(); |
||
| 4434 | /** @var CLpCategory $lp */ |
||
| 4435 | $category = $repo->find($id); |
||
| 4436 | |||
| 4437 | if (null === $category) { |
||
| 4438 | return false; |
||
| 4439 | } |
||
| 4440 | |||
| 4441 | $repoShortcut = Container::getShortcutRepository(); |
||
| 4442 | if ($addShortcut) { |
||
| 4443 | $courseEntity = api_get_course_entity(api_get_course_int_id()); |
||
| 4444 | $repoShortcut->addShortCut($category, $courseEntity, $courseEntity, api_get_session_entity()); |
||
| 4445 | } else { |
||
| 4446 | $repoShortcut->removeShortCut($category); |
||
| 4447 | } |
||
| 4448 | |||
| 4449 | return true; |
||
| 4450 | } |
||
| 4451 | |||
| 4452 | /** |
||
| 4453 | * Check if the learnpath category is visible for a user. |
||
| 4454 | * |
||
| 4455 | * @return bool |
||
| 4456 | */ |
||
| 4457 | public static function categoryIsVisibleForStudent( |
||
| 4458 | CLpCategory $category, |
||
| 4459 | User $user, |
||
| 4460 | Course $course, |
||
| 4461 | Session $session = null |
||
| 4462 | ) { |
||
| 4463 | $isAllowedToEdit = api_is_allowed_to_edit(null, true); |
||
| 4464 | |||
| 4465 | if ($isAllowedToEdit) { |
||
| 4466 | return true; |
||
| 4467 | } |
||
| 4468 | |||
| 4469 | $repo = Container::getLpCategoryRepository(); |
||
| 4470 | $categoryVisibility = $category->isVisible($course, $session); |
||
| 4471 | |||
| 4472 | /*$categoryVisibility = api_get_item_visibility( |
||
| 4473 | $courseInfo, |
||
| 4474 | TOOL_LEARNPATH_CATEGORY, |
||
| 4475 | $category->getId(), |
||
| 4476 | $sessionId |
||
| 4477 | );*/ |
||
| 4478 | |||
| 4479 | if (1 !== $categoryVisibility && -1 != $categoryVisibility) { |
||
| 4480 | return false; |
||
| 4481 | } |
||
| 4482 | |||
| 4483 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
| 4484 | |||
| 4485 | if (false == $subscriptionSettings['allow_add_users_to_lp_category']) { |
||
| 4486 | return true; |
||
| 4487 | } |
||
| 4488 | |||
| 4489 | $noUserSubscribed = false; |
||
| 4490 | $noGroupSubscribed = true; |
||
| 4491 | $users = $category->getUsers(); |
||
| 4492 | if (empty($users) || !$users->count()) { |
||
| 4493 | $noUserSubscribed = true; |
||
| 4494 | } elseif ($category->hasUserAdded($user)) { |
||
| 4495 | return true; |
||
| 4496 | } |
||
| 4497 | |||
| 4498 | $groups = GroupManager::getAllGroupPerUserSubscription($user->getId()); |
||
| 4499 | $em = Database::getManager(); |
||
| 4500 | |||
| 4501 | /** @var ItemPropertyRepository $itemRepo */ |
||
| 4502 | $itemRepo = $em->getRepository('ChamiloCourseBundle:CItemProperty'); |
||
| 4503 | |||
| 4504 | /** @var CourseRepository $courseRepo */ |
||
| 4505 | $courseRepo = $em->getRepository('ChamiloCoreBundle:Course'); |
||
| 4506 | $session = null; |
||
| 4507 | if (!empty($sessionId)) { |
||
| 4508 | $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId); |
||
| 4509 | } |
||
| 4510 | |||
| 4511 | $course = $courseRepo->find($courseId); |
||
| 4512 | |||
| 4513 | if (0 != $courseId) { |
||
| 4514 | // Subscribed groups to a LP |
||
| 4515 | $subscribedGroupsInLp = $itemRepo->getGroupsSubscribedToItem( |
||
| 4516 | TOOL_LEARNPATH_CATEGORY, |
||
| 4517 | $category->getId(), |
||
| 4518 | $course, |
||
| 4519 | $session |
||
| 4520 | ); |
||
| 4521 | } |
||
| 4522 | |||
| 4523 | if (!empty($subscribedGroupsInLp)) { |
||
| 4524 | $noGroupSubscribed = false; |
||
| 4525 | if (!empty($groups)) { |
||
| 4526 | $groups = array_column($groups, 'iid'); |
||
| 4527 | /** @var CItemProperty $item */ |
||
| 4528 | foreach ($subscribedGroupsInLp as $item) { |
||
| 4529 | if ($item->getGroup() && |
||
| 4530 | in_array($item->getGroup()->getId(), $groups) |
||
| 4531 | ) { |
||
| 4532 | return true; |
||
| 4533 | } |
||
| 4534 | } |
||
| 4535 | } |
||
| 4536 | } |
||
| 4537 | $response = $noGroupSubscribed && $noUserSubscribed; |
||
| 4538 | |||
| 4539 | return $response; |
||
| 4540 | } |
||
| 4541 | |||
| 4542 | /** |
||
| 4543 | * Check if a learnpath category is published as course tool. |
||
| 4544 | * |
||
| 4545 | * @param int $courseId |
||
| 4546 | * |
||
| 4547 | * @return bool |
||
| 4548 | */ |
||
| 4549 | public static function categoryIsPublished(CLpCategory $category, $courseId) |
||
| 4550 | { |
||
| 4551 | return false; |
||
| 4552 | $link = self::getCategoryLinkForTool($category->getId()); |
||
|
|
|||
| 4553 | $em = Database::getManager(); |
||
| 4554 | |||
| 4555 | $tools = $em |
||
| 4556 | ->createQuery(" |
||
| 4557 | SELECT t FROM ChamiloCourseBundle:CTool t |
||
| 4558 | WHERE t.course = :course AND |
||
| 4559 | t.name = :name AND |
||
| 4560 | t.image LIKE 'lp_category.%' AND |
||
| 4561 | t.link LIKE :link |
||
| 4562 | ") |
||
| 4563 | ->setParameters([ |
||
| 4564 | 'course' => $courseId, |
||
| 4565 | 'name' => strip_tags($category->getName()), |
||
| 4566 | 'link' => "$link%", |
||
| 4567 | ]) |
||
| 4568 | ->getResult(); |
||
| 4569 | |||
| 4570 | /** @var CTool $tool */ |
||
| 4571 | $tool = current($tools); |
||
| 4572 | |||
| 4573 | return $tool ? $tool->getVisibility() : false; |
||
| 4574 | } |
||
| 4575 | |||
| 4576 | /** |
||
| 4577 | * Restart the whole learnpath. Return the URL of the first element. |
||
| 4578 | * Make sure the results are saved with anoter method. This method should probably be redefined in children classes. |
||
| 4579 | * To use a similar method statically, use the create_new_attempt() method. |
||
| 4580 | * |
||
| 4581 | * @return bool |
||
| 4582 | */ |
||
| 4583 | public function restart() |
||
| 4584 | { |
||
| 4585 | if ($this->debug > 0) { |
||
| 4586 | error_log('In learnpath::restart()', 0); |
||
| 4587 | } |
||
| 4588 | // TODO |
||
| 4589 | // Call autosave method to save the current progress. |
||
| 4590 | //$this->index = 0; |
||
| 4591 | if (api_is_invitee()) { |
||
| 4592 | return false; |
||
| 4593 | } |
||
| 4594 | $session_id = api_get_session_id(); |
||
| 4595 | $course_id = api_get_course_int_id(); |
||
| 4596 | $lp_view_table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 4597 | $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) |
||
| 4598 | VALUES ($course_id, ".$this->lp_id.",".$this->get_user_id().",".($this->attempt + 1).", $session_id)"; |
||
| 4599 | if ($this->debug > 2) { |
||
| 4600 | error_log('Inserting new lp_view for restart: '.$sql, 0); |
||
| 4601 | } |
||
| 4602 | Database::query($sql); |
||
| 4603 | $view_id = Database::insert_id(); |
||
| 4604 | |||
| 4605 | if ($view_id) { |
||
| 4606 | $this->lp_view_id = $view_id; |
||
| 4607 | $this->attempt = $this->attempt + 1; |
||
| 4608 | } else { |
||
| 4609 | $this->error = 'Could not insert into item_view table...'; |
||
| 4610 | |||
| 4611 | return false; |
||
| 4612 | } |
||
| 4613 | $this->autocomplete_parents($this->current); |
||
| 4614 | foreach ($this->items as $index => $dummy) { |
||
| 4615 | $this->items[$index]->restart(); |
||
| 4616 | $this->items[$index]->set_lp_view($this->lp_view_id); |
||
| 4617 | } |
||
| 4618 | $this->first(); |
||
| 4619 | |||
| 4620 | return true; |
||
| 4621 | } |
||
| 4622 | |||
| 4623 | /** |
||
| 4624 | * Saves the current item. |
||
| 4625 | * |
||
| 4626 | * @return bool |
||
| 4627 | */ |
||
| 4628 | public function save_current() |
||
| 4629 | { |
||
| 4630 | $debug = $this->debug; |
||
| 4631 | // TODO: Do a better check on the index pointing to the right item (it is supposed to be working |
||
| 4632 | // on $ordered_items[] but not sure it's always safe to use with $items[]). |
||
| 4633 | if ($debug) { |
||
| 4634 | error_log('save_current() saving item '.$this->current, 0); |
||
| 4635 | error_log(''.print_r($this->items, true), 0); |
||
| 4636 | } |
||
| 4637 | if (isset($this->items[$this->current]) && |
||
| 4638 | is_object($this->items[$this->current]) |
||
| 4639 | ) { |
||
| 4640 | if ($debug) { |
||
| 4641 | error_log('Before save last_scorm_session_time: '.$this->items[$this->current]->getLastScormSessionTime()); |
||
| 4642 | } |
||
| 4643 | |||
| 4644 | $res = $this->items[$this->current]->save( |
||
| 4645 | false, |
||
| 4646 | $this->prerequisites_match($this->current) |
||
| 4647 | ); |
||
| 4648 | $this->autocomplete_parents($this->current); |
||
| 4649 | $status = $this->items[$this->current]->get_status(); |
||
| 4650 | $this->update_queue[$this->current] = $status; |
||
| 4651 | |||
| 4652 | if ($debug) { |
||
| 4653 | error_log('After save last_scorm_session_time: '.$this->items[$this->current]->getLastScormSessionTime()); |
||
| 4654 | } |
||
| 4655 | |||
| 4656 | return $res; |
||
| 4657 | } |
||
| 4658 | |||
| 4659 | return false; |
||
| 4660 | } |
||
| 4661 | |||
| 4662 | /** |
||
| 4663 | * Saves the given item. |
||
| 4664 | * |
||
| 4665 | * @param int $item_id Optional (will take from $_REQUEST if null) |
||
| 4666 | * @param bool $from_outside Save from url params (true) or from current attributes (false). Default true |
||
| 4667 | * |
||
| 4668 | * @return bool |
||
| 4669 | */ |
||
| 4670 | public function save_item($item_id = null, $from_outside = true) |
||
| 4671 | { |
||
| 4672 | $debug = $this->debug; |
||
| 4673 | if ($debug) { |
||
| 4674 | error_log('In learnpath::save_item('.$item_id.','.intval($from_outside).')', 0); |
||
| 4675 | } |
||
| 4676 | // TODO: Do a better check on the index pointing to the right item (it is supposed to be working |
||
| 4677 | // on $ordered_items[] but not sure it's always safe to use with $items[]). |
||
| 4678 | if (empty($item_id)) { |
||
| 4679 | $item_id = (int) $_REQUEST['id']; |
||
| 4680 | } |
||
| 4681 | |||
| 4682 | if (empty($item_id)) { |
||
| 4683 | $item_id = $this->get_current_item_id(); |
||
| 4684 | } |
||
| 4685 | if (isset($this->items[$item_id]) && |
||
| 4686 | is_object($this->items[$item_id]) |
||
| 4687 | ) { |
||
| 4688 | if ($debug) { |
||
| 4689 | error_log('Object exists'); |
||
| 4690 | } |
||
| 4691 | |||
| 4692 | // Saving the item. |
||
| 4693 | $res = $this->items[$item_id]->save( |
||
| 4694 | $from_outside, |
||
| 4695 | $this->prerequisites_match($item_id) |
||
| 4696 | ); |
||
| 4697 | |||
| 4698 | if ($debug) { |
||
| 4699 | error_log('update_queue before:'); |
||
| 4700 | error_log(print_r($this->update_queue, 1)); |
||
| 4701 | } |
||
| 4702 | $this->autocomplete_parents($item_id); |
||
| 4703 | |||
| 4704 | $status = $this->items[$item_id]->get_status(); |
||
| 4705 | $this->update_queue[$item_id] = $status; |
||
| 4706 | |||
| 4707 | if ($debug) { |
||
| 4708 | error_log('get_status(): '.$status); |
||
| 4709 | error_log('update_queue after:'); |
||
| 4710 | error_log(print_r($this->update_queue, 1)); |
||
| 4711 | } |
||
| 4712 | |||
| 4713 | return $res; |
||
| 4714 | } |
||
| 4715 | |||
| 4716 | return false; |
||
| 4717 | } |
||
| 4718 | |||
| 4719 | /** |
||
| 4720 | * Saves the last item seen's ID only in case. |
||
| 4721 | */ |
||
| 4722 | public function save_last() |
||
| 4723 | { |
||
| 4724 | $course_id = api_get_course_int_id(); |
||
| 4725 | $debug = $this->debug; |
||
| 4726 | if ($debug) { |
||
| 4727 | error_log('In learnpath::save_last()', 0); |
||
| 4728 | } |
||
| 4729 | $session_condition = api_get_session_condition( |
||
| 4730 | api_get_session_id(), |
||
| 4731 | true, |
||
| 4732 | false |
||
| 4733 | ); |
||
| 4734 | $table = Database::get_course_table(TABLE_LP_VIEW); |
||
| 4735 | |||
| 4736 | $userId = $this->get_user_id(); |
||
| 4737 | if (empty($userId)) { |
||
| 4738 | $userId = api_get_user_id(); |
||
| 4739 | if ($debug) { |
||
| 4740 | error_log('$this->get_user_id() was empty, used api_get_user_id() instead in '.__FILE__.' line '.__LINE__); |
||
| 4741 | } |
||
| 4742 | } |
||
| 4743 | if (isset($this->current) && !api_is_invitee()) { |
||
| 4744 | if ($debug) { |
||
| 4745 | error_log('Saving current item ('.$this->current.') for later review', 0); |
||
| 4746 | } |
||
| 4747 | $sql = "UPDATE $table SET |
||
| 4748 | last_item = ".$this->get_current_item_id()." |
||
| 4749 | WHERE |
||
| 4750 | c_id = $course_id AND |
||
| 4751 | lp_id = ".$this->get_id()." AND |
||
| 4752 | user_id = ".$userId." ".$session_condition; |
||
| 4753 | |||
| 4754 | if ($debug) { |
||
| 4755 | error_log('Saving last item seen : '.$sql, 0); |
||
| 4756 | } |
||
| 4757 | Database::query($sql); |
||
| 4758 | } |
||
| 4759 | |||
| 4760 | if (!api_is_invitee()) { |
||
| 4761 | // Save progress. |
||
| 4762 | [$progress] = $this->get_progress_bar_text('%'); |
||
| 4763 | $scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable'); |
||
| 4764 | $scoreAsProgress = $this->getUseScoreAsProgress(); |
||
| 4765 | if ($scoreAsProgress && $scoreAsProgressSetting && (null === $score || empty($score) || -1 == $score)) { |
||
| 4766 | if ($debug) { |
||
| 4767 | error_log("Return false: Dont save score: $score"); |
||
| 4768 | error_log("progress: $progress"); |
||
| 4769 | } |
||
| 4770 | |||
| 4771 | return false; |
||
| 4772 | } |
||
| 4773 | |||
| 4774 | if ($scoreAsProgress && $scoreAsProgressSetting) { |
||
| 4775 | $storedProgress = self::getProgress( |
||
| 4776 | $this->get_id(), |
||
| 4777 | $userId, |
||
| 4778 | $course_id, |
||
| 4779 | $this->get_lp_session_id() |
||
| 4780 | ); |
||
| 4781 | |||
| 4782 | // Check if the stored progress is higher than the new value |
||
| 4783 | if ($storedProgress >= $progress) { |
||
| 4784 | if ($debug) { |
||
| 4785 | error_log("Return false: New progress value is lower than stored value - Current value: $storedProgress - New value: $progress [lp ".$this->get_id()." - user ".$userId."]"); |
||
| 4786 | } |
||
| 4787 | |||
| 4788 | return false; |
||
| 4789 | } |
||
| 4790 | } |
||
| 4791 | if ($progress >= 0 && $progress <= 100) { |
||
| 4792 | $progress = (int) $progress; |
||
| 4793 | $sql = "UPDATE $table SET |
||
| 4794 | progress = $progress |
||
| 4795 | WHERE |
||
| 4796 | c_id = $course_id AND |
||
| 4797 | lp_id = ".$this->get_id()." AND |
||
| 4798 | user_id = ".$userId." ".$session_condition; |
||
| 4799 | // Ignore errors as some tables might not have the progress field just yet. |
||
| 4800 | Database::query($sql); |
||
| 4801 | $this->progress_db = $progress; |
||
| 4802 | } |
||
| 4803 | } |
||
| 4804 | } |
||
| 4805 | |||
| 4806 | /** |
||
| 4807 | * Sets the current item ID (checks if valid and authorized first). |
||
| 4808 | * |
||
| 4809 | * @param int $item_id New item ID. If not given or not authorized, defaults to current |
||
| 4810 | */ |
||
| 4811 | public function set_current_item($item_id = null) |
||
| 4812 | { |
||
| 4813 | $debug = $this->debug; |
||
| 4814 | if ($debug) { |
||
| 4815 | error_log('In learnpath::set_current_item('.$item_id.')', 0); |
||
| 4816 | } |
||
| 4817 | if (empty($item_id)) { |
||
| 4818 | if ($debug) { |
||
| 4819 | error_log('No new current item given, ignore...', 0); |
||
| 4820 | } |
||
| 4821 | // Do nothing. |
||
| 4822 | } else { |
||
| 4823 | if ($debug) { |
||
| 4824 | error_log('New current item given is '.$item_id.'...', 0); |
||
| 4825 | } |
||
| 4826 | if (is_numeric($item_id)) { |
||
| 4827 | $item_id = (int) $item_id; |
||
| 4828 | // TODO: Check in database here. |
||
| 4829 | $this->last = $this->current; |
||
| 4830 | $this->current = $item_id; |
||
| 4831 | // TODO: Update $this->index as well. |
||
| 4832 | foreach ($this->ordered_items as $index => $item) { |
||
| 4833 | if ($item == $this->current) { |
||
| 4834 | $this->index = $index; |
||
| 4835 | break; |
||
| 4836 | } |
||
| 4837 | } |
||
| 4838 | if ($debug) { |
||
| 4839 | error_log('set_current_item('.$item_id.') done. Index is now : '.$this->index); |
||
| 4840 | } |
||
| 4841 | } else { |
||
| 4842 | if ($debug) { |
||
| 4843 | error_log('set_current_item('.$item_id.') failed. Not a numeric value: '); |
||
| 4844 | } |
||
| 4845 | } |
||
| 4846 | } |
||
| 4847 | } |
||
| 4848 | |||
| 4849 | /** |
||
| 4850 | * Sets the encoding. |
||
| 4851 | * |
||
| 4852 | * @param string $enc New encoding |
||
| 4853 | * |
||
| 4854 | * @return bool |
||
| 4855 | * |
||
| 4856 | * @todo (as of Chamilo 1.8.8): Check in the future whether this method is needed. |
||
| 4857 | */ |
||
| 4858 | public function set_encoding($enc = 'UTF-8') |
||
| 4859 | { |
||
| 4860 | $enc = api_refine_encoding_id($enc); |
||
| 4861 | if (empty($enc)) { |
||
| 4862 | $enc = api_get_system_encoding(); |
||
| 4863 | } |
||
| 4864 | if (api_is_encoding_supported($enc)) { |
||
| 4865 | $lp = $this->get_id(); |
||
| 4866 | if (0 != $lp) { |
||
| 4867 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 4868 | $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' |
||
| 4869 | WHERE iid = ".$lp; |
||
| 4870 | $res = Database::query($sql); |
||
| 4871 | |||
| 4872 | return $res; |
||
| 4873 | } |
||
| 4874 | } |
||
| 4875 | |||
| 4876 | return false; |
||
| 4877 | } |
||
| 4878 | |||
| 4879 | /** |
||
| 4880 | * Sets the JS lib setting in the database directly. |
||
| 4881 | * This is the JavaScript library file this lp needs to load on startup. |
||
| 4882 | * |
||
| 4883 | * @param string $lib Proximity setting |
||
| 4884 | * |
||
| 4885 | * @return bool True on update success. False otherwise. |
||
| 4886 | */ |
||
| 4887 | public function set_jslib($lib = '') |
||
| 4888 | { |
||
| 4889 | $lp = $this->get_id(); |
||
| 4890 | |||
| 4891 | if (0 != $lp) { |
||
| 4892 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 4893 | $lib = Database::escape_string($lib); |
||
| 4894 | $sql = "UPDATE $tbl_lp SET js_lib = '$lib' |
||
| 4895 | WHERE iid = $lp"; |
||
| 4896 | $res = Database::query($sql); |
||
| 4897 | |||
| 4898 | return $res; |
||
| 4899 | } |
||
| 4900 | |||
| 4901 | return false; |
||
| 4902 | } |
||
| 4903 | |||
| 4904 | /** |
||
| 4905 | * Set index specified prefix terms for all items in this path. |
||
| 4906 | * |
||
| 4907 | * @param string $terms_string Comma-separated list of terms |
||
| 4908 | * @param string $prefix Xapian term prefix |
||
| 4909 | * |
||
| 4910 | * @return bool False on error, true otherwise |
||
| 4911 | */ |
||
| 4912 | public function set_terms_by_prefix($terms_string, $prefix) |
||
| 4913 | { |
||
| 4914 | $course_id = api_get_course_int_id(); |
||
| 4915 | if ('true' !== api_get_setting('search_enabled')) { |
||
| 4916 | return false; |
||
| 4917 | } |
||
| 4918 | |||
| 4919 | if (!extension_loaded('xapian')) { |
||
| 4920 | return false; |
||
| 4921 | } |
||
| 4922 | |||
| 4923 | $terms_string = trim($terms_string); |
||
| 4924 | $terms = explode(',', $terms_string); |
||
| 4925 | array_walk($terms, 'trim_value'); |
||
| 4926 | $stored_terms = $this->get_common_index_terms_by_prefix($prefix); |
||
| 4927 | |||
| 4928 | // Don't do anything if no change, verify only at DB, not the search engine. |
||
| 4929 | if ((0 == count(array_diff($terms, $stored_terms))) && (0 == count(array_diff($stored_terms, $terms)))) { |
||
| 4930 | return false; |
||
| 4931 | } |
||
| 4932 | |||
| 4933 | require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API. |
||
| 4934 | require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php'; |
||
| 4935 | |||
| 4936 | $items_table = Database::get_course_table(TABLE_LP_ITEM); |
||
| 4937 | // TODO: Make query secure agains XSS : use member attr instead of post var. |
||
| 4938 | $lp_id = (int) $_POST['lp_id']; |
||
| 4939 | $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id"; |
||
| 4940 | $result = Database::query($sql); |
||
| 4941 | $di = new ChamiloIndexer(); |
||
| 4942 | |||
| 4943 | while ($lp_item = Database::fetch_array($result)) { |
||
| 4944 | // Get search_did. |
||
| 4945 | $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); |
||
| 4946 | $sql = 'SELECT * FROM %s |
||
| 4947 | WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d |
||
| 4948 | LIMIT 1'; |
||
| 4949 | $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']); |
||
| 4950 | |||
| 4951 | //echo $sql; echo '<br>'; |
||
| 4952 | $res = Database::query($sql); |
||
| 4953 | if (Database::num_rows($res) > 0) { |
||
| 4954 | $se_ref = Database::fetch_array($res); |
||
| 4955 | // Compare terms. |
||
| 4956 | $doc = $di->get_document($se_ref['search_did']); |
||
| 4957 | $xapian_terms = xapian_get_doc_terms($doc, $prefix); |
||
| 4958 | $xterms = []; |
||
| 4959 | foreach ($xapian_terms as $xapian_term) { |
||
| 4960 | $xterms[] = substr($xapian_term['name'], 1); |
||
| 4961 | } |
||
| 4962 | |||
| 4963 | $dterms = $terms; |
||
| 4964 | $missing_terms = array_diff($dterms, $xterms); |
||
| 4965 | $deprecated_terms = array_diff($xterms, $dterms); |
||
| 4966 | |||
| 4967 | // Save it to search engine. |
||
| 4968 | foreach ($missing_terms as $term) { |
||
| 4969 | $doc->add_term($prefix.$term, 1); |
||
| 4970 | } |
||
| 4971 | foreach ($deprecated_terms as $term) { |
||
| 4972 | $doc->remove_term($prefix.$term); |
||
| 4973 | } |
||
| 4974 | $di->getDb()->replace_document((int) $se_ref['search_did'], $doc); |
||
| 4975 | $di->getDb()->flush(); |
||
| 4976 | } |
||
| 4977 | } |
||
| 4978 | |||
| 4979 | return true; |
||
| 4980 | } |
||
| 4981 | |||
| 4982 | /** |
||
| 4983 | * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item. |
||
| 4984 | * |
||
| 4985 | * @param int $id DB ID of the item |
||
| 4986 | */ |
||
| 4987 | public function set_previous_item($id) |
||
| 4988 | { |
||
| 4989 | if ($this->debug > 0) { |
||
| 4990 | error_log('In learnpath::set_previous_item()', 0); |
||
| 4991 | } |
||
| 4992 | $this->last = $id; |
||
| 4993 | } |
||
| 4994 | |||
| 4995 | /** |
||
| 4996 | * Sets use_max_score. |
||
| 4997 | * |
||
| 4998 | * @param int $use_max_score Optional string giving the new location of this learnpath |
||
| 4999 | * |
||
| 5000 | * @return bool True on success / False on error |
||
| 5001 | */ |
||
| 5002 | public function set_use_max_score($use_max_score = 1) |
||
| 5003 | { |
||
| 5004 | $use_max_score = (int) $use_max_score; |
||
| 5005 | $this->use_max_score = $use_max_score; |
||
| 5006 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5007 | $lp_id = $this->get_id(); |
||
| 5008 | $sql = "UPDATE $table SET |
||
| 5009 | use_max_score = '".$this->use_max_score."' |
||
| 5010 | WHERE iid = $lp_id"; |
||
| 5011 | Database::query($sql); |
||
| 5012 | |||
| 5013 | return true; |
||
| 5014 | } |
||
| 5015 | |||
| 5016 | /** |
||
| 5017 | * Sets and saves the expired_on date. |
||
| 5018 | * |
||
| 5019 | * @return bool Returns true if author's name is not empty |
||
| 5020 | */ |
||
| 5021 | public function set_modified_on() |
||
| 5022 | { |
||
| 5023 | $this->modified_on = api_get_utc_datetime(); |
||
| 5024 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5025 | $lp_id = $this->get_id(); |
||
| 5026 | $sql = "UPDATE $table SET modified_on = '".$this->modified_on."' |
||
| 5027 | WHERE iid = $lp_id"; |
||
| 5028 | Database::query($sql); |
||
| 5029 | |||
| 5030 | return true; |
||
| 5031 | } |
||
| 5032 | |||
| 5033 | /** |
||
| 5034 | * Sets the object's error message. |
||
| 5035 | * |
||
| 5036 | * @param string $error Error message. If empty, reinits the error string |
||
| 5037 | */ |
||
| 5038 | public function set_error_msg($error = '') |
||
| 5039 | { |
||
| 5040 | if ($this->debug > 0) { |
||
| 5041 | error_log('In learnpath::set_error_msg()', 0); |
||
| 5042 | } |
||
| 5043 | if (empty($error)) { |
||
| 5044 | $this->error = ''; |
||
| 5045 | } else { |
||
| 5046 | $this->error .= $error; |
||
| 5047 | } |
||
| 5048 | } |
||
| 5049 | |||
| 5050 | /** |
||
| 5051 | * Launches the current item if not 'sco' |
||
| 5052 | * (starts timer and make sure there is a record ready in the DB). |
||
| 5053 | * |
||
| 5054 | * @param bool $allow_new_attempt Whether to allow a new attempt or not |
||
| 5055 | * |
||
| 5056 | * @return bool |
||
| 5057 | */ |
||
| 5058 | public function start_current_item($allow_new_attempt = false) |
||
| 5059 | { |
||
| 5060 | $debug = $this->debug; |
||
| 5061 | if ($debug) { |
||
| 5062 | error_log('In learnpath::start_current_item()'); |
||
| 5063 | error_log('current: '.$this->current); |
||
| 5064 | } |
||
| 5065 | if (0 != $this->current && isset($this->items[$this->current]) && is_object($this->items[$this->current])) { |
||
| 5066 | $type = $this->get_type(); |
||
| 5067 | $item_type = $this->items[$this->current]->get_type(); |
||
| 5068 | if ((2 == $type && 'sco' != $item_type) || |
||
| 5069 | (3 == $type && 'au' != $item_type) || |
||
| 5070 | (1 == $type && TOOL_QUIZ != $item_type && TOOL_HOTPOTATOES != $item_type) |
||
| 5071 | ) { |
||
| 5072 | if ($debug) { |
||
| 5073 | error_log('item type: '.$item_type); |
||
| 5074 | error_log('lp type: '.$type); |
||
| 5075 | } |
||
| 5076 | $this->items[$this->current]->open($allow_new_attempt); |
||
| 5077 | $this->autocomplete_parents($this->current); |
||
| 5078 | $prereq_check = $this->prerequisites_match($this->current); |
||
| 5079 | if ($debug) { |
||
| 5080 | error_log('start_current_item will save item with prereq: '.$prereq_check); |
||
| 5081 | } |
||
| 5082 | $this->items[$this->current]->save(false, $prereq_check); |
||
| 5083 | } |
||
| 5084 | // If sco, then it is supposed to have been updated by some other call. |
||
| 5085 | if ('sco' == $item_type) { |
||
| 5086 | $this->items[$this->current]->restart(); |
||
| 5087 | } |
||
| 5088 | } |
||
| 5089 | if ($debug) { |
||
| 5090 | error_log('lp_view_session_id'); |
||
| 5091 | error_log($this->lp_view_session_id); |
||
| 5092 | error_log('api session id'); |
||
| 5093 | error_log(api_get_session_id()); |
||
| 5094 | error_log('End of learnpath::start_current_item()'); |
||
| 5095 | } |
||
| 5096 | |||
| 5097 | return true; |
||
| 5098 | } |
||
| 5099 | |||
| 5100 | /** |
||
| 5101 | * Stops the processing and counters for the old item (as held in $this->last). |
||
| 5102 | * |
||
| 5103 | * @return bool True/False |
||
| 5104 | */ |
||
| 5105 | public function stop_previous_item() |
||
| 5106 | { |
||
| 5107 | $debug = $this->debug; |
||
| 5108 | if ($debug) { |
||
| 5109 | error_log('In learnpath::stop_previous_item()', 0); |
||
| 5110 | } |
||
| 5111 | |||
| 5112 | if (0 != $this->last && $this->last != $this->current && |
||
| 5113 | isset($this->items[$this->last]) && is_object($this->items[$this->last]) |
||
| 5114 | ) { |
||
| 5115 | if ($debug) { |
||
| 5116 | error_log('In learnpath::stop_previous_item() - '.$this->last.' is object'); |
||
| 5117 | } |
||
| 5118 | switch ($this->get_type()) { |
||
| 5119 | case '3': |
||
| 5120 | if ('au' != $this->items[$this->last]->get_type()) { |
||
| 5121 | if ($debug) { |
||
| 5122 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 3 is <> au'); |
||
| 5123 | } |
||
| 5124 | $this->items[$this->last]->close(); |
||
| 5125 | } else { |
||
| 5126 | if ($debug) { |
||
| 5127 | error_log('In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals'); |
||
| 5128 | } |
||
| 5129 | } |
||
| 5130 | break; |
||
| 5131 | case '2': |
||
| 5132 | if ('sco' != $this->items[$this->last]->get_type()) { |
||
| 5133 | if ($debug) { |
||
| 5134 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 2 is <> sco'); |
||
| 5135 | } |
||
| 5136 | $this->items[$this->last]->close(); |
||
| 5137 | } else { |
||
| 5138 | if ($debug) { |
||
| 5139 | error_log('In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals'); |
||
| 5140 | } |
||
| 5141 | } |
||
| 5142 | break; |
||
| 5143 | case '1': |
||
| 5144 | default: |
||
| 5145 | if ($debug) { |
||
| 5146 | error_log('In learnpath::stop_previous_item() - '.$this->last.' in lp_type 1 is asset'); |
||
| 5147 | } |
||
| 5148 | $this->items[$this->last]->close(); |
||
| 5149 | break; |
||
| 5150 | } |
||
| 5151 | } else { |
||
| 5152 | if ($debug) { |
||
| 5153 | error_log('In learnpath::stop_previous_item() - No previous element found, ignoring...'); |
||
| 5154 | } |
||
| 5155 | |||
| 5156 | return false; |
||
| 5157 | } |
||
| 5158 | |||
| 5159 | return true; |
||
| 5160 | } |
||
| 5161 | |||
| 5162 | /** |
||
| 5163 | * Updates the default view mode from fullscreen to embedded and inversely. |
||
| 5164 | * |
||
| 5165 | * @return string The current default view mode ('fullscreen' or 'embedded') |
||
| 5166 | */ |
||
| 5167 | public function update_default_view_mode() |
||
| 5200 | } |
||
| 5201 | |||
| 5202 | /** |
||
| 5203 | * Updates the default behaviour about auto-commiting SCORM updates. |
||
| 5204 | * |
||
| 5205 | * @return bool True if auto-commit has been set to 'on', false otherwise |
||
| 5206 | */ |
||
| 5207 | public function update_default_scorm_commit() |
||
| 5208 | { |
||
| 5209 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5210 | $sql = "SELECT * FROM $lp_table |
||
| 5211 | WHERE iid = ".$this->get_id(); |
||
| 5212 | $res = Database::query($sql); |
||
| 5213 | if (Database::num_rows($res) > 0) { |
||
| 5214 | $row = Database::fetch_array($res); |
||
| 5215 | $force = $row['force_commit']; |
||
| 5216 | if (1 == $force) { |
||
| 5217 | $force = 0; |
||
| 5218 | $force_return = false; |
||
| 5219 | } elseif (0 == $force) { |
||
| 5220 | $force = 1; |
||
| 5221 | $force_return = true; |
||
| 5222 | } |
||
| 5223 | $sql = "UPDATE $lp_table SET force_commit = $force |
||
| 5224 | WHERE iid = ".$this->get_id(); |
||
| 5225 | Database::query($sql); |
||
| 5226 | $this->force_commit = $force_return; |
||
| 5227 | |||
| 5228 | return $force_return; |
||
| 5229 | } |
||
| 5230 | |||
| 5231 | return -1; |
||
| 5232 | } |
||
| 5233 | |||
| 5234 | /** |
||
| 5235 | * Updates the order of learning paths (goes through all of them by order and fills the gaps). |
||
| 5236 | * |
||
| 5237 | * @return bool True on success, false on failure |
||
| 5238 | */ |
||
| 5239 | public function update_display_order() |
||
| 5240 | { |
||
| 5241 | return; |
||
| 5242 | $course_id = api_get_course_int_id(); |
||
| 5243 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5244 | $sql = "SELECT * FROM $table |
||
| 5245 | WHERE c_id = $course_id |
||
| 5246 | ORDER BY display_order"; |
||
| 5247 | $res = Database::query($sql); |
||
| 5248 | if (false === $res) { |
||
| 5249 | return false; |
||
| 5250 | } |
||
| 5251 | |||
| 5252 | $num = Database::num_rows($res); |
||
| 5253 | // First check the order is correct, globally (might be wrong because |
||
| 5254 | // of versions < 1.8.4). |
||
| 5255 | if ($num > 0) { |
||
| 5256 | $i = 1; |
||
| 5257 | while ($row = Database::fetch_array($res)) { |
||
| 5258 | if ($row['display_order'] != $i) { |
||
| 5259 | // If we find a gap in the order, we need to fix it. |
||
| 5260 | $sql = "UPDATE $table SET display_order = $i |
||
| 5261 | WHERE iid = ".$row['iid']; |
||
| 5262 | Database::query($sql); |
||
| 5263 | } |
||
| 5264 | $i++; |
||
| 5265 | } |
||
| 5266 | } |
||
| 5267 | |||
| 5268 | return true; |
||
| 5269 | } |
||
| 5270 | |||
| 5271 | /** |
||
| 5272 | * Updates the "prevent_reinit" value that enables control on reinitialising items on second view. |
||
| 5273 | * |
||
| 5274 | * @return bool True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case) |
||
| 5275 | */ |
||
| 5276 | public function update_reinit() |
||
| 5277 | { |
||
| 5278 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5279 | $sql = "SELECT * FROM $lp_table |
||
| 5280 | WHERE iid = ".$this->get_id(); |
||
| 5281 | $res = Database::query($sql); |
||
| 5282 | if (Database::num_rows($res) > 0) { |
||
| 5283 | $row = Database::fetch_array($res); |
||
| 5284 | $force = $row['prevent_reinit']; |
||
| 5285 | if (1 == $force) { |
||
| 5286 | $force = 0; |
||
| 5287 | } elseif (0 == $force) { |
||
| 5288 | $force = 1; |
||
| 5289 | } |
||
| 5290 | $sql = "UPDATE $lp_table SET prevent_reinit = $force |
||
| 5291 | WHERE iid = ".$this->get_id(); |
||
| 5292 | Database::query($sql); |
||
| 5293 | $this->prevent_reinit = $force; |
||
| 5294 | |||
| 5295 | return $force; |
||
| 5296 | } |
||
| 5297 | |||
| 5298 | return -1; |
||
| 5299 | } |
||
| 5300 | |||
| 5301 | /** |
||
| 5302 | * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag. |
||
| 5303 | * |
||
| 5304 | * @return string 'single', 'multi' or 'seriousgame' |
||
| 5305 | * |
||
| 5306 | * @author ndiechburg <[email protected]> |
||
| 5307 | */ |
||
| 5308 | public function get_attempt_mode() |
||
| 5309 | { |
||
| 5310 | //Set default value for seriousgame_mode |
||
| 5311 | if (!isset($this->seriousgame_mode)) { |
||
| 5312 | $this->seriousgame_mode = 0; |
||
| 5313 | } |
||
| 5314 | // Set default value for prevent_reinit |
||
| 5315 | if (!isset($this->prevent_reinit)) { |
||
| 5316 | $this->prevent_reinit = 1; |
||
| 5317 | } |
||
| 5318 | if (1 == $this->seriousgame_mode && 1 == $this->prevent_reinit) { |
||
| 5319 | return 'seriousgame'; |
||
| 5320 | } |
||
| 5321 | if (0 == $this->seriousgame_mode && 1 == $this->prevent_reinit) { |
||
| 5322 | return 'single'; |
||
| 5323 | } |
||
| 5324 | if (0 == $this->seriousgame_mode && 0 == $this->prevent_reinit) { |
||
| 5325 | return 'multiple'; |
||
| 5326 | } |
||
| 5327 | |||
| 5328 | return 'single'; |
||
| 5329 | } |
||
| 5330 | |||
| 5331 | /** |
||
| 5332 | * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags. |
||
| 5333 | * |
||
| 5334 | * @param string 'seriousgame', 'single' or 'multiple' |
||
| 5335 | * |
||
| 5336 | * @return bool |
||
| 5337 | * |
||
| 5338 | * @author ndiechburg <[email protected]> |
||
| 5339 | */ |
||
| 5340 | public function set_attempt_mode($mode) |
||
| 5341 | { |
||
| 5342 | switch ($mode) { |
||
| 5343 | case 'seriousgame': |
||
| 5344 | $sg_mode = 1; |
||
| 5345 | $prevent_reinit = 1; |
||
| 5346 | break; |
||
| 5347 | case 'single': |
||
| 5348 | $sg_mode = 0; |
||
| 5349 | $prevent_reinit = 1; |
||
| 5350 | break; |
||
| 5351 | case 'multiple': |
||
| 5352 | $sg_mode = 0; |
||
| 5353 | $prevent_reinit = 0; |
||
| 5354 | break; |
||
| 5355 | default: |
||
| 5356 | $sg_mode = 0; |
||
| 5357 | $prevent_reinit = 0; |
||
| 5358 | break; |
||
| 5359 | } |
||
| 5360 | $this->prevent_reinit = $prevent_reinit; |
||
| 5361 | $this->seriousgame_mode = $sg_mode; |
||
| 5362 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5363 | $sql = "UPDATE $table SET |
||
| 5364 | prevent_reinit = $prevent_reinit , |
||
| 5365 | seriousgame_mode = $sg_mode |
||
| 5366 | WHERE iid = ".$this->get_id(); |
||
| 5367 | $res = Database::query($sql); |
||
| 5368 | if ($res) { |
||
| 5369 | return true; |
||
| 5370 | } else { |
||
| 5371 | return false; |
||
| 5372 | } |
||
| 5373 | } |
||
| 5374 | |||
| 5375 | /** |
||
| 5376 | * Switch between multiple attempt, single attempt or serious_game mode (only for scorm). |
||
| 5377 | * |
||
| 5378 | * @author ndiechburg <[email protected]> |
||
| 5379 | */ |
||
| 5380 | public function switch_attempt_mode() |
||
| 5381 | { |
||
| 5382 | $mode = $this->get_attempt_mode(); |
||
| 5383 | switch ($mode) { |
||
| 5384 | case 'single': |
||
| 5385 | $next_mode = 'multiple'; |
||
| 5386 | break; |
||
| 5387 | case 'multiple': |
||
| 5388 | $next_mode = 'seriousgame'; |
||
| 5389 | break; |
||
| 5390 | case 'seriousgame': |
||
| 5391 | default: |
||
| 5392 | $next_mode = 'single'; |
||
| 5393 | break; |
||
| 5394 | } |
||
| 5395 | $this->set_attempt_mode($next_mode); |
||
| 5396 | } |
||
| 5397 | |||
| 5398 | /** |
||
| 5399 | * Switch the lp in ktm mode. This is a special scorm mode with unique attempt |
||
| 5400 | * but possibility to do again a completed item. |
||
| 5401 | * |
||
| 5402 | * @return bool true if seriousgame_mode has been set to 1, false otherwise |
||
| 5403 | * |
||
| 5404 | * @author ndiechburg <[email protected]> |
||
| 5405 | */ |
||
| 5406 | public function set_seriousgame_mode() |
||
| 5407 | { |
||
| 5408 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5409 | $sql = "SELECT * FROM $lp_table |
||
| 5410 | WHERE iid = ".$this->get_id(); |
||
| 5411 | $res = Database::query($sql); |
||
| 5412 | if (Database::num_rows($res) > 0) { |
||
| 5413 | $row = Database::fetch_array($res); |
||
| 5414 | $force = $row['seriousgame_mode']; |
||
| 5415 | if (1 == $force) { |
||
| 5416 | $force = 0; |
||
| 5417 | } elseif (0 == $force) { |
||
| 5418 | $force = 1; |
||
| 5419 | } |
||
| 5420 | $sql = "UPDATE $lp_table SET seriousgame_mode = $force |
||
| 5421 | WHERE iid = ".$this->get_id(); |
||
| 5422 | Database::query($sql); |
||
| 5423 | $this->seriousgame_mode = $force; |
||
| 5424 | |||
| 5425 | return $force; |
||
| 5426 | } |
||
| 5427 | |||
| 5428 | return -1; |
||
| 5429 | } |
||
| 5430 | |||
| 5431 | /** |
||
| 5432 | * Updates the "scorm_debug" value that shows or hide the debug window. |
||
| 5433 | * |
||
| 5434 | * @return bool True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case) |
||
| 5435 | */ |
||
| 5436 | public function update_scorm_debug() |
||
| 5437 | { |
||
| 5438 | $lp_table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 5439 | $sql = "SELECT * FROM $lp_table |
||
| 5440 | WHERE iid = ".$this->get_id(); |
||
| 5441 | $res = Database::query($sql); |
||
| 5442 | if (Database::num_rows($res) > 0) { |
||
| 5443 | $row = Database::fetch_array($res); |
||
| 5444 | $force = $row['debug']; |
||
| 5445 | if (1 == $force) { |
||
| 5446 | $force = 0; |
||
| 5447 | } elseif (0 == $force) { |
||
| 5448 | $force = 1; |
||
| 5449 | } |
||
| 5450 | $sql = "UPDATE $lp_table SET debug = $force |
||
| 5451 | WHERE iid = ".$this->get_id(); |
||
| 5452 | Database::query($sql); |
||
| 5453 | $this->scorm_debug = $force; |
||
| 5454 | |||
| 5455 | return $force; |
||
| 5456 | } |
||
| 5457 | |||
| 5458 | return -1; |
||
| 5459 | } |
||
| 5460 | |||
| 5461 | /** |
||
| 5462 | * Function that makes a call to the function sort_tree_array and create_tree_array. |
||
| 5463 | * |
||
| 5464 | * @author Kevin Van Den Haute |
||
| 5465 | * |
||
| 5466 | * @param array |
||
| 5467 | */ |
||
| 5468 | public function tree_array($array) |
||
| 5469 | { |
||
| 5470 | $array = $this->sort_tree_array($array); |
||
| 5471 | $this->create_tree_array($array); |
||
| 5472 | } |
||
| 5473 | |||
| 5474 | /** |
||
| 5475 | * Creates an array with the elements of the learning path tree in it. |
||
| 5476 | * |
||
| 5477 | * @author Kevin Van Den Haute |
||
| 5478 | * |
||
| 5479 | * @param array $array |
||
| 5480 | * @param int $parent |
||
| 5481 | * @param int $depth |
||
| 5482 | * @param array $tmp |
||
| 5483 | */ |
||
| 5484 | public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = []) |
||
| 5485 | { |
||
| 5486 | if (is_array($array)) { |
||
| 5487 | for ($i = 0; $i < count($array); $i++) { |
||
| 5488 | if ($array[$i]['parent_item_id'] == $parent) { |
||
| 5489 | if (!in_array($array[$i]['parent_item_id'], $tmp)) { |
||
| 5490 | $tmp[] = $array[$i]['parent_item_id']; |
||
| 5491 | $depth++; |
||
| 5492 | } |
||
| 5493 | $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']); |
||
| 5494 | $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null; |
||
| 5495 | $path = isset($array[$i]['path']) ? $array[$i]['path'] : null; |
||
| 5496 | |||
| 5497 | $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null; |
||
| 5498 | $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null; |
||
| 5499 | $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : ''; |
||
| 5500 | $this->arrMenu[] = [ |
||
| 5501 | 'id' => $array[$i]['id'], |
||
| 5502 | 'ref' => $ref, |
||
| 5503 | 'item_type' => $array[$i]['item_type'], |
||
| 5504 | 'title' => $array[$i]['title'], |
||
| 5505 | 'title_raw' => $array[$i]['title_raw'], |
||
| 5506 | 'path' => $path, |
||
| 5507 | 'description' => $array[$i]['description'], |
||
| 5508 | 'parent_item_id' => $array[$i]['parent_item_id'], |
||
| 5509 | 'previous_item_id' => $array[$i]['previous_item_id'], |
||
| 5510 | 'next_item_id' => $array[$i]['next_item_id'], |
||
| 5511 | 'min_score' => $array[$i]['min_score'], |
||
| 5512 | 'max_score' => $array[$i]['max_score'], |
||
| 5513 | 'mastery_score' => $array[$i]['mastery_score'], |
||
| 5514 | 'display_order' => $array[$i]['display_order'], |
||
| 5515 | 'prerequisite' => $preq, |
||
| 5516 | 'depth' => $depth, |
||
| 5517 | 'audio' => $audio, |
||
| 5518 | 'prerequisite_min_score' => $prerequisiteMinScore, |
||
| 5519 | 'prerequisite_max_score' => $prerequisiteMaxScore, |
||
| 5520 | ]; |
||
| 5521 | $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp); |
||
| 5522 | } |
||
| 5523 | } |
||
| 5524 | } |
||
| 5525 | } |
||
| 5526 | |||
| 5527 | /** |
||
| 5528 | * Sorts a multi dimensional array by parent id and display order. |
||
| 5529 | * |
||
| 5530 | * @author Kevin Van Den Haute |
||
| 5531 | * |
||
| 5532 | * @param array $array (array with al the learning path items in it) |
||
| 5533 | * |
||
| 5534 | * @return array |
||
| 5535 | */ |
||
| 5536 | public function sort_tree_array($array) |
||
| 5537 | { |
||
| 5538 | foreach ($array as $key => $row) { |
||
| 5539 | $parent[$key] = $row['parent_item_id']; |
||
| 5540 | $position[$key] = $row['display_order']; |
||
| 5541 | } |
||
| 5542 | |||
| 5543 | if (count($array) > 0) { |
||
| 5544 | array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array); |
||
| 5545 | } |
||
| 5546 | |||
| 5547 | return $array; |
||
| 5548 | } |
||
| 5549 | |||
| 5550 | /** |
||
| 5551 | * Function that creates a html list of learning path items so that we can add audio files to them. |
||
| 5552 | * |
||
| 5553 | * @author Kevin Van Den Haute |
||
| 5554 | * |
||
| 5555 | * @return string |
||
| 5556 | */ |
||
| 5557 | public function overview() |
||
| 5558 | { |
||
| 5559 | $return = ''; |
||
| 5560 | $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null; |
||
| 5561 | |||
| 5562 | // we need to start a form when we want to update all the mp3 files |
||
| 5563 | if ('true' == $update_audio) { |
||
| 5564 | $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">'; |
||
| 5565 | } |
||
| 5566 | $return .= '<div id="message"></div>'; |
||
| 5567 | if (0 == count($this->items)) { |
||
| 5568 | $return .= Display::return_message(get_lang('You should add some items to your learning path, otherwise you won\'t be able to attach audio files to them'), 'normal'); |
||
| 5569 | } else { |
||
| 5570 | $return_audio = '<table class="table table-hover table-striped data_table">'; |
||
| 5571 | $return_audio .= '<tr>'; |
||
| 5572 | $return_audio .= '<th width="40%">'.get_lang('Title').'</th>'; |
||
| 5573 | $return_audio .= '<th>'.get_lang('Audio').'</th>'; |
||
| 5574 | $return_audio .= '</tr>'; |
||
| 5575 | |||
| 5576 | if ('true' != $update_audio) { |
||
| 5577 | $return .= '<div class="col-md-12">'; |
||
| 5578 | $return .= self::return_new_tree($update_audio); |
||
| 5579 | $return .= '</div>'; |
||
| 5580 | $return .= Display::div( |
||
| 5581 | Display::url(get_lang('Save'), '#', ['id' => 'listSubmit', 'class' => 'btn btn-primary']), |
||
| 5582 | ['style' => 'float:left; margin-top:15px;width:100%'] |
||
| 5583 | ); |
||
| 5584 | } else { |
||
| 5585 | $return_audio .= self::return_new_tree($update_audio); |
||
| 5586 | $return .= $return_audio.'</table>'; |
||
| 5587 | } |
||
| 5588 | |||
| 5589 | // We need to close the form when we are updating the mp3 files. |
||
| 5590 | if ('true' == $update_audio) { |
||
| 5591 | $return .= '<div class="footer-audio">'; |
||
| 5592 | $return .= Display::button( |
||
| 5593 | 'save_audio', |
||
| 5594 | '<em class="fa fa-file-audio-o"></em> '.get_lang('Save audio and organization'), |
||
| 5595 | ['class' => 'btn btn-primary', 'type' => 'submit'] |
||
| 5596 | ); |
||
| 5597 | $return .= '</div>'; |
||
| 5598 | } |
||
| 5599 | } |
||
| 5600 | |||
| 5601 | // We need to close the form when we are updating the mp3 files. |
||
| 5602 | if ('true' == $update_audio && isset($this->arrMenu) && 0 != count($this->arrMenu)) { |
||
| 5603 | $return .= '</form>'; |
||
| 5604 | } |
||
| 5605 | |||
| 5606 | return $return; |
||
| 5607 | } |
||
| 5608 | |||
| 5609 | /** |
||
| 5610 | * @param string $update_audio |
||
| 5611 | * |
||
| 5612 | * @return array |
||
| 5613 | */ |
||
| 5614 | public function processBuildMenuElements($update_audio = 'false') |
||
| 5615 | { |
||
| 5616 | $is_allowed_to_edit = api_is_allowed_to_edit(null, true); |
||
| 5617 | $arrLP = $this->getItemsForForm(); |
||
| 5618 | |||
| 5619 | $this->tree_array($arrLP); |
||
| 5620 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
| 5621 | unset($this->arrMenu); |
||
| 5622 | $default_data = null; |
||
| 5623 | $default_content = null; |
||
| 5624 | $elements = []; |
||
| 5625 | $return_audio = null; |
||
| 5626 | $iconPath = api_get_path(SYS_PUBLIC_PATH).'img/'; |
||
| 5627 | $mainUrl = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq(); |
||
| 5628 | $countItems = count($arrLP); |
||
| 5629 | |||
| 5630 | $upIcon = Display::return_icon( |
||
| 5631 | 'up.png', |
||
| 5632 | get_lang('Up'), |
||
| 5633 | [], |
||
| 5634 | ICON_SIZE_TINY |
||
| 5635 | ); |
||
| 5636 | |||
| 5637 | $disableUpIcon = Display::return_icon( |
||
| 5638 | 'up_na.png', |
||
| 5639 | get_lang('Up'), |
||
| 5640 | [], |
||
| 5641 | ICON_SIZE_TINY |
||
| 5642 | ); |
||
| 5643 | |||
| 5644 | $downIcon = Display::return_icon( |
||
| 5645 | 'down.png', |
||
| 5646 | get_lang('Down'), |
||
| 5647 | [], |
||
| 5648 | ICON_SIZE_TINY |
||
| 5649 | ); |
||
| 5650 | |||
| 5651 | $disableDownIcon = Display::return_icon( |
||
| 5652 | 'down_na.png', |
||
| 5653 | get_lang('Down'), |
||
| 5654 | [], |
||
| 5655 | ICON_SIZE_TINY |
||
| 5656 | ); |
||
| 5657 | |||
| 5658 | $show = api_get_configuration_value('show_full_lp_item_title_in_edition'); |
||
| 5659 | |||
| 5660 | $pluginCalendar = 'true' === api_get_plugin_setting('learning_calendar', 'enabled'); |
||
| 5661 | $plugin = null; |
||
| 5662 | if ($pluginCalendar) { |
||
| 5663 | $plugin = LearningCalendarPlugin::create(); |
||
| 5664 | } |
||
| 5665 | |||
| 5666 | for ($i = 0; $i < $countItems; $i++) { |
||
| 5667 | $parent_id = $arrLP[$i]['parent_item_id']; |
||
| 5668 | $title = $arrLP[$i]['title']; |
||
| 5669 | $title_cut = $arrLP[$i]['title_raw']; |
||
| 5670 | if (false === $show) { |
||
| 5671 | $title_cut = cut($arrLP[$i]['title'], self::MAX_LP_ITEM_TITLE_LENGTH); |
||
| 5672 | } |
||
| 5673 | // Link for the documents |
||
| 5674 | if ('document' === $arrLP[$i]['item_type'] || TOOL_READOUT_TEXT === $arrLP[$i]['item_type']) { |
||
| 5675 | $url = $mainUrl.'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
| 5676 | $title_cut = Display::url( |
||
| 5677 | $title_cut, |
||
| 5678 | $url, |
||
| 5679 | [ |
||
| 5680 | 'class' => 'ajax moved', |
||
| 5681 | 'data-title' => $title, |
||
| 5682 | 'title' => $title, |
||
| 5683 | ] |
||
| 5684 | ); |
||
| 5685 | } |
||
| 5686 | |||
| 5687 | // Detect if type is FINAL_ITEM to set path_id to SESSION |
||
| 5688 | if (TOOL_LP_FINAL_ITEM === $arrLP[$i]['item_type']) { |
||
| 5689 | Session::write('pathItem', $arrLP[$i]['path']); |
||
| 5690 | } |
||
| 5691 | |||
| 5692 | $oddClass = 'row_even'; |
||
| 5693 | if (0 == ($i % 2)) { |
||
| 5694 | $oddClass = 'row_odd'; |
||
| 5695 | } |
||
| 5696 | $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'].'" class="'.$oddClass.'">'; |
||
| 5697 | $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']); |
||
| 5698 | |||
| 5699 | if (file_exists($iconPath.'lp_'.$icon_name.'.png')) { |
||
| 5700 | $icon = Display::return_icon('lp_'.$icon_name.'.png'); |
||
| 5701 | } else { |
||
| 5702 | if (file_exists($iconPath.'lp_'.$icon_name.'.gif')) { |
||
| 5703 | $icon = Display::return_icon('lp_'.$icon_name.'.gif'); |
||
| 5704 | } else { |
||
| 5705 | if (TOOL_LP_FINAL_ITEM === $arrLP[$i]['item_type']) { |
||
| 5706 | $icon = Display::return_icon('certificate.png'); |
||
| 5707 | } else { |
||
| 5708 | $icon = Display::return_icon('folder_document.png'); |
||
| 5709 | } |
||
| 5710 | } |
||
| 5711 | } |
||
| 5712 | |||
| 5713 | // The audio column. |
||
| 5714 | $return_audio .= '<td align="left" style="padding-left:10px;">'; |
||
| 5715 | $audio = ''; |
||
| 5716 | if (!$update_audio || 'true' != $update_audio) { |
||
| 5717 | if (empty($arrLP[$i]['audio'])) { |
||
| 5718 | $audio .= ''; |
||
| 5719 | } |
||
| 5720 | } else { |
||
| 5721 | $types = self::getChapterTypes(); |
||
| 5722 | if (!in_array($arrLP[$i]['item_type'], $types)) { |
||
| 5723 | $audio .= '<input type="file" name="mp3file'.$arrLP[$i]['id'].'" id="mp3file" />'; |
||
| 5724 | if (!empty($arrLP[$i]['audio'])) { |
||
| 5725 | $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br /> |
||
| 5726 | <input type="checkbox" name="removemp3'.$arrLP[$i]['id'].'" id="checkbox'.$arrLP[$i]['id'].'" />'.get_lang('Remove audio'); |
||
| 5727 | } |
||
| 5728 | } |
||
| 5729 | } |
||
| 5730 | |||
| 5731 | $return_audio .= Display::span($icon.' '.$title). |
||
| 5732 | Display::tag( |
||
| 5733 | 'td', |
||
| 5734 | $audio, |
||
| 5735 | ['style' => ''] |
||
| 5736 | ); |
||
| 5737 | $return_audio .= '</td>'; |
||
| 5738 | $move_icon = ''; |
||
| 5739 | $move_item_icon = ''; |
||
| 5740 | $edit_icon = ''; |
||
| 5741 | $delete_icon = ''; |
||
| 5742 | $audio_icon = ''; |
||
| 5743 | $prerequisities_icon = ''; |
||
| 5744 | $forumIcon = ''; |
||
| 5745 | $previewIcon = ''; |
||
| 5746 | $pluginCalendarIcon = ''; |
||
| 5747 | $orderIcons = ''; |
||
| 5748 | $pluginUrl = api_get_path(WEB_PLUGIN_PATH).'learning_calendar/start.php?'; |
||
| 5749 | |||
| 5750 | if ($is_allowed_to_edit) { |
||
| 5751 | if (!$update_audio || 'true' != $update_audio) { |
||
| 5752 | if (TOOL_LP_FINAL_ITEM !== $arrLP[$i]['item_type']) { |
||
| 5753 | $move_icon .= '<a class="moved" href="#">'; |
||
| 5754 | $move_icon .= Display::return_icon( |
||
| 5755 | 'move_everywhere.png', |
||
| 5756 | get_lang('Move'), |
||
| 5757 | [], |
||
| 5758 | ICON_SIZE_TINY |
||
| 5759 | ); |
||
| 5760 | $move_icon .= '</a>'; |
||
| 5761 | } |
||
| 5762 | } |
||
| 5763 | |||
| 5764 | // No edit for this item types |
||
| 5765 | if (!in_array($arrLP[$i]['item_type'], ['sco', 'asset', 'final_item'])) { |
||
| 5766 | if ('dir' != $arrLP[$i]['item_type']) { |
||
| 5767 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">'; |
||
| 5768 | $edit_icon .= Display::return_icon( |
||
| 5769 | 'edit.png', |
||
| 5770 | get_lang('Edit section description/name'), |
||
| 5771 | [], |
||
| 5772 | ICON_SIZE_TINY |
||
| 5773 | ); |
||
| 5774 | $edit_icon .= '</a>'; |
||
| 5775 | |||
| 5776 | if (!in_array($arrLP[$i]['item_type'], ['forum', 'thread'])) { |
||
| 5777 | $forumThread = null; |
||
| 5778 | if (isset($this->items[$arrLP[$i]['id']])) { |
||
| 5779 | $forumThread = $this->items[$arrLP[$i]['id']]->getForumThread( |
||
| 5780 | $this->course_int_id, |
||
| 5781 | $this->lp_session_id |
||
| 5782 | ); |
||
| 5783 | } |
||
| 5784 | if ($forumThread) { |
||
| 5785 | $forumIconUrl = $mainUrl.'&'.http_build_query([ |
||
| 5786 | 'action' => 'dissociate_forum', |
||
| 5787 | 'id' => $arrLP[$i]['id'], |
||
| 5788 | 'lp_id' => $this->lp_id, |
||
| 5789 | ]); |
||
| 5790 | $forumIcon = Display::url( |
||
| 5791 | Display::return_icon( |
||
| 5792 | 'forum.png', |
||
| 5793 | get_lang('Dissociate the forum of this learning path item'), |
||
| 5794 | [], |
||
| 5795 | ICON_SIZE_TINY |
||
| 5796 | ), |
||
| 5797 | $forumIconUrl, |
||
| 5798 | ['class' => 'btn btn-default lp-btn-dissociate-forum'] |
||
| 5799 | ); |
||
| 5800 | } else { |
||
| 5801 | $forumIconUrl = $mainUrl.'&'.http_build_query([ |
||
| 5802 | 'action' => 'create_forum', |
||
| 5803 | 'id' => $arrLP[$i]['id'], |
||
| 5804 | 'lp_id' => $this->lp_id, |
||
| 5805 | ]); |
||
| 5806 | $forumIcon = Display::url( |
||
| 5807 | Display::return_icon( |
||
| 5808 | 'forum.png', |
||
| 5809 | get_lang('Associate a forum to this learning path item'), |
||
| 5810 | [], |
||
| 5811 | ICON_SIZE_TINY |
||
| 5812 | ), |
||
| 5813 | $forumIconUrl, |
||
| 5814 | ['class' => 'btn btn-default lp-btn-associate-forum'] |
||
| 5815 | ); |
||
| 5816 | } |
||
| 5817 | } |
||
| 5818 | } else { |
||
| 5819 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">'; |
||
| 5820 | $edit_icon .= Display::return_icon( |
||
| 5821 | 'edit.png', |
||
| 5822 | get_lang('Edit section description/name'), |
||
| 5823 | [], |
||
| 5824 | ICON_SIZE_TINY |
||
| 5825 | ); |
||
| 5826 | $edit_icon .= '</a>'; |
||
| 5827 | } |
||
| 5828 | } else { |
||
| 5829 | if (TOOL_LP_FINAL_ITEM == $arrLP[$i]['item_type']) { |
||
| 5830 | $edit_icon .= '<a href="'.$mainUrl.'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" class="btn btn-default">'; |
||
| 5831 | $edit_icon .= Display::return_icon( |
||
| 5832 | 'edit.png', |
||
| 5833 | get_lang('Edit'), |
||
| 5834 | [], |
||
| 5835 | ICON_SIZE_TINY |
||
| 5836 | ); |
||
| 5837 | $edit_icon .= '</a>'; |
||
| 5838 | } |
||
| 5839 | } |
||
| 5840 | |||
| 5841 | if ($pluginCalendar) { |
||
| 5842 | $pluginLink = $pluginUrl. |
||
| 5843 | '&action=toggle_visibility&lp_item_id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
| 5844 | $iconCalendar = Display::return_icon('agenda_na.png', get_lang('1 day'), [], ICON_SIZE_TINY); |
||
| 5845 | $itemInfo = $plugin->getItemVisibility($arrLP[$i]['id']); |
||
| 5846 | if ($itemInfo && 1 == $itemInfo['value']) { |
||
| 5847 | $iconCalendar = Display::return_icon('agenda.png', get_lang('1 day'), [], ICON_SIZE_TINY); |
||
| 5848 | } |
||
| 5849 | $pluginCalendarIcon = Display::url( |
||
| 5850 | $iconCalendar, |
||
| 5851 | $pluginLink, |
||
| 5852 | ['class' => 'btn btn-default'] |
||
| 5853 | ); |
||
| 5854 | } |
||
| 5855 | |||
| 5856 | if ('final_item' != $arrLP[$i]['item_type']) { |
||
| 5857 | $orderIcons = Display::url( |
||
| 5858 | $upIcon, |
||
| 5859 | 'javascript:void(0)', |
||
| 5860 | ['class' => 'btn btn-default order_items', 'data-dir' => 'up', 'data-id' => $arrLP[$i]['id']] |
||
| 5861 | ); |
||
| 5862 | $orderIcons .= Display::url( |
||
| 5863 | $downIcon, |
||
| 5864 | 'javascript:void(0)', |
||
| 5865 | ['class' => 'btn btn-default order_items', 'data-dir' => 'down', 'data-id' => $arrLP[$i]['id']] |
||
| 5866 | ); |
||
| 5867 | } |
||
| 5868 | |||
| 5869 | $delete_icon .= ' <a |
||
| 5870 | href="'.$mainUrl.'&action=delete_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" |
||
| 5871 | onclick="return confirmation(\''.addslashes($title).'\');" |
||
| 5872 | class="btn btn-default">'; |
||
| 5873 | $delete_icon .= Display::return_icon( |
||
| 5874 | 'delete.png', |
||
| 5875 | get_lang('Delete section'), |
||
| 5876 | [], |
||
| 5877 | ICON_SIZE_TINY |
||
| 5878 | ); |
||
| 5879 | $delete_icon .= '</a>'; |
||
| 5880 | |||
| 5881 | $url = $mainUrl.'&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
| 5882 | $previewImage = Display::return_icon( |
||
| 5883 | 'preview_view.png', |
||
| 5884 | get_lang('Preview'), |
||
| 5885 | [], |
||
| 5886 | ICON_SIZE_TINY |
||
| 5887 | ); |
||
| 5888 | |||
| 5889 | switch ($arrLP[$i]['item_type']) { |
||
| 5890 | case TOOL_DOCUMENT: |
||
| 5891 | case TOOL_LP_FINAL_ITEM: |
||
| 5892 | case TOOL_READOUT_TEXT: |
||
| 5893 | $urlPreviewLink = $mainUrl.'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id; |
||
| 5894 | $previewIcon = Display::url( |
||
| 5895 | $previewImage, |
||
| 5896 | $urlPreviewLink, |
||
| 5897 | [ |
||
| 5898 | 'target' => '_blank', |
||
| 5899 | 'class' => 'btn btn-default', |
||
| 5900 | 'data-title' => $arrLP[$i]['title'], |
||
| 5901 | 'title' => $arrLP[$i]['title'], |
||
| 5902 | ] |
||
| 5903 | ); |
||
| 5904 | break; |
||
| 5905 | case TOOL_THREAD: |
||
| 5906 | case TOOL_FORUM: |
||
| 5907 | case TOOL_QUIZ: |
||
| 5908 | case TOOL_STUDENTPUBLICATION: |
||
| 5909 | case TOOL_LINK: |
||
| 5910 | $class = 'btn btn-default'; |
||
| 5911 | $target = '_blank'; |
||
| 5912 | $link = self::rl_get_resource_link_for_learnpath( |
||
| 5913 | $this->course_int_id, |
||
| 5914 | $this->lp_id, |
||
| 5915 | $arrLP[$i]['id'], |
||
| 5916 | 0 |
||
| 5917 | ); |
||
| 5918 | $previewIcon = Display::url( |
||
| 5919 | $previewImage, |
||
| 5920 | $link, |
||
| 5921 | [ |
||
| 5922 | 'class' => $class, |
||
| 5923 | 'data-title' => $arrLP[$i]['title'], |
||
| 5924 | 'title' => $arrLP[$i]['title'], |
||
| 5925 | 'target' => $target, |
||
| 5926 | ] |
||
| 5927 | ); |
||
| 5928 | break; |
||
| 5929 | default: |
||
| 5930 | $previewIcon = Display::url( |
||
| 5931 | $previewImage, |
||
| 5932 | $url.'&action=view_item', |
||
| 5933 | ['class' => 'btn btn-default', 'target' => '_blank'] |
||
| 5934 | ); |
||
| 5935 | break; |
||
| 5936 | } |
||
| 5937 | |||
| 5938 | if ('dir' != $arrLP[$i]['item_type']) { |
||
| 5939 | $prerequisities_icon = Display::url( |
||
| 5940 | Display::return_icon( |
||
| 5941 | 'accept.png', |
||
| 5942 | get_lang('Prerequisites'), |
||
| 5943 | [], |
||
| 5944 | ICON_SIZE_TINY |
||
| 5945 | ), |
||
| 5946 | $url.'&action=edit_item_prereq', |
||
| 5947 | ['class' => 'btn btn-default'] |
||
| 5948 | ); |
||
| 5949 | if ('final_item' != $arrLP[$i]['item_type']) { |
||
| 5950 | /*$move_item_icon = Display::url( |
||
| 5951 | Display::return_icon( |
||
| 5952 | 'move.png', |
||
| 5953 | get_lang('Move'), |
||
| 5954 | [], |
||
| 5955 | ICON_SIZE_TINY |
||
| 5956 | ), |
||
| 5957 | $url.'&action=move_item', |
||
| 5958 | ['class' => 'btn btn-default'] |
||
| 5959 | );*/ |
||
| 5960 | } |
||
| 5961 | $audio_icon = Display::url( |
||
| 5962 | Display::return_icon( |
||
| 5963 | 'audio.png', |
||
| 5964 | get_lang('Upload'), |
||
| 5965 | [], |
||
| 5966 | ICON_SIZE_TINY |
||
| 5967 | ), |
||
| 5968 | $url.'&action=add_audio', |
||
| 5969 | ['class' => 'btn btn-default'] |
||
| 5970 | ); |
||
| 5971 | } |
||
| 5972 | } |
||
| 5973 | if ('true' != $update_audio) { |
||
| 5974 | $row = $move_icon.' '.$icon. |
||
| 5975 | Display::span($title_cut). |
||
| 5976 | Display::tag( |
||
| 5977 | 'div', |
||
| 5978 | "<div class=\"btn-group btn-group-xs\"> |
||
| 5979 | $previewIcon |
||
| 5980 | $audio |
||
| 5981 | $edit_icon |
||
| 5982 | $pluginCalendarIcon |
||
| 5983 | $forumIcon |
||
| 5984 | $prerequisities_icon |
||
| 5985 | $move_item_icon |
||
| 5986 | $audio_icon |
||
| 5987 | $orderIcons |
||
| 5988 | $delete_icon |
||
| 5989 | </div>", |
||
| 5990 | ['class' => 'btn-toolbar button_actions'] |
||
| 5991 | ); |
||
| 5992 | } else { |
||
| 5993 | $row = |
||
| 5994 | Display::span($title.$icon). |
||
| 5995 | Display::span($audio, ['class' => 'button_actions']); |
||
| 5996 | } |
||
| 5997 | |||
| 5998 | $default_data[$arrLP[$i]['id']] = $row; |
||
| 5999 | $default_content[$arrLP[$i]['id']] = $arrLP[$i]; |
||
| 6000 | |||
| 6001 | if (empty($parent_id)) { |
||
| 6002 | $elements[$arrLP[$i]['id']]['data'] = $row; |
||
| 6003 | $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type']; |
||
| 6004 | } else { |
||
| 6005 | $parent_arrays = []; |
||
| 6006 | if ($arrLP[$i]['depth'] > 1) { |
||
| 6007 | // Getting list of parents |
||
| 6008 | for ($j = 0; $j < $arrLP[$i]['depth']; $j++) { |
||
| 6009 | foreach ($arrLP as $item) { |
||
| 6010 | if ($item['id'] == $parent_id) { |
||
| 6011 | if (0 == $item['parent_item_id']) { |
||
| 6012 | $parent_id = $item['id']; |
||
| 6013 | break; |
||
| 6014 | } else { |
||
| 6015 | $parent_id = $item['parent_item_id']; |
||
| 6016 | if (empty($parent_arrays)) { |
||
| 6017 | $parent_arrays[] = intval($item['id']); |
||
| 6018 | } |
||
| 6019 | $parent_arrays[] = $parent_id; |
||
| 6020 | break; |
||
| 6021 | } |
||
| 6022 | } |
||
| 6023 | } |
||
| 6024 | } |
||
| 6025 | } |
||
| 6026 | |||
| 6027 | if (!empty($parent_arrays)) { |
||
| 6028 | $parent_arrays = array_reverse($parent_arrays); |
||
| 6029 | $val = '$elements'; |
||
| 6030 | $x = 0; |
||
| 6031 | foreach ($parent_arrays as $item) { |
||
| 6032 | if ($x != count($parent_arrays) - 1) { |
||
| 6033 | $val .= '["'.$item.'"]["children"]'; |
||
| 6034 | } else { |
||
| 6035 | $val .= '["'.$item.'"]["children"]'; |
||
| 6036 | } |
||
| 6037 | $x++; |
||
| 6038 | } |
||
| 6039 | $val .= ""; |
||
| 6040 | $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; "; |
||
| 6041 | eval($code_str); |
||
| 6042 | } else { |
||
| 6043 | $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row; |
||
| 6044 | $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type']; |
||
| 6045 | } |
||
| 6046 | } |
||
| 6047 | } |
||
| 6048 | |||
| 6049 | return [ |
||
| 6050 | 'elements' => $elements, |
||
| 6051 | 'default_data' => $default_data, |
||
| 6052 | 'default_content' => $default_content, |
||
| 6053 | 'return_audio' => $return_audio, |
||
| 6054 | ]; |
||
| 6055 | } |
||
| 6056 | |||
| 6057 | /** |
||
| 6058 | * @param string $updateAudio true/false strings |
||
| 6059 | * |
||
| 6060 | * @return string |
||
| 6061 | */ |
||
| 6062 | public function returnLpItemList($updateAudio) |
||
| 6063 | { |
||
| 6064 | $result = $this->processBuildMenuElements($updateAudio); |
||
| 6065 | |||
| 6066 | $html = self::print_recursive( |
||
| 6067 | $result['elements'], |
||
| 6068 | $result['default_data'], |
||
| 6069 | $result['default_content'] |
||
| 6070 | ); |
||
| 6071 | |||
| 6072 | if (!empty($html)) { |
||
| 6073 | $html .= Display::return_message(get_lang('Drag and drop an element here')); |
||
| 6074 | } |
||
| 6075 | |||
| 6076 | return $html; |
||
| 6077 | } |
||
| 6078 | |||
| 6079 | /** |
||
| 6080 | * @param string $update_audio |
||
| 6081 | * @param bool $drop_element_here |
||
| 6082 | * |
||
| 6083 | * @return string |
||
| 6084 | */ |
||
| 6085 | public function return_new_tree($update_audio = 'false', $drop_element_here = false) |
||
| 6086 | { |
||
| 6087 | $result = $this->processBuildMenuElements($update_audio); |
||
| 6088 | |||
| 6089 | $list = '<ul id="lp_item_list">'; |
||
| 6090 | $tree = $this->print_recursive( |
||
| 6091 | $result['elements'], |
||
| 6092 | $result['default_data'], |
||
| 6093 | $result['default_content'] |
||
| 6094 | ); |
||
| 6095 | |||
| 6096 | if (!empty($tree)) { |
||
| 6097 | $list .= $tree; |
||
| 6098 | } else { |
||
| 6099 | if ($drop_element_here) { |
||
| 6100 | $list .= Display::return_message(get_lang('Drag and drop an element here')); |
||
| 6101 | } |
||
| 6102 | } |
||
| 6103 | $list .= '</ul>'; |
||
| 6104 | |||
| 6105 | $return = Display::panelCollapse( |
||
| 6106 | $this->name, |
||
| 6107 | $list, |
||
| 6108 | 'scorm-list', |
||
| 6109 | null, |
||
| 6110 | 'scorm-list-accordion', |
||
| 6111 | 'scorm-list-collapse' |
||
| 6112 | ); |
||
| 6113 | |||
| 6114 | if ('true' === $update_audio) { |
||
| 6115 | $return = $result['return_audio']; |
||
| 6116 | } |
||
| 6117 | |||
| 6118 | return $return; |
||
| 6119 | } |
||
| 6120 | |||
| 6121 | /** |
||
| 6122 | * @param array $elements |
||
| 6123 | * @param array $default_data |
||
| 6124 | * @param array $default_content |
||
| 6125 | * |
||
| 6126 | * @return string |
||
| 6127 | */ |
||
| 6128 | public function print_recursive($elements, $default_data, $default_content) |
||
| 6129 | { |
||
| 6130 | $return = ''; |
||
| 6131 | foreach ($elements as $key => $item) { |
||
| 6132 | if (isset($item['load_data']) || empty($item['data'])) { |
||
| 6133 | $item['data'] = $default_data[$item['load_data']]; |
||
| 6134 | $item['type'] = $default_content[$item['load_data']]['item_type']; |
||
| 6135 | } |
||
| 6136 | $sub_list = ''; |
||
| 6137 | if (isset($item['type']) && 'dir' === $item['type']) { |
||
| 6138 | // empty value |
||
| 6139 | $sub_list = Display::tag('li', '', ['class' => 'sub_item empty']); |
||
| 6140 | } |
||
| 6141 | if (empty($item['children'])) { |
||
| 6142 | $sub_list = Display::tag('ul', $sub_list, ['id' => 'UL_'.$key, 'class' => 'record li_container']); |
||
| 6143 | $active = null; |
||
| 6144 | if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) { |
||
| 6145 | $active = 'active'; |
||
| 6146 | } |
||
| 6147 | $return .= Display::tag( |
||
| 6148 | 'li', |
||
| 6149 | Display::div($item['data'], ['class' => "item_data $active"]).$sub_list, |
||
| 6150 | ['id' => $key, 'class' => 'record li_container'] |
||
| 6151 | ); |
||
| 6152 | } else { |
||
| 6153 | // Sections |
||
| 6154 | $data = ''; |
||
| 6155 | if (isset($item['children'])) { |
||
| 6156 | $data = self::print_recursive($item['children'], $default_data, $default_content); |
||
| 6157 | } |
||
| 6158 | $sub_list = Display::tag('ul', $sub_list.$data, ['id' => 'UL_'.$key, 'class' => 'record li_container']); |
||
| 6159 | $return .= Display::tag( |
||
| 6160 | 'li', |
||
| 6161 | Display::div($item['data'], ['class' => 'item_data']).$sub_list, |
||
| 6162 | ['id' => $key, 'class' => 'record li_container'] |
||
| 6163 | ); |
||
| 6164 | } |
||
| 6165 | } |
||
| 6166 | |||
| 6167 | return $return; |
||
| 6168 | } |
||
| 6169 | |||
| 6170 | /** |
||
| 6171 | * This function builds the action menu. |
||
| 6172 | * |
||
| 6173 | * @param bool $returnString Optional |
||
| 6174 | * @param bool $showRequirementButtons Optional. Allow show the requirements button |
||
| 6175 | * @param bool $isConfigPage Optional. If is the config page, show the edit button |
||
| 6176 | * @param bool $allowExpand Optional. Allow show the expand/contract button |
||
| 6177 | * @param string $action |
||
| 6178 | * @param array $extraField |
||
| 6179 | * |
||
| 6180 | * @return string |
||
| 6181 | */ |
||
| 6182 | public function build_action_menu( |
||
| 6183 | $returnString = false, |
||
| 6184 | $showRequirementButtons = true, |
||
| 6185 | $isConfigPage = false, |
||
| 6186 | $allowExpand = true, |
||
| 6187 | $action = '', |
||
| 6188 | $extraField = [] |
||
| 6189 | ) { |
||
| 6190 | $actionsRight = ''; |
||
| 6191 | $lpId = $this->lp_id; |
||
| 6192 | if (!isset($extraField['backTo']) && empty($extraField['backTo'])) { |
||
| 6193 | $back = Display::url( |
||
| 6194 | Display::return_icon( |
||
| 6195 | 'back.png', |
||
| 6196 | get_lang('Back to learning paths'), |
||
| 6197 | '', |
||
| 6198 | ICON_SIZE_MEDIUM |
||
| 6199 | ), |
||
| 6200 | 'lp_controller.php?'.api_get_cidreq() |
||
| 6201 | ); |
||
| 6202 | } else { |
||
| 6203 | $back = Display::url( |
||
| 6204 | Display::return_icon( |
||
| 6205 | 'back.png', |
||
| 6206 | get_lang('Back'), |
||
| 6207 | '', |
||
| 6208 | ICON_SIZE_MEDIUM |
||
| 6209 | ), |
||
| 6210 | $extraField['backTo'] |
||
| 6211 | ); |
||
| 6212 | } |
||
| 6213 | |||
| 6214 | /*if ($backToBuild) { |
||
| 6215 | $back = Display::url( |
||
| 6216 | Display::return_icon( |
||
| 6217 | 'back.png', |
||
| 6218 | get_lang('GoBack'), |
||
| 6219 | '', |
||
| 6220 | ICON_SIZE_MEDIUM |
||
| 6221 | ), |
||
| 6222 | "lp_controller.php?action=add_item&type=step&lp_id=$lpId&".api_get_cidreq() |
||
| 6223 | ); |
||
| 6224 | }*/ |
||
| 6225 | |||
| 6226 | $actionsLeft = $back; |
||
| 6227 | |||
| 6228 | $actionsLeft .= Display::url( |
||
| 6229 | Display::return_icon( |
||
| 6230 | 'preview_view.png', |
||
| 6231 | get_lang('Preview'), |
||
| 6232 | '', |
||
| 6233 | ICON_SIZE_MEDIUM |
||
| 6234 | ), |
||
| 6235 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6236 | 'action' => 'view', |
||
| 6237 | 'lp_id' => $lpId, |
||
| 6238 | 'isStudentView' => 'true', |
||
| 6239 | ]) |
||
| 6240 | ); |
||
| 6241 | |||
| 6242 | $actionsLeft .= Display::url( |
||
| 6243 | Display::return_icon( |
||
| 6244 | 'upload_audio.png', |
||
| 6245 | get_lang('Add audio'), |
||
| 6246 | '', |
||
| 6247 | ICON_SIZE_MEDIUM |
||
| 6248 | ), |
||
| 6249 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6250 | 'action' => 'admin_view', |
||
| 6251 | 'lp_id' => $lpId, |
||
| 6252 | 'updateaudio' => 'true', |
||
| 6253 | ]) |
||
| 6254 | ); |
||
| 6255 | |||
| 6256 | $subscriptionSettings = self::getSubscriptionSettings(); |
||
| 6257 | |||
| 6258 | $request = api_request_uri(); |
||
| 6259 | if (false === strpos($request, 'edit')) { |
||
| 6260 | $actionsLeft .= Display::url( |
||
| 6261 | Display::return_icon( |
||
| 6262 | 'settings.png', |
||
| 6263 | get_lang('Course settings'), |
||
| 6264 | '', |
||
| 6265 | ICON_SIZE_MEDIUM |
||
| 6266 | ), |
||
| 6267 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6268 | 'action' => 'edit', |
||
| 6269 | 'lp_id' => $lpId, |
||
| 6270 | ]) |
||
| 6271 | ); |
||
| 6272 | } |
||
| 6273 | |||
| 6274 | if ((false === strpos($request, 'build') && |
||
| 6275 | false === strpos($request, 'add_item')) || |
||
| 6276 | in_array($action, ['add_audio']) |
||
| 6277 | ) { |
||
| 6278 | $actionsLeft .= Display::url( |
||
| 6279 | Display::return_icon( |
||
| 6280 | 'edit.png', |
||
| 6281 | get_lang('Edit'), |
||
| 6282 | '', |
||
| 6283 | ICON_SIZE_MEDIUM |
||
| 6284 | ), |
||
| 6285 | 'lp_controller.php?'.http_build_query([ |
||
| 6286 | 'action' => 'build', |
||
| 6287 | 'lp_id' => $lpId, |
||
| 6288 | ]).'&'.api_get_cidreq() |
||
| 6289 | ); |
||
| 6290 | } |
||
| 6291 | |||
| 6292 | if (false === strpos(api_get_self(), 'lp_subscribe_users.php')) { |
||
| 6293 | if (1 == $this->subscribeUsers && |
||
| 6294 | $subscriptionSettings['allow_add_users_to_lp']) { |
||
| 6295 | $actionsLeft .= Display::url( |
||
| 6296 | Display::return_icon( |
||
| 6297 | 'user.png', |
||
| 6298 | get_lang('Subscribe users to learning path'), |
||
| 6299 | '', |
||
| 6300 | ICON_SIZE_MEDIUM |
||
| 6301 | ), |
||
| 6302 | api_get_path(WEB_CODE_PATH)."lp/lp_subscribe_users.php?lp_id=$lpId&".api_get_cidreq() |
||
| 6303 | ); |
||
| 6304 | } |
||
| 6305 | } |
||
| 6306 | |||
| 6307 | if ($allowExpand) { |
||
| 6308 | /*$actionsLeft .= Display::url( |
||
| 6309 | Display::return_icon( |
||
| 6310 | 'expand.png', |
||
| 6311 | get_lang('Expand'), |
||
| 6312 | ['id' => 'expand'], |
||
| 6313 | ICON_SIZE_MEDIUM |
||
| 6314 | ). |
||
| 6315 | Display::return_icon( |
||
| 6316 | 'contract.png', |
||
| 6317 | get_lang('Collapse'), |
||
| 6318 | ['id' => 'contract', 'class' => 'hide'], |
||
| 6319 | ICON_SIZE_MEDIUM |
||
| 6320 | ), |
||
| 6321 | '#', |
||
| 6322 | ['role' => 'button', 'id' => 'hide_bar_template'] |
||
| 6323 | );*/ |
||
| 6324 | } |
||
| 6325 | |||
| 6326 | if ($showRequirementButtons) { |
||
| 6327 | $buttons = [ |
||
| 6328 | [ |
||
| 6329 | 'title' => get_lang('Set previous step as prerequisite for each step'), |
||
| 6330 | 'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6331 | 'action' => 'set_previous_step_as_prerequisite', |
||
| 6332 | 'lp_id' => $lpId, |
||
| 6333 | ]), |
||
| 6334 | ], |
||
| 6335 | [ |
||
| 6336 | 'title' => get_lang('Clear all prerequisites'), |
||
| 6337 | 'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6338 | 'action' => 'clear_prerequisites', |
||
| 6339 | 'lp_id' => $lpId, |
||
| 6340 | ]), |
||
| 6341 | ], |
||
| 6342 | ]; |
||
| 6343 | $actionsRight = Display::groupButtonWithDropDown( |
||
| 6344 | get_lang('Prerequisites options'), |
||
| 6345 | $buttons, |
||
| 6346 | true |
||
| 6347 | ); |
||
| 6348 | } |
||
| 6349 | |||
| 6350 | if (api_is_platform_admin() && isset($extraField['authorlp'])) { |
||
| 6351 | $actionsLeft .= Display::url( |
||
| 6352 | Display::return_icon( |
||
| 6353 | 'add-groups.png', |
||
| 6354 | get_lang('Author'), |
||
| 6355 | '', |
||
| 6356 | ICON_SIZE_MEDIUM |
||
| 6357 | ), |
||
| 6358 | 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 6359 | 'action' => 'author_view', |
||
| 6360 | 'lp_id' => $lpId, |
||
| 6361 | ]) |
||
| 6362 | ); |
||
| 6363 | } |
||
| 6364 | |||
| 6365 | $toolbar = Display::toolbarAction( |
||
| 6366 | 'actions-lp-controller', |
||
| 6367 | [$actionsLeft, $actionsRight] |
||
| 6368 | ); |
||
| 6369 | |||
| 6370 | if ($returnString) { |
||
| 6371 | return $toolbar; |
||
| 6372 | } |
||
| 6373 | |||
| 6374 | echo $toolbar; |
||
| 6375 | } |
||
| 6376 | |||
| 6377 | /** |
||
| 6378 | * Creates the default learning path folder. |
||
| 6379 | * |
||
| 6380 | * @param array $course |
||
| 6381 | * @param int $creatorId |
||
| 6382 | * |
||
| 6383 | * @return CDocument |
||
| 6384 | */ |
||
| 6385 | public static function generate_learning_path_folder($course, $creatorId = 0) |
||
| 6386 | { |
||
| 6387 | // Creating learning_path folder |
||
| 6388 | $dir = 'learning_path'; |
||
| 6389 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
| 6390 | |||
| 6391 | return create_unexisting_directory( |
||
| 6392 | $course, |
||
| 6393 | $creatorId, |
||
| 6394 | 0, |
||
| 6395 | null, |
||
| 6396 | 0, |
||
| 6397 | '', |
||
| 6398 | $dir, |
||
| 6399 | get_lang('Learning paths'), |
||
| 6400 | 0 |
||
| 6401 | ); |
||
| 6402 | } |
||
| 6403 | |||
| 6404 | /** |
||
| 6405 | * @param array $course |
||
| 6406 | * @param string $lp_name |
||
| 6407 | * @param int $creatorId |
||
| 6408 | * |
||
| 6409 | * @return CDocument |
||
| 6410 | */ |
||
| 6411 | public function generate_lp_folder($course, $lp_name = '', $creatorId = 0) |
||
| 6412 | { |
||
| 6413 | $filepath = ''; |
||
| 6414 | $dir = '/learning_path/'; |
||
| 6415 | |||
| 6416 | if (empty($lp_name)) { |
||
| 6417 | $lp_name = $this->name; |
||
| 6418 | } |
||
| 6419 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
| 6420 | $parent = self::generate_learning_path_folder($course, $creatorId); |
||
| 6421 | |||
| 6422 | // Limits title size |
||
| 6423 | $title = api_substr(api_replace_dangerous_char($lp_name), 0, 80); |
||
| 6424 | $dir = $dir.$title; |
||
| 6425 | |||
| 6426 | // Creating LP folder |
||
| 6427 | $documentId = null; |
||
| 6428 | $folder = null; |
||
| 6429 | if ($parent) { |
||
| 6430 | $folder = create_unexisting_directory( |
||
| 6431 | $course, |
||
| 6432 | $creatorId, |
||
| 6433 | 0, |
||
| 6434 | 0, |
||
| 6435 | 0, |
||
| 6436 | $filepath, |
||
| 6437 | $dir, |
||
| 6438 | $lp_name, |
||
| 6439 | '', |
||
| 6440 | false, |
||
| 6441 | false, |
||
| 6442 | $parent |
||
| 6443 | ); |
||
| 6444 | } |
||
| 6445 | |||
| 6446 | return $folder; |
||
| 6447 | } |
||
| 6448 | |||
| 6449 | /** |
||
| 6450 | * Create a new document //still needs some finetuning. |
||
| 6451 | * |
||
| 6452 | * @param array $courseInfo |
||
| 6453 | * @param string $content |
||
| 6454 | * @param string $title |
||
| 6455 | * @param string $extension |
||
| 6456 | * @param int $parentId |
||
| 6457 | * @param int $creatorId creator id |
||
| 6458 | * |
||
| 6459 | * @return int |
||
| 6460 | */ |
||
| 6461 | public function create_document( |
||
| 6462 | $courseInfo, |
||
| 6463 | $content = '', |
||
| 6464 | $title = '', |
||
| 6465 | $extension = 'html', |
||
| 6466 | $parentId = 0, |
||
| 6467 | $creatorId = 0 |
||
| 6468 | ) { |
||
| 6469 | if (!empty($courseInfo)) { |
||
| 6470 | $course_id = $courseInfo['real_id']; |
||
| 6471 | } else { |
||
| 6472 | $course_id = api_get_course_int_id(); |
||
| 6473 | } |
||
| 6474 | |||
| 6475 | $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId; |
||
| 6476 | $sessionId = api_get_session_id(); |
||
| 6477 | |||
| 6478 | // Generates folder |
||
| 6479 | $this->generate_lp_folder($courseInfo); |
||
| 6480 | // stripslashes() before calling api_replace_dangerous_char() because $_POST['title'] |
||
| 6481 | // is already escaped twice when it gets here. |
||
| 6482 | $originalTitle = !empty($title) ? $title : $_POST['title']; |
||
| 6483 | if (!empty($title)) { |
||
| 6484 | $title = api_replace_dangerous_char(stripslashes($title)); |
||
| 6485 | } else { |
||
| 6486 | $title = api_replace_dangerous_char(stripslashes($_POST['title'])); |
||
| 6487 | } |
||
| 6488 | |||
| 6489 | $title = disable_dangerous_file($title); |
||
| 6490 | $filename = $title; |
||
| 6491 | $content = !empty($content) ? $content : $_POST['content_lp']; |
||
| 6492 | $tmp_filename = $filename; |
||
| 6493 | /*$i = 0; |
||
| 6494 | while (file_exists($filepath.$tmp_filename.'.'.$extension)) { |
||
| 6495 | $tmp_filename = $filename.'_'.++$i; |
||
| 6496 | }*/ |
||
| 6497 | $filename = $tmp_filename.'.'.$extension; |
||
| 6498 | |||
| 6499 | if ('html' === $extension) { |
||
| 6500 | $content = stripslashes($content); |
||
| 6501 | $content = str_replace( |
||
| 6502 | api_get_path(WEB_COURSE_PATH), |
||
| 6503 | api_get_path(REL_PATH).'courses/', |
||
| 6504 | $content |
||
| 6505 | ); |
||
| 6506 | |||
| 6507 | // Change the path of mp3 to absolute. |
||
| 6508 | // The first regexp deals with :// urls. |
||
| 6509 | /*$content = preg_replace( |
||
| 6510 | "|(flashvars=\"file=)([^:/]+)/|", |
||
| 6511 | "$1".api_get_path( |
||
| 6512 | REL_COURSE_PATH |
||
| 6513 | ).$courseInfo['path'].'/document/', |
||
| 6514 | $content |
||
| 6515 | );*/ |
||
| 6516 | // The second regexp deals with audio/ urls. |
||
| 6517 | /*$content = preg_replace( |
||
| 6518 | "|(flashvars=\"file=)([^/]+)/|", |
||
| 6519 | "$1".api_get_path( |
||
| 6520 | REL_COURSE_PATH |
||
| 6521 | ).$courseInfo['path'].'/document/$2/', |
||
| 6522 | $content |
||
| 6523 | );*/ |
||
| 6524 | // For flv player: To prevent edition problem with firefox, |
||
| 6525 | // we have to use a strange tip (don't blame me please). |
||
| 6526 | $content = str_replace( |
||
| 6527 | '</body>', |
||
| 6528 | '<style type="text/css">body{}</style></body>', |
||
| 6529 | $content |
||
| 6530 | ); |
||
| 6531 | } |
||
| 6532 | |||
| 6533 | //$save_file_path = $dir.$filename; |
||
| 6534 | |||
| 6535 | $document = DocumentManager::addDocument( |
||
| 6536 | $courseInfo, |
||
| 6537 | null, |
||
| 6538 | 'file', |
||
| 6539 | '', |
||
| 6540 | $tmp_filename, |
||
| 6541 | '', |
||
| 6542 | 0, //readonly |
||
| 6543 | true, |
||
| 6544 | null, |
||
| 6545 | $sessionId, |
||
| 6546 | $creatorId, |
||
| 6547 | false, |
||
| 6548 | $content, |
||
| 6549 | $parentId |
||
| 6550 | ); |
||
| 6551 | |||
| 6552 | $document_id = $document->getIid(); |
||
| 6553 | if ($document_id) { |
||
| 6554 | $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : ''; |
||
| 6555 | $new_title = $originalTitle; |
||
| 6556 | |||
| 6557 | if ($new_comment || $new_title) { |
||
| 6558 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
| 6559 | $ct = ''; |
||
| 6560 | if ($new_comment) { |
||
| 6561 | $ct .= ", comment='".Database::escape_string($new_comment)."'"; |
||
| 6562 | } |
||
| 6563 | if ($new_title) { |
||
| 6564 | $ct .= ", title='".Database::escape_string($new_title)."' "; |
||
| 6565 | } |
||
| 6566 | |||
| 6567 | $sql = "UPDATE $tbl_doc SET ".substr($ct, 1)." |
||
| 6568 | WHERE iid = $document_id "; |
||
| 6569 | Database::query($sql); |
||
| 6570 | } |
||
| 6571 | } |
||
| 6572 | |||
| 6573 | return $document_id; |
||
| 6574 | } |
||
| 6575 | |||
| 6576 | /** |
||
| 6577 | * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'. |
||
| 6578 | */ |
||
| 6579 | public function edit_document() |
||
| 6580 | { |
||
| 6581 | $repo = Container::getDocumentRepository(); |
||
| 6582 | if (isset($_REQUEST['document_id']) && !empty($_REQUEST['document_id'])) { |
||
| 6583 | $id = (int) $_REQUEST['document_id']; |
||
| 6584 | /** @var CDocument $document */ |
||
| 6585 | $document = $repo->find($id); |
||
| 6586 | if ($document->getResourceNode()->hasEditableTextContent()) { |
||
| 6587 | $repo->updateResourceFileContent($document, $_REQUEST['content_lp']); |
||
| 6588 | /* $nodeRepo = Container::getDocumentRepository()->getResourceNodeRepository(); |
||
| 6589 | $nodeRepo->update($document->getResourceNode()); |
||
| 6590 | var_dump($document->getResourceNode()->getContent()); |
||
| 6591 | exit;*/ |
||
| 6592 | } |
||
| 6593 | $document->setTitle($_REQUEST['title']); |
||
| 6594 | $repo->update($document); |
||
| 6595 | } |
||
| 6596 | } |
||
| 6597 | |||
| 6598 | /** |
||
| 6599 | * Displays the selected item, with a panel for manipulating the item. |
||
| 6600 | * |
||
| 6601 | * @param CLpItem $lpItem |
||
| 6602 | * @param string $msg |
||
| 6603 | * @param bool $show_actions |
||
| 6604 | * |
||
| 6605 | * @return string |
||
| 6606 | */ |
||
| 6607 | public function display_item($lpItem, $msg = null, $show_actions = true) |
||
| 6608 | { |
||
| 6609 | $course_id = api_get_course_int_id(); |
||
| 6610 | $return = ''; |
||
| 6611 | |||
| 6612 | if (empty($lpItem)) { |
||
| 6613 | return ''; |
||
| 6614 | } |
||
| 6615 | $item_id = $lpItem->getIid(); |
||
| 6616 | $itemType = $lpItem->getItemType(); |
||
| 6617 | $lpId = $lpItem->getLp()->getIid(); |
||
| 6618 | $path = $lpItem->getPath(); |
||
| 6619 | |||
| 6620 | Session::write('parent_item_id', 'dir' === $itemType ? $item_id : 0); |
||
| 6621 | |||
| 6622 | // Prevents wrong parent selection for document, see Bug#1251. |
||
| 6623 | if ('dir' !== $itemType) { |
||
| 6624 | Session::write('parent_item_id', $lpItem->getParentItemId()); |
||
| 6625 | } |
||
| 6626 | |||
| 6627 | if ($show_actions) { |
||
| 6628 | $return .= $this->displayItemMenu($lpItem); |
||
| 6629 | } |
||
| 6630 | $return .= '<div style="padding:10px;">'; |
||
| 6631 | |||
| 6632 | if ('' != $msg) { |
||
| 6633 | $return .= $msg; |
||
| 6634 | } |
||
| 6635 | |||
| 6636 | $return .= '<h3>'.$lpItem->getTitle().'</h3>'; |
||
| 6637 | |||
| 6638 | switch ($itemType) { |
||
| 6639 | case TOOL_THREAD: |
||
| 6640 | $link = $this->rl_get_resource_link_for_learnpath( |
||
| 6641 | $course_id, |
||
| 6642 | $lpId, |
||
| 6643 | $item_id, |
||
| 6644 | 0 |
||
| 6645 | ); |
||
| 6646 | $return .= Display::url( |
||
| 6647 | get_lang('Go to thread'), |
||
| 6648 | $link, |
||
| 6649 | ['class' => 'btn btn-primary'] |
||
| 6650 | ); |
||
| 6651 | break; |
||
| 6652 | case TOOL_FORUM: |
||
| 6653 | $return .= Display::url( |
||
| 6654 | get_lang('Go to the forum'), |
||
| 6655 | api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$path, |
||
| 6656 | ['class' => 'btn btn-primary'] |
||
| 6657 | ); |
||
| 6658 | break; |
||
| 6659 | case TOOL_QUIZ: |
||
| 6660 | if (!empty($path)) { |
||
| 6661 | $exercise = new Exercise(); |
||
| 6662 | $exercise->read($path); |
||
| 6663 | $return .= $exercise->description.'<br />'; |
||
| 6664 | $return .= Display::url( |
||
| 6665 | get_lang('Go to exercise'), |
||
| 6666 | api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&exerciseId='.$exercise->id, |
||
| 6667 | ['class' => 'btn btn-primary'] |
||
| 6668 | ); |
||
| 6669 | } |
||
| 6670 | break; |
||
| 6671 | case TOOL_LP_FINAL_ITEM: |
||
| 6672 | $return .= $this->getSavedFinalItem(); |
||
| 6673 | break; |
||
| 6674 | case TOOL_DOCUMENT: |
||
| 6675 | case TOOL_READOUT_TEXT: |
||
| 6676 | $repo = Container::getDocumentRepository(); |
||
| 6677 | /** @var CDocument $document */ |
||
| 6678 | $document = $repo->find($lpItem->getPath()); |
||
| 6679 | $return .= $this->display_document($document, true, true); |
||
| 6680 | break; |
||
| 6681 | case TOOL_HOTPOTATOES: |
||
| 6682 | $return .= $this->display_document($document, false, true); |
||
| 6683 | break; |
||
| 6684 | } |
||
| 6685 | $return .= '</div>'; |
||
| 6686 | |||
| 6687 | return $return; |
||
| 6688 | } |
||
| 6689 | |||
| 6690 | /** |
||
| 6691 | * Shows the needed forms for editing a specific item. |
||
| 6692 | * |
||
| 6693 | * @param CLpItem $lpItem |
||
| 6694 | * |
||
| 6695 | * @throws Exception |
||
| 6696 | * @throws HTML_QuickForm_Error |
||
| 6697 | * |
||
| 6698 | * @return string |
||
| 6699 | */ |
||
| 6700 | public function display_edit_item($lpItem, $excludeExtraFields = []) |
||
| 6784 | } |
||
| 6785 | |||
| 6786 | /** |
||
| 6787 | * Function that displays a list with al the resources that |
||
| 6788 | * could be added to the learning path. |
||
| 6789 | * |
||
| 6790 | * @throws Exception |
||
| 6791 | * @throws HTML_QuickForm_Error |
||
| 6792 | * |
||
| 6793 | * @return bool |
||
| 6794 | */ |
||
| 6795 | public function displayResources() |
||
| 6796 | { |
||
| 6797 | // Get all the docs. |
||
| 6798 | $documents = $this->get_documents(true); |
||
| 6799 | |||
| 6800 | // Get all the exercises. |
||
| 6801 | $exercises = $this->get_exercises(); |
||
| 6802 | |||
| 6803 | // Get all the links. |
||
| 6804 | $links = $this->get_links(); |
||
| 6805 | |||
| 6806 | // Get all the student publications. |
||
| 6807 | $works = $this->get_student_publications(); |
||
| 6808 | |||
| 6809 | // Get all the forums. |
||
| 6810 | $forums = $this->get_forums(); |
||
| 6811 | |||
| 6812 | // Get the final item form (see BT#11048) . |
||
| 6813 | $finish = $this->getFinalItemForm(); |
||
| 6814 | |||
| 6815 | $headers = [ |
||
| 6816 | Display::return_icon('folder_document.png', get_lang('Documents'), [], ICON_SIZE_BIG), |
||
| 6817 | Display::return_icon('quiz.png', get_lang('Tests'), [], ICON_SIZE_BIG), |
||
| 6818 | Display::return_icon('links.png', get_lang('Links'), [], ICON_SIZE_BIG), |
||
| 6819 | Display::return_icon('works.png', get_lang('Assignments'), [], ICON_SIZE_BIG), |
||
| 6820 | Display::return_icon('forum.png', get_lang('Forums'), [], ICON_SIZE_BIG), |
||
| 6821 | Display::return_icon('add_learnpath_section.png', get_lang('Add section'), [], ICON_SIZE_BIG), |
||
| 6822 | Display::return_icon('certificate.png', get_lang('Certificate'), [], ICON_SIZE_BIG), |
||
| 6823 | ]; |
||
| 6824 | |||
| 6825 | echo Display::return_message( |
||
| 6826 | get_lang('Click on the [Learner view] button to see your learning path'), |
||
| 6827 | 'normal' |
||
| 6828 | ); |
||
| 6829 | $section = $this->displayNewSectionForm(); |
||
| 6830 | $selected = isset($_REQUEST['lp_build_selected']) ? (int) $_REQUEST['lp_build_selected'] : 0; |
||
| 6831 | |||
| 6832 | echo Display::tabs( |
||
| 6833 | $headers, |
||
| 6834 | [ |
||
| 6835 | $documents, |
||
| 6836 | $exercises, |
||
| 6837 | $links, |
||
| 6838 | $works, |
||
| 6839 | $forums, |
||
| 6840 | $section, |
||
| 6841 | $finish, |
||
| 6842 | ], |
||
| 6843 | 'resource_tab', |
||
| 6844 | [], |
||
| 6845 | [], |
||
| 6846 | $selected |
||
| 6847 | ); |
||
| 6848 | |||
| 6849 | return true; |
||
| 6850 | } |
||
| 6851 | |||
| 6852 | /** |
||
| 6853 | * Returns the extension of a document. |
||
| 6854 | * |
||
| 6855 | * @param string $filename |
||
| 6856 | * |
||
| 6857 | * @return string Extension (part after the last dot) |
||
| 6858 | */ |
||
| 6859 | public function get_extension($filename) |
||
| 6860 | { |
||
| 6861 | $explode = explode('.', $filename); |
||
| 6862 | |||
| 6863 | return $explode[count($explode) - 1]; |
||
| 6864 | } |
||
| 6865 | |||
| 6866 | /** |
||
| 6867 | * @return string |
||
| 6868 | */ |
||
| 6869 | public function getCurrentBuildingModeURL() |
||
| 6870 | { |
||
| 6871 | $pathItem = isset($_GET['path_item']) ? (int) $_GET['path_item'] : ''; |
||
| 6872 | $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : ''; |
||
| 6873 | $id = isset($_GET['id']) ? (int) $_GET['id'] : ''; |
||
| 6874 | $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : ''; |
||
| 6875 | |||
| 6876 | $currentUrl = api_get_self().'?'.api_get_cidreq(). |
||
| 6877 | '&action='.$action.'&lp_id='.$this->lp_id.'&path_item='.$pathItem.'&view='.$view.'&id='.$id; |
||
| 6878 | |||
| 6879 | return $currentUrl; |
||
| 6880 | } |
||
| 6881 | |||
| 6882 | /** |
||
| 6883 | * Displays a document by id. |
||
| 6884 | * |
||
| 6885 | * @param CDocument $document |
||
| 6886 | * @param bool $show_title |
||
| 6887 | * @param bool $iframe |
||
| 6888 | * @param bool $edit_link |
||
| 6889 | * |
||
| 6890 | * @return string |
||
| 6891 | */ |
||
| 6892 | public function display_document($document, $show_title = false, $iframe = true, $edit_link = false) |
||
| 6893 | { |
||
| 6894 | $return = ''; |
||
| 6895 | if (!$document) { |
||
| 6896 | return ''; |
||
| 6897 | } |
||
| 6898 | |||
| 6899 | $repo = Container::getDocumentRepository(); |
||
| 6900 | |||
| 6901 | // TODO: Add a path filter. |
||
| 6902 | if ($iframe) { |
||
| 6903 | //$url = api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.str_replace('%2F', '/', urlencode($row_doc['path'])).'?'.api_get_cidreq(); |
||
| 6904 | $url = $repo->getResourceFileUrl($document); |
||
| 6905 | |||
| 6906 | $return .= '<iframe |
||
| 6907 | id="learnpath_preview_frame" |
||
| 6908 | frameborder="0" |
||
| 6909 | height="400" |
||
| 6910 | width="100%" |
||
| 6911 | scrolling="auto" |
||
| 6912 | src="'.$url.'"></iframe>'; |
||
| 6913 | } else { |
||
| 6914 | $return = $repo->getResourceFileContent($document); |
||
| 6915 | } |
||
| 6916 | |||
| 6917 | return $return; |
||
| 6918 | } |
||
| 6919 | |||
| 6920 | /** |
||
| 6921 | * Return HTML form to add/edit a link item. |
||
| 6922 | * |
||
| 6923 | * @param string $action (add/edit) |
||
| 6924 | * @param CLpItem $lpItem |
||
| 6925 | * @param CLink $link |
||
| 6926 | * |
||
| 6927 | * @throws Exception |
||
| 6928 | * @throws HTML_QuickForm_Error |
||
| 6929 | * |
||
| 6930 | * @return string HTML form |
||
| 6931 | */ |
||
| 6932 | public function display_link_form($action, $lpItem, $link) |
||
| 6933 | { |
||
| 6934 | $item_url = ''; |
||
| 6935 | if ($link) { |
||
| 6936 | $item_url = stripslashes($link->getUrl()); |
||
| 6937 | } |
||
| 6938 | $form = new FormValidator( |
||
| 6939 | 'edit_link', |
||
| 6940 | 'POST', |
||
| 6941 | $this->getCurrentBuildingModeURL() |
||
| 6942 | ); |
||
| 6943 | |||
| 6944 | LearnPathItemForm::setForm($form, $action, $this, $lpItem); |
||
| 6945 | |||
| 6946 | $urlAttributes = ['class' => 'learnpath_item_form']; |
||
| 6947 | $urlAttributes['disabled'] = 'disabled'; |
||
| 6948 | $form->addElement('url', 'url', get_lang('URL'), $urlAttributes); |
||
| 6949 | $form->setDefault('url', $item_url); |
||
| 6950 | |||
| 6951 | $form->addButtonSave(get_lang('Save'), 'submit_button'); |
||
| 6952 | |||
| 6953 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
| 6954 | } |
||
| 6955 | |||
| 6956 | /** |
||
| 6957 | * Return HTML form to add/edit a quiz. |
||
| 6958 | * |
||
| 6959 | * @param string $action Action (add/edit) |
||
| 6960 | * @param CLpItem $lpItem Item ID if already exists |
||
| 6961 | * @param CQuiz $exercise Extra information (quiz ID if integer) |
||
| 6962 | * |
||
| 6963 | * @throws Exception |
||
| 6964 | * |
||
| 6965 | * @return string HTML form |
||
| 6966 | */ |
||
| 6967 | public function display_quiz_form($action, $lpItem, $exercise) |
||
| 6968 | { |
||
| 6969 | $form = new FormValidator( |
||
| 6970 | 'quiz_form', |
||
| 6971 | 'POST', |
||
| 6972 | $this->getCurrentBuildingModeURL() |
||
| 6973 | ); |
||
| 6974 | |||
| 6975 | LearnPathItemForm::setForm($form, $action, $this, $lpItem); |
||
| 6976 | |||
| 6977 | $form->addButtonSave(get_lang('Save'), 'submit_button'); |
||
| 6978 | |||
| 6979 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
| 6980 | } |
||
| 6981 | |||
| 6982 | /** |
||
| 6983 | * Return the form to display the forum edit/add option. |
||
| 6984 | * |
||
| 6985 | * @param CLpItem $lpItem |
||
| 6986 | * |
||
| 6987 | * @throws Exception |
||
| 6988 | * |
||
| 6989 | * @return string HTML form |
||
| 6990 | */ |
||
| 6991 | public function display_forum_form($action, $lpItem, $resource) |
||
| 6992 | { |
||
| 6993 | $form = new FormValidator( |
||
| 6994 | 'forum_form', |
||
| 6995 | 'POST', |
||
| 6996 | $this->getCurrentBuildingModeURL() |
||
| 6997 | ); |
||
| 6998 | LearnPathItemForm::setForm($form, $action, $this, $lpItem); |
||
| 6999 | |||
| 7000 | if ('add' === $action) { |
||
| 7001 | $form->addButtonSave(get_lang('Add forum to course'), 'submit_button'); |
||
| 7002 | } else { |
||
| 7003 | $form->addButtonSave(get_lang('Edit the current forum'), 'submit_button'); |
||
| 7004 | } |
||
| 7005 | |||
| 7006 | return '<div class="sectioncomment">'.$form->returnForm().'</div>'; |
||
| 7007 | } |
||
| 7008 | |||
| 7009 | /** |
||
| 7010 | * Return HTML form to add/edit forum threads. |
||
| 7011 | * |
||
| 7012 | * @param string $action |
||
| 7013 | * @param CLpItem $lpItem |
||
| 7014 | * @param string $resource |
||
| 7015 | * |
||
| 7016 | * @throws Exception |
||
| 7017 | * |
||
| 7018 | * @return string HTML form |
||
| 7019 | */ |
||
| 7020 | public function display_thread_form($action, $lpItem, $resource) |
||
| 7021 | { |
||
| 7022 | $form = new FormValidator( |
||
| 7023 | 'thread_form', |
||
| 7024 | 'POST', |
||
| 7025 | $this->getCurrentBuildingModeURL() |
||
| 7026 | ); |
||
| 7027 | |||
| 7028 | LearnPathItemForm::setForm($form, 'edit', $this, $lpItem); |
||
| 7029 | |||
| 7030 | $form->addButtonSave(get_lang('Save'), 'submit_button'); |
||
| 7031 | |||
| 7032 | return $form->returnForm(); |
||
| 7033 | } |
||
| 7034 | |||
| 7035 | /** |
||
| 7036 | * Return the HTML form to display an item (generally a dir item). |
||
| 7037 | * |
||
| 7038 | * @param CLpItem $lpItem |
||
| 7039 | * @param string $action |
||
| 7040 | * |
||
| 7041 | * @throws Exception |
||
| 7042 | * @throws HTML_QuickForm_Error |
||
| 7043 | * |
||
| 7044 | * @return string HTML form |
||
| 7045 | */ |
||
| 7046 | public function display_item_form( |
||
| 7047 | $lpItem, |
||
| 7048 | $action = 'add_item' |
||
| 7049 | ) { |
||
| 7050 | $item_type = $lpItem->getItemType(); |
||
| 7051 | |||
| 7052 | $url = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id; |
||
| 7053 | |||
| 7054 | $form = new FormValidator('form_'.$item_type, 'POST', $url); |
||
| 7055 | LearnPathItemForm::setForm($form, 'edit', $this, $lpItem); |
||
| 7056 | |||
| 7057 | $form->addButtonSave(get_lang('Save section'), 'submit_button'); |
||
| 7058 | |||
| 7059 | return $form->returnForm(); |
||
| 7060 | } |
||
| 7061 | |||
| 7062 | /** |
||
| 7063 | * Return HTML form to add/edit a student publication (work). |
||
| 7064 | * |
||
| 7065 | * @param string $action |
||
| 7066 | * @param CStudentPublication $resource |
||
| 7067 | * |
||
| 7068 | * @throws Exception |
||
| 7069 | * |
||
| 7070 | * @return string HTML form |
||
| 7071 | */ |
||
| 7072 | public function display_student_publication_form($action, CLpItem $lpItem, $resource) |
||
| 7073 | { |
||
| 7074 | $form = new FormValidator('frm_student_publication', 'post', '#'); |
||
| 7075 | LearnPathItemForm::setForm($form, 'edit', $this, $lpItem); |
||
| 7076 | |||
| 7077 | $form->addButtonSave(get_lang('Save'), 'submit_button'); |
||
| 7078 | |||
| 7079 | $return = '<div class="sectioncomment">'; |
||
| 7080 | $return .= $form->returnForm(); |
||
| 7081 | $return .= '</div>'; |
||
| 7082 | |||
| 7083 | return $return; |
||
| 7084 | } |
||
| 7085 | |||
| 7086 | public function displayNewSectionForm() |
||
| 7087 | { |
||
| 7088 | $action = 'add_item'; |
||
| 7089 | $item_type = 'dir'; |
||
| 7090 | |||
| 7091 | $lpItem = new CLpItem(); |
||
| 7092 | $lpItem->setTitle('dir'); |
||
| 7093 | $lpItem->setItemType('dir'); |
||
| 7094 | |||
| 7095 | $url = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id; |
||
| 7096 | |||
| 7097 | $form = new FormValidator('form_'.$item_type, 'POST', $url); |
||
| 7098 | LearnPathItemForm::setForm($form, 'add', $this, $lpItem); |
||
| 7099 | |||
| 7100 | $form->addButtonSave(get_lang('Save section'), 'submit_button'); |
||
| 7101 | $form->addElement('hidden', 'type', 'dir'); |
||
| 7102 | |||
| 7103 | return $form->returnForm(); |
||
| 7104 | } |
||
| 7105 | |||
| 7106 | /** |
||
| 7107 | * Returns the form to update or create a document. |
||
| 7108 | * |
||
| 7109 | * @param string $action (add/edit) |
||
| 7110 | * @param CLpItem $lpItem |
||
| 7111 | * |
||
| 7112 | * @throws HTML_QuickForm_Error |
||
| 7113 | * @throws Exception |
||
| 7114 | * |
||
| 7115 | * @return string HTML form |
||
| 7116 | */ |
||
| 7117 | public function displayDocumentForm($action = 'add', $lpItem = null) |
||
| 7118 | { |
||
| 7119 | $courseInfo = api_get_course_info(); |
||
| 7120 | |||
| 7121 | $form = new FormValidator( |
||
| 7122 | 'form', |
||
| 7123 | 'POST', |
||
| 7124 | $this->getCurrentBuildingModeURL(), |
||
| 7125 | '', |
||
| 7126 | ['enctype' => 'multipart/form-data'] |
||
| 7127 | ); |
||
| 7128 | |||
| 7129 | $data = $this->generate_lp_folder($courseInfo); |
||
| 7130 | |||
| 7131 | if (null !== $lpItem) { |
||
| 7132 | LearnPathItemForm::setForm($form, $action, $this, $lpItem); |
||
| 7133 | } |
||
| 7134 | |||
| 7135 | switch ($action) { |
||
| 7136 | case 'add': |
||
| 7137 | $folders = DocumentManager::get_all_document_folders( |
||
| 7138 | $courseInfo, |
||
| 7139 | 0, |
||
| 7140 | true |
||
| 7141 | ); |
||
| 7142 | DocumentManager::build_directory_selector( |
||
| 7143 | $folders, |
||
| 7144 | '', |
||
| 7145 | [], |
||
| 7146 | true, |
||
| 7147 | $form, |
||
| 7148 | 'directory_parent_id' |
||
| 7149 | ); |
||
| 7150 | |||
| 7151 | if ($data) { |
||
| 7152 | $defaults['directory_parent_id'] = $data->getIid(); |
||
| 7153 | } |
||
| 7154 | |||
| 7155 | break; |
||
| 7156 | } |
||
| 7157 | |||
| 7158 | $form->addButtonSave(get_lang('Save'), 'submit_button'); |
||
| 7159 | |||
| 7160 | return $form->returnForm(); |
||
| 7161 | } |
||
| 7162 | |||
| 7163 | /** |
||
| 7164 | * @param array $courseInfo |
||
| 7165 | * @param string $content |
||
| 7166 | * @param string $title |
||
| 7167 | * @param int $parentId |
||
| 7168 | * |
||
| 7169 | * @throws \Doctrine\ORM\ORMException |
||
| 7170 | * @throws \Doctrine\ORM\OptimisticLockException |
||
| 7171 | * @throws \Doctrine\ORM\TransactionRequiredException |
||
| 7172 | * |
||
| 7173 | * @return int |
||
| 7174 | */ |
||
| 7175 | public function createReadOutText($courseInfo, $content = '', $title = '', $parentId = 0) |
||
| 7176 | { |
||
| 7177 | $creatorId = api_get_user_id(); |
||
| 7178 | $sessionId = api_get_session_id(); |
||
| 7179 | |||
| 7180 | // Generates folder |
||
| 7181 | $result = $this->generate_lp_folder($courseInfo); |
||
| 7182 | $dir = $result['dir']; |
||
| 7183 | |||
| 7184 | if (empty($parentId) || '/' == $parentId) { |
||
| 7185 | $postDir = isset($_POST['dir']) ? $_POST['dir'] : $dir; |
||
| 7186 | $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting. |
||
| 7187 | |||
| 7188 | if ('/' === $parentId) { |
||
| 7189 | $dir = '/'; |
||
| 7190 | } |
||
| 7191 | |||
| 7192 | // Please, do not modify this dirname formatting. |
||
| 7193 | if (strstr($dir, '..')) { |
||
| 7194 | $dir = '/'; |
||
| 7195 | } |
||
| 7196 | |||
| 7197 | if (!empty($dir[0]) && '.' == $dir[0]) { |
||
| 7198 | $dir = substr($dir, 1); |
||
| 7199 | } |
||
| 7200 | if (!empty($dir[0]) && '/' != $dir[0]) { |
||
| 7201 | $dir = '/'.$dir; |
||
| 7202 | } |
||
| 7203 | if (isset($dir[strlen($dir) - 1]) && '/' != $dir[strlen($dir) - 1]) { |
||
| 7204 | $dir .= '/'; |
||
| 7205 | } |
||
| 7206 | } else { |
||
| 7207 | $parentInfo = DocumentManager::get_document_data_by_id( |
||
| 7208 | $parentId, |
||
| 7209 | $courseInfo['code'] |
||
| 7210 | ); |
||
| 7211 | if (!empty($parentInfo)) { |
||
| 7212 | $dir = $parentInfo['path'].'/'; |
||
| 7213 | } |
||
| 7214 | } |
||
| 7215 | |||
| 7216 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
| 7217 | |||
| 7218 | if (!is_dir($filepath)) { |
||
| 7219 | $dir = '/'; |
||
| 7220 | $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir; |
||
| 7221 | } |
||
| 7222 | |||
| 7223 | $originalTitle = !empty($title) ? $title : $_POST['title']; |
||
| 7224 | |||
| 7225 | if (!empty($title)) { |
||
| 7226 | $title = api_replace_dangerous_char(stripslashes($title)); |
||
| 7227 | } else { |
||
| 7228 | $title = api_replace_dangerous_char(stripslashes($_POST['title'])); |
||
| 7229 | } |
||
| 7230 | |||
| 7231 | $title = disable_dangerous_file($title); |
||
| 7232 | $filename = $title; |
||
| 7233 | $content = !empty($content) ? $content : $_POST['content_lp']; |
||
| 7234 | $tmpFileName = $filename; |
||
| 7235 | |||
| 7236 | $i = 0; |
||
| 7237 | while (file_exists($filepath.$tmpFileName.'.html')) { |
||
| 7238 | $tmpFileName = $filename.'_'.++$i; |
||
| 7239 | } |
||
| 7240 | |||
| 7241 | $filename = $tmpFileName.'.html'; |
||
| 7242 | $content = stripslashes($content); |
||
| 7243 | |||
| 7244 | if (file_exists($filepath.$filename)) { |
||
| 7245 | return 0; |
||
| 7246 | } |
||
| 7247 | |||
| 7248 | $putContent = file_put_contents($filepath.$filename, $content); |
||
| 7249 | |||
| 7250 | if (false === $putContent) { |
||
| 7251 | return 0; |
||
| 7252 | } |
||
| 7253 | |||
| 7254 | $fileSize = filesize($filepath.$filename); |
||
| 7255 | $saveFilePath = $dir.$filename; |
||
| 7256 | |||
| 7257 | $document = DocumentManager::addDocument( |
||
| 7258 | $courseInfo, |
||
| 7259 | $saveFilePath, |
||
| 7260 | 'file', |
||
| 7261 | $fileSize, |
||
| 7262 | $tmpFileName, |
||
| 7263 | '', |
||
| 7264 | 0, //readonly |
||
| 7265 | true, |
||
| 7266 | null, |
||
| 7267 | $sessionId, |
||
| 7268 | $creatorId |
||
| 7269 | ); |
||
| 7270 | |||
| 7271 | $documentId = $document->getId(); |
||
| 7272 | |||
| 7273 | if (!$document) { |
||
| 7274 | return 0; |
||
| 7275 | } |
||
| 7276 | |||
| 7277 | $newComment = isset($_POST['comment']) ? trim($_POST['comment']) : ''; |
||
| 7278 | $newTitle = $originalTitle; |
||
| 7279 | |||
| 7280 | if ($newComment || $newTitle) { |
||
| 7281 | $em = Database::getManager(); |
||
| 7282 | |||
| 7283 | if ($newComment) { |
||
| 7284 | $document->setComment($newComment); |
||
| 7285 | } |
||
| 7286 | |||
| 7287 | if ($newTitle) { |
||
| 7288 | $document->setTitle($newTitle); |
||
| 7289 | } |
||
| 7290 | |||
| 7291 | $em->persist($document); |
||
| 7292 | $em->flush(); |
||
| 7293 | } |
||
| 7294 | |||
| 7295 | return $documentId; |
||
| 7296 | } |
||
| 7297 | |||
| 7298 | /** |
||
| 7299 | * Displays the menu for manipulating a step. |
||
| 7300 | * |
||
| 7301 | * @return string |
||
| 7302 | */ |
||
| 7303 | public function displayItemMenu(CLpItem $lpItem) |
||
| 7304 | { |
||
| 7305 | $item_id = $lpItem->getIid(); |
||
| 7306 | $audio = $lpItem->getAudio(); |
||
| 7307 | $itemType = $lpItem->getItemType(); |
||
| 7308 | $path = $lpItem->getPath(); |
||
| 7309 | |||
| 7310 | $return = '<div class="actions">'; |
||
| 7311 | $audio_player = null; |
||
| 7312 | // We display an audio player if needed. |
||
| 7313 | if (!empty($audio)) { |
||
| 7314 | /*$webAudioPath = '../..'.api_get_path(REL_COURSE_PATH).$_course['path'].'/document/audio/'.$row['audio']; |
||
| 7315 | $audio_player .= '<div class="lp_mediaplayer" id="container">' |
||
| 7316 | .'<audio src="'.$webAudioPath.'" controls>' |
||
| 7317 | .'</div><br>';*/ |
||
| 7318 | } |
||
| 7319 | |||
| 7320 | $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$item_id.'&lp_id='.$this->lp_id; |
||
| 7321 | |||
| 7322 | if (TOOL_LP_FINAL_ITEM !== $itemType) { |
||
| 7323 | $return .= Display::url( |
||
| 7324 | Display::return_icon( |
||
| 7325 | 'edit.png', |
||
| 7326 | get_lang('Edit'), |
||
| 7327 | [], |
||
| 7328 | ICON_SIZE_SMALL |
||
| 7329 | ), |
||
| 7330 | $url.'&action=edit_item&path_item='.$path |
||
| 7331 | ); |
||
| 7332 | |||
| 7333 | /*$return .= Display::url( |
||
| 7334 | Display::return_icon( |
||
| 7335 | 'move.png', |
||
| 7336 | get_lang('Move'), |
||
| 7337 | [], |
||
| 7338 | ICON_SIZE_SMALL |
||
| 7339 | ), |
||
| 7340 | $url.'&action=move_item' |
||
| 7341 | );*/ |
||
| 7342 | } |
||
| 7343 | |||
| 7344 | // Commented for now as prerequisites cannot be added to chapters. |
||
| 7345 | if ('dir' !== $itemType) { |
||
| 7346 | $return .= Display::url( |
||
| 7347 | Display::return_icon( |
||
| 7348 | 'accept.png', |
||
| 7349 | get_lang('Prerequisites'), |
||
| 7350 | [], |
||
| 7351 | ICON_SIZE_SMALL |
||
| 7352 | ), |
||
| 7353 | $url.'&action=edit_item_prereq' |
||
| 7354 | ); |
||
| 7355 | } |
||
| 7356 | $return .= Display::url( |
||
| 7357 | Display::return_icon( |
||
| 7358 | 'delete.png', |
||
| 7359 | get_lang('Delete'), |
||
| 7360 | [], |
||
| 7361 | ICON_SIZE_SMALL |
||
| 7362 | ), |
||
| 7363 | $url.'&action=delete_item' |
||
| 7364 | ); |
||
| 7365 | |||
| 7366 | /*if (in_array($itemType, [TOOL_DOCUMENT, TOOL_LP_FINAL_ITEM, TOOL_HOTPOTATOES])) { |
||
| 7367 | $documentData = DocumentManager::get_document_data_by_id($path, $course_code); |
||
| 7368 | if (empty($documentData)) { |
||
| 7369 | // Try with iid |
||
| 7370 | $table = Database::get_course_table(TABLE_DOCUMENT); |
||
| 7371 | $sql = "SELECT path FROM $table |
||
| 7372 | WHERE |
||
| 7373 | c_id = ".api_get_course_int_id()." AND |
||
| 7374 | iid = ".$path." AND |
||
| 7375 | path NOT LIKE '%_DELETED_%'"; |
||
| 7376 | $result = Database::query($sql); |
||
| 7377 | $documentData = Database::fetch_array($result); |
||
| 7378 | if ($documentData) { |
||
| 7379 | $documentData['absolute_path_from_document'] = '/document'.$documentData['path']; |
||
| 7380 | } |
||
| 7381 | } |
||
| 7382 | if (isset($documentData['absolute_path_from_document'])) { |
||
| 7383 | $return .= get_lang('File').': '.$documentData['absolute_path_from_document']; |
||
| 7384 | } |
||
| 7385 | }*/ |
||
| 7386 | |||
| 7387 | $return .= '</div>'; |
||
| 7388 | |||
| 7389 | if (!empty($audio_player)) { |
||
| 7390 | $return .= $audio_player; |
||
| 7391 | } |
||
| 7392 | |||
| 7393 | return $return; |
||
| 7394 | } |
||
| 7395 | |||
| 7396 | /** |
||
| 7397 | * Creates the javascript needed for filling up the checkboxes without page reload. |
||
| 7398 | * |
||
| 7399 | * @return string |
||
| 7400 | */ |
||
| 7401 | public function get_js_dropdown_array() |
||
| 7402 | { |
||
| 7403 | $course_id = api_get_course_int_id(); |
||
| 7404 | $return = 'var child_name = new Array();'."\n"; |
||
| 7405 | $return .= 'var child_value = new Array();'."\n\n"; |
||
| 7406 | $return .= 'child_name[0] = new Array();'."\n"; |
||
| 7407 | $return .= 'child_value[0] = new Array();'."\n\n"; |
||
| 7408 | |||
| 7409 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 7410 | $sql = "SELECT * FROM ".$tbl_lp_item." |
||
| 7411 | WHERE |
||
| 7412 | lp_id = ".$this->lp_id." AND |
||
| 7413 | parent_item_id = 0 |
||
| 7414 | ORDER BY display_order ASC"; |
||
| 7415 | Database::query($sql); |
||
| 7416 | $i = 0; |
||
| 7417 | |||
| 7418 | $list = $this->getItemsForForm(true); |
||
| 7419 | |||
| 7420 | foreach ($list as $row_zero) { |
||
| 7421 | if (TOOL_LP_FINAL_ITEM !== $row_zero['item_type']) { |
||
| 7422 | if (TOOL_QUIZ == $row_zero['item_type']) { |
||
| 7423 | $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']); |
||
| 7424 | } |
||
| 7425 | $js_var = json_encode(get_lang('After').' '.$row_zero['title']); |
||
| 7426 | $return .= 'child_name[0]['.$i.'] = '.$js_var.' ;'."\n"; |
||
| 7427 | $return .= 'child_value[0]['.$i++.'] = "'.$row_zero['iid'].'";'."\n"; |
||
| 7428 | } |
||
| 7429 | } |
||
| 7430 | |||
| 7431 | $return .= "\n"; |
||
| 7432 | $sql = "SELECT * FROM $tbl_lp_item |
||
| 7433 | WHERE lp_id = ".$this->lp_id; |
||
| 7434 | $res = Database::query($sql); |
||
| 7435 | while ($row = Database::fetch_array($res)) { |
||
| 7436 | $sql_parent = "SELECT * FROM ".$tbl_lp_item." |
||
| 7437 | WHERE |
||
| 7438 | parent_item_id = ".$row['iid']." |
||
| 7439 | ORDER BY display_order ASC"; |
||
| 7440 | $res_parent = Database::query($sql_parent); |
||
| 7441 | $i = 0; |
||
| 7442 | $return .= 'child_name['.$row['iid'].'] = new Array();'."\n"; |
||
| 7443 | $return .= 'child_value['.$row['iid'].'] = new Array();'."\n\n"; |
||
| 7444 | |||
| 7445 | while ($row_parent = Database::fetch_array($res_parent)) { |
||
| 7446 | $js_var = json_encode(get_lang('After').' '.$this->cleanItemTitle($row_parent['title'])); |
||
| 7447 | $return .= 'child_name['.$row['iid'].']['.$i.'] = '.$js_var.' ;'."\n"; |
||
| 7448 | $return .= 'child_value['.$row['iid'].']['.$i++.'] = "'.$row_parent['iid'].'";'."\n"; |
||
| 7449 | } |
||
| 7450 | $return .= "\n"; |
||
| 7451 | } |
||
| 7452 | |||
| 7453 | $return .= " |
||
| 7454 | function load_cbo(id) { |
||
| 7455 | if (!id) { |
||
| 7456 | return false; |
||
| 7457 | } |
||
| 7458 | |||
| 7459 | var cbo = document.getElementById('previous'); |
||
| 7460 | for(var i = cbo.length - 1; i > 0; i--) { |
||
| 7461 | cbo.options[i] = null; |
||
| 7462 | } |
||
| 7463 | |||
| 7464 | var k=0; |
||
| 7465 | for(var i = 1; i <= child_name[id].length; i++){ |
||
| 7466 | var option = new Option(child_name[id][i - 1], child_value[id][i - 1]); |
||
| 7467 | option.style.paddingLeft = '40px'; |
||
| 7468 | cbo.options[i] = option; |
||
| 7469 | k = i; |
||
| 7470 | } |
||
| 7471 | |||
| 7472 | cbo.options[k].selected = true; |
||
| 7473 | //$('#previous').selectpicker('refresh'); |
||
| 7474 | }"; |
||
| 7475 | |||
| 7476 | return $return; |
||
| 7477 | } |
||
| 7478 | |||
| 7479 | /** |
||
| 7480 | * Display the form to allow moving an item. |
||
| 7481 | * |
||
| 7482 | * @param CLpItem $lpItem |
||
| 7483 | * |
||
| 7484 | * @throws Exception |
||
| 7485 | * @throws HTML_QuickForm_Error |
||
| 7486 | * |
||
| 7487 | * @return string HTML form |
||
| 7488 | */ |
||
| 7489 | public function display_move_item($lpItem) |
||
| 7490 | { |
||
| 7491 | $return = ''; |
||
| 7492 | $path = $lpItem->getPath(); |
||
| 7493 | |||
| 7494 | if ($lpItem) { |
||
| 7495 | $itemType = $lpItem->getItemType(); |
||
| 7496 | switch ($itemType) { |
||
| 7497 | case 'dir': |
||
| 7498 | case 'asset': |
||
| 7499 | $return .= $this->displayItemMenu($lpItem); |
||
| 7500 | $return .= $this->display_item_form( |
||
| 7501 | $lpItem, |
||
| 7502 | get_lang('Move the current section'), |
||
| 7503 | 'move', |
||
| 7504 | $row |
||
| 7505 | ); |
||
| 7506 | break; |
||
| 7507 | case TOOL_DOCUMENT: |
||
| 7508 | $return .= $this->displayItemMenu($lpItem); |
||
| 7509 | $return .= $this->displayDocumentForm('move', $lpItem); |
||
| 7510 | break; |
||
| 7511 | case TOOL_LINK: |
||
| 7512 | $link = null; |
||
| 7513 | if (!empty($path)) { |
||
| 7514 | $repo = Container::getLinkRepository(); |
||
| 7515 | $link = $repo->find($path); |
||
| 7516 | } |
||
| 7517 | $return .= $this->displayItemMenu($lpItem); |
||
| 7518 | $return .= $this->display_link_form('move', $lpItem, $link); |
||
| 7519 | break; |
||
| 7520 | case TOOL_HOTPOTATOES: |
||
| 7521 | $return .= $this->displayItemMenu($lpItem); |
||
| 7522 | $return .= $this->display_link_form('move', $lpItem, $row); |
||
| 7523 | break; |
||
| 7524 | case TOOL_QUIZ: |
||
| 7525 | $return .= $this->displayItemMenu($lpItem); |
||
| 7526 | $return .= $this->display_quiz_form('move', $lpItem, $row); |
||
| 7527 | break; |
||
| 7528 | case TOOL_STUDENTPUBLICATION: |
||
| 7529 | $return .= $this->displayItemMenu($lpItem); |
||
| 7530 | $return .= $this->display_student_publication_form('move', $lpItem, $row); |
||
| 7531 | break; |
||
| 7532 | case TOOL_FORUM: |
||
| 7533 | $return .= $this->displayItemMenu($lpItem); |
||
| 7534 | $return .= $this->display_forum_form('move', $lpItem, $row); |
||
| 7535 | break; |
||
| 7536 | case TOOL_THREAD: |
||
| 7537 | $return .= $this->displayItemMenu($lpItem); |
||
| 7538 | $return .= $this->display_forum_form('move', $lpItem, $row); |
||
| 7539 | break; |
||
| 7540 | } |
||
| 7541 | } |
||
| 7542 | |||
| 7543 | return $return; |
||
| 7544 | } |
||
| 7545 | |||
| 7546 | /** |
||
| 7547 | * Return HTML form to allow prerequisites selection. |
||
| 7548 | * |
||
| 7549 | * @todo use FormValidator |
||
| 7550 | * |
||
| 7551 | * @return string HTML form |
||
| 7552 | */ |
||
| 7553 | public function display_item_prerequisites_form(CLpItem $lpItem) |
||
| 7554 | { |
||
| 7555 | $course_id = api_get_course_int_id(); |
||
| 7556 | $prerequisiteId = $lpItem->getPrerequisite(); |
||
| 7557 | $itemId = $lpItem->getIid(); |
||
| 7558 | |||
| 7559 | $return = '<legend>'; |
||
| 7560 | $return .= get_lang('Add/edit prerequisites'); |
||
| 7561 | $return .= '</legend>'; |
||
| 7562 | $return .= '<form method="POST">'; |
||
| 7563 | $return .= '<div class="table-responsive">'; |
||
| 7564 | $return .= '<table class="table table-hover">'; |
||
| 7565 | $return .= '<thead>'; |
||
| 7566 | $return .= '<tr>'; |
||
| 7567 | $return .= '<th>'.get_lang('Prerequisites').'</th>'; |
||
| 7568 | $return .= '<th width="140">'.get_lang('minimum').'</th>'; |
||
| 7569 | $return .= '<th width="140">'.get_lang('maximum').'</th>'; |
||
| 7570 | $return .= '</tr>'; |
||
| 7571 | $return .= '</thead>'; |
||
| 7572 | |||
| 7573 | // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146 |
||
| 7574 | $return .= '<tbody>'; |
||
| 7575 | $return .= '<tr>'; |
||
| 7576 | $return .= '<td colspan="3">'; |
||
| 7577 | $return .= '<div class="radio learnpath"><label for="idnone">'; |
||
| 7578 | $return .= '<input checked="checked" id="idnone" name="prerequisites" type="radio" />'; |
||
| 7579 | $return .= get_lang('none').'</label>'; |
||
| 7580 | $return .= '</div>'; |
||
| 7581 | $return .= '</tr>'; |
||
| 7582 | |||
| 7583 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 7584 | $sql = "SELECT * FROM $tbl_lp_item |
||
| 7585 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
| 7586 | $result = Database::query($sql); |
||
| 7587 | |||
| 7588 | $selectedMinScore = []; |
||
| 7589 | $selectedMaxScore = []; |
||
| 7590 | $masteryScore = []; |
||
| 7591 | while ($row = Database::fetch_array($result)) { |
||
| 7592 | if ($row['iid'] == $itemId) { |
||
| 7593 | $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score']; |
||
| 7594 | $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score']; |
||
| 7595 | } |
||
| 7596 | $masteryScore[$row['iid']] = $row['mastery_score']; |
||
| 7597 | } |
||
| 7598 | |||
| 7599 | $arrLP = $this->getItemsForForm(); |
||
| 7600 | $this->tree_array($arrLP); |
||
| 7601 | $arrLP = isset($this->arrMenu) ? $this->arrMenu : []; |
||
| 7602 | unset($this->arrMenu); |
||
| 7603 | |||
| 7604 | for ($i = 0; $i < count($arrLP); $i++) { |
||
| 7605 | $item = $arrLP[$i]; |
||
| 7606 | |||
| 7607 | if ($item['id'] == $itemId) { |
||
| 7608 | break; |
||
| 7609 | } |
||
| 7610 | |||
| 7611 | $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score']; |
||
| 7612 | $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']] : 0; |
||
| 7613 | $masteryScoreAsMinValue = isset($masteryScore[$item['id']]) ? $masteryScore[$item['id']] : 0; |
||
| 7614 | |||
| 7615 | $return .= '<tr>'; |
||
| 7616 | $return .= '<td '.((TOOL_QUIZ != $item['item_type'] && TOOL_HOTPOTATOES != $item['item_type']) ? ' colspan="3"' : '').'>'; |
||
| 7617 | $return .= '<div style="margin-left:'.($item['depth'] * 20).'px;" class="radio learnpath">'; |
||
| 7618 | $return .= '<label for="id'.$item['id'].'">'; |
||
| 7619 | |||
| 7620 | $checked = ''; |
||
| 7621 | if (null !== $prerequisiteId) { |
||
| 7622 | $checked = in_array($prerequisiteId, [$item['id'], $item['ref']]) ? ' checked="checked" ' : ''; |
||
| 7623 | } |
||
| 7624 | |||
| 7625 | $disabled = 'dir' === $item['item_type'] ? ' disabled="disabled" ' : ''; |
||
| 7626 | |||
| 7627 | $return .= '<input '.$checked.' '.$disabled.' id="id'.$item['id'].'" name="prerequisites" type="radio" value="'.$item['id'].'" />'; |
||
| 7628 | |||
| 7629 | $icon_name = str_replace(' ', '', $item['item_type']); |
||
| 7630 | if (file_exists('../img/lp_'.$icon_name.'.png')) { |
||
| 7631 | $return .= Display::return_icon('lp_'.$icon_name.'.png'); |
||
| 7632 | } else { |
||
| 7633 | if (file_exists('../img/lp_'.$icon_name.'.png')) { |
||
| 7634 | $return .= Display::return_icon('lp_'.$icon_name.'.png'); |
||
| 7635 | } else { |
||
| 7636 | $return .= Display::return_icon('folder_document.png'); |
||
| 7637 | } |
||
| 7638 | } |
||
| 7639 | |||
| 7640 | $return .= $item['title'].'</label>'; |
||
| 7641 | $return .= '</div>'; |
||
| 7642 | $return .= '</td>'; |
||
| 7643 | |||
| 7644 | if (TOOL_QUIZ == $item['item_type']) { |
||
| 7645 | // lets update max_score Tests information depending of the Tests Advanced properties |
||
| 7646 | $lpItemObj = new LpItem($course_id, $item['id']); |
||
| 7647 | $exercise = new Exercise($course_id); |
||
| 7648 | $exercise->read($lpItemObj->path); |
||
| 7649 | $lpItemObj->max_score = $exercise->get_max_score(); |
||
| 7650 | $lpItemObj->update(); |
||
| 7651 | $item['max_score'] = $lpItemObj->max_score; |
||
| 7652 | |||
| 7653 | if (empty($selectedMinScoreValue) && !empty($masteryScoreAsMinValue)) { |
||
| 7654 | // Backwards compatibility with 1.9.x use mastery_score as min value |
||
| 7655 | $selectedMinScoreValue = $masteryScoreAsMinValue; |
||
| 7656 | } |
||
| 7657 | |||
| 7658 | $return .= '<td>'; |
||
| 7659 | $return .= '<input |
||
| 7660 | class="form-control" |
||
| 7661 | size="4" maxlength="3" |
||
| 7662 | name="min_'.$item['id'].'" |
||
| 7663 | type="number" |
||
| 7664 | min="0" |
||
| 7665 | step="any" |
||
| 7666 | max="'.$item['max_score'].'" |
||
| 7667 | value="'.$selectedMinScoreValue.'" |
||
| 7668 | />'; |
||
| 7669 | $return .= '</td>'; |
||
| 7670 | $return .= '<td>'; |
||
| 7671 | $return .= '<input |
||
| 7672 | class="form-control" |
||
| 7673 | size="4" |
||
| 7674 | maxlength="3" |
||
| 7675 | name="max_'.$item['id'].'" |
||
| 7676 | type="number" |
||
| 7677 | min="0" |
||
| 7678 | step="any" |
||
| 7679 | max="'.$item['max_score'].'" |
||
| 7680 | value="'.$selectedMaxScoreValue.'" |
||
| 7681 | />'; |
||
| 7682 | $return .= '</td>'; |
||
| 7683 | } |
||
| 7684 | |||
| 7685 | if (TOOL_HOTPOTATOES == $item['item_type']) { |
||
| 7686 | $return .= '<td>'; |
||
| 7687 | $return .= '<input |
||
| 7688 | size="4" |
||
| 7689 | maxlength="3" |
||
| 7690 | name="min_'.$item['id'].'" |
||
| 7691 | type="number" |
||
| 7692 | min="0" |
||
| 7693 | step="any" |
||
| 7694 | max="'.$item['max_score'].'" |
||
| 7695 | value="'.$selectedMinScoreValue.'" |
||
| 7696 | />'; |
||
| 7697 | $return .= '</td>'; |
||
| 7698 | $return .= '<td>'; |
||
| 7699 | $return .= '<input |
||
| 7700 | size="4" |
||
| 7701 | maxlength="3" |
||
| 7702 | name="max_'.$item['id'].'" |
||
| 7703 | type="number" |
||
| 7704 | min="0" |
||
| 7705 | step="any" |
||
| 7706 | max="'.$item['max_score'].'" |
||
| 7707 | value="'.$selectedMaxScoreValue.'" |
||
| 7708 | />'; |
||
| 7709 | $return .= '</td>'; |
||
| 7710 | } |
||
| 7711 | $return .= '</tr>'; |
||
| 7712 | } |
||
| 7713 | $return .= '<tr>'; |
||
| 7714 | $return .= '</tr>'; |
||
| 7715 | $return .= '</tbody>'; |
||
| 7716 | $return .= '</table>'; |
||
| 7717 | $return .= '</div>'; |
||
| 7718 | $return .= '<div class="form-group">'; |
||
| 7719 | $return .= '<button class="btn btn-primary" name="submit_button" type="submit">'. |
||
| 7720 | get_lang('Save prerequisites settings').'</button>'; |
||
| 7721 | $return .= '</form>'; |
||
| 7722 | |||
| 7723 | return $return; |
||
| 7724 | } |
||
| 7725 | |||
| 7726 | /** |
||
| 7727 | * Return HTML list to allow prerequisites selection for lp. |
||
| 7728 | * |
||
| 7729 | * @return string HTML form |
||
| 7730 | */ |
||
| 7731 | public function display_lp_prerequisites_list() |
||
| 7732 | { |
||
| 7733 | $course_id = api_get_course_int_id(); |
||
| 7734 | $lp_id = $this->lp_id; |
||
| 7735 | $tbl_lp = Database::get_course_table(TABLE_LP_MAIN); |
||
| 7736 | $prerequisiteId = $this->entity->getPrerequisite(); |
||
| 7737 | |||
| 7738 | $repo = Container::getLpRepository(); |
||
| 7739 | $qb = $repo->findAllByCourse(api_get_course_entity(), api_get_session_entity()); |
||
| 7740 | /** @var CLp[] $lps */ |
||
| 7741 | $lps = $qb->getQuery()->getResult(); |
||
| 7742 | |||
| 7743 | //$session_id = api_get_session_id(); |
||
| 7744 | /*$session_condition = api_get_session_condition($session_id, true, true); |
||
| 7745 | $sql = "SELECT * FROM $tbl_lp |
||
| 7746 | WHERE c_id = $course_id $session_condition |
||
| 7747 | ORDER BY display_order "; |
||
| 7748 | $rs = Database::query($sql);*/ |
||
| 7749 | $return = ''; |
||
| 7750 | $return .= '<select name="prerequisites" class="form-control">'; |
||
| 7751 | $return .= '<option value="0">'.get_lang('none').'</option>'; |
||
| 7752 | |||
| 7753 | foreach ($lps as $lp) { |
||
| 7754 | $myLpId = $lp->getIid(); |
||
| 7755 | if ($myLpId == $lp_id) { |
||
| 7756 | continue; |
||
| 7757 | } |
||
| 7758 | $return .= '<option |
||
| 7759 | value="'.$myLpId.'" '.(($myLpId == $prerequisiteId) ? ' selected ' : '').'>'. |
||
| 7760 | $lp->getName(). |
||
| 7761 | '</option>'; |
||
| 7762 | } |
||
| 7763 | |||
| 7764 | $return .= '</select>'; |
||
| 7765 | |||
| 7766 | return $return; |
||
| 7767 | } |
||
| 7768 | |||
| 7769 | /** |
||
| 7770 | * Creates a list with all the documents in it. |
||
| 7771 | * |
||
| 7772 | * @param bool $showInvisibleFiles |
||
| 7773 | * |
||
| 7774 | * @throws Exception |
||
| 7775 | * @throws HTML_QuickForm_Error |
||
| 7776 | * |
||
| 7777 | * @return string |
||
| 7778 | */ |
||
| 7779 | public function get_documents($showInvisibleFiles = false) |
||
| 7780 | { |
||
| 7781 | $course_info = api_get_course_info(); |
||
| 7782 | $sessionId = api_get_session_id(); |
||
| 7783 | $documentTree = DocumentManager::get_document_preview( |
||
| 7784 | $course_info, |
||
| 7785 | $this->lp_id, |
||
| 7786 | null, |
||
| 7787 | $sessionId, |
||
| 7788 | true, |
||
| 7789 | null, |
||
| 7790 | null, |
||
| 7791 | $showInvisibleFiles, |
||
| 7792 | true |
||
| 7793 | ); |
||
| 7794 | |||
| 7795 | $form = new FormValidator( |
||
| 7796 | 'form_upload', |
||
| 7797 | 'POST', |
||
| 7798 | $this->getCurrentBuildingModeURL(), |
||
| 7799 | '', |
||
| 7800 | ['enctype' => 'multipart/form-data'] |
||
| 7801 | ); |
||
| 7802 | |||
| 7803 | $folders = DocumentManager::get_all_document_folders( |
||
| 7804 | api_get_course_info(), |
||
| 7805 | 0, |
||
| 7806 | true |
||
| 7807 | ); |
||
| 7808 | |||
| 7809 | $folder = $this->generate_lp_folder(api_get_course_info()); |
||
| 7810 | |||
| 7811 | DocumentManager::build_directory_selector( |
||
| 7812 | $folders, |
||
| 7813 | $folder->getIid(), |
||
| 7814 | [], |
||
| 7815 | true, |
||
| 7816 | $form, |
||
| 7817 | 'directory_parent_id' |
||
| 7818 | ); |
||
| 7819 | |||
| 7820 | $group = [ |
||
| 7821 | $form->createElement( |
||
| 7822 | 'radio', |
||
| 7823 | 'if_exists', |
||
| 7824 | get_lang('If file exists:'), |
||
| 7825 | get_lang('Do nothing'), |
||
| 7826 | 'nothing' |
||
| 7827 | ), |
||
| 7828 | $form->createElement( |
||
| 7829 | 'radio', |
||
| 7830 | 'if_exists', |
||
| 7831 | null, |
||
| 7832 | get_lang('Overwrite the existing file'), |
||
| 7833 | 'overwrite' |
||
| 7834 | ), |
||
| 7835 | $form->createElement( |
||
| 7836 | 'radio', |
||
| 7837 | 'if_exists', |
||
| 7838 | null, |
||
| 7839 | get_lang('Rename the uploaded file if it exists'), |
||
| 7840 | 'rename' |
||
| 7841 | ), |
||
| 7842 | ]; |
||
| 7843 | $form->addGroup($group, null, get_lang('If file exists:')); |
||
| 7844 | |||
| 7845 | $fileExistsOption = api_get_setting('document_if_file_exists_option'); |
||
| 7846 | $defaultFileExistsOption = 'rename'; |
||
| 7847 | if (!empty($fileExistsOption)) { |
||
| 7848 | $defaultFileExistsOption = $fileExistsOption; |
||
| 7849 | } |
||
| 7850 | $form->setDefaults(['if_exists' => $defaultFileExistsOption]); |
||
| 7851 | |||
| 7852 | // Check box options |
||
| 7853 | $form->addElement( |
||
| 7854 | 'checkbox', |
||
| 7855 | 'unzip', |
||
| 7856 | get_lang('Options'), |
||
| 7857 | get_lang('Uncompress zip') |
||
| 7858 | ); |
||
| 7859 | |||
| 7860 | $url = api_get_path(WEB_AJAX_PATH).'document.ajax.php?'.api_get_cidreq().'&a=upload_file&curdirpath='; |
||
| 7861 | $form->addMultipleUpload($url); |
||
| 7862 | |||
| 7863 | $lpItem = new CLpItem(); |
||
| 7864 | $lpItem->setTitle(''); |
||
| 7865 | $lpItem->setItemType(TOOL_DOCUMENT); |
||
| 7866 | $new = $this->displayDocumentForm('add', $lpItem); |
||
| 7867 | |||
| 7868 | /*$lpItem = new CLpItem(); |
||
| 7869 | $lpItem->setItemType(TOOL_READOUT_TEXT); |
||
| 7870 | $frmReadOutText = $this->displayDocumentForm('add');*/ |
||
| 7871 | |||
| 7872 | $headers = [ |
||
| 7873 | get_lang('Files'), |
||
| 7874 | get_lang('Create a new document'), |
||
| 7875 | //get_lang('Create read-out text'), |
||
| 7876 | get_lang('Upload'), |
||
| 7877 | ]; |
||
| 7878 | |||
| 7879 | return Display::tabs( |
||
| 7880 | $headers, |
||
| 7881 | [$documentTree, $new, $form->returnForm()], |
||
| 7882 | 'subtab' |
||
| 7883 | ); |
||
| 7884 | } |
||
| 7885 | |||
| 7886 | /** |
||
| 7887 | * Creates a list with all the exercises (quiz) in it. |
||
| 7888 | * |
||
| 7889 | * @return string |
||
| 7890 | */ |
||
| 7891 | public function get_exercises() |
||
| 7892 | { |
||
| 7893 | $course_id = api_get_course_int_id(); |
||
| 7894 | $session_id = api_get_session_id(); |
||
| 7895 | $userInfo = api_get_user_info(); |
||
| 7896 | |||
| 7897 | $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST); |
||
| 7898 | $condition_session = api_get_session_condition($session_id, true, true); |
||
| 7899 | $setting = 'true' === api_get_setting('lp.show_invisible_exercise_in_lp_toc'); |
||
| 7900 | |||
| 7901 | //$activeCondition = ' active <> -1 '; |
||
| 7902 | $active = 2; |
||
| 7903 | if ($setting) { |
||
| 7904 | $active = 1; |
||
| 7905 | //$activeCondition = ' active = 1 '; |
||
| 7906 | } |
||
| 7907 | |||
| 7908 | $categoryCondition = ''; |
||
| 7909 | |||
| 7910 | $keyword = $_REQUEST['keyword'] ?? null; |
||
| 7911 | $categoryId = $_REQUEST['category_id'] ?? null; |
||
| 7912 | /*if (api_get_configuration_value('allow_exercise_categories') && !empty($categoryId)) { |
||
| 7913 | $categoryCondition = " AND exercise_category_id = $categoryId "; |
||
| 7914 | } |
||
| 7915 | |||
| 7916 | $keywordCondition = ''; |
||
| 7917 | |||
| 7918 | if (!empty($keyword)) { |
||
| 7919 | $keyword = Database::escape_string($keyword); |
||
| 7920 | $keywordCondition = " AND title LIKE '%$keyword%' "; |
||
| 7921 | } |
||
| 7922 | */ |
||
| 7923 | $course = api_get_course_entity($course_id); |
||
| 7924 | $session = api_get_session_entity($session_id); |
||
| 7925 | |||
| 7926 | $qb = Container::getQuizRepository()->findAllByCourse($course, $session, $keyword, $active, false, $categoryId); |
||
| 7927 | /** @var CQuiz[] $exercises */ |
||
| 7928 | $exercises = $qb->getQuery()->getResult(); |
||
| 7929 | |||
| 7930 | /*$sql_quiz = "SELECT * FROM $tbl_quiz |
||
| 7931 | WHERE |
||
| 7932 | c_id = $course_id AND |
||
| 7933 | $activeCondition |
||
| 7934 | $condition_session |
||
| 7935 | $categoryCondition |
||
| 7936 | $keywordCondition |
||
| 7937 | ORDER BY title ASC"; |
||
| 7938 | $res_quiz = Database::query($sql_quiz);*/ |
||
| 7939 | |||
| 7940 | $currentUrl = api_get_self().'?'.api_get_cidreq().'&action=add_item&type=step&lp_id='.$this->lp_id.'#resource_tab-2'; |
||
| 7941 | |||
| 7942 | // Create a search-box |
||
| 7943 | $form = new FormValidator('search_simple', 'get', $currentUrl); |
||
| 7944 | $form->addHidden('action', 'add_item'); |
||
| 7945 | $form->addHidden('type', 'step'); |
||
| 7946 | $form->addHidden('lp_id', $this->lp_id); |
||
| 7947 | $form->addHidden('lp_build_selected', '2'); |
||
| 7948 | |||
| 7949 | $form->addCourseHiddenParams(); |
||
| 7950 | $form->addText( |
||
| 7951 | 'keyword', |
||
| 7952 | get_lang('Search'), |
||
| 7953 | false, |
||
| 7954 | [ |
||
| 7955 | 'aria-label' => get_lang('Search'), |
||
| 7956 | ] |
||
| 7957 | ); |
||
| 7958 | |||
| 7959 | if (api_get_configuration_value('allow_exercise_categories')) { |
||
| 7960 | $manager = new ExerciseCategoryManager(); |
||
| 7961 | $options = $manager->getCategoriesForSelect(api_get_course_int_id()); |
||
| 7962 | if (!empty($options)) { |
||
| 7963 | $form->addSelect( |
||
| 7964 | 'category_id', |
||
| 7965 | get_lang('Category'), |
||
| 7966 | $options, |
||
| 7967 | ['placeholder' => get_lang('Please select an option')] |
||
| 7968 | ); |
||
| 7969 | } |
||
| 7970 | } |
||
| 7971 | |||
| 7972 | $form->addButtonSearch(get_lang('Search')); |
||
| 7973 | $return = $form->returnForm(); |
||
| 7974 | |||
| 7975 | $return .= '<ul class="lp_resource">'; |
||
| 7976 | $return .= '<li class="lp_resource_element">'; |
||
| 7977 | $return .= Display::return_icon('new_exercice.png'); |
||
| 7978 | $return .= '<a |
||
| 7979 | href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise_admin.php?'.api_get_cidreq().'&lp_id='.$this->lp_id.'">'. |
||
| 7980 | get_lang('New test').'</a>'; |
||
| 7981 | $return .= '</li>'; |
||
| 7982 | |||
| 7983 | $previewIcon = Display::return_icon( |
||
| 7984 | 'preview_view.png', |
||
| 7985 | get_lang('Preview') |
||
| 7986 | ); |
||
| 7987 | $quizIcon = Display::return_icon('quiz.png', '', [], ICON_SIZE_TINY); |
||
| 7988 | $moveIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
| 7989 | $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq(); |
||
| 7990 | foreach ($exercises as $exercise) { |
||
| 7991 | $exerciseId = $exercise->getIid(); |
||
| 7992 | $title = strip_tags(api_html_entity_decode($exercise->getTitle())); |
||
| 7993 | $visibility = $exercise->isVisible($course, $session); |
||
| 7994 | |||
| 7995 | $link = Display::url( |
||
| 7996 | $previewIcon, |
||
| 7997 | $exerciseUrl.'&exerciseId='.$exerciseId, |
||
| 7998 | ['target' => '_blank'] |
||
| 7999 | ); |
||
| 8000 | $return .= '<li class="lp_resource_element" title="'.$title.'">'; |
||
| 8001 | $return .= Display::url($moveIcon, '#', ['class' => 'moved']); |
||
| 8002 | $return .= $quizIcon; |
||
| 8003 | $sessionStar = ''; |
||
| 8004 | /*$sessionStar = api_get_session_image( |
||
| 8005 | $row_quiz['session_id'], |
||
| 8006 | $userInfo['status'] |
||
| 8007 | );*/ |
||
| 8008 | $return .= Display::url( |
||
| 8009 | Security::remove_XSS(cut($title, 80)).$link.$sessionStar, |
||
| 8010 | api_get_self().'?'. |
||
| 8011 | api_get_cidreq().'&action=add_item&type='.TOOL_QUIZ.'&file='.$exerciseId.'&lp_id='.$this->lp_id, |
||
| 8012 | [ |
||
| 8013 | 'class' => false === $visibility ? 'moved text-muted ' : 'moved link_with_id', |
||
| 8014 | 'data_type' => 'quiz', |
||
| 8015 | 'data_id' => $exerciseId, |
||
| 8016 | ] |
||
| 8017 | ); |
||
| 8018 | $return .= '</li>'; |
||
| 8019 | } |
||
| 8020 | |||
| 8021 | $return .= '</ul>'; |
||
| 8022 | |||
| 8023 | return $return; |
||
| 8024 | } |
||
| 8025 | |||
| 8026 | /** |
||
| 8027 | * Creates a list with all the links in it. |
||
| 8028 | * |
||
| 8029 | * @return string |
||
| 8030 | */ |
||
| 8031 | public function get_links() |
||
| 8032 | { |
||
| 8033 | $sessionId = api_get_session_id(); |
||
| 8034 | $repo = Container::getLinkRepository(); |
||
| 8035 | |||
| 8036 | $course = api_get_course_entity(); |
||
| 8037 | $session = api_get_session_entity($sessionId); |
||
| 8038 | $qb = $repo->getResourcesByCourse($course, $session); |
||
| 8039 | /** @var CLink[] $links */ |
||
| 8040 | $links = $qb->getQuery()->getResult(); |
||
| 8041 | |||
| 8042 | $selfUrl = api_get_self(); |
||
| 8043 | $courseIdReq = api_get_cidreq(); |
||
| 8044 | $userInfo = api_get_user_info(); |
||
| 8045 | |||
| 8046 | $moveEverywhereIcon = Display::return_icon( |
||
| 8047 | 'move_everywhere.png', |
||
| 8048 | get_lang('Move'), |
||
| 8049 | [], |
||
| 8050 | ICON_SIZE_TINY |
||
| 8051 | ); |
||
| 8052 | |||
| 8053 | /*$condition_session = api_get_session_condition( |
||
| 8054 | $session_id, |
||
| 8055 | true, |
||
| 8056 | true, |
||
| 8057 | 'link.session_id' |
||
| 8058 | ); |
||
| 8059 | |||
| 8060 | $sql = "SELECT |
||
| 8061 | link.id as link_id, |
||
| 8062 | link.title as link_title, |
||
| 8063 | link.session_id as link_session_id, |
||
| 8064 | link.category_id as category_id, |
||
| 8065 | link_category.category_title as category_title |
||
| 8066 | FROM $tbl_link as link |
||
| 8067 | LEFT JOIN $linkCategoryTable as link_category |
||
| 8068 | ON (link.category_id = link_category.id AND link.c_id = link_category.c_id) |
||
| 8069 | WHERE link.c_id = $course_id $condition_session |
||
| 8070 | ORDER BY link_category.category_title ASC, link.title ASC"; |
||
| 8071 | $result = Database::query($sql);*/ |
||
| 8072 | $categorizedLinks = []; |
||
| 8073 | $categories = []; |
||
| 8074 | |||
| 8075 | foreach ($links as $link) { |
||
| 8076 | $categoryId = null !== $link->getCategory() ? $link->getCategory()->getIid() : 0; |
||
| 8077 | |||
| 8078 | if (empty($categoryId)) { |
||
| 8079 | $categories[0] = get_lang('Uncategorized'); |
||
| 8080 | } else { |
||
| 8081 | $category = $link->getCategory(); |
||
| 8082 | $categories[$categoryId] = $category->getCategoryTitle(); |
||
| 8083 | } |
||
| 8084 | $categorizedLinks[$categoryId][$link->getIid()] = $link; |
||
| 8085 | } |
||
| 8086 | |||
| 8087 | $linksHtmlCode = |
||
| 8088 | '<script> |
||
| 8089 | function toggle_tool(tool, id) { |
||
| 8090 | if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){ |
||
| 8091 | document.getElementById(tool+"_"+id+"_content").style.display = "block"; |
||
| 8092 | document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('remove.gif').'"; |
||
| 8093 | } else { |
||
| 8094 | document.getElementById(tool+"_"+id+"_content").style.display = "none"; |
||
| 8095 | document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.png').'"; |
||
| 8096 | } |
||
| 8097 | } |
||
| 8098 | </script> |
||
| 8099 | |||
| 8100 | <ul class="lp_resource"> |
||
| 8101 | <li class="lp_resource_element"> |
||
| 8102 | '.Display::return_icon('linksnew.gif').' |
||
| 8103 | <a |
||
| 8104 | href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" |
||
| 8105 | title="'.get_lang('Add a link').'">'. |
||
| 8106 | get_lang('Add a link').' |
||
| 8107 | </a> |
||
| 8108 | </li>'; |
||
| 8109 | $linkIcon = Display::return_icon('links.png', '', [], ICON_SIZE_TINY); |
||
| 8110 | foreach ($categorizedLinks as $categoryId => $links) { |
||
| 8111 | $linkNodes = null; |
||
| 8112 | /** @var CLink $link */ |
||
| 8113 | foreach ($links as $key => $link) { |
||
| 8114 | $title = $link->getTitle(); |
||
| 8115 | |||
| 8116 | $linkUrl = Display::url( |
||
| 8117 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
| 8118 | api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$key, |
||
| 8119 | ['target' => '_blank'] |
||
| 8120 | ); |
||
| 8121 | |||
| 8122 | if ($link->isVisible($course, $session)) { |
||
| 8123 | //$sessionStar = api_get_session_image($linkSessionId, $userInfo['status']); |
||
| 8124 | $sessionStar = ''; |
||
| 8125 | $url = $selfUrl.'?'.$courseIdReq.'&action=add_item&type='.TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id; |
||
| 8126 | $link = Display::url( |
||
| 8127 | Security::remove_XSS($title).$sessionStar.$linkUrl, |
||
| 8128 | $url, |
||
| 8129 | [ |
||
| 8130 | 'class' => 'moved link_with_id', |
||
| 8131 | 'data_id' => $key, |
||
| 8132 | 'data_type' => TOOL_LINK, |
||
| 8133 | 'title' => $title, |
||
| 8134 | ] |
||
| 8135 | ); |
||
| 8136 | $linkNodes .= |
||
| 8137 | '<li class="lp_resource_element"> |
||
| 8138 | <a class="moved" href="#">'. |
||
| 8139 | $moveEverywhereIcon. |
||
| 8140 | '</a> |
||
| 8141 | '.$linkIcon.$link.' |
||
| 8142 | </li>'; |
||
| 8143 | } |
||
| 8144 | } |
||
| 8145 | $linksHtmlCode .= |
||
| 8146 | '<li> |
||
| 8147 | <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle"> |
||
| 8148 | <img src="'.Display::returnIconPath('add.png').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener" |
||
| 8149 | align="absbottom" /> |
||
| 8150 | </a> |
||
| 8151 | <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span> |
||
| 8152 | </li> |
||
| 8153 | <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>'; |
||
| 8154 | } |
||
| 8155 | $linksHtmlCode .= '</ul>'; |
||
| 8156 | |||
| 8157 | return $linksHtmlCode; |
||
| 8158 | } |
||
| 8159 | |||
| 8160 | /** |
||
| 8161 | * Creates a list with all the student publications in it. |
||
| 8162 | * |
||
| 8163 | * @return string |
||
| 8164 | */ |
||
| 8165 | public function get_student_publications() |
||
| 8166 | { |
||
| 8167 | $return = '<ul class="lp_resource">'; |
||
| 8168 | $return .= '<li class="lp_resource_element">'; |
||
| 8169 | /* |
||
| 8170 | $return .= Display::return_icon('works_new.gif'); |
||
| 8171 | $return .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&lp_id='.$this->lp_id.'">'. |
||
| 8172 | get_lang('Add the Assignments tool to the course').'</a>'; |
||
| 8173 | $return .= '</li>';*/ |
||
| 8174 | |||
| 8175 | $works = getWorkListTeacher(0, 100, null, null, null); |
||
| 8176 | if (!empty($works)) { |
||
| 8177 | $icon = Display::return_icon('works.png', '', [], ICON_SIZE_TINY); |
||
| 8178 | foreach ($works as $work) { |
||
| 8179 | $link = Display::url( |
||
| 8180 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
| 8181 | api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$work['iid'], |
||
| 8182 | ['target' => '_blank'] |
||
| 8183 | ); |
||
| 8184 | |||
| 8185 | $return .= '<li class="lp_resource_element">'; |
||
| 8186 | $return .= '<a class="moved" href="#">'; |
||
| 8187 | $return .= Display::return_icon( |
||
| 8188 | 'move_everywhere.png', |
||
| 8189 | get_lang('Move'), |
||
| 8190 | [], |
||
| 8191 | ICON_SIZE_TINY |
||
| 8192 | ); |
||
| 8193 | $return .= '</a> '; |
||
| 8194 | |||
| 8195 | $return .= $icon; |
||
| 8196 | $return .= Display::url( |
||
| 8197 | Security::remove_XSS(cut(strip_tags($work['title']), 80)).' '.$link, |
||
| 8198 | api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&file='.$work['iid'].'&lp_id='.$this->lp_id, |
||
| 8199 | [ |
||
| 8200 | 'class' => 'moved link_with_id', |
||
| 8201 | 'data_id' => $work['iid'], |
||
| 8202 | 'data_type' => TOOL_STUDENTPUBLICATION, |
||
| 8203 | 'title' => Security::remove_XSS(cut(strip_tags($work['title']), 80)), |
||
| 8204 | ] |
||
| 8205 | ); |
||
| 8206 | |||
| 8207 | $return .= '</li>'; |
||
| 8208 | } |
||
| 8209 | } |
||
| 8210 | |||
| 8211 | $return .= '</ul>'; |
||
| 8212 | |||
| 8213 | return $return; |
||
| 8214 | } |
||
| 8215 | |||
| 8216 | /** |
||
| 8217 | * Creates a list with all the forums in it. |
||
| 8218 | * |
||
| 8219 | * @return string |
||
| 8220 | */ |
||
| 8221 | public function get_forums() |
||
| 8222 | { |
||
| 8223 | $forumCategories = get_forum_categories(); |
||
| 8224 | $forumsInNoCategory = get_forums_in_category(0); |
||
| 8225 | if (!empty($forumsInNoCategory)) { |
||
| 8226 | $forumCategories = array_merge( |
||
| 8227 | $forumCategories, |
||
| 8228 | [ |
||
| 8229 | [ |
||
| 8230 | 'cat_id' => 0, |
||
| 8231 | 'session_id' => 0, |
||
| 8232 | 'visibility' => 1, |
||
| 8233 | 'cat_comment' => null, |
||
| 8234 | ], |
||
| 8235 | ] |
||
| 8236 | ); |
||
| 8237 | } |
||
| 8238 | |||
| 8239 | $a_forums = []; |
||
| 8240 | $courseEntity = api_get_course_entity(api_get_course_int_id()); |
||
| 8241 | $sessionEntity = api_get_session_entity(api_get_session_id()); |
||
| 8242 | |||
| 8243 | foreach ($forumCategories as $forumCategory) { |
||
| 8244 | // The forums in this category. |
||
| 8245 | $forumsInCategory = get_forums_in_category($forumCategory->getIid()); |
||
| 8246 | if (!empty($forumsInCategory)) { |
||
| 8247 | foreach ($forumsInCategory as $forum) { |
||
| 8248 | if ($forum->isVisible($courseEntity, $sessionEntity)) { |
||
| 8249 | $a_forums[] = $forum; |
||
| 8250 | } |
||
| 8251 | } |
||
| 8252 | } |
||
| 8253 | } |
||
| 8254 | |||
| 8255 | $return = '<ul class="lp_resource">'; |
||
| 8256 | |||
| 8257 | // First add link |
||
| 8258 | $return .= '<li class="lp_resource_element">'; |
||
| 8259 | $return .= Display::return_icon('new_forum.png'); |
||
| 8260 | $return .= Display::url( |
||
| 8261 | get_lang('Create a new forum'), |
||
| 8262 | api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq().'&'.http_build_query([ |
||
| 8263 | 'action' => 'add', |
||
| 8264 | 'content' => 'forum', |
||
| 8265 | 'lp_id' => $this->lp_id, |
||
| 8266 | ]), |
||
| 8267 | ['title' => get_lang('Create a new forum')] |
||
| 8268 | ); |
||
| 8269 | $return .= '</li>'; |
||
| 8270 | |||
| 8271 | $return .= '<script> |
||
| 8272 | function toggle_forum(forum_id) { |
||
| 8273 | if (document.getElementById("forum_"+forum_id+"_content").style.display == "none") { |
||
| 8274 | document.getElementById("forum_"+forum_id+"_content").style.display = "block"; |
||
| 8275 | document.getElementById("forum_"+forum_id+"_opener").src = "'.Display::returnIconPath('remove.gif').'"; |
||
| 8276 | } else { |
||
| 8277 | document.getElementById("forum_"+forum_id+"_content").style.display = "none"; |
||
| 8278 | document.getElementById("forum_"+forum_id+"_opener").src = "'.Display::returnIconPath('add.png').'"; |
||
| 8279 | } |
||
| 8280 | } |
||
| 8281 | </script>'; |
||
| 8282 | $moveIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY); |
||
| 8283 | foreach ($a_forums as $forum) { |
||
| 8284 | $forumId = $forum->getIid(); |
||
| 8285 | $title = Security::remove_XSS($forum->getForumTitle()); |
||
| 8286 | $link = Display::url( |
||
| 8287 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
| 8288 | api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$forumId, |
||
| 8289 | ['target' => '_blank'] |
||
| 8290 | ); |
||
| 8291 | |||
| 8292 | $return .= '<li class="lp_resource_element">'; |
||
| 8293 | $return .= '<a class="moved" href="#">'; |
||
| 8294 | $return .= $moveIcon; |
||
| 8295 | $return .= ' </a>'; |
||
| 8296 | $return .= Display::return_icon('forum.png', '', [], ICON_SIZE_TINY); |
||
| 8297 | |||
| 8298 | $moveLink = Display::url( |
||
| 8299 | $title.' '.$link, |
||
| 8300 | api_get_self().'?'. |
||
| 8301 | api_get_cidreq().'&action=add_item&type='.TOOL_FORUM.'&forum_id='.$forumId.'&lp_id='.$this->lp_id, |
||
| 8302 | [ |
||
| 8303 | 'class' => 'moved link_with_id', |
||
| 8304 | 'data_id' => $forumId, |
||
| 8305 | 'data_type' => TOOL_FORUM, |
||
| 8306 | 'title' => $title, |
||
| 8307 | 'style' => 'vertical-align:middle', |
||
| 8308 | ] |
||
| 8309 | ); |
||
| 8310 | $return .= '<a onclick="javascript:toggle_forum('.$forumId.');" style="cursor:hand; vertical-align:middle"> |
||
| 8311 | <img |
||
| 8312 | src="'.Display::returnIconPath('add.png').'" |
||
| 8313 | id="forum_'.$forumId.'_opener" align="absbottom" |
||
| 8314 | /> |
||
| 8315 | </a> |
||
| 8316 | '.$moveLink; |
||
| 8317 | $return .= '</li>'; |
||
| 8318 | |||
| 8319 | $return .= '<div style="display:none" id="forum_'.$forumId.'_content">'; |
||
| 8320 | $threads = get_threads($forumId); |
||
| 8321 | if (is_array($threads)) { |
||
| 8322 | foreach ($threads as $thread) { |
||
| 8323 | $threadId = $thread->getIid(); |
||
| 8324 | $link = Display::url( |
||
| 8325 | Display::return_icon('preview_view.png', get_lang('Preview')), |
||
| 8326 | api_get_path(WEB_CODE_PATH). |
||
| 8327 | 'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$threadId, |
||
| 8328 | ['target' => '_blank'] |
||
| 8329 | ); |
||
| 8330 | |||
| 8331 | $return .= '<li class="lp_resource_element">'; |
||
| 8332 | $return .= ' <a class="moved" href="#">'; |
||
| 8333 | $return .= $moveIcon; |
||
| 8334 | $return .= ' </a>'; |
||
| 8335 | $return .= Display::return_icon('forumthread.png', get_lang('Thread'), [], ICON_SIZE_TINY); |
||
| 8336 | $return .= '<a |
||
| 8337 | class="moved link_with_id" |
||
| 8338 | data_id="'.$thread->getIid().'" |
||
| 8339 | data_type="'.TOOL_THREAD.'" |
||
| 8340 | title="'.$thread->getThreadTitle().'" |
||
| 8341 | href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_THREAD.'&thread_id='.$threadId.'&lp_id='.$this->lp_id.'" |
||
| 8342 | >'. |
||
| 8343 | Security::remove_XSS($thread->getThreadTitle()).' '.$link.'</a>'; |
||
| 8344 | $return .= '</li>'; |
||
| 8345 | } |
||
| 8346 | } |
||
| 8347 | $return .= '</div>'; |
||
| 8348 | } |
||
| 8349 | $return .= '</ul>'; |
||
| 8350 | |||
| 8351 | return $return; |
||
| 8352 | } |
||
| 8353 | |||
| 8354 | /** |
||
| 8355 | * // TODO: The output encoding should be equal to the system encoding. |
||
| 8356 | * |
||
| 8357 | * Exports the learning path as a SCORM package. This is the main function that |
||
| 8358 | * gathers the content, transforms it, writes the imsmanifest.xml file, zips the |
||
| 8359 | * whole thing and returns the zip. |
||
| 8360 | * |
||
| 8361 | * This method needs to be called in PHP5, as it will fail with non-adequate |
||
| 8362 | * XML package (like the ones for PHP4), and it is *not* a static method, so |
||
| 8363 | * you need to call it on a learnpath object. |
||
| 8364 | * |
||
| 8365 | * @TODO The method might be redefined later on in the scorm class itself to avoid |
||
| 8366 | * creating a SCORM structure if there is one already. However, if the initial SCORM |
||
| 8367 | * path has been modified, it should use the generic method here below. |
||
| 8368 | * |
||
| 8369 | * @return string Returns the zip package string, or null if error |
||
| 8370 | */ |
||
| 8371 | public function scormExport() |
||
| 8372 | { |
||
| 8373 | api_set_more_memory_and_time_limits(); |
||
| 8374 | |||
| 8375 | $_course = api_get_course_info(); |
||
| 8376 | $course_id = $_course['real_id']; |
||
| 8377 | // Create the zip handler (this will remain available throughout the method). |
||
| 8378 | $archivePath = api_get_path(SYS_ARCHIVE_PATH); |
||
| 8379 | $sys_course_path = api_get_path(SYS_COURSE_PATH); |
||
| 8380 | $temp_dir_short = uniqid('scorm_export', true); |
||
| 8381 | $temp_zip_dir = $archivePath.'/'.$temp_dir_short; |
||
| 8382 | $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip'; |
||
| 8383 | $zip_folder = new PclZip($temp_zip_file); |
||
| 8384 | $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path(); |
||
| 8385 | $root_path = $main_path = api_get_path(SYS_PATH); |
||
| 8386 | $files_cleanup = []; |
||
| 8387 | |||
| 8388 | // Place to temporarily stash the zip file. |
||
| 8389 | // create the temp dir if it doesn't exist |
||
| 8390 | // or do a cleanup before creating the zip file. |
||
| 8391 | if (!is_dir($temp_zip_dir)) { |
||
| 8392 | mkdir($temp_zip_dir, api_get_permissions_for_new_directories()); |
||
| 8393 | } else { |
||
| 8394 | // Cleanup: Check the temp dir for old files and delete them. |
||
| 8395 | $handle = opendir($temp_zip_dir); |
||
| 8396 | while (false !== ($file = readdir($handle))) { |
||
| 8397 | if ('.' != $file && '..' != $file) { |
||
| 8398 | unlink("$temp_zip_dir/$file"); |
||
| 8399 | } |
||
| 8400 | } |
||
| 8401 | closedir($handle); |
||
| 8402 | } |
||
| 8403 | $zip_files = $zip_files_abs = $zip_files_dist = []; |
||
| 8404 | if (is_dir($current_course_path.'/scorm/'.$this->path) && |
||
| 8405 | is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml') |
||
| 8406 | ) { |
||
| 8407 | // Remove the possible . at the end of the path. |
||
| 8408 | $dest_path_to_lp = '.' == substr($this->path, -1) ? substr($this->path, 0, -1) : $this->path; |
||
| 8409 | $dest_path_to_scorm_folder = str_replace('//', '/', $temp_zip_dir.'/scorm/'.$dest_path_to_lp); |
||
| 8410 | mkdir( |
||
| 8411 | $dest_path_to_scorm_folder, |
||
| 8412 | api_get_permissions_for_new_directories(), |
||
| 8413 | true |
||
| 8414 | ); |
||
| 8415 | copyr( |
||
| 8416 | $current_course_path.'/scorm/'.$this->path, |
||
| 8417 | $dest_path_to_scorm_folder, |
||
| 8418 | ['imsmanifest'], |
||
| 8419 | $zip_files |
||
| 8420 | ); |
||
| 8421 | } |
||
| 8422 | |||
| 8423 | // Build a dummy imsmanifest structure. |
||
| 8424 | // Do not add to the zip yet (we still need it). |
||
| 8425 | // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content |
||
| 8426 | // Aggregation Model official document, section "2.3 Content Packaging". |
||
| 8427 | // We are going to build a UTF-8 encoded manifest. |
||
| 8428 | // Later we will recode it to the desired (and supported) encoding. |
||
| 8429 | $xmldoc = new DOMDocument('1.0'); |
||
| 8430 | $root = $xmldoc->createElement('manifest'); |
||
| 8431 | $root->setAttribute('identifier', 'SingleCourseManifest'); |
||
| 8432 | $root->setAttribute('version', '1.1'); |
||
| 8433 | $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2'); |
||
| 8434 | $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2'); |
||
| 8435 | $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); |
||
| 8436 | $root->setAttribute( |
||
| 8437 | 'xsi:schemaLocation', |
||
| 8438 | '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' |
||
| 8439 | ); |
||
| 8440 | // Build mandatory sub-root container elements. |
||
| 8441 | $metadata = $xmldoc->createElement('metadata'); |
||
| 8442 | $md_schema = $xmldoc->createElement('schema', 'ADL SCORM'); |
||
| 8443 | $metadata->appendChild($md_schema); |
||
| 8444 | $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2'); |
||
| 8445 | $metadata->appendChild($md_schemaversion); |
||
| 8446 | $root->appendChild($metadata); |
||
| 8447 | |||
| 8448 | $organizations = $xmldoc->createElement('organizations'); |
||
| 8449 | $resources = $xmldoc->createElement('resources'); |
||
| 8450 | |||
| 8451 | // Build the only organization we will use in building our learnpaths. |
||
| 8452 | $organizations->setAttribute('default', 'chamilo_scorm_export'); |
||
| 8453 | $organization = $xmldoc->createElement('organization'); |
||
| 8454 | $organization->setAttribute('identifier', 'chamilo_scorm_export'); |
||
| 8455 | // To set the title of the SCORM entity (=organization), we take the name given |
||
| 8456 | // in Chamilo and convert it to HTML entities using the Chamilo charset (not the |
||
| 8457 | // learning path charset) as it is the encoding that defines how it is stored |
||
| 8458 | // in the database. Then we convert it to HTML entities again as the "&" character |
||
| 8459 | // alone is not authorized in XML (must be &). |
||
| 8460 | // The title is then decoded twice when extracting (see scorm::parse_manifest). |
||
| 8461 | $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name())); |
||
| 8462 | $organization->appendChild($org_title); |
||
| 8463 | $folder_name = 'document'; |
||
| 8464 | |||
| 8465 | // Removes the learning_path/scorm_folder path when exporting see #4841 |
||
| 8466 | $path_to_remove = ''; |
||
| 8467 | $path_to_replace = ''; |
||
| 8468 | $result = $this->generate_lp_folder($_course); |
||
| 8469 | if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) { |
||
| 8470 | $path_to_remove = 'document'.$result['dir']; |
||
| 8471 | $path_to_replace = $folder_name.'/'; |
||
| 8472 | } |
||
| 8473 | |||
| 8474 | // Fixes chamilo scorm exports |
||
| 8475 | if ('chamilo_scorm_export' === $this->ref) { |
||
| 8476 | $path_to_remove = 'scorm/'.$this->path.'/document/'; |
||
| 8477 | } |
||
| 8478 | |||
| 8479 | // For each element, add it to the imsmanifest structure, then add it to the zip. |
||
| 8480 | $link_updates = []; |
||
| 8481 | $links_to_create = []; |
||
| 8482 | foreach ($this->ordered_items as $index => $itemId) { |
||
| 8483 | /** @var learnpathItem $item */ |
||
| 8484 | $item = $this->items[$itemId]; |
||
| 8485 | if (!in_array($item->type, [TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION])) { |
||
| 8486 | // Get included documents from this item. |
||
| 8487 | if ('sco' === $item->type) { |
||
| 8488 | $inc_docs = $item->get_resources_from_source( |
||
| 8489 | null, |
||
| 8490 | $current_course_path.'/scorm/'.$this->path.'/'.$item->get_path() |
||
| 8491 | ); |
||
| 8492 | } else { |
||
| 8493 | $inc_docs = $item->get_resources_from_source(); |
||
| 8494 | } |
||
| 8495 | |||
| 8496 | // Give a child element <item> to the <organization> element. |
||
| 8497 | $my_item_id = $item->get_id(); |
||
| 8498 | $my_item = $xmldoc->createElement('item'); |
||
| 8499 | $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id); |
||
| 8500 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id); |
||
| 8501 | $my_item->setAttribute('isvisible', 'true'); |
||
| 8502 | // Give a child element <title> to the <item> element. |
||
| 8503 | $my_title = $xmldoc->createElement( |
||
| 8504 | 'title', |
||
| 8505 | htmlspecialchars( |
||
| 8506 | api_utf8_encode($item->get_title()), |
||
| 8507 | ENT_QUOTES, |
||
| 8508 | 'UTF-8' |
||
| 8509 | ) |
||
| 8510 | ); |
||
| 8511 | $my_item->appendChild($my_title); |
||
| 8512 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
| 8513 | $my_prereqs = $xmldoc->createElement( |
||
| 8514 | 'adlcp:prerequisites', |
||
| 8515 | $this->get_scorm_prereq_string($my_item_id) |
||
| 8516 | ); |
||
| 8517 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
| 8518 | $my_item->appendChild($my_prereqs); |
||
| 8519 | // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported. |
||
| 8520 | //$xmldoc->createElement('adlcp:maxtimeallowed',''); |
||
| 8521 | // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported. |
||
| 8522 | //$xmldoc->createElement('adlcp:timelimitaction',''); |
||
| 8523 | // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported. |
||
| 8524 | //$xmldoc->createElement('adlcp:datafromlms',''); |
||
| 8525 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
| 8526 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
| 8527 | $my_item->appendChild($my_masteryscore); |
||
| 8528 | |||
| 8529 | // Attach this item to the organization element or hits parent if there is one. |
||
| 8530 | if (!empty($item->parent) && 0 != $item->parent) { |
||
| 8531 | $children = $organization->childNodes; |
||
| 8532 | $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent); |
||
| 8533 | if (is_object($possible_parent)) { |
||
| 8534 | $possible_parent->appendChild($my_item); |
||
| 8535 | } else { |
||
| 8536 | if ($this->debug > 0) { |
||
| 8537 | error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); |
||
| 8538 | } |
||
| 8539 | } |
||
| 8540 | } else { |
||
| 8541 | if ($this->debug > 0) { |
||
| 8542 | error_log('No parent'); |
||
| 8543 | } |
||
| 8544 | $organization->appendChild($my_item); |
||
| 8545 | } |
||
| 8546 | |||
| 8547 | // Get the path of the file(s) from the course directory root. |
||
| 8548 | $my_file_path = $item->get_file_path('scorm/'.$this->path.'/'); |
||
| 8549 | $my_xml_file_path = $my_file_path; |
||
| 8550 | if (!empty($path_to_remove)) { |
||
| 8551 | // From docs |
||
| 8552 | $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path); |
||
| 8553 | |||
| 8554 | // From quiz |
||
| 8555 | if ('chamilo_scorm_export' === $this->ref) { |
||
| 8556 | $path_to_remove = 'scorm/'.$this->path.'/'; |
||
| 8557 | $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path); |
||
| 8558 | } |
||
| 8559 | } |
||
| 8560 | |||
| 8561 | $my_sub_dir = dirname($my_file_path); |
||
| 8562 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
| 8563 | $my_xml_sub_dir = $my_sub_dir; |
||
| 8564 | // Give a <resource> child to the <resources> element |
||
| 8565 | $my_resource = $xmldoc->createElement('resource'); |
||
| 8566 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
| 8567 | $my_resource->setAttribute('type', 'webcontent'); |
||
| 8568 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
| 8569 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
| 8570 | if ('sco' === $item->type) { |
||
| 8571 | $my_resource->setAttribute('adlcp:scormtype', 'sco'); |
||
| 8572 | } else { |
||
| 8573 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
| 8574 | } |
||
| 8575 | // xml:base is the base directory to find the files declared in this resource. |
||
| 8576 | $my_resource->setAttribute('xml:base', ''); |
||
| 8577 | // Give a <file> child to the <resource> element. |
||
| 8578 | $my_file = $xmldoc->createElement('file'); |
||
| 8579 | $my_file->setAttribute('href', $my_xml_file_path); |
||
| 8580 | $my_resource->appendChild($my_file); |
||
| 8581 | |||
| 8582 | // Dependency to other files - not yet supported. |
||
| 8583 | $i = 1; |
||
| 8584 | if ($inc_docs) { |
||
| 8585 | foreach ($inc_docs as $doc_info) { |
||
| 8586 | if (count($doc_info) < 1 || empty($doc_info[0])) { |
||
| 8587 | continue; |
||
| 8588 | } |
||
| 8589 | $my_dep = $xmldoc->createElement('resource'); |
||
| 8590 | $res_id = 'RESOURCE_'.$item->get_id().'_'.$i; |
||
| 8591 | $my_dep->setAttribute('identifier', $res_id); |
||
| 8592 | $my_dep->setAttribute('type', 'webcontent'); |
||
| 8593 | $my_dep->setAttribute('adlcp:scormtype', 'asset'); |
||
| 8594 | $my_dep_file = $xmldoc->createElement('file'); |
||
| 8595 | // Check type of URL. |
||
| 8596 | if ('remote' == $doc_info[1]) { |
||
| 8597 | // Remote file. Save url as is. |
||
| 8598 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 8599 | $my_dep->setAttribute('xml:base', ''); |
||
| 8600 | } elseif ('local' === $doc_info[1]) { |
||
| 8601 | switch ($doc_info[2]) { |
||
| 8602 | case 'url': |
||
| 8603 | // Local URL - save path as url for now, don't zip file. |
||
| 8604 | $abs_path = api_get_path(SYS_PATH). |
||
| 8605 | str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
| 8606 | $current_dir = dirname($abs_path); |
||
| 8607 | $current_dir = str_replace('\\', '/', $current_dir); |
||
| 8608 | $file_path = realpath($abs_path); |
||
| 8609 | $file_path = str_replace('\\', '/', $file_path); |
||
| 8610 | $my_dep_file->setAttribute('href', $file_path); |
||
| 8611 | $my_dep->setAttribute('xml:base', ''); |
||
| 8612 | if (false !== strstr($file_path, $main_path)) { |
||
| 8613 | // The calculated real path is really inside Chamilo's root path. |
||
| 8614 | // Reduce file path to what's under the DocumentRoot. |
||
| 8615 | $replace = $file_path; |
||
| 8616 | $file_path = substr($file_path, strlen($root_path) - 1); |
||
| 8617 | $destinationFile = $file_path; |
||
| 8618 | |||
| 8619 | if (false !== strstr($file_path, 'upload/users')) { |
||
| 8620 | $pos = strpos($file_path, 'my_files/'); |
||
| 8621 | if (false !== $pos) { |
||
| 8622 | $onlyDirectory = str_replace( |
||
| 8623 | 'upload/users/', |
||
| 8624 | '', |
||
| 8625 | substr($file_path, $pos, strlen($file_path)) |
||
| 8626 | ); |
||
| 8627 | } |
||
| 8628 | $replace = $onlyDirectory; |
||
| 8629 | $destinationFile = $replace; |
||
| 8630 | } |
||
| 8631 | $zip_files_abs[] = $file_path; |
||
| 8632 | $link_updates[$my_file_path][] = [ |
||
| 8633 | 'orig' => $doc_info[0], |
||
| 8634 | 'dest' => $destinationFile, |
||
| 8635 | 'replace' => $replace, |
||
| 8636 | ]; |
||
| 8637 | $my_dep_file->setAttribute('href', $file_path); |
||
| 8638 | $my_dep->setAttribute('xml:base', ''); |
||
| 8639 | } elseif (empty($file_path)) { |
||
| 8640 | $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path; |
||
| 8641 | $file_path = str_replace('//', '/', $file_path); |
||
| 8642 | if (file_exists($file_path)) { |
||
| 8643 | // We get the relative path. |
||
| 8644 | $file_path = substr($file_path, strlen($current_dir)); |
||
| 8645 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
| 8646 | $link_updates[$my_file_path][] = [ |
||
| 8647 | 'orig' => $doc_info[0], |
||
| 8648 | 'dest' => $file_path, |
||
| 8649 | ]; |
||
| 8650 | $my_dep_file->setAttribute('href', $file_path); |
||
| 8651 | $my_dep->setAttribute('xml:base', ''); |
||
| 8652 | } |
||
| 8653 | } |
||
| 8654 | break; |
||
| 8655 | case 'abs': |
||
| 8656 | // Absolute path from DocumentRoot. Save file and leave path as is in the zip. |
||
| 8657 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 8658 | $my_dep->setAttribute('xml:base', ''); |
||
| 8659 | |||
| 8660 | // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas |
||
| 8661 | // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar |
||
| 8662 | $abs_img_path_without_subdir = $doc_info[0]; |
||
| 8663 | $relp = api_get_path(REL_PATH); // The url-append config param. |
||
| 8664 | $pos = strpos($abs_img_path_without_subdir, $relp); |
||
| 8665 | if (0 === $pos) { |
||
| 8666 | $abs_img_path_without_subdir = trim('/'.substr($abs_img_path_without_subdir, strlen($relp))); |
||
| 8667 | } |
||
| 8668 | |||
| 8669 | $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir); |
||
| 8670 | $file_path = str_replace(['\\', '//'], '/', $file_path); |
||
| 8671 | |||
| 8672 | // Prepare the current directory path (until just under 'document') with a trailing slash. |
||
| 8673 | $cur_path = '/' == substr($current_course_path, -1) ? $current_course_path : $current_course_path.'/'; |
||
| 8674 | // Check if the current document is in that path. |
||
| 8675 | if (false !== strstr($file_path, $cur_path)) { |
||
| 8676 | $destinationFile = substr($file_path, strlen($cur_path)); |
||
| 8677 | $filePathNoCoursePart = substr($file_path, strlen($cur_path)); |
||
| 8678 | |||
| 8679 | $fileToTest = $cur_path.$my_file_path; |
||
| 8680 | if (!empty($path_to_remove)) { |
||
| 8681 | $fileToTest = str_replace( |
||
| 8682 | $path_to_remove.'/', |
||
| 8683 | $path_to_replace, |
||
| 8684 | $cur_path.$my_file_path |
||
| 8685 | ); |
||
| 8686 | } |
||
| 8687 | |||
| 8688 | $relative_path = api_get_relative_path($fileToTest, $file_path); |
||
| 8689 | |||
| 8690 | // Put the current document in the zip (this array is the array |
||
| 8691 | // that will manage documents already in the course folder - relative). |
||
| 8692 | $zip_files[] = $filePathNoCoursePart; |
||
| 8693 | // Update the links to the current document in the |
||
| 8694 | // containing document (make them relative). |
||
| 8695 | $link_updates[$my_file_path][] = [ |
||
| 8696 | 'orig' => $doc_info[0], |
||
| 8697 | 'dest' => $destinationFile, |
||
| 8698 | 'replace' => $relative_path, |
||
| 8699 | ]; |
||
| 8700 | |||
| 8701 | $my_dep_file->setAttribute('href', $file_path); |
||
| 8702 | $my_dep->setAttribute('xml:base', ''); |
||
| 8703 | } elseif (false !== strstr($file_path, $main_path)) { |
||
| 8704 | // The calculated real path is really inside Chamilo's root path. |
||
| 8705 | // Reduce file path to what's under the DocumentRoot. |
||
| 8706 | $file_path = substr($file_path, strlen($root_path)); |
||
| 8707 | $zip_files_abs[] = $file_path; |
||
| 8708 | $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path]; |
||
| 8709 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8710 | $my_dep->setAttribute('xml:base', ''); |
||
| 8711 | } elseif (empty($file_path)) { |
||
| 8712 | // Probably this is an image inside "/main" directory |
||
| 8713 | $file_path = api_get_path(SYS_PATH).$abs_img_path_without_subdir; |
||
| 8714 | $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
| 8715 | |||
| 8716 | if (file_exists($file_path)) { |
||
| 8717 | if (false !== strstr($file_path, 'main/default_course_document')) { |
||
| 8718 | // We get the relative path. |
||
| 8719 | $pos = strpos($file_path, 'main/default_course_document/'); |
||
| 8720 | if (false !== $pos) { |
||
| 8721 | $onlyDirectory = str_replace( |
||
| 8722 | 'main/default_course_document/', |
||
| 8723 | '', |
||
| 8724 | substr($file_path, $pos, strlen($file_path)) |
||
| 8725 | ); |
||
| 8726 | } |
||
| 8727 | |||
| 8728 | $destinationFile = 'default_course_document/'.$onlyDirectory; |
||
| 8729 | $fileAbs = substr($file_path, strlen(api_get_path(SYS_PATH))); |
||
| 8730 | $zip_files_abs[] = $fileAbs; |
||
| 8731 | $link_updates[$my_file_path][] = [ |
||
| 8732 | 'orig' => $doc_info[0], |
||
| 8733 | 'dest' => $destinationFile, |
||
| 8734 | ]; |
||
| 8735 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8736 | $my_dep->setAttribute('xml:base', ''); |
||
| 8737 | } |
||
| 8738 | } |
||
| 8739 | } |
||
| 8740 | break; |
||
| 8741 | case 'rel': |
||
| 8742 | // Path relative to the current document. |
||
| 8743 | // Save xml:base as current document's directory and save file in zip as subdir.file_path |
||
| 8744 | if ('..' === substr($doc_info[0], 0, 2)) { |
||
| 8745 | // Relative path going up. |
||
| 8746 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
| 8747 | $current_dir = str_replace('\\', '/', $current_dir); |
||
| 8748 | $file_path = realpath($current_dir.$doc_info[0]); |
||
| 8749 | $file_path = str_replace('\\', '/', $file_path); |
||
| 8750 | if (false !== strstr($file_path, $main_path)) { |
||
| 8751 | // The calculated real path is really inside Chamilo's root path. |
||
| 8752 | // Reduce file path to what's under the DocumentRoot. |
||
| 8753 | $file_path = substr($file_path, strlen($root_path)); |
||
| 8754 | $zip_files_abs[] = $file_path; |
||
| 8755 | $link_updates[$my_file_path][] = ['orig' => $doc_info[0], 'dest' => $file_path]; |
||
| 8756 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8757 | $my_dep->setAttribute('xml:base', ''); |
||
| 8758 | } |
||
| 8759 | } else { |
||
| 8760 | $zip_files[] = $my_sub_dir.'/'.$doc_info[0]; |
||
| 8761 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 8762 | $my_dep->setAttribute('xml:base', $my_xml_sub_dir); |
||
| 8763 | } |
||
| 8764 | break; |
||
| 8765 | default: |
||
| 8766 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 8767 | $my_dep->setAttribute('xml:base', ''); |
||
| 8768 | break; |
||
| 8769 | } |
||
| 8770 | } |
||
| 8771 | $my_dep->appendChild($my_dep_file); |
||
| 8772 | $resources->appendChild($my_dep); |
||
| 8773 | $dependency = $xmldoc->createElement('dependency'); |
||
| 8774 | $dependency->setAttribute('identifierref', $res_id); |
||
| 8775 | $my_resource->appendChild($dependency); |
||
| 8776 | $i++; |
||
| 8777 | } |
||
| 8778 | } |
||
| 8779 | $resources->appendChild($my_resource); |
||
| 8780 | $zip_files[] = $my_file_path; |
||
| 8781 | } else { |
||
| 8782 | // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it. |
||
| 8783 | switch ($item->type) { |
||
| 8784 | case TOOL_LINK: |
||
| 8785 | $my_item = $xmldoc->createElement('item'); |
||
| 8786 | $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id()); |
||
| 8787 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id()); |
||
| 8788 | $my_item->setAttribute('isvisible', 'true'); |
||
| 8789 | // Give a child element <title> to the <item> element. |
||
| 8790 | $my_title = $xmldoc->createElement( |
||
| 8791 | 'title', |
||
| 8792 | htmlspecialchars( |
||
| 8793 | api_utf8_encode($item->get_title()), |
||
| 8794 | ENT_QUOTES, |
||
| 8795 | 'UTF-8' |
||
| 8796 | ) |
||
| 8797 | ); |
||
| 8798 | $my_item->appendChild($my_title); |
||
| 8799 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
| 8800 | $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string()); |
||
| 8801 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
| 8802 | $my_item->appendChild($my_prereqs); |
||
| 8803 | // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported. |
||
| 8804 | //$xmldoc->createElement('adlcp:maxtimeallowed', ''); |
||
| 8805 | // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported. |
||
| 8806 | //$xmldoc->createElement('adlcp:timelimitaction', ''); |
||
| 8807 | // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported. |
||
| 8808 | //$xmldoc->createElement('adlcp:datafromlms', ''); |
||
| 8809 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
| 8810 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
| 8811 | $my_item->appendChild($my_masteryscore); |
||
| 8812 | |||
| 8813 | // Attach this item to the organization element or its parent if there is one. |
||
| 8814 | if (!empty($item->parent) && 0 != $item->parent) { |
||
| 8815 | $children = $organization->childNodes; |
||
| 8816 | for ($i = 0; $i < $children->length; $i++) { |
||
| 8817 | $item_temp = $children->item($i); |
||
| 8818 | if ('item' == $item_temp->nodeName) { |
||
| 8819 | if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) { |
||
| 8820 | $item_temp->appendChild($my_item); |
||
| 8821 | } |
||
| 8822 | } |
||
| 8823 | } |
||
| 8824 | } else { |
||
| 8825 | $organization->appendChild($my_item); |
||
| 8826 | } |
||
| 8827 | |||
| 8828 | $my_file_path = 'link_'.$item->get_id().'.html'; |
||
| 8829 | $sql = 'SELECT url, title FROM '.Database::get_course_table(TABLE_LINK).' |
||
| 8830 | WHERE c_id = '.$course_id.' AND id = '.$item->path; |
||
| 8831 | $rs = Database::query($sql); |
||
| 8832 | if ($link = Database::fetch_array($rs)) { |
||
| 8833 | $url = $link['url']; |
||
| 8834 | $title = stripslashes($link['title']); |
||
| 8835 | $links_to_create[$my_file_path] = ['title' => $title, 'url' => $url]; |
||
| 8836 | $my_xml_file_path = $my_file_path; |
||
| 8837 | $my_sub_dir = dirname($my_file_path); |
||
| 8838 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
| 8839 | $my_xml_sub_dir = $my_sub_dir; |
||
| 8840 | // Give a <resource> child to the <resources> element. |
||
| 8841 | $my_resource = $xmldoc->createElement('resource'); |
||
| 8842 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
| 8843 | $my_resource->setAttribute('type', 'webcontent'); |
||
| 8844 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
| 8845 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
| 8846 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
| 8847 | // xml:base is the base directory to find the files declared in this resource. |
||
| 8848 | $my_resource->setAttribute('xml:base', ''); |
||
| 8849 | // give a <file> child to the <resource> element. |
||
| 8850 | $my_file = $xmldoc->createElement('file'); |
||
| 8851 | $my_file->setAttribute('href', $my_xml_file_path); |
||
| 8852 | $my_resource->appendChild($my_file); |
||
| 8853 | $resources->appendChild($my_resource); |
||
| 8854 | } |
||
| 8855 | break; |
||
| 8856 | case TOOL_QUIZ: |
||
| 8857 | $exe_id = $item->path; |
||
| 8858 | // Should be using ref when everything will be cleaned up in this regard. |
||
| 8859 | $exe = new Exercise(); |
||
| 8860 | $exe->read($exe_id); |
||
| 8861 | $my_item = $xmldoc->createElement('item'); |
||
| 8862 | $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id()); |
||
| 8863 | $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id()); |
||
| 8864 | $my_item->setAttribute('isvisible', 'true'); |
||
| 8865 | // Give a child element <title> to the <item> element. |
||
| 8866 | $my_title = $xmldoc->createElement( |
||
| 8867 | 'title', |
||
| 8868 | htmlspecialchars( |
||
| 8869 | api_utf8_encode($item->get_title()), |
||
| 8870 | ENT_QUOTES, |
||
| 8871 | 'UTF-8' |
||
| 8872 | ) |
||
| 8873 | ); |
||
| 8874 | $my_item->appendChild($my_title); |
||
| 8875 | $my_max_score = $xmldoc->createElement('max_score', $item->get_max()); |
||
| 8876 | $my_item->appendChild($my_max_score); |
||
| 8877 | // Give a child element <adlcp:prerequisites> to the <item> element. |
||
| 8878 | $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string()); |
||
| 8879 | $my_prereqs->setAttribute('type', 'aicc_script'); |
||
| 8880 | $my_item->appendChild($my_prereqs); |
||
| 8881 | // Give a child element <adlcp:masteryscore> to the <item> element. |
||
| 8882 | $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score()); |
||
| 8883 | $my_item->appendChild($my_masteryscore); |
||
| 8884 | |||
| 8885 | // Attach this item to the organization element or hits parent if there is one. |
||
| 8886 | if (!empty($item->parent) && 0 != $item->parent) { |
||
| 8887 | $children = $organization->childNodes; |
||
| 8888 | $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent); |
||
| 8889 | if ($possible_parent) { |
||
| 8890 | if ($possible_parent->getAttribute('identifier') === 'ITEM_'.$item->parent) { |
||
| 8891 | $possible_parent->appendChild($my_item); |
||
| 8892 | } |
||
| 8893 | } |
||
| 8894 | } else { |
||
| 8895 | $organization->appendChild($my_item); |
||
| 8896 | } |
||
| 8897 | |||
| 8898 | // Get the path of the file(s) from the course directory root |
||
| 8899 | //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/'); |
||
| 8900 | $my_file_path = 'quiz_'.$item->get_id().'.html'; |
||
| 8901 | // Write the contents of the exported exercise into a (big) html file |
||
| 8902 | // to later pack it into the exported SCORM. The file will be removed afterwards. |
||
| 8903 | $scormExercise = new ScormExercise($exe, true); |
||
| 8904 | $contents = $scormExercise->export(); |
||
| 8905 | |||
| 8906 | $tmp_file_path = $archivePath.$temp_dir_short.'/'.$my_file_path; |
||
| 8907 | $res = file_put_contents($tmp_file_path, $contents); |
||
| 8908 | if (false === $res) { |
||
| 8909 | error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0); |
||
| 8910 | } |
||
| 8911 | $files_cleanup[] = $tmp_file_path; |
||
| 8912 | $my_xml_file_path = $my_file_path; |
||
| 8913 | $my_sub_dir = dirname($my_file_path); |
||
| 8914 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
| 8915 | $my_xml_sub_dir = $my_sub_dir; |
||
| 8916 | // Give a <resource> child to the <resources> element. |
||
| 8917 | $my_resource = $xmldoc->createElement('resource'); |
||
| 8918 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
| 8919 | $my_resource->setAttribute('type', 'webcontent'); |
||
| 8920 | $my_resource->setAttribute('href', $my_xml_file_path); |
||
| 8921 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
| 8922 | $my_resource->setAttribute('adlcp:scormtype', 'sco'); |
||
| 8923 | // xml:base is the base directory to find the files declared in this resource. |
||
| 8924 | $my_resource->setAttribute('xml:base', ''); |
||
| 8925 | // Give a <file> child to the <resource> element. |
||
| 8926 | $my_file = $xmldoc->createElement('file'); |
||
| 8927 | $my_file->setAttribute('href', $my_xml_file_path); |
||
| 8928 | $my_resource->appendChild($my_file); |
||
| 8929 | |||
| 8930 | // Get included docs. |
||
| 8931 | $inc_docs = $item->get_resources_from_source(null, $tmp_file_path); |
||
| 8932 | |||
| 8933 | // Dependency to other files - not yet supported. |
||
| 8934 | $i = 1; |
||
| 8935 | foreach ($inc_docs as $doc_info) { |
||
| 8936 | if (count($doc_info) < 1 || empty($doc_info[0])) { |
||
| 8937 | continue; |
||
| 8938 | } |
||
| 8939 | $my_dep = $xmldoc->createElement('resource'); |
||
| 8940 | $res_id = 'RESOURCE_'.$item->get_id().'_'.$i; |
||
| 8941 | $my_dep->setAttribute('identifier', $res_id); |
||
| 8942 | $my_dep->setAttribute('type', 'webcontent'); |
||
| 8943 | $my_dep->setAttribute('adlcp:scormtype', 'asset'); |
||
| 8944 | $my_dep_file = $xmldoc->createElement('file'); |
||
| 8945 | // Check type of URL. |
||
| 8946 | if ('remote' == $doc_info[1]) { |
||
| 8947 | // Remote file. Save url as is. |
||
| 8948 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 8949 | $my_dep->setAttribute('xml:base', ''); |
||
| 8950 | } elseif ('local' == $doc_info[1]) { |
||
| 8951 | switch ($doc_info[2]) { |
||
| 8952 | case 'url': // Local URL - save path as url for now, don't zip file. |
||
| 8953 | // Save file but as local file (retrieve from URL). |
||
| 8954 | $abs_path = api_get_path(SYS_PATH). |
||
| 8955 | str_replace(api_get_path(WEB_PATH), '', $doc_info[0]); |
||
| 8956 | $current_dir = dirname($abs_path); |
||
| 8957 | $current_dir = str_replace('\\', '/', $current_dir); |
||
| 8958 | $file_path = realpath($abs_path); |
||
| 8959 | $file_path = str_replace('\\', '/', $file_path); |
||
| 8960 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8961 | $my_dep->setAttribute('xml:base', ''); |
||
| 8962 | if (false !== strstr($file_path, $main_path)) { |
||
| 8963 | // The calculated real path is really inside the chamilo root path. |
||
| 8964 | // Reduce file path to what's under the DocumentRoot. |
||
| 8965 | $file_path = substr($file_path, strlen($root_path)); |
||
| 8966 | $zip_files_abs[] = $file_path; |
||
| 8967 | $link_updates[$my_file_path][] = [ |
||
| 8968 | 'orig' => $doc_info[0], |
||
| 8969 | 'dest' => 'document/'.$file_path, |
||
| 8970 | ]; |
||
| 8971 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8972 | $my_dep->setAttribute('xml:base', ''); |
||
| 8973 | } elseif (empty($file_path)) { |
||
| 8974 | $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path; |
||
| 8975 | $file_path = str_replace('//', '/', $file_path); |
||
| 8976 | if (file_exists($file_path)) { |
||
| 8977 | $file_path = substr($file_path, strlen($current_dir)); |
||
| 8978 | // We get the relative path. |
||
| 8979 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
| 8980 | $link_updates[$my_file_path][] = [ |
||
| 8981 | 'orig' => $doc_info[0], |
||
| 8982 | 'dest' => 'document/'.$file_path, |
||
| 8983 | ]; |
||
| 8984 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 8985 | $my_dep->setAttribute('xml:base', ''); |
||
| 8986 | } |
||
| 8987 | } |
||
| 8988 | break; |
||
| 8989 | case 'abs': |
||
| 8990 | // Absolute path from DocumentRoot. Save file and leave path as is in the zip. |
||
| 8991 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
| 8992 | $current_dir = str_replace('\\', '/', $current_dir); |
||
| 8993 | $file_path = realpath($doc_info[0]); |
||
| 8994 | $file_path = str_replace('\\', '/', $file_path); |
||
| 8995 | $my_dep_file->setAttribute('href', $file_path); |
||
| 8996 | $my_dep->setAttribute('xml:base', ''); |
||
| 8997 | |||
| 8998 | if (false !== strstr($file_path, $main_path)) { |
||
| 8999 | // The calculated real path is really inside the chamilo root path. |
||
| 9000 | // Reduce file path to what's under the DocumentRoot. |
||
| 9001 | $file_path = substr($file_path, strlen($root_path)); |
||
| 9002 | $zip_files_abs[] = $file_path; |
||
| 9003 | $link_updates[$my_file_path][] = [ |
||
| 9004 | 'orig' => $doc_info[0], |
||
| 9005 | 'dest' => $file_path, |
||
| 9006 | ]; |
||
| 9007 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 9008 | $my_dep->setAttribute('xml:base', ''); |
||
| 9009 | } elseif (empty($file_path)) { |
||
| 9010 | $docSysPartPath = str_replace( |
||
| 9011 | api_get_path(REL_COURSE_PATH), |
||
| 9012 | '', |
||
| 9013 | $doc_info[0] |
||
| 9014 | ); |
||
| 9015 | |||
| 9016 | $docSysPartPathNoCourseCode = str_replace( |
||
| 9017 | $_course['directory'].'/', |
||
| 9018 | '', |
||
| 9019 | $docSysPartPath |
||
| 9020 | ); |
||
| 9021 | |||
| 9022 | $docSysPath = api_get_path(SYS_COURSE_PATH).$docSysPartPath; |
||
| 9023 | if (file_exists($docSysPath)) { |
||
| 9024 | $file_path = $docSysPartPathNoCourseCode; |
||
| 9025 | $zip_files[] = $my_sub_dir.'/'.$file_path; |
||
| 9026 | $link_updates[$my_file_path][] = [ |
||
| 9027 | 'orig' => $doc_info[0], |
||
| 9028 | 'dest' => $file_path, |
||
| 9029 | ]; |
||
| 9030 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 9031 | $my_dep->setAttribute('xml:base', ''); |
||
| 9032 | } |
||
| 9033 | } |
||
| 9034 | break; |
||
| 9035 | case 'rel': |
||
| 9036 | // Path relative to the current document. Save xml:base as current document's |
||
| 9037 | // directory and save file in zip as subdir.file_path |
||
| 9038 | if ('..' === substr($doc_info[0], 0, 2)) { |
||
| 9039 | // Relative path going up. |
||
| 9040 | $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/'; |
||
| 9041 | $current_dir = str_replace('\\', '/', $current_dir); |
||
| 9042 | $file_path = realpath($current_dir.$doc_info[0]); |
||
| 9043 | $file_path = str_replace('\\', '/', $file_path); |
||
| 9044 | if (false !== strstr($file_path, $main_path)) { |
||
| 9045 | // The calculated real path is really inside Chamilo's root path. |
||
| 9046 | // Reduce file path to what's under the DocumentRoot. |
||
| 9047 | |||
| 9048 | $file_path = substr($file_path, strlen($root_path)); |
||
| 9049 | $file_path_dest = $file_path; |
||
| 9050 | |||
| 9051 | // File path is courses/CHAMILO/document/.... |
||
| 9052 | $info_file_path = explode('/', $file_path); |
||
| 9053 | if ('courses' == $info_file_path[0]) { |
||
| 9054 | // Add character "/" in file path. |
||
| 9055 | $file_path_dest = 'document/'.$file_path; |
||
| 9056 | } |
||
| 9057 | $zip_files_abs[] = $file_path; |
||
| 9058 | |||
| 9059 | $link_updates[$my_file_path][] = [ |
||
| 9060 | 'orig' => $doc_info[0], |
||
| 9061 | 'dest' => $file_path_dest, |
||
| 9062 | ]; |
||
| 9063 | $my_dep_file->setAttribute('href', 'document/'.$file_path); |
||
| 9064 | $my_dep->setAttribute('xml:base', ''); |
||
| 9065 | } |
||
| 9066 | } else { |
||
| 9067 | $zip_files[] = $my_sub_dir.'/'.$doc_info[0]; |
||
| 9068 | $my_dep_file->setAttribute('href', $doc_info[0]); |
||
| 9069 | $my_dep->setAttribute('xml:base', $my_xml_sub_dir); |
||
| 9070 | } |
||
| 9071 | break; |
||
| 9072 | default: |
||
| 9073 | $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/ |
||
| 9074 | $my_dep->setAttribute('xml:base', ''); |
||
| 9075 | break; |
||
| 9076 | } |
||
| 9077 | } |
||
| 9078 | $my_dep->appendChild($my_dep_file); |
||
| 9079 | $resources->appendChild($my_dep); |
||
| 9080 | $dependency = $xmldoc->createElement('dependency'); |
||
| 9081 | $dependency->setAttribute('identifierref', $res_id); |
||
| 9082 | $my_resource->appendChild($dependency); |
||
| 9083 | $i++; |
||
| 9084 | } |
||
| 9085 | $resources->appendChild($my_resource); |
||
| 9086 | $zip_files[] = $my_file_path; |
||
| 9087 | break; |
||
| 9088 | default: |
||
| 9089 | // Get the path of the file(s) from the course directory root |
||
| 9090 | $my_file_path = 'non_exportable.html'; |
||
| 9091 | //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8'); |
||
| 9092 | $my_xml_file_path = $my_file_path; |
||
| 9093 | $my_sub_dir = dirname($my_file_path); |
||
| 9094 | $my_sub_dir = str_replace('\\', '/', $my_sub_dir); |
||
| 9095 | //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8'); |
||
| 9096 | $my_xml_sub_dir = $my_sub_dir; |
||
| 9097 | // Give a <resource> child to the <resources> element. |
||
| 9098 | $my_resource = $xmldoc->createElement('resource'); |
||
| 9099 | $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id()); |
||
| 9100 | $my_resource->setAttribute('type', 'webcontent'); |
||
| 9101 | $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path); |
||
| 9102 | // adlcp:scormtype can be either 'sco' or 'asset'. |
||
| 9103 | $my_resource->setAttribute('adlcp:scormtype', 'asset'); |
||
| 9104 | // xml:base is the base directory to find the files declared in this resource. |
||
| 9105 | $my_resource->setAttribute('xml:base', ''); |
||
| 9106 | // Give a <file> child to the <resource> element. |
||
| 9107 | $my_file = $xmldoc->createElement('file'); |
||
| 9108 | $my_file->setAttribute('href', 'document/'.$my_xml_file_path); |
||
| 9109 | $my_resource->appendChild($my_file); |
||
| 9110 | $resources->appendChild($my_resource); |
||
| 9111 | break; |
||
| 9112 | } |
||
| 9113 | } |
||
| 9114 | } |
||
| 9115 | $organizations->appendChild($organization); |
||
| 9116 | $root->appendChild($organizations); |
||
| 9117 | $root->appendChild($resources); |
||
| 9118 | $xmldoc->appendChild($root); |
||
| 9119 | |||
| 9120 | $copyAll = api_get_configuration_value('add_all_files_in_lp_export'); |
||
| 9121 | |||
| 9122 | // then add the file to the zip, then destroy the file (this is done automatically). |
||
| 9123 | // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138 |
||
| 9124 | foreach ($zip_files as $file_path) { |
||
| 9125 | if (empty($file_path)) { |
||
| 9126 | continue; |
||
| 9127 | } |
||
| 9128 | |||
| 9129 | $filePath = $sys_course_path.$_course['path'].'/'.$file_path; |
||
| 9130 | $dest_file = $archivePath.$temp_dir_short.'/'.$file_path; |
||
| 9131 | |||
| 9132 | if (!empty($path_to_remove) && !empty($path_to_replace)) { |
||
| 9133 | $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file); |
||
| 9134 | } |
||
| 9135 | |||
| 9136 | $this->create_path($dest_file); |
||
| 9137 | @copy($filePath, $dest_file); |
||
| 9138 | |||
| 9139 | // Check if the file needs a link update. |
||
| 9140 | if (in_array($file_path, array_keys($link_updates))) { |
||
| 9141 | $string = file_get_contents($dest_file); |
||
| 9142 | unlink($dest_file); |
||
| 9143 | foreach ($link_updates[$file_path] as $old_new) { |
||
| 9144 | // This is an ugly hack that allows .flv files to be found by the flv player that |
||
| 9145 | // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs |
||
| 9146 | // to find the flv to play in document/main/, so we replace main/ in the flv path by |
||
| 9147 | // ../../.. to return from inc/lib/flv_player to the document/main path. |
||
| 9148 | if ('flv' === substr($old_new['dest'], -3) && |
||
| 9149 | 'main/' === substr($old_new['dest'], 0, 5) |
||
| 9150 | ) { |
||
| 9151 | $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']); |
||
| 9152 | } elseif ('flv' === substr($old_new['dest'], -3) && |
||
| 9153 | 'video/' === substr($old_new['dest'], 0, 6) |
||
| 9154 | ) { |
||
| 9155 | $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']); |
||
| 9156 | } |
||
| 9157 | |||
| 9158 | // Fix to avoid problems with default_course_document |
||
| 9159 | if (false === strpos('main/default_course_document', $old_new['dest'])) { |
||
| 9160 | $newDestination = $old_new['dest']; |
||
| 9161 | if (isset($old_new['replace']) && !empty($old_new['replace'])) { |
||
| 9162 | $newDestination = $old_new['replace']; |
||
| 9163 | } |
||
| 9164 | } else { |
||
| 9165 | $newDestination = str_replace('document/', '', $old_new['dest']); |
||
| 9166 | } |
||
| 9167 | $string = str_replace($old_new['orig'], $newDestination, $string); |
||
| 9168 | |||
| 9169 | // Add files inside the HTMLs |
||
| 9170 | $new_path = str_replace(api_get_path(REL_COURSE_PATH), '', $old_new['orig']); |
||
| 9171 | $destinationFile = $archivePath.$temp_dir_short.'/'.$old_new['dest']; |
||
| 9172 | if (file_exists($sys_course_path.$new_path) && is_file($sys_course_path.$new_path)) { |
||
| 9173 | copy( |
||
| 9174 | $sys_course_path.$new_path, |
||
| 9175 | $destinationFile |
||
| 9176 | ); |
||
| 9177 | } |
||
| 9178 | } |
||
| 9179 | file_put_contents($dest_file, $string); |
||
| 9180 | } |
||
| 9181 | |||
| 9182 | if (file_exists($filePath) && $copyAll) { |
||
| 9183 | $extension = $this->get_extension($filePath); |
||
| 9184 | if (in_array($extension, ['html', 'html'])) { |
||
| 9185 | $containerOrigin = dirname($filePath); |
||
| 9186 | $containerDestination = dirname($dest_file); |
||
| 9187 | |||
| 9188 | $finder = new Finder(); |
||
| 9189 | $finder->files()->in($containerOrigin) |
||
| 9190 | ->notName('*_DELETED_*') |
||
| 9191 | ->exclude('share_folder') |
||
| 9192 | ->exclude('chat_files') |
||
| 9193 | ->exclude('certificates') |
||
| 9194 | ; |
||
| 9195 | |||
| 9196 | if (is_dir($containerOrigin) && |
||
| 9197 | is_dir($containerDestination) |
||
| 9198 | ) { |
||
| 9199 | $fs = new Filesystem(); |
||
| 9200 | $fs->mirror( |
||
| 9201 | $containerOrigin, |
||
| 9202 | $containerDestination, |
||
| 9203 | $finder |
||
| 9204 | ); |
||
| 9205 | } |
||
| 9206 | } |
||
| 9207 | } |
||
| 9208 | } |
||
| 9209 | |||
| 9210 | foreach ($zip_files_abs as $file_path) { |
||
| 9211 | if (empty($file_path)) { |
||
| 9212 | continue; |
||
| 9213 | } |
||
| 9214 | |||
| 9215 | if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) { |
||
| 9216 | continue; |
||
| 9217 | } |
||
| 9218 | |||
| 9219 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$file_path; |
||
| 9220 | if (false !== strstr($file_path, 'upload/users')) { |
||
| 9221 | $pos = strpos($file_path, 'my_files/'); |
||
| 9222 | if (false !== $pos) { |
||
| 9223 | $onlyDirectory = str_replace( |
||
| 9224 | 'upload/users/', |
||
| 9225 | '', |
||
| 9226 | substr($file_path, $pos, strlen($file_path)) |
||
| 9227 | ); |
||
| 9228 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$onlyDirectory; |
||
| 9229 | } |
||
| 9230 | } |
||
| 9231 | |||
| 9232 | if (false !== strstr($file_path, 'default_course_document/')) { |
||
| 9233 | $replace = str_replace('/main', '', $file_path); |
||
| 9234 | $dest_file = $archivePath.$temp_dir_short.'/document/'.$replace; |
||
| 9235 | } |
||
| 9236 | |||
| 9237 | if (empty($dest_file)) { |
||
| 9238 | continue; |
||
| 9239 | } |
||
| 9240 | |||
| 9241 | $this->create_path($dest_file); |
||
| 9242 | copy($main_path.$file_path, $dest_file); |
||
| 9243 | // Check if the file needs a link update. |
||
| 9244 | if (in_array($file_path, array_keys($link_updates))) { |
||
| 9245 | $string = file_get_contents($dest_file); |
||
| 9246 | unlink($dest_file); |
||
| 9247 | foreach ($link_updates[$file_path] as $old_new) { |
||
| 9248 | // This is an ugly hack that allows .flv files to be found by the flv player that |
||
| 9249 | // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs |
||
| 9250 | // to find the flv to play in document/main/, so we replace main/ in the flv path by |
||
| 9251 | // ../../.. to return from inc/lib/flv_player to the document/main path. |
||
| 9252 | if ('flv' == substr($old_new['dest'], -3) && |
||
| 9253 | 'main/' == substr($old_new['dest'], 0, 5) |
||
| 9254 | ) { |
||
| 9255 | $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']); |
||
| 9256 | } |
||
| 9257 | $string = str_replace($old_new['orig'], $old_new['dest'], $string); |
||
| 9258 | } |
||
| 9259 | file_put_contents($dest_file, $string); |
||
| 9260 | } |
||
| 9261 | } |
||
| 9262 | |||
| 9263 | if (is_array($links_to_create)) { |
||
| 9264 | foreach ($links_to_create as $file => $link) { |
||
| 9265 | $content = '<!DOCTYPE html><head> |
||
| 9266 | <meta charset="'.api_get_language_isocode().'" /> |
||
| 9267 | <title>'.$link['title'].'</title> |
||
| 9268 | </head> |
||
| 9269 | <body dir="'.api_get_text_direction().'"> |
||
| 9270 | <div style="text-align:center"> |
||
| 9271 | <a href="'.$link['url'].'">'.$link['title'].'</a></div> |
||
| 9272 | </body> |
||
| 9273 | </html>'; |
||
| 9274 | file_put_contents($archivePath.$temp_dir_short.'/'.$file, $content); |
||
| 9275 | } |
||
| 9276 | } |
||
| 9277 | |||
| 9278 | // Add non exportable message explanation. |
||
| 9279 | $lang_not_exportable = get_lang('This learning object or activity is not SCORM compliant. That\'s why it is not exportable.'); |
||
| 9280 | $file_content = '<!DOCTYPE html><head> |
||
| 9281 | <meta charset="'.api_get_language_isocode().'" /> |
||
| 9282 | <title>'.$lang_not_exportable.'</title> |
||
| 9283 | <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" /> |
||
| 9284 | </head> |
||
| 9285 | <body dir="'.api_get_text_direction().'">'; |
||
| 9286 | $file_content .= |
||
| 9287 | <<<EOD |
||
| 9288 | <style> |
||
| 9289 | .error-message { |
||
| 9290 | font-family: arial, verdana, helvetica, sans-serif; |
||
| 9291 | border-width: 1px; |
||
| 9292 | border-style: solid; |
||
| 9293 | left: 50%; |
||
| 9294 | margin: 10px auto; |
||
| 9295 | min-height: 30px; |
||
| 9296 | padding: 5px; |
||
| 9297 | right: 50%; |
||
| 9298 | width: 500px; |
||
| 9299 | background-color: #FFD1D1; |
||
| 9300 | border-color: #FF0000; |
||
| 9301 | color: #000; |
||
| 9302 | } |
||
| 9303 | </style> |
||
| 9304 | <body> |
||
| 9305 | <div class="error-message"> |
||
| 9306 | $lang_not_exportable |
||
| 9307 | </div> |
||
| 9308 | </body> |
||
| 9309 | </html> |
||
| 9310 | EOD; |
||
| 9311 | if (!is_dir($archivePath.$temp_dir_short.'/document')) { |
||
| 9312 | @mkdir($archivePath.$temp_dir_short.'/document', api_get_permissions_for_new_directories()); |
||
| 9313 | } |
||
| 9314 | file_put_contents($archivePath.$temp_dir_short.'/document/non_exportable.html', $file_content); |
||
| 9315 | |||
| 9316 | // Add the extra files that go along with a SCORM package. |
||
| 9317 | $main_code_path = api_get_path(SYS_CODE_PATH).'lp/packaging/'; |
||
| 9318 | |||
| 9319 | $fs = new Filesystem(); |
||
| 9320 | $fs->mirror($main_code_path, $archivePath.$temp_dir_short); |
||
| 9321 | |||
| 9322 | // Finalize the imsmanifest structure, add to the zip, then return the zip. |
||
| 9323 | $manifest = @$xmldoc->saveXML(); |
||
| 9324 | $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now. |
||
| 9325 | file_put_contents($archivePath.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest); |
||
| 9326 | $zip_folder->add( |
||
| 9327 | $archivePath.'/'.$temp_dir_short, |
||
| 9328 | PCLZIP_OPT_REMOVE_PATH, |
||
| 9329 | $archivePath.'/'.$temp_dir_short.'/' |
||
| 9330 | ); |
||
| 9331 | |||
| 9332 | // Clean possible temporary files. |
||
| 9333 | foreach ($files_cleanup as $file) { |
||
| 9334 | $res = unlink($file); |
||
| 9335 | if (false === $res) { |
||
| 9336 | error_log( |
||
| 9337 | 'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__, |
||
| 9338 | 0 |
||
| 9339 | ); |
||
| 9340 | } |
||
| 9341 | } |
||
| 9342 | $name = api_replace_dangerous_char($this->get_name()).'.zip'; |
||
| 9343 | DocumentManager::file_send_for_download($temp_zip_file, true, $name); |
||
| 9344 | } |
||
| 9345 | |||
| 9346 | /** |
||
| 9347 | * @param int $lp_id |
||
| 9348 | * |
||
| 9349 | * @return bool |
||
| 9350 | */ |
||
| 9351 | public function scorm_export_to_pdf($lp_id) |
||
| 9352 | { |
||
| 9353 | $lp_id = (int) $lp_id; |
||
| 9354 | $files_to_export = []; |
||
| 9355 | |||
| 9356 | $sessionId = api_get_session_id(); |
||
| 9357 | $course_data = api_get_course_info($this->cc); |
||
| 9358 | |||
| 9359 | if (!empty($course_data)) { |
||
| 9360 | $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path; |
||
| 9361 | $list = self::get_flat_ordered_items_list($lp_id); |
||
| 9362 | if (!empty($list)) { |
||
| 9363 | foreach ($list as $item_id) { |
||
| 9364 | $item = $this->items[$item_id]; |
||
| 9365 | switch ($item->type) { |
||
| 9366 | case 'document': |
||
| 9367 | // Getting documents from a LP with chamilo documents |
||
| 9368 | $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc); |
||
| 9369 | // Try loading document from the base course. |
||
| 9370 | if (empty($file_data) && !empty($sessionId)) { |
||
| 9371 | $file_data = DocumentManager::get_document_data_by_id( |
||
| 9372 | $item->path, |
||
| 9373 | $this->cc, |
||
| 9374 | false, |
||
| 9375 | 0 |
||
| 9376 | ); |
||
| 9377 | } |
||
| 9378 | $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path']; |
||
| 9379 | if (file_exists($file_path)) { |
||
| 9380 | $files_to_export[] = [ |
||
| 9381 | 'title' => $item->get_title(), |
||
| 9382 | 'path' => $file_path, |
||
| 9383 | ]; |
||
| 9384 | } |
||
| 9385 | break; |
||
| 9386 | case 'asset': //commes from a scorm package generated by chamilo |
||
| 9387 | case 'sco': |
||
| 9388 | $file_path = $scorm_path.'/'.$item->path; |
||
| 9389 | if (file_exists($file_path)) { |
||
| 9390 | $files_to_export[] = [ |
||
| 9391 | 'title' => $item->get_title(), |
||
| 9392 | 'path' => $file_path, |
||
| 9393 | ]; |
||
| 9394 | } |
||
| 9395 | break; |
||
| 9396 | case 'dir': |
||
| 9397 | $files_to_export[] = [ |
||
| 9398 | 'title' => $item->get_title(), |
||
| 9399 | 'path' => null, |
||
| 9400 | ]; |
||
| 9401 | break; |
||
| 9402 | } |
||
| 9403 | } |
||
| 9404 | } |
||
| 9405 | |||
| 9406 | $pdf = new PDF(); |
||
| 9407 | $result = $pdf->html_to_pdf( |
||
| 9408 | $files_to_export, |
||
| 9409 | $this->name, |
||
| 9410 | $this->cc, |
||
| 9411 | true, |
||
| 9412 | true, |
||
| 9413 | true, |
||
| 9414 | $this->get_name() |
||
| 9415 | ); |
||
| 9416 | |||
| 9417 | return $result; |
||
| 9418 | } |
||
| 9419 | |||
| 9420 | return false; |
||
| 9421 | } |
||
| 9422 | |||
| 9423 | /** |
||
| 9424 | * Temp function to be moved in main_api or the best place around for this. |
||
| 9425 | * Creates a file path if it doesn't exist. |
||
| 9426 | * |
||
| 9427 | * @param string $path |
||
| 9428 | */ |
||
| 9429 | public function create_path($path) |
||
| 9430 | { |
||
| 9431 | $path_bits = explode('/', dirname($path)); |
||
| 9432 | |||
| 9433 | // IS_WINDOWS_OS has been defined in main_api.lib.php |
||
| 9434 | $path_built = IS_WINDOWS_OS ? '' : '/'; |
||
| 9435 | foreach ($path_bits as $bit) { |
||
| 9436 | if (!empty($bit)) { |
||
| 9437 | $new_path = $path_built.$bit; |
||
| 9438 | if (is_dir($new_path)) { |
||
| 9439 | $path_built = $new_path.'/'; |
||
| 9440 | } else { |
||
| 9441 | mkdir($new_path, api_get_permissions_for_new_directories()); |
||
| 9442 | $path_built = $new_path.'/'; |
||
| 9443 | } |
||
| 9444 | } |
||
| 9445 | } |
||
| 9446 | } |
||
| 9447 | |||
| 9448 | /** |
||
| 9449 | * @param int $lp_id |
||
| 9450 | * @param string $status |
||
| 9451 | */ |
||
| 9452 | public function set_autolaunch($lp_id, $status) |
||
| 9479 | } |
||
| 9480 | } |
||
| 9481 | |||
| 9482 | /** |
||
| 9483 | * Gets previous_item_id for the next element of the lp_item table. |
||
| 9484 | * |
||
| 9485 | * @author Isaac flores paz |
||
| 9486 | * |
||
| 9487 | * @return int Previous item ID |
||
| 9488 | */ |
||
| 9489 | public function select_previous_item_id() |
||
| 9490 | { |
||
| 9491 | $course_id = api_get_course_int_id(); |
||
| 9492 | $table_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 9493 | |||
| 9494 | // Get the max order of the items |
||
| 9495 | $sql = "SELECT max(display_order) AS display_order FROM $table_lp_item |
||
| 9496 | WHERE c_id = $course_id AND lp_id = ".$this->lp_id; |
||
| 9497 | $rs_max_order = Database::query($sql); |
||
| 9498 | $row_max_order = Database::fetch_object($rs_max_order); |
||
| 9499 | $max_order = $row_max_order->display_order; |
||
| 9500 | // Get the previous item ID |
||
| 9501 | $sql = "SELECT iid as previous FROM $table_lp_item |
||
| 9502 | WHERE |
||
| 9503 | c_id = $course_id AND |
||
| 9504 | lp_id = ".$this->lp_id." AND |
||
| 9505 | display_order = '$max_order' "; |
||
| 9506 | $rs_max = Database::query($sql); |
||
| 9507 | $row_max = Database::fetch_object($rs_max); |
||
| 9508 | |||
| 9509 | // Return the previous item ID |
||
| 9510 | return $row_max->previous; |
||
| 9511 | } |
||
| 9512 | |||
| 9513 | /** |
||
| 9514 | * Copies an LP. |
||
| 9515 | */ |
||
| 9516 | public function copy() |
||
| 9517 | { |
||
| 9518 | // Course builder |
||
| 9519 | $cb = new CourseBuilder(); |
||
| 9520 | |||
| 9521 | //Setting tools that will be copied |
||
| 9522 | $cb->set_tools_to_build(['learnpaths']); |
||
| 9523 | |||
| 9524 | //Setting elements that will be copied |
||
| 9525 | $cb->set_tools_specific_id_list( |
||
| 9526 | ['learnpaths' => [$this->lp_id]] |
||
| 9527 | ); |
||
| 9528 | |||
| 9529 | $course = $cb->build(); |
||
| 9530 | |||
| 9531 | //Course restorer |
||
| 9532 | $course_restorer = new CourseRestorer($course); |
||
| 9533 | $course_restorer->set_add_text_in_items(true); |
||
| 9534 | $course_restorer->set_tool_copy_settings( |
||
| 9535 | ['learnpaths' => ['reset_dates' => true]] |
||
| 9536 | ); |
||
| 9537 | $course_restorer->restore( |
||
| 9538 | api_get_course_id(), |
||
| 9539 | api_get_session_id(), |
||
| 9540 | false, |
||
| 9541 | false |
||
| 9542 | ); |
||
| 9543 | } |
||
| 9544 | |||
| 9545 | /** |
||
| 9546 | * Verify document size. |
||
| 9547 | * |
||
| 9548 | * @param string $s |
||
| 9549 | * |
||
| 9550 | * @return bool |
||
| 9551 | */ |
||
| 9552 | public static function verify_document_size($s) |
||
| 9553 | { |
||
| 9554 | $post_max = ini_get('post_max_size'); |
||
| 9555 | if ('M' == substr($post_max, -1, 1)) { |
||
| 9556 | $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024; |
||
| 9557 | } elseif ('G' == substr($post_max, -1, 1)) { |
||
| 9558 | $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024; |
||
| 9559 | } |
||
| 9560 | $upl_max = ini_get('upload_max_filesize'); |
||
| 9561 | if ('M' == substr($upl_max, -1, 1)) { |
||
| 9562 | $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024; |
||
| 9563 | } elseif ('G' == substr($upl_max, -1, 1)) { |
||
| 9564 | $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024; |
||
| 9565 | } |
||
| 9566 | |||
| 9567 | $repo = Container::getDocumentRepository(); |
||
| 9568 | $documents_total_space = $repo->getTotalSpace(api_get_course_int_id()); |
||
| 9569 | |||
| 9570 | $course_max_space = DocumentManager::get_course_quota(); |
||
| 9571 | $total_size = filesize($s) + $documents_total_space; |
||
| 9572 | if (filesize($s) > $post_max || filesize($s) > $upl_max || $total_size > $course_max_space) { |
||
| 9573 | return true; |
||
| 9574 | } |
||
| 9575 | |||
| 9576 | return false; |
||
| 9577 | } |
||
| 9578 | |||
| 9579 | /** |
||
| 9580 | * Clear LP prerequisites. |
||
| 9581 | */ |
||
| 9582 | public function clearPrerequisites() |
||
| 9583 | { |
||
| 9584 | $course_id = $this->get_course_int_id(); |
||
| 9585 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 9586 | $lp_id = $this->get_id(); |
||
| 9587 | // Cleaning prerequisites |
||
| 9588 | $sql = "UPDATE $tbl_lp_item SET prerequisite = '' |
||
| 9589 | WHERE lp_id = $lp_id"; |
||
| 9590 | Database::query($sql); |
||
| 9591 | |||
| 9592 | // Cleaning mastery score for exercises |
||
| 9593 | $sql = "UPDATE $tbl_lp_item SET mastery_score = '' |
||
| 9594 | WHERE lp_id = $lp_id AND item_type = 'quiz'"; |
||
| 9595 | Database::query($sql); |
||
| 9596 | } |
||
| 9597 | |||
| 9598 | public function set_previous_step_as_prerequisite_for_all_items() |
||
| 9599 | { |
||
| 9600 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 9601 | $course_id = $this->get_course_int_id(); |
||
| 9602 | $lp_id = $this->get_id(); |
||
| 9603 | |||
| 9604 | if (!empty($this->items)) { |
||
| 9605 | $previous_item_id = null; |
||
| 9606 | $previous_item_max = 0; |
||
| 9607 | $previous_item_type = null; |
||
| 9608 | $last_item_not_dir = null; |
||
| 9609 | $last_item_not_dir_type = null; |
||
| 9610 | $last_item_not_dir_max = null; |
||
| 9611 | |||
| 9612 | foreach ($this->ordered_items as $itemId) { |
||
| 9613 | $item = $this->getItem($itemId); |
||
| 9614 | // if there was a previous item... (otherwise jump to set it) |
||
| 9615 | if (!empty($previous_item_id)) { |
||
| 9616 | $current_item_id = $item->get_id(); //save current id |
||
| 9617 | if ('dir' != $item->get_type()) { |
||
| 9618 | // Current item is not a folder, so it qualifies to get a prerequisites |
||
| 9619 | if ('quiz' == $last_item_not_dir_type) { |
||
| 9620 | // if previous is quiz, mark its max score as default score to be achieved |
||
| 9621 | $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_dir_max' |
||
| 9622 | WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $last_item_not_dir"; |
||
| 9623 | Database::query($sql); |
||
| 9624 | } |
||
| 9625 | // now simply update the prerequisite to set it to the last non-chapter item |
||
| 9626 | $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_dir' |
||
| 9627 | WHERE c_id = $course_id AND lp_id = $lp_id AND iid = $current_item_id"; |
||
| 9628 | Database::query($sql); |
||
| 9629 | // record item as 'non-chapter' reference |
||
| 9630 | $last_item_not_dir = $item->get_id(); |
||
| 9631 | $last_item_not_dir_type = $item->get_type(); |
||
| 9632 | $last_item_not_dir_max = $item->get_max(); |
||
| 9633 | } |
||
| 9634 | } else { |
||
| 9635 | if ('dir' != $item->get_type()) { |
||
| 9636 | // Current item is not a folder (but it is the first item) so record as last "non-chapter" item |
||
| 9637 | $last_item_not_dir = $item->get_id(); |
||
| 9638 | $last_item_not_dir_type = $item->get_type(); |
||
| 9639 | $last_item_not_dir_max = $item->get_max(); |
||
| 9640 | } |
||
| 9641 | } |
||
| 9642 | // Saving the item as "previous item" for the next loop |
||
| 9643 | $previous_item_id = $item->get_id(); |
||
| 9644 | $previous_item_max = $item->get_max(); |
||
| 9645 | $previous_item_type = $item->get_type(); |
||
| 9646 | } |
||
| 9647 | } |
||
| 9648 | } |
||
| 9649 | |||
| 9650 | /** |
||
| 9651 | * @param array $params |
||
| 9652 | * |
||
| 9653 | * @return int |
||
| 9654 | */ |
||
| 9655 | public static function createCategory($params) |
||
| 9656 | { |
||
| 9657 | $courseEntity = api_get_course_entity(api_get_course_int_id()); |
||
| 9658 | |||
| 9659 | $item = new CLpCategory(); |
||
| 9660 | $item |
||
| 9661 | ->setName($params['name']) |
||
| 9662 | ->setParent($courseEntity) |
||
| 9663 | ->addCourseLink($courseEntity, api_get_session_entity()) |
||
| 9664 | ; |
||
| 9665 | |||
| 9666 | $repo = Container::getLpCategoryRepository(); |
||
| 9667 | $repo->create($item); |
||
| 9668 | |||
| 9669 | return $item->getIid(); |
||
| 9670 | } |
||
| 9671 | |||
| 9672 | /** |
||
| 9673 | * @param array $params |
||
| 9674 | */ |
||
| 9675 | public static function updateCategory($params) |
||
| 9676 | { |
||
| 9677 | $em = Database::getManager(); |
||
| 9678 | /** @var CLpCategory $item */ |
||
| 9679 | $item = $em->find('ChamiloCourseBundle:CLpCategory', $params['id']); |
||
| 9680 | if ($item) { |
||
| 9681 | $item->setName($params['name']); |
||
| 9682 | $em->persist($item); |
||
| 9683 | $em->flush(); |
||
| 9684 | } |
||
| 9685 | } |
||
| 9686 | |||
| 9687 | /** |
||
| 9688 | * @param int $id |
||
| 9689 | */ |
||
| 9690 | public static function moveUpCategory($id) |
||
| 9691 | { |
||
| 9692 | $id = (int) $id; |
||
| 9693 | $em = Database::getManager(); |
||
| 9694 | /** @var CLpCategory $item */ |
||
| 9695 | $item = $em->find('ChamiloCourseBundle:CLpCategory', $id); |
||
| 9696 | if ($item) { |
||
| 9697 | $position = $item->getPosition() - 1; |
||
| 9698 | $item->setPosition($position); |
||
| 9699 | $em->persist($item); |
||
| 9700 | $em->flush(); |
||
| 9701 | } |
||
| 9702 | } |
||
| 9703 | |||
| 9704 | /** |
||
| 9705 | * @param int $id |
||
| 9706 | */ |
||
| 9707 | public static function moveDownCategory($id) |
||
| 9708 | { |
||
| 9709 | $id = (int) $id; |
||
| 9710 | $em = Database::getManager(); |
||
| 9711 | /** @var CLpCategory $item */ |
||
| 9712 | $item = $em->find('ChamiloCourseBundle:CLpCategory', $id); |
||
| 9713 | if ($item) { |
||
| 9714 | $position = $item->getPosition() + 1; |
||
| 9715 | $item->setPosition($position); |
||
| 9716 | $em->persist($item); |
||
| 9717 | $em->flush(); |
||
| 9718 | } |
||
| 9719 | } |
||
| 9720 | |||
| 9721 | public static function getLpList($courseId) |
||
| 9722 | { |
||
| 9723 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 9724 | $courseId = (int) $courseId; |
||
| 9725 | |||
| 9726 | $sql = "SELECT * FROM $table WHERE c_id = $courseId"; |
||
| 9727 | $result = Database::query($sql); |
||
| 9728 | |||
| 9729 | return Database::store_result($result, 'ASSOC'); |
||
| 9730 | } |
||
| 9731 | |||
| 9732 | /** |
||
| 9733 | * @param int $courseId |
||
| 9734 | * |
||
| 9735 | * @return int|mixed |
||
| 9736 | */ |
||
| 9737 | public static function getCountCategories($courseId) |
||
| 9738 | { |
||
| 9739 | if (empty($courseId)) { |
||
| 9740 | return 0; |
||
| 9741 | } |
||
| 9742 | $repo = Container::getLpCategoryRepository(); |
||
| 9743 | $qb = $repo->getResourcesByCourse(api_get_course_entity($courseId)); |
||
| 9744 | $qb->addSelect('count(resource)'); |
||
| 9745 | |||
| 9746 | return $qb->getSingleScalarResult(); |
||
| 9747 | } |
||
| 9748 | |||
| 9749 | /** |
||
| 9750 | * @param int $courseId |
||
| 9751 | * |
||
| 9752 | * @return CLpCategory[] |
||
| 9753 | */ |
||
| 9754 | public static function getCategories($courseId) |
||
| 9755 | { |
||
| 9756 | $em = Database::getManager(); |
||
| 9757 | |||
| 9758 | // Using doctrine extensions |
||
| 9759 | $repo = Container::getLpCategoryRepository(); |
||
| 9760 | $qb = $repo->getResourcesByCourse(api_get_course_entity($courseId)); |
||
| 9761 | |||
| 9762 | return $qb->getQuery()->getResult(); |
||
| 9763 | |||
| 9764 | //return $repo->getBySortableGroupsQuery(['cId' => $courseId])->getResult(); |
||
| 9765 | } |
||
| 9766 | |||
| 9767 | public static function getCategorySessionId($id) |
||
| 9768 | { |
||
| 9769 | if (false === api_get_configuration_value('allow_session_lp_category')) { |
||
| 9770 | return 0; |
||
| 9771 | } |
||
| 9772 | |||
| 9773 | $table = Database::get_course_table(TABLE_LP_CATEGORY); |
||
| 9774 | $id = (int) $id; |
||
| 9775 | |||
| 9776 | $sql = "SELECT session_id FROM $table WHERE iid = $id"; |
||
| 9777 | $result = Database::query($sql); |
||
| 9778 | $result = Database::fetch_array($result, 'ASSOC'); |
||
| 9779 | |||
| 9780 | if ($result) { |
||
| 9781 | return (int) $result['session_id']; |
||
| 9782 | } |
||
| 9783 | |||
| 9784 | return 0; |
||
| 9785 | } |
||
| 9786 | |||
| 9787 | /** |
||
| 9788 | * @param int $id |
||
| 9789 | */ |
||
| 9790 | public static function deleteCategory($id): bool |
||
| 9791 | { |
||
| 9792 | $repo = Container::getLpCategoryRepository(); |
||
| 9793 | /** @var CLpCategory $category */ |
||
| 9794 | $category = $repo->find($id); |
||
| 9795 | if ($category) { |
||
| 9796 | $em = Database::getManager(); |
||
| 9797 | $lps = $category->getLps(); |
||
| 9798 | |||
| 9799 | foreach ($lps as $lp) { |
||
| 9800 | $lp->setCategory(null); |
||
| 9801 | $em->persist($lp); |
||
| 9802 | } |
||
| 9803 | |||
| 9804 | // Removing category. |
||
| 9805 | $em->remove($category); |
||
| 9806 | $em->flush(); |
||
| 9807 | |||
| 9808 | return true; |
||
| 9809 | } |
||
| 9810 | |||
| 9811 | return false; |
||
| 9812 | } |
||
| 9813 | |||
| 9814 | /** |
||
| 9815 | * @param int $courseId |
||
| 9816 | * @param bool $addSelectOption |
||
| 9817 | * |
||
| 9818 | * @return mixed |
||
| 9819 | */ |
||
| 9820 | public static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false) |
||
| 9821 | { |
||
| 9822 | $repo = Container::getLpCategoryRepository(); |
||
| 9823 | $items = $repo->getResourcesByCourse(api_get_course_entity($courseId)); |
||
| 9824 | $cats = []; |
||
| 9825 | if ($addSelectOption) { |
||
| 9826 | $cats = [get_lang('Select a category')]; |
||
| 9827 | } |
||
| 9828 | |||
| 9829 | if (!empty($items)) { |
||
| 9830 | foreach ($items as $cat) { |
||
| 9831 | $cats[$cat->getIid()] = $cat->getName(); |
||
| 9832 | } |
||
| 9833 | } |
||
| 9834 | |||
| 9835 | return $cats; |
||
| 9836 | } |
||
| 9837 | |||
| 9838 | /** |
||
| 9839 | * @param string $courseCode |
||
| 9840 | * @param int $lpId |
||
| 9841 | * @param int $user_id |
||
| 9842 | * |
||
| 9843 | * @return learnpath |
||
| 9844 | */ |
||
| 9845 | public static function getLpFromSession($courseCode, $lpId, $user_id) |
||
| 9846 | { |
||
| 9847 | $debug = 0; |
||
| 9848 | $learnPath = null; |
||
| 9849 | $lpObject = Session::read('lpobject'); |
||
| 9850 | |||
| 9851 | if (null !== $lpObject) { |
||
| 9852 | $learnPath = UnserializeApi::unserialize('lp', $lpObject); |
||
| 9853 | if ($debug) { |
||
| 9854 | error_log('getLpFromSession: unserialize'); |
||
| 9855 | error_log('------getLpFromSession------'); |
||
| 9856 | error_log('------unserialize------'); |
||
| 9857 | error_log("lp_view_session_id: ".$learnPath->lp_view_session_id); |
||
| 9858 | error_log("api_get_sessionid: ".api_get_session_id()); |
||
| 9859 | } |
||
| 9860 | } |
||
| 9861 | |||
| 9862 | if (!is_object($learnPath)) { |
||
| 9863 | $repo = Container::getLpRepository(); |
||
| 9864 | $lp = $repo->find($lpId); |
||
| 9865 | $learnPath = new learnpath($lp, api_get_course_info($courseCode), $user_id); |
||
| 9866 | if ($debug) { |
||
| 9867 | error_log('------getLpFromSession------'); |
||
| 9868 | error_log('getLpFromSession: create new learnpath'); |
||
| 9869 | error_log("create new LP with $courseCode - $lpId - $user_id"); |
||
| 9870 | error_log("lp_view_session_id: ".$learnPath->lp_view_session_id); |
||
| 9871 | error_log("api_get_sessionid: ".api_get_session_id()); |
||
| 9872 | } |
||
| 9873 | } |
||
| 9874 | |||
| 9875 | return $learnPath; |
||
| 9876 | } |
||
| 9877 | |||
| 9878 | /** |
||
| 9879 | * @param int $itemId |
||
| 9880 | * |
||
| 9881 | * @return learnpathItem|false |
||
| 9882 | */ |
||
| 9883 | public function getItem($itemId) |
||
| 9884 | { |
||
| 9885 | if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) { |
||
| 9886 | return $this->items[$itemId]; |
||
| 9887 | } |
||
| 9888 | |||
| 9889 | return false; |
||
| 9890 | } |
||
| 9891 | |||
| 9892 | /** |
||
| 9893 | * @return int |
||
| 9894 | */ |
||
| 9895 | public function getCurrentAttempt() |
||
| 9896 | { |
||
| 9897 | $attempt = $this->getItem($this->get_current_item_id()); |
||
| 9898 | if ($attempt) { |
||
| 9899 | return $attempt->get_attempt_id(); |
||
| 9900 | } |
||
| 9901 | |||
| 9902 | return 0; |
||
| 9903 | } |
||
| 9904 | |||
| 9905 | /** |
||
| 9906 | * @return int |
||
| 9907 | */ |
||
| 9908 | public function getCategoryId() |
||
| 9909 | { |
||
| 9910 | return (int) $this->categoryId; |
||
| 9911 | } |
||
| 9912 | |||
| 9913 | /** |
||
| 9914 | * Get whether this is a learning path with the possibility to subscribe |
||
| 9915 | * users or not. |
||
| 9916 | * |
||
| 9917 | * @return int |
||
| 9918 | */ |
||
| 9919 | public function getSubscribeUsers() |
||
| 9920 | { |
||
| 9921 | return $this->subscribeUsers; |
||
| 9922 | } |
||
| 9923 | |||
| 9924 | /** |
||
| 9925 | * Calculate the count of stars for a user in this LP |
||
| 9926 | * This calculation is based on the following rules: |
||
| 9927 | * - the student gets one star when he gets to 50% of the learning path |
||
| 9928 | * - the student gets a second star when the average score of all tests inside the learning path >= 50% |
||
| 9929 | * - the student gets a third star when the average score of all tests inside the learning path >= 80% |
||
| 9930 | * - the student gets the final star when the score for the *last* test is >= 80%. |
||
| 9931 | * |
||
| 9932 | * @param int $sessionId Optional. The session ID |
||
| 9933 | * |
||
| 9934 | * @return int The count of stars |
||
| 9935 | */ |
||
| 9936 | public function getCalculateStars($sessionId = 0) |
||
| 9937 | { |
||
| 9938 | $stars = 0; |
||
| 9939 | $progress = self::getProgress( |
||
| 9940 | $this->lp_id, |
||
| 9941 | $this->user_id, |
||
| 9942 | $this->course_int_id, |
||
| 9943 | $sessionId |
||
| 9944 | ); |
||
| 9945 | |||
| 9946 | if ($progress >= 50) { |
||
| 9947 | $stars++; |
||
| 9948 | } |
||
| 9949 | |||
| 9950 | // Calculate stars chapters evaluation |
||
| 9951 | $exercisesItems = $this->getExercisesItems(); |
||
| 9952 | |||
| 9953 | if (!empty($exercisesItems)) { |
||
| 9954 | $totalResult = 0; |
||
| 9955 | |||
| 9956 | foreach ($exercisesItems as $exerciseItem) { |
||
| 9957 | $exerciseResultInfo = Event::getExerciseResultsByUser( |
||
| 9958 | $this->user_id, |
||
| 9959 | $exerciseItem->path, |
||
| 9960 | $this->course_int_id, |
||
| 9961 | $sessionId, |
||
| 9962 | $this->lp_id, |
||
| 9963 | $exerciseItem->db_id |
||
| 9964 | ); |
||
| 9965 | |||
| 9966 | $exerciseResultInfo = end($exerciseResultInfo); |
||
| 9967 | |||
| 9968 | if (!$exerciseResultInfo) { |
||
| 9969 | continue; |
||
| 9970 | } |
||
| 9971 | |||
| 9972 | if (!empty($exerciseResultInfo['max_score'])) { |
||
| 9973 | $exerciseResult = $exerciseResultInfo['score'] * 100 / $exerciseResultInfo['max_score']; |
||
| 9974 | } else { |
||
| 9975 | $exerciseResult = 0; |
||
| 9976 | } |
||
| 9977 | $totalResult += $exerciseResult; |
||
| 9978 | } |
||
| 9979 | |||
| 9980 | $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1); |
||
| 9981 | |||
| 9982 | if ($totalExerciseAverage >= 50) { |
||
| 9983 | $stars++; |
||
| 9984 | } |
||
| 9985 | |||
| 9986 | if ($totalExerciseAverage >= 80) { |
||
| 9987 | $stars++; |
||
| 9988 | } |
||
| 9989 | } |
||
| 9990 | |||
| 9991 | // Calculate star for final evaluation |
||
| 9992 | $finalEvaluationItem = $this->getFinalEvaluationItem(); |
||
| 9993 | |||
| 9994 | if (!empty($finalEvaluationItem)) { |
||
| 9995 | $evaluationResultInfo = Event::getExerciseResultsByUser( |
||
| 9996 | $this->user_id, |
||
| 9997 | $finalEvaluationItem->path, |
||
| 9998 | $this->course_int_id, |
||
| 9999 | $sessionId, |
||
| 10000 | $this->lp_id, |
||
| 10001 | $finalEvaluationItem->db_id |
||
| 10002 | ); |
||
| 10003 | |||
| 10004 | $evaluationResultInfo = end($evaluationResultInfo); |
||
| 10005 | |||
| 10006 | if ($evaluationResultInfo) { |
||
| 10007 | $evaluationResult = $evaluationResultInfo['score'] * 100 / $evaluationResultInfo['max_score']; |
||
| 10008 | |||
| 10009 | if ($evaluationResult >= 80) { |
||
| 10010 | $stars++; |
||
| 10011 | } |
||
| 10012 | } |
||
| 10013 | } |
||
| 10014 | |||
| 10015 | return $stars; |
||
| 10016 | } |
||
| 10017 | |||
| 10018 | /** |
||
| 10019 | * Get the items of exercise type. |
||
| 10020 | * |
||
| 10021 | * @return array The items. Otherwise return false |
||
| 10022 | */ |
||
| 10023 | public function getExercisesItems() |
||
| 10024 | { |
||
| 10025 | $exercises = []; |
||
| 10026 | foreach ($this->items as $item) { |
||
| 10027 | if ('quiz' != $item->type) { |
||
| 10028 | continue; |
||
| 10029 | } |
||
| 10030 | $exercises[] = $item; |
||
| 10031 | } |
||
| 10032 | |||
| 10033 | array_pop($exercises); |
||
| 10034 | |||
| 10035 | return $exercises; |
||
| 10036 | } |
||
| 10037 | |||
| 10038 | /** |
||
| 10039 | * Get the item of exercise type (evaluation type). |
||
| 10040 | * |
||
| 10041 | * @return array The final evaluation. Otherwise return false |
||
| 10042 | */ |
||
| 10043 | public function getFinalEvaluationItem() |
||
| 10044 | { |
||
| 10045 | $exercises = []; |
||
| 10046 | foreach ($this->items as $item) { |
||
| 10047 | if (TOOL_QUIZ !== $item->type) { |
||
| 10048 | continue; |
||
| 10049 | } |
||
| 10050 | |||
| 10051 | $exercises[] = $item; |
||
| 10052 | } |
||
| 10053 | |||
| 10054 | return array_pop($exercises); |
||
| 10055 | } |
||
| 10056 | |||
| 10057 | /** |
||
| 10058 | * Calculate the total points achieved for the current user in this learning path. |
||
| 10059 | * |
||
| 10060 | * @param int $sessionId Optional. The session Id |
||
| 10061 | * |
||
| 10062 | * @return int |
||
| 10063 | */ |
||
| 10064 | public function getCalculateScore($sessionId = 0) |
||
| 10111 | } |
||
| 10112 | |||
| 10113 | /** |
||
| 10114 | * Check if URL is not allowed to be show in a iframe. |
||
| 10115 | * |
||
| 10116 | * @param string $src |
||
| 10117 | * |
||
| 10118 | * @return string |
||
| 10119 | */ |
||
| 10120 | public function fixBlockedLinks($src) |
||
| 10121 | { |
||
| 10122 | $urlInfo = parse_url($src); |
||
| 10123 | |||
| 10124 | $platformProtocol = 'https'; |
||
| 10125 | if (false === strpos(api_get_path(WEB_CODE_PATH), 'https')) { |
||
| 10126 | $platformProtocol = 'http'; |
||
| 10127 | } |
||
| 10128 | |||
| 10129 | $protocolFixApplied = false; |
||
| 10130 | //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme |
||
| 10131 | $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null; |
||
| 10132 | $host = isset($urlInfo['host']) ? $urlInfo['host'] : null; |
||
| 10133 | |||
| 10134 | if ($platformProtocol != $scheme) { |
||
| 10135 | Session::write('x_frame_source', $src); |
||
| 10136 | $src = 'blank.php?error=x_frames_options'; |
||
| 10137 | $protocolFixApplied = true; |
||
| 10138 | } |
||
| 10139 | |||
| 10140 | if (false == $protocolFixApplied) { |
||
| 10141 | if (false === strpos(api_get_path(WEB_PATH), $host)) { |
||
| 10142 | // Check X-Frame-Options |
||
| 10143 | $ch = curl_init(); |
||
| 10144 | $options = [ |
||
| 10145 | CURLOPT_URL => $src, |
||
| 10146 | CURLOPT_RETURNTRANSFER => true, |
||
| 10147 | CURLOPT_HEADER => true, |
||
| 10148 | CURLOPT_FOLLOWLOCATION => true, |
||
| 10149 | CURLOPT_ENCODING => "", |
||
| 10150 | CURLOPT_AUTOREFERER => true, |
||
| 10151 | CURLOPT_CONNECTTIMEOUT => 120, |
||
| 10152 | CURLOPT_TIMEOUT => 120, |
||
| 10153 | CURLOPT_MAXREDIRS => 10, |
||
| 10154 | ]; |
||
| 10155 | |||
| 10156 | $proxySettings = api_get_configuration_value('proxy_settings'); |
||
| 10157 | if (!empty($proxySettings) && |
||
| 10158 | isset($proxySettings['curl_setopt_array']) |
||
| 10159 | ) { |
||
| 10160 | $options[CURLOPT_PROXY] = $proxySettings['curl_setopt_array']['CURLOPT_PROXY']; |
||
| 10161 | $options[CURLOPT_PROXYPORT] = $proxySettings['curl_setopt_array']['CURLOPT_PROXYPORT']; |
||
| 10162 | } |
||
| 10163 | |||
| 10164 | curl_setopt_array($ch, $options); |
||
| 10165 | $response = curl_exec($ch); |
||
| 10166 | $httpCode = curl_getinfo($ch); |
||
| 10167 | $headers = substr($response, 0, $httpCode['header_size']); |
||
| 10168 | |||
| 10169 | $error = false; |
||
| 10170 | if (stripos($headers, 'X-Frame-Options: DENY') > -1 |
||
| 10171 | //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1 |
||
| 10172 | ) { |
||
| 10173 | $error = true; |
||
| 10174 | } |
||
| 10175 | |||
| 10176 | if ($error) { |
||
| 10177 | Session::write('x_frame_source', $src); |
||
| 10178 | $src = 'blank.php?error=x_frames_options'; |
||
| 10179 | } |
||
| 10180 | } |
||
| 10181 | } |
||
| 10182 | |||
| 10183 | return $src; |
||
| 10184 | } |
||
| 10185 | |||
| 10186 | /** |
||
| 10187 | * Check if this LP has a created forum in the basis course. |
||
| 10188 | * |
||
| 10189 | * @deprecated |
||
| 10190 | * |
||
| 10191 | * @return bool |
||
| 10192 | */ |
||
| 10193 | public function lpHasForum() |
||
| 10194 | { |
||
| 10195 | $forumTable = Database::get_course_table(TABLE_FORUM); |
||
| 10196 | $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); |
||
| 10197 | |||
| 10198 | $fakeFrom = " |
||
| 10199 | $forumTable f |
||
| 10200 | INNER JOIN $itemProperty ip |
||
| 10201 | ON (f.forum_id = ip.ref AND f.c_id = ip.c_id) |
||
| 10202 | "; |
||
| 10203 | |||
| 10204 | $resultData = Database::select( |
||
| 10205 | 'COUNT(f.iid) AS qty', |
||
| 10206 | $fakeFrom, |
||
| 10207 | [ |
||
| 10208 | 'where' => [ |
||
| 10209 | 'ip.visibility != ? AND ' => 2, |
||
| 10210 | 'ip.tool = ? AND ' => TOOL_FORUM, |
||
| 10211 | 'f.c_id = ? AND ' => intval($this->course_int_id), |
||
| 10212 | 'f.lp_id = ?' => intval($this->lp_id), |
||
| 10213 | ], |
||
| 10214 | ], |
||
| 10215 | 'first' |
||
| 10216 | ); |
||
| 10217 | |||
| 10218 | return $resultData['qty'] > 0; |
||
| 10219 | } |
||
| 10220 | |||
| 10221 | /** |
||
| 10222 | * Get the forum for this learning path. |
||
| 10223 | * |
||
| 10224 | * @param int $sessionId |
||
| 10225 | * |
||
| 10226 | * @return array |
||
| 10227 | */ |
||
| 10228 | public function getForum($sessionId = 0) |
||
| 10229 | { |
||
| 10230 | $repo = Container::getForumRepository(); |
||
| 10231 | |||
| 10232 | $course = api_get_course_entity(); |
||
| 10233 | $session = api_get_session_entity($sessionId); |
||
| 10234 | $qb = $repo->getResourcesByCourse($course, $session); |
||
| 10235 | |||
| 10236 | return $qb->getQuery()->getResult(); |
||
| 10237 | } |
||
| 10238 | |||
| 10239 | /** |
||
| 10240 | * Create a forum for this learning path. |
||
| 10241 | * |
||
| 10242 | * @return int The forum ID if was created. Otherwise return false |
||
| 10243 | */ |
||
| 10244 | public function createForum(CForumCategory $forumCategory) |
||
| 10245 | { |
||
| 10246 | return store_forum( |
||
| 10247 | [ |
||
| 10248 | 'lp_id' => $this->lp_id, |
||
| 10249 | 'forum_title' => $this->name, |
||
| 10250 | 'forum_comment' => null, |
||
| 10251 | 'forum_category' => $forumCategory->getIid(), |
||
| 10252 | 'students_can_edit_group' => ['students_can_edit' => 0], |
||
| 10253 | 'allow_new_threads_group' => ['allow_new_threads' => 0], |
||
| 10254 | 'default_view_type_group' => ['default_view_type' => 'flat'], |
||
| 10255 | 'group_forum' => 0, |
||
| 10256 | 'public_private_group_forum_group' => ['public_private_group_forum' => 'public'], |
||
| 10257 | ], |
||
| 10258 | [], |
||
| 10259 | true |
||
| 10260 | ); |
||
| 10261 | } |
||
| 10262 | |||
| 10263 | /** |
||
| 10264 | * Get the LP Final Item form. |
||
| 10265 | * |
||
| 10266 | * @throws Exception |
||
| 10267 | * @throws HTML_QuickForm_Error |
||
| 10268 | * |
||
| 10269 | * @return string |
||
| 10270 | */ |
||
| 10271 | public function getFinalItemForm() |
||
| 10272 | { |
||
| 10273 | $finalItem = $this->getFinalItem(); |
||
| 10274 | $title = ''; |
||
| 10275 | |||
| 10276 | if ($finalItem) { |
||
| 10277 | $title = $finalItem->get_title(); |
||
| 10278 | $buttonText = get_lang('Save'); |
||
| 10279 | $content = $this->getSavedFinalItem(); |
||
| 10280 | } else { |
||
| 10281 | $buttonText = get_lang('Add this document to the course'); |
||
| 10282 | $content = $this->getFinalItemTemplate(); |
||
| 10283 | } |
||
| 10284 | |||
| 10285 | $editorConfig = [ |
||
| 10286 | 'ToolbarSet' => 'LearningPathDocuments', |
||
| 10287 | 'Width' => '100%', |
||
| 10288 | 'Height' => '500', |
||
| 10289 | 'FullPage' => true, |
||
| 10290 | // 'CreateDocumentDir' => $relative_prefix, |
||
| 10291 | // 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/', |
||
| 10292 | // 'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path, |
||
| 10293 | ]; |
||
| 10294 | |||
| 10295 | $url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([ |
||
| 10296 | 'type' => 'document', |
||
| 10297 | 'lp_id' => $this->lp_id, |
||
| 10298 | ]); |
||
| 10299 | |||
| 10300 | $form = new FormValidator('final_item', 'POST', $url); |
||
| 10301 | $form->addText('title', get_lang('Title')); |
||
| 10302 | $form->addButtonSave($buttonText); |
||
| 10303 | $form->addHtml( |
||
| 10304 | Display::return_message( |
||
| 10305 | 'Variables :</br></br> <b>((certificate))</b> </br> <b>((skill))</b>', |
||
| 10306 | 'normal', |
||
| 10307 | false |
||
| 10308 | ) |
||
| 10309 | ); |
||
| 10310 | |||
| 10311 | $renderer = $form->defaultRenderer(); |
||
| 10312 | $renderer->setElementTemplate(' {label}{element}', 'content_lp_certificate'); |
||
| 10313 | |||
| 10314 | $form->addHtmlEditor( |
||
| 10315 | 'content_lp_certificate', |
||
| 10316 | null, |
||
| 10317 | true, |
||
| 10318 | false, |
||
| 10319 | $editorConfig, |
||
| 10320 | true |
||
| 10321 | ); |
||
| 10322 | $form->addHidden('action', 'add_final_item'); |
||
| 10323 | $form->addHidden('path', Session::read('pathItem')); |
||
| 10324 | $form->addHidden('previous', $this->get_last()); |
||
| 10325 | $form->setDefaults( |
||
| 10326 | ['title' => $title, 'content_lp_certificate' => $content] |
||
| 10327 | ); |
||
| 10328 | |||
| 10329 | if ($form->validate()) { |
||
| 10330 | $values = $form->exportValues(); |
||
| 10331 | $lastItemId = $this->getLastInFirstLevel(); |
||
| 10332 | |||
| 10333 | if (!$finalItem) { |
||
| 10334 | $documentId = $this->create_document( |
||
| 10335 | $this->course_info, |
||
| 10336 | $values['content_lp_certificate'], |
||
| 10337 | $values['title'] |
||
| 10338 | ); |
||
| 10339 | $this->add_item( |
||
| 10340 | 0, |
||
| 10341 | $lastItemId, |
||
| 10342 | 'final_item', |
||
| 10343 | $documentId, |
||
| 10344 | $values['title'], |
||
| 10345 | '' |
||
| 10346 | ); |
||
| 10347 | |||
| 10348 | Display::addFlash( |
||
| 10349 | Display::return_message(get_lang('Added')) |
||
| 10350 | ); |
||
| 10351 | } else { |
||
| 10352 | $this->edit_document($this->course_info); |
||
| 10353 | } |
||
| 10354 | } |
||
| 10355 | |||
| 10356 | return $form->returnForm(); |
||
| 10357 | } |
||
| 10358 | |||
| 10359 | /** |
||
| 10360 | * Check if the current lp item is first, both, last or none from lp list. |
||
| 10361 | * |
||
| 10362 | * @param int $currentItemId |
||
| 10363 | * |
||
| 10364 | * @return string |
||
| 10365 | */ |
||
| 10366 | public function isFirstOrLastItem($currentItemId) |
||
| 10367 | { |
||
| 10368 | $lpItemId = []; |
||
| 10369 | $typeListNotToVerify = self::getChapterTypes(); |
||
| 10370 | |||
| 10371 | // Using get_toc() function instead $this->items because returns the correct order of the items |
||
| 10372 | foreach ($this->get_toc() as $item) { |
||
| 10373 | if (!in_array($item['type'], $typeListNotToVerify)) { |
||
| 10374 | $lpItemId[] = $item['id']; |
||
| 10375 | } |
||
| 10376 | } |
||
| 10377 | |||
| 10378 | $lastLpItemIndex = count($lpItemId) - 1; |
||
| 10379 | $position = array_search($currentItemId, $lpItemId); |
||
| 10380 | |||
| 10381 | switch ($position) { |
||
| 10382 | case 0: |
||
| 10383 | if (!$lastLpItemIndex) { |
||
| 10384 | $answer = 'both'; |
||
| 10385 | break; |
||
| 10386 | } |
||
| 10387 | |||
| 10388 | $answer = 'first'; |
||
| 10389 | break; |
||
| 10390 | case $lastLpItemIndex: |
||
| 10391 | $answer = 'last'; |
||
| 10392 | break; |
||
| 10393 | default: |
||
| 10394 | $answer = 'none'; |
||
| 10395 | } |
||
| 10396 | |||
| 10397 | return $answer; |
||
| 10398 | } |
||
| 10399 | |||
| 10400 | /** |
||
| 10401 | * Get whether this is a learning path with the accumulated SCORM time or not. |
||
| 10402 | * |
||
| 10403 | * @return int |
||
| 10404 | */ |
||
| 10405 | public function getAccumulateScormTime() |
||
| 10408 | } |
||
| 10409 | |||
| 10410 | /** |
||
| 10411 | * Returns an HTML-formatted link to a resource, to incorporate directly into |
||
| 10412 | * the new learning path tool. |
||
| 10413 | * |
||
| 10414 | * The function is a big switch on tool type. |
||
| 10415 | * In each case, we query the corresponding table for information and build the link |
||
| 10416 | * with that information. |
||
| 10417 | * |
||
| 10418 | * @author Yannick Warnier <[email protected]> - rebranding based on |
||
| 10419 | * previous work (display_addedresource_link_in_learnpath()) |
||
| 10420 | * |
||
| 10421 | * @param int $course_id Course code |
||
| 10422 | * @param int $learningPathId The learning path ID (in lp table) |
||
| 10423 | * @param int $id_in_path the unique index in the items table |
||
| 10424 | * @param int $lpViewId |
||
| 10425 | * |
||
| 10426 | * @return string |
||
| 10427 | */ |
||
| 10428 | public static function rl_get_resource_link_for_learnpath( |
||
| 10429 | $course_id, |
||
| 10430 | $learningPathId, |
||
| 10431 | $id_in_path, |
||
| 10432 | $lpViewId |
||
| 10433 | ) { |
||
| 10434 | $session_id = api_get_session_id(); |
||
| 10435 | |||
| 10436 | $learningPathId = (int) $learningPathId; |
||
| 10437 | $id_in_path = (int) $id_in_path; |
||
| 10438 | $lpViewId = (int) $lpViewId; |
||
| 10439 | |||
| 10440 | $em = Database::getManager(); |
||
| 10441 | $lpItemRepo = $em->getRepository(CLpItem::class); |
||
| 10442 | |||
| 10443 | /** @var CLpItem $rowItem */ |
||
| 10444 | $rowItem = $lpItemRepo->findOneBy([ |
||
| 10445 | 'lp' => $learningPathId, |
||
| 10446 | 'iid' => $id_in_path, |
||
| 10447 | ]); |
||
| 10448 | |||
| 10449 | if (!$rowItem) { |
||
| 10450 | // Try one more time with "id" |
||
| 10451 | /** @var CLpItem $rowItem */ |
||
| 10452 | $rowItem = $lpItemRepo->findOneBy([ |
||
| 10453 | 'lp' => $learningPathId, |
||
| 10454 | 'id' => $id_in_path, |
||
| 10455 | ]); |
||
| 10456 | |||
| 10457 | if (!$rowItem) { |
||
| 10458 | return -1; |
||
| 10459 | } |
||
| 10460 | } |
||
| 10461 | |||
| 10462 | $type = $rowItem->getItemType(); |
||
| 10463 | $id = empty($rowItem->getPath()) ? '0' : $rowItem->getPath(); |
||
| 10464 | $main_dir_path = api_get_path(WEB_CODE_PATH); |
||
| 10465 | $link = ''; |
||
| 10466 | $extraParams = api_get_cidreq(true, true, 'learnpath').'&sid='.$session_id; |
||
| 10467 | |||
| 10468 | switch ($type) { |
||
| 10469 | case 'dir': |
||
| 10470 | return $main_dir_path.'lp/blank.php'; |
||
| 10471 | case TOOL_CALENDAR_EVENT: |
||
| 10472 | return $main_dir_path.'calendar/agenda.php?agenda_id='.$id.'&'.$extraParams; |
||
| 10473 | case TOOL_ANNOUNCEMENT: |
||
| 10474 | return $main_dir_path.'announcements/announcements.php?ann_id='.$id.'&'.$extraParams; |
||
| 10475 | case TOOL_LINK: |
||
| 10476 | $linkInfo = Link::getLinkInfo($id); |
||
| 10477 | if (isset($linkInfo['url'])) { |
||
| 10478 | return $linkInfo['url']; |
||
| 10479 | } |
||
| 10480 | |||
| 10481 | return ''; |
||
| 10482 | case TOOL_QUIZ: |
||
| 10483 | if (empty($id)) { |
||
| 10484 | return ''; |
||
| 10485 | } |
||
| 10486 | |||
| 10487 | // Get the lp_item_view with the highest view_count. |
||
| 10488 | $learnpathItemViewResult = $em |
||
| 10489 | ->getRepository('ChamiloCourseBundle:CLpItemView') |
||
| 10490 | ->findBy( |
||
| 10491 | ['cId' => $course_id, 'lpItemId' => $rowItem->getIid(), 'lpViewId' => $lpViewId], |
||
| 10492 | ['viewCount' => 'DESC'], |
||
| 10493 | 1 |
||
| 10494 | ); |
||
| 10495 | /** @var CLpItemView $learnpathItemViewData */ |
||
| 10496 | $learnpathItemViewData = current($learnpathItemViewResult); |
||
| 10497 | $learnpathItemViewId = $learnpathItemViewData ? $learnpathItemViewData->getIid() : 0; |
||
| 10498 | |||
| 10499 | return $main_dir_path.'exercise/overview.php?'.$extraParams.'&' |
||
| 10500 | .http_build_query([ |
||
| 10501 | 'lp_init' => 1, |
||
| 10502 | 'learnpath_item_view_id' => $learnpathItemViewId, |
||
| 10503 | 'learnpath_id' => $learningPathId, |
||
| 10504 | 'learnpath_item_id' => $id_in_path, |
||
| 10505 | 'exerciseId' => $id, |
||
| 10506 | ]); |
||
| 10507 | case TOOL_HOTPOTATOES: //lowercase because of strtolower above |
||
| 10508 | /*$TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT); |
||
| 10509 | $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND iid=$id"); |
||
| 10510 | $myrow = Database::fetch_array($result); |
||
| 10511 | $path = $myrow['path']; |
||
| 10512 | |||
| 10513 | return $main_dir_path.'exercise/showinframes.php?file='.$path.'&cid='.$course_code.'&uid=' |
||
| 10514 | .api_get_user_id().'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path |
||
| 10515 | .'&lp_view_id='.$lpViewId.'&'.$extraParams;*/ |
||
| 10516 | case TOOL_FORUM: |
||
| 10517 | return $main_dir_path.'forum/viewforum.php?forum='.$id.'&lp=true&'.$extraParams; |
||
| 10518 | case TOOL_THREAD: |
||
| 10519 | // forum post |
||
| 10520 | $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD); |
||
| 10521 | if (empty($id)) { |
||
| 10522 | return ''; |
||
| 10523 | } |
||
| 10524 | $sql = "SELECT * FROM $tbl_topics WHERE c_id = $course_id AND iid=$id"; |
||
| 10525 | $result = Database::query($sql); |
||
| 10526 | $myrow = Database::fetch_array($result); |
||
| 10527 | |||
| 10528 | return $main_dir_path.'forum/viewthread.php?thread='.$id.'&forum='.$myrow['forum_id'].'&lp=true&' |
||
| 10529 | .$extraParams; |
||
| 10530 | case TOOL_POST: |
||
| 10531 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
| 10532 | $result = Database::query("SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=$id"); |
||
| 10533 | $myrow = Database::fetch_array($result); |
||
| 10534 | |||
| 10535 | return $main_dir_path.'forum/viewthread.php?post='.$id.'&thread='.$myrow['thread_id'].'&forum=' |
||
| 10536 | .$myrow['forum_id'].'&lp=true&'.$extraParams; |
||
| 10537 | case TOOL_READOUT_TEXT: |
||
| 10538 | return api_get_path(WEB_CODE_PATH). |
||
| 10539 | 'lp/readout_text.php?&id='.$id.'&lp_id='.$learningPathId.'&'.$extraParams; |
||
| 10540 | case TOOL_DOCUMENT: |
||
| 10541 | $repo = Container::getDocumentRepository(); |
||
| 10542 | $document = $repo->find($rowItem->getPath()); |
||
| 10543 | $params = [ |
||
| 10544 | 'cid' => $course_id, |
||
| 10545 | 'sid' => $session_id, |
||
| 10546 | ]; |
||
| 10547 | $file = $repo->getResourceFileUrl($document, $params, UrlGeneratorInterface::ABSOLUTE_URL); |
||
| 10548 | |||
| 10549 | return $file; |
||
| 10550 | |||
| 10551 | $documentPathInfo = pathinfo($document->getPath()); |
||
| 10552 | $mediaSupportedFiles = ['mp3', 'mp4', 'ogv', 'ogg', 'flv', 'm4v']; |
||
| 10553 | $extension = isset($documentPathInfo['extension']) ? $documentPathInfo['extension'] : ''; |
||
| 10554 | $showDirectUrl = !in_array($extension, $mediaSupportedFiles); |
||
| 10555 | |||
| 10556 | $openmethod = 2; |
||
| 10557 | $officedoc = false; |
||
| 10558 | Session::write('openmethod', $openmethod); |
||
| 10559 | Session::write('officedoc', $officedoc); |
||
| 10560 | |||
| 10561 | if ($showDirectUrl) { |
||
| 10562 | $file = $main_course_path.'document'.$document->getPath().'?'.$extraParams; |
||
| 10563 | if (api_get_configuration_value('allow_pdf_viewerjs_in_lp')) { |
||
| 10564 | if (Link::isPdfLink($file)) { |
||
| 10565 | $pdfUrl = api_get_path(WEB_LIBRARY_PATH).'javascript/ViewerJS/index.html#'.$file; |
||
| 10566 | |||
| 10567 | return $pdfUrl; |
||
| 10568 | } |
||
| 10569 | } |
||
| 10570 | |||
| 10571 | return $file; |
||
| 10572 | } |
||
| 10573 | |||
| 10574 | return api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.'&'.$extraParams; |
||
| 10575 | case TOOL_LP_FINAL_ITEM: |
||
| 10576 | return api_get_path(WEB_CODE_PATH).'lp/lp_final_item.php?&id='.$id.'&lp_id='.$learningPathId.'&' |
||
| 10577 | .$extraParams; |
||
| 10578 | case 'assignments': |
||
| 10579 | return $main_dir_path.'work/work.php?'.$extraParams; |
||
| 10580 | case TOOL_DROPBOX: |
||
| 10581 | return $main_dir_path.'dropbox/index.php?'.$extraParams; |
||
| 10582 | case 'introduction_text': //DEPRECATED |
||
| 10583 | return ''; |
||
| 10584 | case TOOL_COURSE_DESCRIPTION: |
||
| 10585 | return $main_dir_path.'course_description?'.$extraParams; |
||
| 10586 | case TOOL_GROUP: |
||
| 10587 | return $main_dir_path.'group/group.php?'.$extraParams; |
||
| 10588 | case TOOL_USER: |
||
| 10589 | return $main_dir_path.'user/user.php?'.$extraParams; |
||
| 10590 | case TOOL_STUDENTPUBLICATION: |
||
| 10591 | if (!empty($rowItem->getPath())) { |
||
| 10592 | return $main_dir_path.'work/work_list.php?id='.$rowItem->getPath().'&'.$extraParams; |
||
| 10593 | } |
||
| 10594 | |||
| 10595 | return $main_dir_path.'work/work.php?'.api_get_cidreq().'&id='.$rowItem->getPath().'&'.$extraParams; |
||
| 10596 | } |
||
| 10597 | |||
| 10598 | return $link; |
||
| 10599 | } |
||
| 10600 | |||
| 10601 | /** |
||
| 10602 | * Gets the name of a resource (generally used in learnpath when no name is provided). |
||
| 10603 | * |
||
| 10604 | * @author Yannick Warnier <[email protected]> |
||
| 10605 | * |
||
| 10606 | * @param string $course_code Course code |
||
| 10607 | * @param int $learningPathId |
||
| 10608 | * @param int $id_in_path The resource ID |
||
| 10609 | * |
||
| 10610 | * @return string |
||
| 10611 | */ |
||
| 10612 | public static function rl_get_resource_name($course_code, $learningPathId, $id_in_path) |
||
| 10613 | { |
||
| 10614 | $_course = api_get_course_info($course_code); |
||
| 10615 | if (empty($_course)) { |
||
| 10616 | return ''; |
||
| 10617 | } |
||
| 10618 | $course_id = $_course['real_id']; |
||
| 10619 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 10620 | $learningPathId = (int) $learningPathId; |
||
| 10621 | $id_in_path = (int) $id_in_path; |
||
| 10622 | |||
| 10623 | $sql = "SELECT item_type, title, ref |
||
| 10624 | FROM $tbl_lp_item |
||
| 10625 | WHERE c_id = $course_id AND lp_id = $learningPathId AND iid = $id_in_path"; |
||
| 10626 | $res_item = Database::query($sql); |
||
| 10627 | |||
| 10628 | if (Database::num_rows($res_item) < 1) { |
||
| 10629 | return ''; |
||
| 10630 | } |
||
| 10631 | $row_item = Database::fetch_array($res_item); |
||
| 10632 | $type = strtolower($row_item['item_type']); |
||
| 10633 | $id = $row_item['ref']; |
||
| 10634 | $output = ''; |
||
| 10635 | |||
| 10636 | switch ($type) { |
||
| 10637 | case TOOL_CALENDAR_EVENT: |
||
| 10638 | $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA); |
||
| 10639 | $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id"); |
||
| 10640 | $myrow = Database::fetch_array($result); |
||
| 10641 | $output = $myrow['title']; |
||
| 10642 | break; |
||
| 10643 | case TOOL_ANNOUNCEMENT: |
||
| 10644 | $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT); |
||
| 10645 | $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id"); |
||
| 10646 | $myrow = Database::fetch_array($result); |
||
| 10647 | $output = $myrow['title']; |
||
| 10648 | break; |
||
| 10649 | case TOOL_LINK: |
||
| 10650 | // Doesn't take $target into account. |
||
| 10651 | $TABLETOOLLINK = Database::get_course_table(TABLE_LINK); |
||
| 10652 | $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id"); |
||
| 10653 | $myrow = Database::fetch_array($result); |
||
| 10654 | $output = $myrow['title']; |
||
| 10655 | break; |
||
| 10656 | case TOOL_QUIZ: |
||
| 10657 | $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST); |
||
| 10658 | $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id = $id"); |
||
| 10659 | $myrow = Database::fetch_array($result); |
||
| 10660 | $output = $myrow['title']; |
||
| 10661 | break; |
||
| 10662 | case TOOL_FORUM: |
||
| 10663 | $TBL_FORUMS = Database::get_course_table(TABLE_FORUM); |
||
| 10664 | $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id = $id"); |
||
| 10665 | $myrow = Database::fetch_array($result); |
||
| 10666 | $output = $myrow['forum_name']; |
||
| 10667 | break; |
||
| 10668 | case TOOL_THREAD: |
||
| 10669 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
| 10670 | // Grabbing the title of the post. |
||
| 10671 | $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id; |
||
| 10672 | $result_title = Database::query($sql_title); |
||
| 10673 | $myrow_title = Database::fetch_array($result_title); |
||
| 10674 | $output = $myrow_title['post_title']; |
||
| 10675 | break; |
||
| 10676 | case TOOL_POST: |
||
| 10677 | $tbl_post = Database::get_course_table(TABLE_FORUM_POST); |
||
| 10678 | $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id"; |
||
| 10679 | $result = Database::query($sql); |
||
| 10680 | $post = Database::fetch_array($result); |
||
| 10681 | $output = $post['post_title']; |
||
| 10682 | break; |
||
| 10683 | case 'dir': |
||
| 10684 | case TOOL_DOCUMENT: |
||
| 10685 | $title = $row_item['title']; |
||
| 10686 | $output = '-'; |
||
| 10687 | if (!empty($title)) { |
||
| 10688 | $output = $title; |
||
| 10689 | } |
||
| 10690 | break; |
||
| 10691 | case 'hotpotatoes': |
||
| 10692 | $tbl_doc = Database::get_course_table(TABLE_DOCUMENT); |
||
| 10693 | $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND iid = $id"); |
||
| 10694 | $myrow = Database::fetch_array($result); |
||
| 10695 | $pathname = explode('/', $myrow['path']); // Making a correct name for the link. |
||
| 10696 | $last = count($pathname) - 1; // Making a correct name for the link. |
||
| 10697 | $filename = $pathname[$last]; // Making a correct name for the link. |
||
| 10698 | $myrow['path'] = rawurlencode($myrow['path']); |
||
| 10699 | $output = $filename; |
||
| 10700 | break; |
||
| 10701 | } |
||
| 10702 | |||
| 10703 | return stripslashes($output); |
||
| 10704 | } |
||
| 10705 | |||
| 10706 | /** |
||
| 10707 | * Get the parent names for the current item. |
||
| 10708 | * |
||
| 10709 | * @param int $newItemId Optional. The item ID |
||
| 10710 | * |
||
| 10711 | * @return array |
||
| 10712 | */ |
||
| 10713 | public function getCurrentItemParentNames($newItemId = 0) |
||
| 10714 | { |
||
| 10715 | $newItemId = $newItemId ?: $this->get_current_item_id(); |
||
| 10716 | $return = []; |
||
| 10717 | $item = $this->getItem($newItemId); |
||
| 10718 | $parent = $this->getItem($item->get_parent()); |
||
| 10719 | |||
| 10720 | while ($parent) { |
||
| 10721 | $return[] = $parent->get_title(); |
||
| 10722 | $parent = $this->getItem($parent->get_parent()); |
||
| 10723 | } |
||
| 10724 | |||
| 10725 | return array_reverse($return); |
||
| 10726 | } |
||
| 10727 | |||
| 10728 | /** |
||
| 10729 | * Reads and process "lp_subscription_settings" setting. |
||
| 10730 | * |
||
| 10731 | * @return array |
||
| 10732 | */ |
||
| 10733 | public static function getSubscriptionSettings() |
||
| 10734 | { |
||
| 10735 | $subscriptionSettings = api_get_configuration_value('lp_subscription_settings'); |
||
| 10736 | if (empty($subscriptionSettings)) { |
||
| 10737 | // By default allow both settings |
||
| 10738 | $subscriptionSettings = [ |
||
| 10739 | 'allow_add_users_to_lp' => true, |
||
| 10740 | 'allow_add_users_to_lp_category' => true, |
||
| 10741 | ]; |
||
| 10742 | } else { |
||
| 10743 | $subscriptionSettings = $subscriptionSettings['options']; |
||
| 10744 | } |
||
| 10745 | |||
| 10746 | return $subscriptionSettings; |
||
| 10747 | } |
||
| 10748 | |||
| 10749 | /** |
||
| 10750 | * Exports a LP to a courseBuilder zip file. It adds the documents related to the LP. |
||
| 10751 | */ |
||
| 10752 | public function exportToCourseBuildFormat() |
||
| 10753 | { |
||
| 10754 | if (!api_is_allowed_to_edit()) { |
||
| 10755 | return false; |
||
| 10756 | } |
||
| 10757 | |||
| 10758 | $courseBuilder = new CourseBuilder(); |
||
| 10759 | $itemList = []; |
||
| 10760 | /** @var learnpathItem $item */ |
||
| 10761 | foreach ($this->items as $item) { |
||
| 10762 | $itemList[$item->get_type()][] = $item->get_path(); |
||
| 10763 | } |
||
| 10764 | |||
| 10765 | if (empty($itemList)) { |
||
| 10766 | return false; |
||
| 10767 | } |
||
| 10768 | |||
| 10769 | if (isset($itemList['document'])) { |
||
| 10770 | // Get parents |
||
| 10771 | foreach ($itemList['document'] as $documentId) { |
||
| 10772 | $documentInfo = DocumentManager::get_document_data_by_id($documentId, api_get_course_id(), true); |
||
| 10773 | if (!empty($documentInfo['parents'])) { |
||
| 10774 | foreach ($documentInfo['parents'] as $parentInfo) { |
||
| 10775 | if (in_array($parentInfo['iid'], $itemList['document'])) { |
||
| 10776 | continue; |
||
| 10777 | } |
||
| 10778 | $itemList['document'][] = $parentInfo['iid']; |
||
| 10779 | } |
||
| 10780 | } |
||
| 10781 | } |
||
| 10782 | |||
| 10783 | $courseInfo = api_get_course_info(); |
||
| 10784 | foreach ($itemList['document'] as $documentId) { |
||
| 10785 | $documentInfo = DocumentManager::get_document_data_by_id($documentId, api_get_course_id()); |
||
| 10786 | $items = DocumentManager::get_resources_from_source_html( |
||
| 10787 | $documentInfo['absolute_path'], |
||
| 10788 | true, |
||
| 10789 | TOOL_DOCUMENT |
||
| 10790 | ); |
||
| 10791 | |||
| 10792 | if (!empty($items)) { |
||
| 10793 | foreach ($items as $item) { |
||
| 10794 | // Get information about source url |
||
| 10795 | $url = $item[0]; // url |
||
| 10796 | $scope = $item[1]; // scope (local, remote) |
||
| 10797 | $type = $item[2]; // type (rel, abs, url) |
||
| 10798 | |||
| 10799 | $origParseUrl = parse_url($url); |
||
| 10800 | $realOrigPath = isset($origParseUrl['path']) ? $origParseUrl['path'] : null; |
||
| 10801 | |||
| 10802 | if ('local' === $scope) { |
||
| 10803 | if ('abs' === $type || 'rel' === $type) { |
||
| 10804 | $documentFile = strstr($realOrigPath, 'document'); |
||
| 10805 | if (false !== strpos($realOrigPath, $documentFile)) { |
||
| 10806 | $documentFile = str_replace('document', '', $documentFile); |
||
| 10807 | $itemDocumentId = DocumentManager::get_document_id($courseInfo, $documentFile); |
||
| 10808 | // Document found! Add it to the list |
||
| 10809 | if ($itemDocumentId) { |
||
| 10810 | $itemList['document'][] = $itemDocumentId; |
||
| 10811 | } |
||
| 10812 | } |
||
| 10813 | } |
||
| 10814 | } |
||
| 10815 | } |
||
| 10816 | } |
||
| 10817 | } |
||
| 10818 | |||
| 10819 | $courseBuilder->build_documents( |
||
| 10820 | api_get_session_id(), |
||
| 10821 | $this->get_course_int_id(), |
||
| 10822 | true, |
||
| 10823 | $itemList['document'] |
||
| 10824 | ); |
||
| 10825 | } |
||
| 10826 | |||
| 10827 | if (isset($itemList['quiz'])) { |
||
| 10828 | $courseBuilder->build_quizzes( |
||
| 10829 | api_get_session_id(), |
||
| 10830 | $this->get_course_int_id(), |
||
| 10831 | true, |
||
| 10832 | $itemList['quiz'] |
||
| 10833 | ); |
||
| 10834 | } |
||
| 10835 | |||
| 10836 | /*if (!empty($itemList['thread'])) { |
||
| 10837 | $postList = []; |
||
| 10838 | foreach ($itemList['thread'] as $postId) { |
||
| 10839 | $post = get_post_information($postId); |
||
| 10840 | if ($post) { |
||
| 10841 | if (!isset($itemList['forum'])) { |
||
| 10842 | $itemList['forum'] = []; |
||
| 10843 | } |
||
| 10844 | $itemList['forum'][] = $post['forum_id']; |
||
| 10845 | $postList[] = $postId; |
||
| 10846 | } |
||
| 10847 | } |
||
| 10848 | |||
| 10849 | if (!empty($postList)) { |
||
| 10850 | $courseBuilder->build_forum_posts( |
||
| 10851 | $this->get_course_int_id(), |
||
| 10852 | null, |
||
| 10853 | null, |
||
| 10854 | $postList |
||
| 10855 | ); |
||
| 10856 | } |
||
| 10857 | }*/ |
||
| 10858 | |||
| 10859 | if (!empty($itemList['thread'])) { |
||
| 10860 | $threadList = []; |
||
| 10861 | $em = Database::getManager(); |
||
| 10862 | $repo = $em->getRepository('ChamiloCourseBundle:CForumThread'); |
||
| 10863 | foreach ($itemList['thread'] as $threadId) { |
||
| 10864 | /** @var CForumThread $thread */ |
||
| 10865 | $thread = $repo->find($threadId); |
||
| 10866 | if ($thread) { |
||
| 10867 | $itemList['forum'][] = $thread->getForum() ? $thread->getForum()->getIid() : 0; |
||
| 10868 | $threadList[] = $thread->getIid(); |
||
| 10869 | } |
||
| 10870 | } |
||
| 10871 | |||
| 10872 | if (!empty($threadList)) { |
||
| 10873 | $courseBuilder->build_forum_topics( |
||
| 10874 | api_get_session_id(), |
||
| 10875 | $this->get_course_int_id(), |
||
| 10876 | null, |
||
| 10877 | $threadList |
||
| 10878 | ); |
||
| 10879 | } |
||
| 10880 | } |
||
| 10881 | |||
| 10882 | $forumCategoryList = []; |
||
| 10883 | if (isset($itemList['forum'])) { |
||
| 10884 | foreach ($itemList['forum'] as $forumId) { |
||
| 10885 | $forumInfo = get_forums($forumId); |
||
| 10886 | $forumCategoryList[] = $forumInfo['forum_category']; |
||
| 10887 | } |
||
| 10888 | } |
||
| 10889 | |||
| 10890 | if (!empty($forumCategoryList)) { |
||
| 10891 | $courseBuilder->build_forum_category( |
||
| 10892 | api_get_session_id(), |
||
| 10893 | $this->get_course_int_id(), |
||
| 10894 | true, |
||
| 10895 | $forumCategoryList |
||
| 10896 | ); |
||
| 10897 | } |
||
| 10898 | |||
| 10899 | if (!empty($itemList['forum'])) { |
||
| 10900 | $courseBuilder->build_forums( |
||
| 10901 | api_get_session_id(), |
||
| 10902 | $this->get_course_int_id(), |
||
| 10903 | true, |
||
| 10904 | $itemList['forum'] |
||
| 10905 | ); |
||
| 10906 | } |
||
| 10907 | |||
| 10908 | if (isset($itemList['link'])) { |
||
| 10909 | $courseBuilder->build_links( |
||
| 10910 | api_get_session_id(), |
||
| 10911 | $this->get_course_int_id(), |
||
| 10912 | true, |
||
| 10913 | $itemList['link'] |
||
| 10914 | ); |
||
| 10915 | } |
||
| 10916 | |||
| 10917 | if (!empty($itemList['student_publication'])) { |
||
| 10918 | $courseBuilder->build_works( |
||
| 10919 | api_get_session_id(), |
||
| 10920 | $this->get_course_int_id(), |
||
| 10921 | true, |
||
| 10922 | $itemList['student_publication'] |
||
| 10923 | ); |
||
| 10924 | } |
||
| 10925 | |||
| 10926 | $courseBuilder->build_learnpaths( |
||
| 10927 | api_get_session_id(), |
||
| 10928 | $this->get_course_int_id(), |
||
| 10929 | true, |
||
| 10930 | [$this->get_id()], |
||
| 10931 | false |
||
| 10932 | ); |
||
| 10933 | |||
| 10934 | $courseBuilder->restoreDocumentsFromList(); |
||
| 10935 | |||
| 10936 | $zipFile = CourseArchiver::createBackup($courseBuilder->course); |
||
| 10937 | $zipPath = CourseArchiver::getBackupDir().$zipFile; |
||
| 10938 | $result = DocumentManager::file_send_for_download( |
||
| 10939 | $zipPath, |
||
| 10940 | true, |
||
| 10941 | $this->get_name().'.zip' |
||
| 10942 | ); |
||
| 10943 | |||
| 10944 | if ($result) { |
||
| 10945 | api_not_allowed(); |
||
| 10946 | } |
||
| 10947 | |||
| 10948 | return true; |
||
| 10949 | } |
||
| 10950 | |||
| 10951 | /** |
||
| 10952 | * Get whether this is a learning path with the accumulated work time or not. |
||
| 10953 | * |
||
| 10954 | * @return int |
||
| 10955 | */ |
||
| 10956 | public function getAccumulateWorkTime() |
||
| 10957 | { |
||
| 10958 | return (int) $this->accumulateWorkTime; |
||
| 10959 | } |
||
| 10960 | |||
| 10961 | /** |
||
| 10962 | * Get whether this is a learning path with the accumulated work time or not. |
||
| 10963 | * |
||
| 10964 | * @return int |
||
| 10965 | */ |
||
| 10966 | public function getAccumulateWorkTimeTotalCourse() |
||
| 10967 | { |
||
| 10968 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 10969 | $sql = "SELECT SUM(accumulate_work_time) AS total |
||
| 10970 | FROM $table |
||
| 10971 | WHERE c_id = ".$this->course_int_id; |
||
| 10972 | $result = Database::query($sql); |
||
| 10973 | $row = Database::fetch_array($result); |
||
| 10974 | |||
| 10975 | return (int) $row['total']; |
||
| 10976 | } |
||
| 10977 | |||
| 10978 | /** |
||
| 10979 | * @param int $lpId |
||
| 10980 | * @param int $courseId |
||
| 10981 | * |
||
| 10982 | * @return mixed |
||
| 10983 | */ |
||
| 10984 | public static function getAccumulateWorkTimePrerequisite($lpId, $courseId) |
||
| 10985 | { |
||
| 10986 | $lpId = (int) $lpId; |
||
| 10987 | $courseId = (int) $courseId; |
||
| 10988 | |||
| 10989 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 10990 | $sql = "SELECT accumulate_work_time |
||
| 10991 | FROM $table |
||
| 10992 | WHERE c_id = $courseId AND id = $lpId"; |
||
| 10993 | $result = Database::query($sql); |
||
| 10994 | $row = Database::fetch_array($result); |
||
| 10995 | |||
| 10996 | return $row['accumulate_work_time']; |
||
| 10997 | } |
||
| 10998 | |||
| 10999 | /** |
||
| 11000 | * @param int $courseId |
||
| 11001 | * |
||
| 11002 | * @return int |
||
| 11003 | */ |
||
| 11004 | public static function getAccumulateWorkTimeTotal($courseId) |
||
| 11005 | { |
||
| 11006 | $table = Database::get_course_table(TABLE_LP_MAIN); |
||
| 11007 | $courseId = (int) $courseId; |
||
| 11008 | $sql = "SELECT SUM(accumulate_work_time) AS total |
||
| 11009 | FROM $table |
||
| 11010 | WHERE c_id = $courseId"; |
||
| 11011 | $result = Database::query($sql); |
||
| 11012 | $row = Database::fetch_array($result); |
||
| 11013 | |||
| 11014 | return (int) $row['total']; |
||
| 11015 | } |
||
| 11016 | |||
| 11017 | /** |
||
| 11018 | * In order to use the lp icon option you need to create the "lp_icon" LP extra field |
||
| 11019 | * and put the images in. |
||
| 11020 | * |
||
| 11021 | * @return array |
||
| 11022 | */ |
||
| 11023 | public static function getIconSelect() |
||
| 11024 | { |
||
| 11025 | $theme = api_get_visual_theme(); |
||
| 11026 | $path = api_get_path(SYS_PUBLIC_PATH).'css/themes/'.$theme.'/lp_icons/'; |
||
| 11027 | $icons = ['' => get_lang('Please select an option')]; |
||
| 11028 | |||
| 11029 | if (is_dir($path)) { |
||
| 11030 | $finder = new Finder(); |
||
| 11031 | $finder->files()->in($path); |
||
| 11032 | $allowedExtensions = ['jpeg', 'jpg', 'png']; |
||
| 11033 | /** @var SplFileInfo $file */ |
||
| 11034 | foreach ($finder as $file) { |
||
| 11035 | if (in_array(strtolower($file->getExtension()), $allowedExtensions)) { |
||
| 11036 | $icons[$file->getFilename()] = $file->getFilename(); |
||
| 11037 | } |
||
| 11038 | } |
||
| 11039 | } |
||
| 11040 | |||
| 11041 | return $icons; |
||
| 11042 | } |
||
| 11043 | |||
| 11044 | /** |
||
| 11045 | * @param int $lpId |
||
| 11046 | * |
||
| 11047 | * @return string |
||
| 11048 | */ |
||
| 11049 | public static function getSelectedIcon($lpId) |
||
| 11050 | { |
||
| 11051 | $extraFieldValue = new ExtraFieldValue('lp'); |
||
| 11052 | $lpIcon = $extraFieldValue->get_values_by_handler_and_field_variable($lpId, 'lp_icon'); |
||
| 11053 | $icon = ''; |
||
| 11054 | if (!empty($lpIcon) && isset($lpIcon['value'])) { |
||
| 11055 | $icon = $lpIcon['value']; |
||
| 11056 | } |
||
| 11057 | |||
| 11058 | return $icon; |
||
| 11059 | } |
||
| 11060 | |||
| 11061 | /** |
||
| 11062 | * @param int $lpId |
||
| 11063 | * |
||
| 11064 | * @return string |
||
| 11065 | */ |
||
| 11066 | public static function getSelectedIconHtml($lpId) |
||
| 11067 | { |
||
| 11068 | $icon = self::getSelectedIcon($lpId); |
||
| 11069 | |||
| 11070 | if (empty($icon)) { |
||
| 11071 | return ''; |
||
| 11072 | } |
||
| 11073 | |||
| 11074 | $theme = api_get_visual_theme(); |
||
| 11075 | $path = api_get_path(WEB_PUBLIC_PATH).'css/themes/'.$theme.'/lp_icons/'.$icon; |
||
| 11076 | |||
| 11077 | return Display::img($path); |
||
| 11078 | } |
||
| 11079 | |||
| 11080 | /** |
||
| 11081 | * @param string $value |
||
| 11082 | * |
||
| 11083 | * @return string |
||
| 11084 | */ |
||
| 11085 | public function cleanItemTitle($value) |
||
| 11086 | { |
||
| 11087 | $value = Security::remove_XSS(strip_tags($value)); |
||
| 11088 | |||
| 11089 | return $value; |
||
| 11090 | } |
||
| 11091 | |||
| 11092 | public function setItemTitle(FormValidator $form) |
||
| 11093 | { |
||
| 11094 | if (api_get_configuration_value('save_titles_as_html')) { |
||
| 11095 | $form->addHtmlEditor( |
||
| 11096 | 'title', |
||
| 11097 | get_lang('Title'), |
||
| 11098 | true, |
||
| 11099 | false, |
||
| 11100 | ['ToolbarSet' => 'TitleAsHtml', 'id' => uniqid('editor')] |
||
| 11101 | ); |
||
| 11102 | } else { |
||
| 11103 | $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle', 'class' => 'learnpath_item_form']); |
||
| 11104 | $form->applyFilter('title', 'trim'); |
||
| 11105 | $form->applyFilter('title', 'html_filter'); |
||
| 11106 | } |
||
| 11107 | } |
||
| 11108 | |||
| 11109 | /** |
||
| 11110 | * @return array |
||
| 11111 | */ |
||
| 11112 | public function getItemsForForm($addParentCondition = false) |
||
| 11113 | { |
||
| 11114 | $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM); |
||
| 11115 | |||
| 11116 | $sql = "SELECT * FROM $tbl_lp_item |
||
| 11117 | WHERE lp_id = ".$this->lp_id; |
||
| 11118 | |||
| 11119 | if ($addParentCondition) { |
||
| 11120 | $sql .= ' AND parent_item_id IS NULL '; |
||
| 11121 | } |
||
| 11122 | $sql .= ' ORDER BY display_order ASC'; |
||
| 11123 | |||
| 11124 | $result = Database::query($sql); |
||
| 11125 | $arrLP = []; |
||
| 11126 | while ($row = Database::fetch_array($result)) { |
||
| 11127 | $arrLP[] = [ |
||
| 11128 | 'iid' => $row['iid'], |
||
| 11129 | 'id' => $row['iid'], |
||
| 11130 | 'item_type' => $row['item_type'], |
||
| 11131 | 'title' => $this->cleanItemTitle($row['title']), |
||
| 11132 | 'title_raw' => $row['title'], |
||
| 11133 | 'path' => $row['path'], |
||
| 11134 | 'description' => Security::remove_XSS($row['description']), |
||
| 11135 | 'parent_item_id' => $row['parent_item_id'], |
||
| 11136 | 'previous_item_id' => $row['previous_item_id'], |
||
| 11137 | 'next_item_id' => $row['next_item_id'], |
||
| 11138 | 'display_order' => $row['display_order'], |
||
| 11139 | 'max_score' => $row['max_score'], |
||
| 11140 | 'min_score' => $row['min_score'], |
||
| 11141 | 'mastery_score' => $row['mastery_score'], |
||
| 11142 | 'prerequisite' => $row['prerequisite'], |
||
| 11143 | 'max_time_allowed' => $row['max_time_allowed'], |
||
| 11144 | 'prerequisite_min_score' => $row['prerequisite_min_score'], |
||
| 11145 | 'prerequisite_max_score' => $row['prerequisite_max_score'], |
||
| 11146 | ]; |
||
| 11147 | } |
||
| 11148 | |||
| 11149 | return $arrLP; |
||
| 11150 | } |
||
| 11151 | |||
| 11152 | /** |
||
| 11153 | * Gets whether this SCORM learning path has been marked to use the score |
||
| 11154 | * as progress. Takes into account whether the learnpath matches (SCORM |
||
| 11155 | * content + less than 2 items). |
||
| 11156 | * |
||
| 11157 | * @return bool True if the score should be used as progress, false otherwise |
||
| 11158 | */ |
||
| 11159 | public function getUseScoreAsProgress() |
||
| 11160 | { |
||
| 11161 | // If not a SCORM, we don't care about the setting |
||
| 11162 | if (2 != $this->get_type()) { |
||
| 11163 | return false; |
||
| 11164 | } |
||
| 11165 | // If more than one step in the SCORM, we don't care about the setting |
||
| 11166 | if ($this->get_total_items_count() > 1) { |
||
| 11167 | return false; |
||
| 11168 | } |
||
| 11169 | $extraFieldValue = new ExtraFieldValue('lp'); |
||
| 11170 | $doUseScore = false; |
||
| 11171 | $useScore = $extraFieldValue->get_values_by_handler_and_field_variable( |
||
| 11172 | $this->get_id(), |
||
| 11173 | 'use_score_as_progress' |
||
| 11174 | ); |
||
| 11175 | if (!empty($useScore) && isset($useScore['value'])) { |
||
| 11176 | $doUseScore = $useScore['value']; |
||
| 11177 | } |
||
| 11178 | |||
| 11179 | return $doUseScore; |
||
| 11180 | } |
||
| 11181 | |||
| 11182 | /** |
||
| 11183 | * Get the user identifier (user_id or username |
||
| 11184 | * Depends on scorm_api_username_as_student_id in app/config/configuration.php. |
||
| 11185 | * |
||
| 11186 | * @return string User ID or username, depending on configuration setting |
||
| 11187 | */ |
||
| 11188 | public static function getUserIdentifierForExternalServices() |
||
| 11202 | } |
||
| 11203 | } |
||
| 11204 | |||
| 11205 | /** |
||
| 11206 | * Save the new order for learning path items. |
||
| 11207 | * |
||
| 11208 | * We have to update parent_item_id, previous_item_id, next_item_id, display_order in the database. |
||
| 11209 | * |
||
| 11210 | * @param array $orderList A associative array with item ID as key and parent ID as value. |
||
| 11211 | * @param int $courseId |
||
| 11212 | */ |
||
| 11213 | public static function sortItemByOrderList(array $orderList, $courseId = 0) |
||
| 11214 | { |
||
| 11215 | $courseId = $courseId ?: api_get_course_int_id(); |
||
| 11216 | $itemList = new LpItemOrderList(); |
||
| 11217 | |||
| 11259 | ] |
||
| 11260 | ); |
||
| 11261 | } |
||
| 11262 | } |
||
| 11263 | |||
| 11264 | /** |
||
| 11265 | * Get the depth level of LP item. |
||
| 11266 | * |
||
| 11267 | * @param array $items |
||
| 11268 | * @param int $currentItemId |
||
| 11269 | * |
||
| 11270 | * @return int |
||
| 11271 | */ |
||
| 11272 | private static function get_level_for_item($items, $currentItemId) |
||
| 11273 | { |
||
| 11274 | $parentItemId = 0; |
||
| 11275 | if (isset($items[$currentItemId])) { |
||
| 11276 | $parentItemId = $items[$currentItemId]->parent; |
||
| 11277 | } |
||
| 11278 | |||
| 11279 | if (0 == $parentItemId) { |
||
| 11280 | return 0; |
||
| 11281 | } |
||
| 11282 | |||
| 11283 | return self::get_level_for_item($items, $parentItemId) + 1; |
||
| 11284 | } |
||
| 11285 | |||
| 11286 | /** |
||
| 11287 | * Generate the link for a learnpath category as course tool. |
||
| 11288 | * |
||
| 11289 | * @param int $categoryId |
||
| 11290 | * |
||
| 11291 | * @return string |
||
| 11292 | */ |
||
| 11293 | private static function getCategoryLinkForTool($categoryId) |
||
| 11294 | { |
||
| 11295 | $categoryId = (int) $categoryId; |
||
| 11296 | $link = 'lp/lp_controller.php?'.api_get_cidreq().'&' |
||
| 11297 | .http_build_query( |
||
| 11298 | [ |
||
| 11299 | 'action' => 'view_category', |
||
| 11300 | 'id' => $categoryId, |
||
| 11301 | ] |
||
| 11302 | ); |
||
| 11303 | |||
| 11304 | return $link; |
||
| 11305 | } |
||
| 11306 | |||
| 11307 | /** |
||
| 11308 | * Return the scorm item type object with spaces replaced with _ |
||
| 11309 | * The return result is use to build a css classname like scorm_type_$return. |
||
| 11310 | * |
||
| 11311 | * @param $in_type |
||
| 11312 | * |
||
| 11313 | * @return mixed |
||
| 11314 | */ |
||
| 11315 | private static function format_scorm_type_item($in_type) |
||
| 11316 | { |
||
| 11317 | return str_replace(' ', '_', $in_type); |
||
| 11318 | } |
||
| 11319 | |||
| 11320 | /** |
||
| 11321 | * Check and obtain the lp final item if exist. |
||
| 11322 | * |
||
| 11323 | * @return learnpathItem |
||
| 11324 | */ |
||
| 11325 | private function getFinalItem() |
||
| 11326 | { |
||
| 11327 | if (empty($this->items)) { |
||
| 11328 | return null; |
||
| 11329 | } |
||
| 11330 | |||
| 11331 | foreach ($this->items as $item) { |
||
| 11332 | if ('final_item' !== $item->type) { |
||
| 11333 | continue; |
||
| 11334 | } |
||
| 11335 | |||
| 11336 | return $item; |
||
| 11337 | } |
||
| 11338 | } |
||
| 11339 | |||
| 11340 | /** |
||
| 11341 | * Get the LP Final Item Template. |
||
| 11342 | * |
||
| 11343 | * @return string |
||
| 11344 | */ |
||
| 11345 | private function getFinalItemTemplate() |
||
| 11346 | { |
||
| 11347 | return file_get_contents(api_get_path(SYS_CODE_PATH).'lp/final_item_template/template.html'); |
||
| 11348 | } |
||
| 11349 | |||
| 11350 | /** |
||
| 11351 | * Get the LP Final Item Url. |
||
| 11352 | * |
||
| 11353 | * @return string |
||
| 11354 | */ |
||
| 11355 | private function getSavedFinalItem() |
||
| 11368 | } |
||
| 11369 | } |
||
| 11370 |
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return,dieorexitstatements that have been added for debug purposes.In the above example, the last
return falsewill never be executed, because a return statement has already been met in every possible execution path.