Completed
Push — master ( 17e47f...c8c08e )
by Julito
65:46 queued 26:40
created

lp_ajax_save_item.php ➔ save_item()   F

Complexity

Conditions 112
Paths > 20000

Size

Total Lines 434
Code Lines 218

Duplication

Lines 52
Ratio 11.98 %

Importance

Changes 0
Metric Value
cc 112
eloc 218
nc 4294967295
nop 18
dl 52
loc 434
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
use Chamilo\CoreBundle\Framework\Container;
6
7
/**
8
 * This script contains the server part of the AJAX interaction process.
9
 * The client part is located * in lp_api.php or other api's.
10
 * @package chamilo.learnpath
11
 * @author Yannick Warnier <[email protected]>
12
 */
13
14
// Flag to allow for anonymous user - needs to be set before global.inc.php.
15
$use_anonymous = true;
16
//require_once '../inc/global.inc.php';
17
18
/**
19
 * Writes an item's new values into the database and returns the operation result
20
 * @param   int $lp_id Learnpath ID
21
 * @param   int $user_id User ID
22
 * @param   int $view_id View ID
23
 * @param   int $item_id Item ID
24
 * @param   float  $score Current score
25
 * @param   float  $max Maximum score
26
 * @param   float  $min Minimum score
27
 * @param   string  $status Lesson status
28
 * @param   int  $time Session time
29
 * @param   string  $suspend Suspend data
30
 * @param   string  $location Lesson location
31
 * @param   array   $interactions Interactions array
32
 * @param   string  $core_exit Core exit SCORM string
33
 * @param   int     $sessionId Session ID
34
 * @param   int     $courseId Course ID
35
 * @param   int     $lmsFinish Whether the call was issued from SCORM's LMSFinish()
36
 * @param   int     $userNavigatesAway Whether the user is moving to another item
37
 * @param   int     $statusSignalReceived Whether the SCO called SetValue(lesson_status)
38
 * @return bool|null|string The resulting JS string
39
 */
40
function save_item(
41
    $lp_id,
42
    $user_id,
43
    $view_id,
44
    $item_id,
45
    $score = -1.0,
46
    $max = -1.0,
47
    $min = -1.0,
48
    $status = '',
49
    $time = 0,
50
    $suspend = '',
51
    $location = '',
52
    $interactions = array(),
53
    $core_exit = 'none',
54
    $sessionId = null,
55
    $courseId = null,
56
    $lmsFinish = 0,
57
    $userNavigatesAway = 0,
58
    $statusSignalReceived = 0
59
) {
60
    //global $debug;
61
    $debug = 0;
62
    $return = null;
63
64
    if ($debug > 0) {
65
        error_log('lp_ajax_save_item.php : save_item() params: ');
66
        error_log("item_id: $item_id");
67
        error_log("lp_id: $lp_id - user_id: - $user_id - view_id: $view_id - item_id: $item_id");
68
        error_log("score: $score - max:$max - min: $min - status:$status");
69
        error_log("time:$time - suspend: $suspend - location: $location - core_exit: $core_exit");
70
        error_log("finish: $lmsFinish - navigatesAway: $userNavigatesAway");
71
    }
72
73
    $myLP = learnpath::getLpFromSession(api_get_course_id(), $lp_id, $user_id);
74
75
    if (!is_a($myLP, 'learnpath')) {
76
        if ($debug) {
77
            error_log("mylp variable is not an learnpath object");
78
        }
79
80
        return null;
81
    }
82
83
    $prerequisitesCheck = $myLP->prerequisites_match($item_id);
84
85
    /** @var learnpathItem $myLPI */
86
    $myLPI = isset($myLP->items[$item_id]) ? $myLP->items[$item_id] : '';
87
88
    if (empty($myLPI)) {
89
        if ($debug > 0) {
90
            error_log("item #$item_id not found in the items array: ".print_r($myLP->items, 1));
91
        }
92
93
        return false;
94
    }
95
96
    // This functions sets the $this->db_item_view_id variable needed in get_status() see BT#5069
97
    $myLPI->set_lp_view($view_id);
98
99
    // Launch the prerequisites check and set error if needed
100
    if ($prerequisitesCheck !== true) {
101
        // If prerequisites were not matched, don't update any item info
102
        if ($debug) {
103
            error_log("prereq_check: ".intval($prerequisitesCheck));
104
        }
105
106
        return $return;
107
    } else {
108
        if ($debug > 1) {
109
            error_log('Prerequisites are OK');
110
        }
111
112 View Code Duplication
        if (isset($max) && $max != -1) {
113
            $myLPI->max_score = $max;
114
            $myLPI->set_max_score($max);
115
            if ($debug > 1) {
116
                error_log("Setting max_score: $max");
117
            }
118
        }
119
120 View Code Duplication
        if (isset($min) && $min != -1 && $min != 'undefined') {
121
            $myLPI->min_score = $min;
122
            if ($debug > 1) {
123
                error_log("Setting min_score: $min");
124
            }
125
        }
126
127
        // set_score function used to save the status, but this is not the case anymore
128
        if (isset($score) && $score != -1) {
129
            if ($debug > 1) {
130
                error_log('Calling set_score('.$score.')', 0);
131
                error_log('set_score changes the status to failed/passed if mastery score is provided', 0);
132
            }
133
134
            $myLPI->set_score($score);
135
136
            if ($debug > 1) {
137
                error_log('Done calling set_score '.$myLPI->get_score(), 0);
138
            }
139
        } else {
140
            if ($debug > 1) {
141
                error_log("Score not updated");
142
            }
143
        }
144
145
        $statusIsSet = false;
146
        // Default behaviour.
147 View Code Duplication
        if (isset($status) && $status != '' && $status != 'undefined') {
148
            if ($debug > 1) {
149
                error_log('Calling set_status('.$status.')', 0);
150
            }
151
152
            $myLPI->set_status($status);
153
            $statusIsSet = true;
154
            if ($debug > 1) {
155
                error_log('Done calling set_status: checking from memory: '.$myLPI->get_status(false), 0);
156
            }
157
        } else {
158
            if ($debug > 1) {
159
                error_log("Status not updated");
160
            }
161
        }
162
163
        $my_type = $myLPI->get_type();
164
        // Set status to completed for hotpotatoes if score > 80%.
165
        if ($my_type == 'hotpotatoes') {
166
            if ((empty($status) || $status == 'undefined' || $status == 'not attempted') && $max > 0) {
167
                if (($score/$max) > 0.8) {
168
                    $myStatus = 'completed';
169
                    if ($debug > 1) {
170
                        error_log('Calling set_status('.$myStatus.') for hotpotatoes', 0);
171
                    }
172
                    $myLPI->set_status($myStatus);
173
                    $statusIsSet = true;
174
                    if ($debug > 1) {
175
                        error_log('Done calling set_status for hotpotatoes - now '.$myLPI->get_status(false), 0);
176
                    }
177
                }
178
            } elseif ($status == 'completed' && $max > 0 && ($score/$max) < 0.8) {
179
                $myStatus = 'failed';
180
                if ($debug > 1) {
181
                    error_log('Calling set_status('.$myStatus.') for hotpotatoes', 0);
182
                }
183
                $myLPI->set_status($myStatus);
184
                $statusIsSet = true;
185
                if ($debug > 1) {
186
                    error_log('Done calling set_status for hotpotatoes - now '.$myLPI->get_status(false), 0);
187
                }
188
            }
189
        } elseif ($my_type == 'sco') {
190
            /*
191
             * This is a specific implementation for SCORM 1.2, matching page 26 of SCORM 1.2's RTE
192
             * "Normally the SCO determines its own status and passes it to the LMS.
193
             * 1) If cmi.core.credit is set to "credit" and there is a mastery
194
             *    score in the manifest (adlcp:masteryscore), the LMS can change
195
             *    the status to either passed or failed depending on the
196
             *    student's score compared to the mastery score.
197
             * 2) If there is no mastery score in the manifest
198
             *    (adlcp:masteryscore), the LMS cannot override SCO
199
             *    determined status.
200
             * 3) If the student is taking the SCO for no-credit, there is no
201
             *    change to the lesson_status, with one exception.  If the
202
             *    lesson_mode is "browse", the lesson_status may change to
203
             *    "browsed" even if the cmi.core.credit is set to no-credit.
204
             * "
205
             * Additionally, the LMS behaviour should be:
206
             * If a SCO sets the cmi.core.lesson_status then there is no problem.
207
             * However, the SCORM does not force the SCO to set the cmi.core.lesson_status.
208
             * There is some additional requirements that must be adhered to
209
             * successfully handle these cases:
210
             * Upon initial launch
211
             *   the LMS should set the cmi.core.lesson_status to "not attempted".
212
             * Upon receiving the LMSFinish() call or the user navigates away,
213
             *   the LMS should set the cmi.core.lesson_status for the SCO to "completed".
214
             * After setting the cmi.core.lesson_status to "completed",
215
             *   the LMS should now check to see if a Mastery Score has been
216
             *   specified in the cmi.student_data.mastery_score, if supported,
217
             *   or the manifest that the SCO is a member of.
218
             *   If a Mastery Score is provided and the SCO did set the
219
             *   cmi.core.score.raw, the LMS shall compare the cmi.core.score.raw
220
             *   to the Mastery Score and set the cmi.core.lesson_status to
221
             *   either "passed" or "failed".  If no Mastery Score is provided,
222
             *   the LMS will leave the cmi.core.lesson_status as "completed"
223
             */
224
            $masteryScore = $myLPI->get_mastery_score();
225
            if ($masteryScore == -1 || empty($masteryScore)) {
226
                $masteryScore = false;
227
            }
228
            $credit = $myLPI->get_credit();
229
230
            /**
231
             * 1) If cmi.core.credit is set to "credit" and there is a mastery
232
             *    score in the manifest (adlcp:masteryscore), the LMS can change
233
             *    the status to either passed or failed depending on the
234
             *    student's score compared to the mastery score.
235
             */
236
            if ($credit == 'credit' &&
237
                $masteryScore &&
238
                (isset($score) && $score != -1) &&
239
                !$statusIsSet && !$statusSignalReceived
240
            ) {
241
                if ($score >= $masteryScore) {
242
                    $myLPI->set_status('passed');
243
                } else {
244
                    $myLPI->set_status('failed');
245
                }
246
                $statusIsSet = true;
247
            }
248
249
            /**
250
             *  2) If there is no mastery score in the manifest
251
             *    (adlcp:masteryscore), the LMS cannot override SCO
252
             *    determined status.
253
             */
254
            if (!$statusIsSet && !$masteryScore && !$statusSignalReceived) {
255
                if (!empty($status)) {
256
                    $myLPI->set_status($status);
257
                    $statusIsSet = true;
258
                }
259
                //if no status was set directly, we keep the previous one
260
            }
261
262
            /**
263
             * 3) If the student is taking the SCO for no-credit, there is no
264
             *    change to the lesson_status, with one exception. If the
265
             *    lesson_mode is "browse", the lesson_status may change to
266
             *    "browsed" even if the cmi.core.credit is set to no-credit.
267
             */
268
            if (!$statusIsSet && $credit == 'no-credit' && !$statusSignalReceived) {
269
                $mode = $myLPI->get_lesson_mode();
270
                if ($mode == 'browse' && $status == 'browsed') {
271
                    $myLPI->set_status($status);
272
                    $statusIsSet = true;
273
                }
274
                //if no status was set directly, we keep the previous one
275
            }
276
277
            /**
278
             * If a SCO sets the cmi.core.lesson_status then there is no problem.
279
             * However, the SCORM does not force the SCO to set the
280
             * cmi.core.lesson_status.  There is some additional requirements
281
             * that must be adhered to successfully handle these cases:
282
             */
283
            if (!$statusIsSet && empty($status) && !$statusSignalReceived) {
284
                /**
285
                 * Upon initial launch the LMS should set the
286
                 * cmi.core.lesson_status to "not attempted".
287
                 */
288
                // this case should be handled by LMSInitialize() and xajax_switch_item()
289
                /**
290
                 * Upon receiving the LMSFinish() call or the user navigates
291
                 * away, the LMS should set the cmi.core.lesson_status for the
292
                 * SCO to "completed".
293
                 */
294
                if ($lmsFinish || $userNavigatesAway) {
295
                    $myStatus = 'completed';
296
                    /**
297
                     * After setting the cmi.core.lesson_status to "completed",
298
                     *   the LMS should now check to see if a Mastery Score has been
299
                     *   specified in the cmi.student_data.mastery_score, if supported,
300
                     *   or the manifest that the SCO is a member of.
301
                     *   If a Mastery Score is provided and the SCO did set the
302
                     *   cmi.core.score.raw, the LMS shall compare the cmi.core.score.raw
303
                     *   to the Mastery Score and set the cmi.core.lesson_status to
304
                     *   either "passed" or "failed".  If no Mastery Score is provided,
305
                     *   the LMS will leave the cmi.core.lesson_status as "completed”
306
                     */
307
                    if ($masteryScore && (isset($score) && $score != -1)) {
308
                        if ($score >= $masteryScore) {
309
                            $myStatus = 'passed';
310
                        } else {
311
                            $myStatus = 'failed';
312
                        }
313
                    }
314
                    $myLPI->set_status($myStatus);
315
                    $statusIsSet = true;
316
                }
317
            }
318
            // End of type=='sco'
319
        }
320
321
        // If no previous condition changed the SCO status, proceed with a
322
        // generic behaviour
323
        if (!$statusIsSet && !$statusSignalReceived) {
324
            // Default behaviour
325 View Code Duplication
            if (isset($status) && $status != '' && $status != 'undefined') {
326
                if ($debug > 1) {
327
                    error_log('Calling set_status('.$status.')', 0);
328
                }
329
330
                $myLPI->set_status($status);
331
332
                if ($debug > 1) {
333
                    error_log('Done calling set_status: checking from memory: '.$myLPI->get_status(false), 0);
334
                }
335
            } else {
336
                if ($debug > 1) {
337
                    error_log("Status not updated");
338
                }
339
            }
340
        }
341
342
        if (isset($time) && $time != '' && $time != 'undefined') {
343
            // If big integer, then it's a timestamp, otherwise it's normal scorm time.
344
            if ($debug > 1) {
345
                error_log('Calling set_time('.$time.') ', 0);
346
            }
347
            if ($time == intval(strval($time)) && $time > 1000000) {
348
                if ($debug > 1) {
349
                    error_log("Time is INT");
350
                }
351
                $real_time = time() - $time;
352
                if ($debug > 1) {
353
                    error_log('Calling $real_time '.$real_time.' ', 0);
354
                }
355
                $myLPI->set_time($real_time, 'int');
356
            } else {
357
                if ($debug > 1) {
358
                    error_log("Time is in SCORM format");
359
                }
360
                if ($debug > 1) {
361
                    error_log('Calling $time '.$time.' ', 0);
362
                }
363
                $myLPI->set_time($time, 'scorm');
364
            }
365
        }
366
367
        if (isset($suspend) && $suspend != '' && $suspend != 'undefined') {
368
            $myLPI->current_data = $suspend;
369
        }
370
371
        if (isset($location) && $location != '' && $location!='undefined') {
372
            $myLPI->set_lesson_location($location);
373
        }
374
375
        // Deal with interactions provided in arrays in the following format:
376
        // id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
377
        if (is_array($interactions) && count($interactions) > 0) {
378
            foreach ($interactions as $index => $interaction) {
379
                //$mylpi->add_interaction($index,$interactions[$index]);
380
                //fix DT#4444
381
                $clean_interaction = str_replace('@.|@', ',', $interactions[$index]);
382
                $myLPI->add_interaction($index, $clean_interaction);
383
            }
384
        }
385
386
        if ($core_exit != 'undefined') {
387
            $myLPI->set_core_exit($core_exit);
388
        }
389
        $myLP->save_item($item_id, false);
390
    }
391
392
    $myStatusInDB = $myLPI->get_status(true);
393
    if ($debug) {
394
        error_log("Status in DB: $myStatusInDB");
395
    }
396
397
    if ($myStatusInDB != 'completed' &&
398
        $myStatusInDB != 'passed' &&
399
        $myStatusInDB != 'browsed' &&
400
        $myStatusInDB != 'failed'
401
    ) {
402
        $myStatusInMemory = $myLPI->get_status(false);
403
        if ($myStatusInMemory != $myStatusInDB) {
404
            $myStatus = $myStatusInMemory;
405
        } else {
406
            $myStatus = $myStatusInDB;
407
        }
408
    } else {
409
        $myStatus = $myStatusInDB;
410
    }
411
412
    $myTotal = $myLP->get_total_items_count_without_chapters();
413
    $myComplete = $myLP->get_complete_items_count();
414
    $myProgressMode = $myLP->get_progress_bar_mode();
415
    $myProgressMode = $myProgressMode == '' ? '%' : $myProgressMode;
416
417
    if ($debug > 1) {
418
        error_log("mystatus: $myStatus", 0);
419
        error_log("myprogress_mode: $myProgressMode", 0);
420
        error_log("progress: $myComplete / $myTotal", 0);
421
    }
422
423
    if ($myLPI->get_type() != 'sco') {
424
        // If this object's JS status has not been updated by the SCORM API, update now.
425
        $return .= "olms.lesson_status='".$myStatus."';";
426
    }
427
    $return .= "update_toc('".$myStatus."','".$item_id."');";
428
    $update_list = $myLP->get_update_queue();
429
430
    foreach ($update_list as $my_upd_id => $my_upd_status) {
431
        if ($my_upd_id != $item_id) {
432
            /* Only update the status from other items (i.e. parents and brothers),
433
            do not update current as we just did it already. */
434
            $return .= "update_toc('".$my_upd_status."','".$my_upd_id."');";
435
        }
436
    }
437
    $return .= "update_progress_bar('$myComplete', '$myTotal', '$myProgressMode');";
438
    $isLoginAs = Container::getAuthorizationChecker()->isGranted('ROLE_PREVIOUS_ADMIN');
439
    if (!$isLoginAs) {
440
        $tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
441
442
        $sql = "SELECT login_id, login_date
443
                FROM $tbl_track_login
444
                WHERE login_user_id= ".api_get_user_id()."
445
                ORDER BY login_date DESC
446
                LIMIT 0,1";
447
448
        $q_last_connection = Database::query($sql);
449 View Code Duplication
        if (Database::num_rows($q_last_connection) > 0) {
450
            $current_time = api_get_utc_datetime();
451
            $row = Database::fetch_array($q_last_connection);
452
            $i_id_last_connection = $row['login_id'];
453
            $sql = "UPDATE $tbl_track_login
454
                    SET logout_date='".$current_time."'
455
                    WHERE login_id = $i_id_last_connection";
456
            Database::query($sql);
457
        }
458
    }
459
460
    if ($myLP->get_type() == 2) {
461
         $return .= "update_stats();";
462
    }
463
464
    // To be sure progress is updated.
465
    $myLP->save_last();
466
467
    Session::write('lpobject', serialize($myLP));
468
    if ($debug > 0) {
469
        error_log('---------------- lp_ajax_save_item.php : save_item end ----- ');
470
    }
471
472
    return $return;
473
}
474
475
$interactions = array();
476 View Code Duplication
if (isset($_REQUEST['interact'])) {
477
    if (is_array($_REQUEST['interact'])) {
478
        foreach ($_REQUEST['interact'] as $idx => $interac) {
479
            $interactions[$idx] = preg_split('/,/', substr($interac, 1, -1));
480
            if (!isset($interactions[$idx][7])) { // Make sure there are 7 elements.
481
                $interactions[$idx][7] = '';
482
            }
483
        }
484
    }
485
}
486
487
echo save_item(
488
    (!empty($_REQUEST['lid']) ? $_REQUEST['lid'] : null),
489
    (!empty($_REQUEST['uid']) ? $_REQUEST['uid'] : null),
490
    (!empty($_REQUEST['vid']) ? $_REQUEST['vid'] : null),
491
    (!empty($_REQUEST['iid']) ? $_REQUEST['iid'] : null),
492
    (!empty($_REQUEST['s']) ? $_REQUEST['s'] : null),
493
    (!empty($_REQUEST['max']) ? $_REQUEST['max'] : null),
494
    (!empty($_REQUEST['min']) ? $_REQUEST['min'] : null),
495
    (!empty($_REQUEST['status']) ? $_REQUEST['status'] : null),
496
    (!empty($_REQUEST['t']) ? $_REQUEST['t'] : null),
497
    (!empty($_REQUEST['suspend']) ? $_REQUEST['suspend'] : null),
498
    (!empty($_REQUEST['loc']) ? $_REQUEST['loc'] : null),
499
    $interactions,
500
    (!empty($_REQUEST['core_exit']) ? $_REQUEST['core_exit'] : ''),
501
    (!empty($_REQUEST['session_id']) ? $_REQUEST['session_id'] : ''),
502
    (!empty($_REQUEST['course_id']) ? $_REQUEST['course_id'] : ''),
503
    (empty($_REQUEST['finish']) ? 0 : 1),
504
    (empty($_REQUEST['userNavigatesAway']) ? 0 : 1),
505
    (empty($_REQUEST['statusSignalReceived']) ? 0 : 1)
506
);
507
508
Container::$legacyTemplate = 'layout_empty.html.twig';
509