Issues (2037)

main/inc/lib/api.lib.php (1 issue)

Code
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\SettingsCurrent;
6
use Chamilo\CourseBundle\Entity\CItemProperty;
7
use Chamilo\UserBundle\Entity\User;
8
use ChamiloSession as Session;
9
use League\OAuth2\Client\Provider\GenericProvider;
10
use PHPMailer\PHPMailer\OAuth;
11
use PHPMailer\PHPMailer\PHPMailer;
12
use Symfony\Component\Finder\Finder;
13
14
/**
15
 * This is a code library for Chamilo.
16
 * It is included by default in every Chamilo file (through including the global.inc.php)
17
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
18
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
19
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
20
 */
21
22
// PHP version requirement.
23
define('REQUIRED_PHP_VERSION', '7.4');
24
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
25
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
26
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
27
28
// USER STATUS CONSTANTS
29
/** global status of a user: student */
30
define('STUDENT', 5);
31
/** global status of a user: course manager */
32
define('COURSEMANAGER', 1);
33
/** global status of a user: session admin */
34
define('SESSIONADMIN', 3);
35
/** global status of a user: human ressource manager */
36
define('DRH', 4);
37
/** global status of a user: human ressource manager */
38
define('ANONYMOUS', 6);
39
/** global status of a user: low security, necessary for inserting data from
40
 * the teacher through HTMLPurifier */
41
define('COURSEMANAGERLOWSECURITY', 10);
42
// Soft user status
43
define('PLATFORM_ADMIN', 11);
44
define('SESSION_COURSE_COACH', 12);
45
define('SESSION_GENERAL_COACH', 13);
46
define('COURSE_STUDENT', 14); //student subscribed in a course
47
define('SESSION_STUDENT', 15); //student subscribed in a session course
48
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
49
define('STUDENT_BOSS', 17); // student is boss
50
define('INVITEE', 20);
51
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
52
define('COURSE_EXLEARNER', 22);
53
54
// Table of status
55
$_status_list[COURSEMANAGER] = 'teacher'; // 1
56
$_status_list[SESSIONADMIN] = 'session_admin'; // 3
57
$_status_list[DRH] = 'drh'; // 4
58
$_status_list[STUDENT] = 'user'; // 5
59
$_status_list[ANONYMOUS] = 'anonymous'; // 6
60
$_status_list[INVITEE] = 'invited'; // 20
61
62
// COURSE VISIBILITY CONSTANTS
63
/** only visible for course admin */
64
define('COURSE_VISIBILITY_CLOSED', 0);
65
/** only visible for users registered in the course */
66
define('COURSE_VISIBILITY_REGISTERED', 1);
67
/** Open for all registered users on the platform */
68
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
69
/** Open for the whole world */
70
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
71
/** Invisible to all except admin */
72
define('COURSE_VISIBILITY_HIDDEN', 4);
73
74
define('COURSE_REQUEST_PENDING', 0);
75
define('COURSE_REQUEST_ACCEPTED', 1);
76
define('COURSE_REQUEST_REJECTED', 2);
77
define('DELETE_ACTION_ENABLED', false);
78
79
// EMAIL SENDING RECIPIENT CONSTANTS
80
define('SEND_EMAIL_EVERYONE', 1);
81
define('SEND_EMAIL_STUDENTS', 2);
82
define('SEND_EMAIL_TEACHERS', 3);
83
84
// SESSION VISIBILITY CONSTANTS
85
define('SESSION_VISIBLE_READ_ONLY', 1);
86
define('SESSION_VISIBLE', 2);
87
define('SESSION_INVISIBLE', 3); // not available
88
define('SESSION_AVAILABLE', 4);
89
90
define('SESSION_LINK_TARGET', '_self');
91
92
define('SUBSCRIBE_ALLOWED', 1);
93
define('SUBSCRIBE_NOT_ALLOWED', 0);
94
define('UNSUBSCRIBE_ALLOWED', 1);
95
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
96
97
// SURVEY VISIBILITY CONSTANTS
98
define('SURVEY_VISIBLE_TUTOR', 0);
99
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
100
define('SURVEY_VISIBLE_PUBLIC', 2);
101
102
// CONSTANTS defining all tools, using the english version
103
/* When you add a new tool you must add it into function api_get_tools_lists() too */
104
define('TOOL_DOCUMENT', 'document');
105
define('TOOL_LP_FINAL_ITEM', 'final_item');
106
define('TOOL_READOUT_TEXT', 'readout_text');
107
define('TOOL_THUMBNAIL', 'thumbnail');
108
define('TOOL_HOTPOTATOES', 'hotpotatoes');
109
define('TOOL_CALENDAR_EVENT', 'calendar_event');
110
define('TOOL_LINK', 'link');
111
define('TOOL_LINK_CATEGORY', 'link_category');
112
define('TOOL_COURSE_DESCRIPTION', 'course_description');
113
define('TOOL_SEARCH', 'search');
114
define('TOOL_LEARNPATH', 'learnpath');
115
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
116
define('TOOL_AGENDA', 'agenda');
117
define('TOOL_ANNOUNCEMENT', 'announcement');
118
define('TOOL_FORUM', 'forum');
119
define('TOOL_FORUM_CATEGORY', 'forum_category');
120
define('TOOL_FORUM_THREAD', 'forum_thread');
121
define('TOOL_FORUM_POST', 'forum_post');
122
define('TOOL_FORUM_ATTACH', 'forum_attachment');
123
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
124
define('TOOL_THREAD', 'thread');
125
define('TOOL_POST', 'post');
126
define('TOOL_DROPBOX', 'dropbox');
127
define('TOOL_QUIZ', 'quiz');
128
define('TOOL_TEST_CATEGORY', 'test_category');
129
define('TOOL_USER', 'user');
130
define('TOOL_GROUP', 'group');
131
define('TOOL_BLOGS', 'blog_management');
132
define('TOOL_CHAT', 'chat');
133
define('TOOL_STUDENTPUBLICATION', 'student_publication');
134
define('TOOL_TRACKING', 'tracking');
135
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
136
define('TOOL_COURSE_SETTING', 'course_setting');
137
define('TOOL_BACKUP', 'backup');
138
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
139
define('TOOL_RECYCLE_COURSE', 'recycle_course');
140
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
141
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
142
define('TOOL_UPLOAD', 'file_upload');
143
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
144
define('TOOL_SURVEY', 'survey');
145
define('TOOL_WIKI', 'wiki');
146
define('TOOL_GLOSSARY', 'glossary');
147
define('TOOL_GRADEBOOK', 'gradebook');
148
define('TOOL_NOTEBOOK', 'notebook');
149
define('TOOL_ATTENDANCE', 'attendance');
150
define('TOOL_COURSE_PROGRESS', 'course_progress');
151
define('TOOL_PORTFOLIO', 'portfolio');
152
define('TOOL_PORTFOLIO_COMMENT', 'portfolio_comment');
153
define('TOOL_PLAGIARISM', 'compilatio');
154
define('TOOL_XAPI', 'xapi');
155
define('TOOL_H5P', 'h5p');
156
157
// CONSTANTS defining Chamilo interface sections
158
define('SECTION_CAMPUS', 'mycampus');
159
define('SECTION_COURSES', 'mycourses');
160
define('SECTION_CATALOG', 'catalog');
161
define('SECTION_MYPROFILE', 'myprofile');
162
define('SECTION_MYAGENDA', 'myagenda');
163
define('SECTION_COURSE_ADMIN', 'course_admin');
164
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
165
define('SECTION_MYGRADEBOOK', 'mygradebook');
166
define('SECTION_TRACKING', 'session_my_space');
167
define('SECTION_SOCIAL', 'social-network');
168
define('SECTION_DASHBOARD', 'dashboard');
169
define('SECTION_REPORTS', 'reports');
170
define('SECTION_GLOBAL', 'global');
171
define('SECTION_INCLUDE', 'include');
172
define('SECTION_CUSTOMPAGE', 'custompage');
173
174
// CONSTANT name for local authentication source
175
define('PLATFORM_AUTH_SOURCE', 'platform');
176
define('CAS_AUTH_SOURCE', 'cas');
177
define('LDAP_AUTH_SOURCE', 'extldap');
178
define('IMS_LTI_SOURCE', 'ims_lti');
179
180
// CONSTANT defining the default HotPotatoes files directory
181
define('DIR_HOTPOTATOES', '/HotPotatoes_files');
182
183
// event logs types
184
define('LOG_COURSE_DELETE', 'course_deleted');
185
define('LOG_COURSE_CREATE', 'course_created');
186
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
187
188
// @todo replace 'soc_gr' with social_group
189
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
190
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
191
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
192
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
193
194
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
195
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
196
197
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
198
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
199
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
200
define('LOG_GROUP_PORTAL_COURSE_SUBSCRIBED', 'soc_gr_c_subs');
201
define('LOG_GROUP_PORTAL_COURSE_UNSUBSCRIBED', 'soc_gr_c_unsubs');
202
define('LOG_GROUP_PORTAL_SESSION_SUBSCRIBED', 'soc_gr_s_subs');
203
define('LOG_GROUP_PORTAL_SESSION_UNSUBSCRIBED', 'soc_gr_s_unsubs');
204
205
define('LOG_USER_DELETE', 'user_deleted');
206
define('LOG_USER_CREATE', 'user_created');
207
define('LOG_USER_UPDATE', 'user_updated');
208
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
209
define('LOG_USER_ENABLE', 'user_enable');
210
define('LOG_USER_DISABLE', 'user_disable');
211
define('LOG_USER_ANONYMIZE', 'user_anonymized');
212
define('LOG_USER_FIELD_CREATE', 'user_field_created');
213
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
214
define('LOG_SESSION_CREATE', 'session_created');
215
define('LOG_SESSION_DELETE', 'session_deleted');
216
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
217
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
218
define('LOG_SESSION_ADD_USER', 'session_add_user');
219
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
220
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
221
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
222
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
223
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
224
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
225
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
226
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
227
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
228
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
229
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
230
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
231
define('LOG_PROMOTION_CREATE', 'promotion_created');
232
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
233
define('LOG_CAREER_CREATE', 'career_created');
234
define('LOG_CAREER_DELETE', 'career_deleted');
235
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
236
define('LOG_WIKI_ACCESS', 'wiki_page_view');
237
define('LOG_EXERCISE_CREATE', 'exe_created');
238
define('LOG_EXERCISE_UPDATE', 'exe_updated');
239
define('LOG_EXERCISE_DELETE', 'exe_deleted');
240
define('LOG_LP_CREATE', 'lp_created');
241
define('LOG_LP_UPDATE', 'lp_updated');
242
define('LOG_LP_DELETE', 'lp_deleted');
243
// All results from an exercise
244
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
245
define('LOG_EXERCISE_RESULT_DELETE_INCOMPLETE', 'exe_incomplete_results_deleted');
246
// Logs only the one attempt
247
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
248
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
249
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
250
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
251
252
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
253
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
254
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
255
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
256
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
257
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
258
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
259
260
// Event logs data types (max 20 chars)
261
define('LOG_COURSE_CODE', 'course_code');
262
define('LOG_COURSE_ID', 'course_id');
263
define('LOG_USER_ID', 'user_id');
264
define('LOG_USER_OBJECT', 'user_object');
265
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
266
define('LOG_SESSION_ID', 'session_id');
267
268
define('LOG_QUESTION_ID', 'question_id');
269
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
270
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
271
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
272
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
273
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
274
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
275
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
276
define('LOG_CAREER_ID', 'career_id');
277
define('LOG_PROMOTION_ID', 'promotion_id');
278
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
279
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
280
define('LOG_GRADEBOOK_ID', 'gradebook_id');
281
define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
282
define('LOG_EXERCISE_ID', 'exercise_id');
283
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
284
define('LOG_LP_ID', 'lp_id');
285
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
286
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
287
288
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
289
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
290
define('LOG_WORK_DATA', 'work_data_array');
291
292
define('LOG_MY_FOLDER_PATH', 'path');
293
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
294
295
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
296
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
297
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
298
299
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
300
301
define('LOG_QUESTION_CREATED', 'question_created');
302
define('LOG_QUESTION_UPDATED', 'question_updated');
303
define('LOG_QUESTION_DELETED', 'question_deleted');
304
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
305
306
define('LOG_SURVEY_ID', 'survey_id');
307
define('LOG_SURVEY_CREATED', 'survey_created');
308
define('LOG_SURVEY_DELETED', 'survey_deleted');
309
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
310
311
define('LOG_WS', 'access_ws_');
312
313
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
314
315
//used when login_is_email setting is true
316
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
317
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
318
319
// This constant is a result of Windows OS detection, it has a boolean value:
320
// true whether the server runs on Windows OS, false otherwise.
321
define('IS_WINDOWS_OS', api_is_windows_os());
322
323
// Checks for installed optional php-extensions.
324
// intl extension (from PECL), it is installed by default as of PHP 5.3.0.
325
define('INTL_INSTALLED', function_exists('intl_get_error_code'));
326
// iconv extension, for PHP5 on Windows it is installed by default.
327
define('ICONV_INSTALLED', function_exists('iconv'));
328
define('MBSTRING_INSTALLED', function_exists('mb_strlen')); // mbstring extension.
329
330
// Patterns for processing paths. Examples.
331
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
332
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
333
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
334
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
335
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
336
// basic (leaf elements)
337
define('REL_CODE_PATH', 'REL_CODE_PATH');
338
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
339
define('REL_HOME_PATH', 'REL_HOME_PATH');
340
341
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
342
define('WEB_PATH', 'WEB_PATH');
343
define('WEB_APP_PATH', 'WEB_APP_PATH');
344
define('SYS_PATH', 'SYS_PATH');
345
define('SYS_APP_PATH', 'SYS_APP_PATH');
346
define('SYS_UPLOAD_PATH', 'SYS_UPLOAD_PATH');
347
define('WEB_UPLOAD_PATH', 'WEB_UPLOAD_PATH');
348
349
define('REL_PATH', 'REL_PATH');
350
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
351
define('SYS_COURSE_PATH', 'SYS_COURSE_PATH');
352
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
353
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
354
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
355
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
356
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
357
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
358
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
359
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
360
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
361
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
362
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
363
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
364
define('SYS_INC_PATH', 'SYS_INC_PATH');
365
define('LIBRARY_PATH', 'LIBRARY_PATH');
366
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
367
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
368
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
369
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
370
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
371
define('WEB_TEMPLATE_PATH', 'WEB_TEMPLATE_PATH');
372
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
373
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
374
define('SYS_HOME_PATH', 'SYS_HOME_PATH');
375
define('WEB_HOME_PATH', 'WEB_HOME_PATH');
376
define('WEB_FONTS_PATH', 'WEB_FONTS_PATH');
377
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
378
379
define('SYS_DEFAULT_COURSE_DOCUMENT_PATH', 'SYS_DEFAULT_COURSE_DOCUMENT_PATH');
380
define('REL_DEFAULT_COURSE_DOCUMENT_PATH', 'REL_DEFAULT_COURSE_DOCUMENT_PATH');
381
define('WEB_DEFAULT_COURSE_DOCUMENT_PATH', 'WEB_DEFAULT_COURSE_DOCUMENT_PATH');
382
383
// Relations type with Course manager
384
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
385
define('SESSION_RELATION_TYPE_COURSE_MANAGER', 1);
386
387
// Relations type with Human resources manager
388
define('COURSE_RELATION_TYPE_RRHH', 1);
389
define('SESSION_RELATION_TYPE_RRHH', 1);
390
391
//User image sizes
392
define('USER_IMAGE_SIZE_ORIGINAL', 1);
393
define('USER_IMAGE_SIZE_BIG', 2);
394
define('USER_IMAGE_SIZE_MEDIUM', 3);
395
define('USER_IMAGE_SIZE_SMALL', 4);
396
397
// Relation type between users
398
define('USER_UNKNOWN', 0);
399
define('USER_RELATION_TYPE_UNKNOWN', 1);
400
define('USER_RELATION_TYPE_PARENT', 2); // should be deprecated is useless
401
define('USER_RELATION_TYPE_FRIEND', 3);
402
define('USER_RELATION_TYPE_GOODFRIEND', 4); // should be deprecated is useless
403
define('USER_RELATION_TYPE_ENEMY', 5); // should be deprecated is useless
404
define('USER_RELATION_TYPE_DELETED', 6);
405
define('USER_RELATION_TYPE_RRHH', 7);
406
define('USER_RELATION_TYPE_BOSS', 8);
407
define('USER_RELATION_TYPE_HRM_REQUEST', 9);
408
409
// Gradebook link constants
410
// Please do not change existing values, they are used in the database !
411
define('GRADEBOOK_ITEM_LIMIT', 1000);
412
413
define('LINK_EXERCISE', 1);
414
define('LINK_DROPBOX', 2);
415
define('LINK_STUDENTPUBLICATION', 3);
416
define('LINK_LEARNPATH', 4);
417
define('LINK_FORUM_THREAD', 5);
418
//define('LINK_WORK',6);
419
define('LINK_ATTENDANCE', 7);
420
define('LINK_SURVEY', 8);
421
define('LINK_HOTPOTATOES', 9);
422
define('LINK_PORTFOLIO', 10);
423
424
// Score display types constants
425
define('SCORE_DIV', 1); // X / Y
426
define('SCORE_PERCENT', 2); // XX %
427
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
428
define('SCORE_AVERAGE', 4); // XX %
429
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
430
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
431
define('SCORE_SIMPLE', 7); // X
432
define('SCORE_IGNORE_SPLIT', 8); //  ??
433
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
434
define('SCORE_CUSTOM', 10); // Good!
435
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
436
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
437
define('SCORE_ONLY_SCORE', 13); // X - Good!
438
define('SCORE_NUMERIC', 14);
439
440
define('SCORE_BOTH', 1);
441
define('SCORE_ONLY_DEFAULT', 2);
442
define('SCORE_ONLY_CUSTOM', 3);
443
444
// From display.lib.php
445
446
define('MAX_LENGTH_BREADCRUMB', 100);
447
define('ICON_SIZE_ATOM', 8);
448
define('ICON_SIZE_TINY', 16);
449
define('ICON_SIZE_SMALL', 22);
450
define('ICON_SIZE_MEDIUM', 32);
451
define('ICON_SIZE_LARGE', 48);
452
define('ICON_SIZE_BIG', 64);
453
define('ICON_SIZE_HUGE', 128);
454
define('SHOW_TEXT_NEAR_ICONS', false);
455
456
// Session catalog
457
define('CATALOG_COURSES', 0);
458
define('CATALOG_SESSIONS', 1);
459
define('CATALOG_COURSES_SESSIONS', 2);
460
461
// Hook type events, pre-process and post-process.
462
// All means to be executed for both hook event types
463
define('HOOK_EVENT_TYPE_PRE', 0);
464
define('HOOK_EVENT_TYPE_POST', 1);
465
define('HOOK_EVENT_TYPE_ALL', 10);
466
467
define('CAREER_STATUS_ACTIVE', 1);
468
define('CAREER_STATUS_INACTIVE', 0);
469
470
define('PROMOTION_STATUS_ACTIVE', 1);
471
define('PROMOTION_STATUS_INACTIVE', 0);
472
473
// Group permissions
474
define('GROUP_PERMISSION_OPEN', '1');
475
define('GROUP_PERMISSION_CLOSED', '2');
476
477
// Group user permissions
478
define('GROUP_USER_PERMISSION_ADMIN', '1'); // the admin of a group
479
define('GROUP_USER_PERMISSION_READER', '2'); // a normal user
480
define('GROUP_USER_PERMISSION_PENDING_INVITATION', '3'); // When an admin/moderator invites a user
481
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', '4'); // an user joins a group
482
define('GROUP_USER_PERMISSION_MODERATOR', '5'); // a moderator
483
define('GROUP_USER_PERMISSION_ANONYMOUS', '6'); // an anonymous user
484
define('GROUP_USER_PERMISSION_HRM', '7'); // a human resources manager
485
486
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
487
define('GROUP_IMAGE_SIZE_BIG', 2);
488
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
489
define('GROUP_IMAGE_SIZE_SMALL', 4);
490
define('GROUP_TITLE_LENGTH', 50);
491
492
// Exercise
493
// @todo move into a class
494
define('ALL_ON_ONE_PAGE', 1);
495
define('ONE_PER_PAGE', 2);
496
497
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
498
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
499
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
500
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
501
502
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
503
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
504
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
505
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
506
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
507
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
508
define('RESULT_DISABLE_RANKING', 6);
509
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
510
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
511
define('RESULT_DISABLE_RADAR', 9);
512
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
513
514
define('EXERCISE_MAX_NAME_SIZE', 80);
515
516
// Question types (edit next array as well when adding values)
517
// @todo move into a class
518
define('UNIQUE_ANSWER', 1);
519
define('MULTIPLE_ANSWER', 2);
520
define('FILL_IN_BLANKS', 3);
521
define('MATCHING', 4);
522
define('FREE_ANSWER', 5);
523
define('HOT_SPOT', 6);
524
define('HOT_SPOT_ORDER', 7);
525
define('HOT_SPOT_DELINEATION', 8);
526
define('MULTIPLE_ANSWER_COMBINATION', 9);
527
define('UNIQUE_ANSWER_NO_OPTION', 10);
528
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
529
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
530
define('ORAL_EXPRESSION', 13);
531
define('GLOBAL_MULTIPLE_ANSWER', 14);
532
define('MEDIA_QUESTION', 15);
533
define('CALCULATED_ANSWER', 16);
534
define('UNIQUE_ANSWER_IMAGE', 17);
535
define('DRAGGABLE', 18);
536
define('MATCHING_DRAGGABLE', 19);
537
define('ANNOTATION', 20);
538
define('READING_COMPREHENSION', 21);
539
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
540
define('UPLOAD_ANSWER', 23);
541
define('MATCHING_COMBINATION', 24);
542
define('MATCHING_DRAGGABLE_COMBINATION', 25);
543
define('HOT_SPOT_COMBINATION', 26);
544
define('FILL_IN_BLANKS_COMBINATION', 27);
545
define('MULTIPLE_ANSWER_DROPDOWN_COMBINATION', 28);
546
define('MULTIPLE_ANSWER_DROPDOWN', 29);
547
548
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
549
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
550
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
551
552
// Question selection type
553
define('EX_Q_SELECTION_ORDERED', 1);
554
define('EX_Q_SELECTION_RANDOM', 2);
555
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
556
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
557
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
558
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
559
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
560
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
561
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
562
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
563
564
// Used to save the skill_rel_item table
565
define('ITEM_TYPE_EXERCISE', 1);
566
define('ITEM_TYPE_HOTPOTATOES', 2);
567
define('ITEM_TYPE_LINK', 3);
568
define('ITEM_TYPE_LEARNPATH', 4);
569
define('ITEM_TYPE_GRADEBOOK', 5);
570
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
571
//define('ITEM_TYPE_FORUM', 7);
572
define('ITEM_TYPE_ATTENDANCE', 8);
573
define('ITEM_TYPE_SURVEY', 9);
574
define('ITEM_TYPE_FORUM_THREAD', 10);
575
define('ITEM_TYPE_PORTFOLIO', 11);
576
define('ITEM_TYPE_GRADEBOOK_EVALUATION', 12);
577
578
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
579
define(
580
    'QUESTION_TYPES',
581
    UNIQUE_ANSWER.':'.
582
    MULTIPLE_ANSWER.':'.
583
    FILL_IN_BLANKS.':'.
584
    MATCHING.':'.
585
    FREE_ANSWER.':'.
586
    HOT_SPOT.':'.
587
    HOT_SPOT_ORDER.':'.
588
    HOT_SPOT_DELINEATION.':'.
589
    MULTIPLE_ANSWER_COMBINATION.':'.
590
    UNIQUE_ANSWER_NO_OPTION.':'.
591
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
592
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
593
    ORAL_EXPRESSION.':'.
594
    GLOBAL_MULTIPLE_ANSWER.':'.
595
    MEDIA_QUESTION.':'.
596
    CALCULATED_ANSWER.':'.
597
    UNIQUE_ANSWER_IMAGE.':'.
598
    DRAGGABLE.':'.
599
    MATCHING_DRAGGABLE.':'.
600
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
601
    ANNOTATION
602
);
603
604
//Some alias used in the QTI exports
605
define('MCUA', 1);
606
define('TF', 1);
607
define('MCMA', 2);
608
define('FIB', 3);
609
610
// Skills
611
define('SKILL_TYPE_REQUIREMENT', 'required');
612
define('SKILL_TYPE_ACQUIRED', 'acquired');
613
define('SKILL_TYPE_BOTH', 'both');
614
615
// Message
616
define('MESSAGE_STATUS_NEW', '0');
617
define('MESSAGE_STATUS_UNREAD', '1');
618
//2 ??
619
define('MESSAGE_STATUS_DELETED', '3');
620
define('MESSAGE_STATUS_OUTBOX', '4');
621
define('MESSAGE_STATUS_INVITATION_PENDING', '5');
622
define('MESSAGE_STATUS_INVITATION_ACCEPTED', '6');
623
define('MESSAGE_STATUS_INVITATION_DENIED', '7');
624
define('MESSAGE_STATUS_WALL', '8');
625
define('MESSAGE_STATUS_WALL_DELETE', '9');
626
define('MESSAGE_STATUS_WALL_POST', '10');
627
define('MESSAGE_STATUS_CONVERSATION', '11');
628
define('MESSAGE_STATUS_FORUM', '12');
629
define('MESSAGE_STATUS_PROMOTED', '13');
630
631
// Images
632
define('IMAGE_WALL_SMALL_SIZE', 200);
633
define('IMAGE_WALL_MEDIUM_SIZE', 500);
634
define('IMAGE_WALL_BIG_SIZE', 2000);
635
define('IMAGE_WALL_SMALL', 'small');
636
define('IMAGE_WALL_MEDIUM', 'medium');
637
define('IMAGE_WALL_BIG', 'big');
638
639
// Social PLUGIN PLACES
640
define('SOCIAL_LEFT_PLUGIN', 1);
641
define('SOCIAL_CENTER_PLUGIN', 2);
642
define('SOCIAL_RIGHT_PLUGIN', 3);
643
define('CUT_GROUP_NAME', 50);
644
645
/**
646
 * FormValidator Filter.
647
 */
648
define('NO_HTML', 1);
649
define('STUDENT_HTML', 2);
650
define('TEACHER_HTML', 3);
651
define('STUDENT_HTML_FULLPAGE', 4);
652
define('TEACHER_HTML_FULLPAGE', 5);
653
654
// Timeline
655
define('TIMELINE_STATUS_ACTIVE', '1');
656
define('TIMELINE_STATUS_INACTIVE', '2');
657
658
// Event email template class
659
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
660
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
661
662
// Course home
663
define('SHORTCUTS_HORIZONTAL', 0);
664
define('SHORTCUTS_VERTICAL', 1);
665
666
// Image class
667
define('IMAGE_PROCESSOR', 'gd'); // 'imagick' or 'gd' strings
668
669
// Course copy
670
define('FILE_SKIP', 1);
671
define('FILE_RENAME', 2);
672
define('FILE_OVERWRITE', 3);
673
define('UTF8_CONVERT', false); //false by default
674
675
define('DOCUMENT', 'file');
676
define('FOLDER', 'folder');
677
678
define('RESOURCE_ASSET', 'asset');
679
define('RESOURCE_DOCUMENT', 'document');
680
define('RESOURCE_GLOSSARY', 'glossary');
681
define('RESOURCE_EVENT', 'calendar_event');
682
define('RESOURCE_LINK', 'link');
683
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
684
define('RESOURCE_LEARNPATH', 'learnpath');
685
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
686
define('RESOURCE_ANNOUNCEMENT', 'announcement');
687
define('RESOURCE_FORUM', 'forum');
688
define('RESOURCE_FORUMTOPIC', 'thread');
689
define('RESOURCE_FORUMPOST', 'post');
690
define('RESOURCE_QUIZ', 'quiz');
691
define('RESOURCE_TEST_CATEGORY', 'test_category');
692
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
693
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
694
define('RESOURCE_LINKCATEGORY', 'Link_Category');
695
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
696
define('RESOURCE_SCORM', 'Scorm');
697
define('RESOURCE_SURVEY', 'survey');
698
define('RESOURCE_SURVEYQUESTION', 'survey_question');
699
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
700
define('RESOURCE_WIKI', 'wiki');
701
define('RESOURCE_THEMATIC', 'thematic');
702
define('RESOURCE_ATTENDANCE', 'attendance');
703
define('RESOURCE_WORK', 'work');
704
define('RESOURCE_SESSION_COURSE', 'session_course');
705
define('RESOURCE_GRADEBOOK', 'gradebook');
706
define('RESOURCE_XAPI_TOOL', 'xapi_tool');
707
define('RESOURCE_H5P_TOOL', 'h5p_tool');
708
define('ADD_THEMATIC_PLAN', 6);
709
710
// Max online users to show per page (whoisonline)
711
define('MAX_ONLINE_USERS', 12);
712
713
// Number of characters maximum to show in preview of course blog posts
714
define('BLOG_MAX_PREVIEW_CHARS', 800);
715
// HTML string to replace with a 'Read more...' link
716
define('BLOG_PAGE_BREAK', '<div style="page-break-after: always"><span style="display: none;">&nbsp;</span></div>');
717
718
// Make sure the CHAMILO_LOAD_WYSIWYG constant is defined
719
// To remove CKeditor libs from HTML, set this constant to true before loading
720
if (!defined('CHAMILO_LOAD_WYSIWYG')) {
721
    define('CHAMILO_LOAD_WYSIWYG', true);
722
}
723
724
/* Constants for course home */
725
define('TOOL_PUBLIC', 'Public');
726
define('TOOL_PUBLIC_BUT_HIDDEN', 'PublicButHide');
727
define('TOOL_COURSE_ADMIN', 'courseAdmin');
728
define('TOOL_PLATFORM_ADMIN', 'platformAdmin');
729
define('TOOL_AUTHORING', 'toolauthoring');
730
define('TOOL_INTERACTION', 'toolinteraction');
731
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
732
define('TOOL_ADMIN', 'tooladmin');
733
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
734
define('TOOL_DRH', 'tool_drh');
735
define('TOOL_STUDENT_VIEW', 'toolstudentview');
736
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
737
738
/**
739
 * Inclusion of internationalization libraries.
740
 */
741
require_once __DIR__.'/internationalization.lib.php';
742
743
/**
744
 * Returns a path to a certain resource within the Chamilo area, specifyed through a parameter.
745
 * Also, this function provides conversion between path types, in this case the input path points inside the Chamilo area too.
746
 *
747
 * See $_configuration['course_folder'] in the configuration.php to alter the WEB_COURSE_PATH and SYS_COURSE_PATH parameters.
748
749
 *
750
 * @param string $path (optional)   A path which type is to be converted. Also, it may be a defined constant for a path.
751
 *                     This parameter has meaning when $type parameter has one of the following values: TO_WEB, TO_SYS, TO_REL. Otherwise it is ignored.
752
 *
753
 * @return string the requested path or the converted path
754
 *
755
 * Notes about the current behaviour model:
756
 * 1. Windows back-slashes are converted to slashes in the result.
757
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
758
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
759
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
760
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
761
 * no mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
762
 * It has not been identified as needed yet.
763
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
764
 *
765
 * For examples go to: *
766
 * See main/admin/system_status.php?section=paths
767
 *
768
 * Vchamilo changes : allow using an alternate configuration
769
 * to get vchamilo  instance paths
770
 */
771
function api_get_path($path = '', $configuration = [])
772
{
773
    global $paths;
774
775
    // get proper configuration data if exists
776
    global $_configuration;
777
778
    $emptyConfigurationParam = false;
779
    if (empty($configuration)) {
780
        $configuration = (array) $_configuration;
781
        $emptyConfigurationParam = true;
782
    }
783
784
    $course_folder = 'courses/';
785
    static $root_web = '';
786
    $root_sys = null;
787
    if ($_configuration) {
788
        $root_sys = $_configuration['root_sys'];
789
    }
790
791
    // If no $root_web has been set so far *and* no custom config has been passed to the function
792
    // then re-use the previously-calculated (run-specific) $root_web and skip this complex calculation
793
    if (empty($root_web) || $emptyConfigurationParam === false || empty($configuration)) {
794
        // Resolve master hostname.
795
        if (!empty($configuration) && array_key_exists('root_web', $configuration)) {
796
            $root_web = $configuration['root_web'];
797
        } else {
798
            $root_web = '';
799
            // Try guess it from server.
800
            if (defined('SYSTEM_INSTALLATION') && SYSTEM_INSTALLATION) {
801
                if (($pos = strpos(($requested_page_rel = api_get_self()), 'main/install')) !== false) {
802
                    $root_rel = substr($requested_page_rel, 0, $pos);
803
                    // See http://www.mediawiki.org/wiki/Manual:$wgServer
804
                    $server_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
805
                    $server_name =
806
                        isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME']
807
                            : (isset($_SERVER['HOSTNAME']) ? $_SERVER['HOSTNAME']
808
                            : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
809
                                : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR']
810
                                    : 'localhost')));
811
                    if (isset($_SERVER['SERVER_PORT']) &&
812
                        !strpos($server_name, ':') &&
813
                        (($server_protocol == 'http' && $_SERVER['SERVER_PORT'] != 80) ||
814
                        ($server_protocol == 'https' && $_SERVER['SERVER_PORT'] != 443))
815
                    ) {
816
                        $server_name .= ":".$_SERVER['SERVER_PORT'];
817
                    }
818
                    $root_web = $server_protocol.'://'.$server_name.$root_rel;
819
                    $root_sys = str_replace('\\', '/', realpath(__DIR__.'/../../../')).'/';
820
                }
821
                // Here we give up, so we don't touch anything.
822
            }
823
        }
824
    }
825
826
    if (isset($configuration['multiple_access_urls']) &&
827
        $configuration['multiple_access_urls']
828
    ) {
829
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the api.lib.php.
830
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
831
            // We look into the DB the function api_get_access_url
832
            $urlInfo = api_get_access_url($configuration['access_url']);
833
            // Avoid default value
834
            $defaultValues = ['http://localhost/', 'https://localhost/'];
835
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
836
                $root_web = $urlInfo['active'] == 1 ? $urlInfo['url'] : $configuration['root_web'];
837
            }
838
        }
839
    }
840
841
    $paths = [];
842
    // Initialise cache with default values.
843
    if (!array_key_exists($root_web, $paths)) {
844
        $paths[$root_web] = [
845
            WEB_PATH => '',
846
            SYS_PATH => '',
847
            REL_PATH => '',
848
            WEB_COURSE_PATH => '',
849
            SYS_COURSE_PATH => '',
850
            REL_COURSE_PATH => '',
851
            WEB_CODE_PATH => 'main/',
852
            SYS_CODE_PATH => 'main/',
853
            REL_CODE_PATH => '/main/',
854
            SYS_LANG_PATH => 'lang/',
855
            WEB_IMG_PATH => 'img/',
856
            WEB_CSS_PATH => 'web/css/',
857
            SYS_CSS_PATH => 'app/Resources/public/css/',
858
            SYS_PLUGIN_PATH => 'plugin/',
859
            WEB_PLUGIN_PATH => 'plugin/',
860
            WEB_PLUGIN_ASSET_PATH => 'public/plugins/',
861
            SYS_ARCHIVE_PATH => 'app/cache/',
862
            WEB_ARCHIVE_PATH => 'app/cache/',
863
            SYS_HOME_PATH => 'app/home/',
864
            WEB_HOME_PATH => 'app/home/',
865
            REL_HOME_PATH => 'app/home/',
866
            SYS_APP_PATH => 'app/',
867
            WEB_APP_PATH => 'app/',
868
            SYS_UPLOAD_PATH => 'app/upload/',
869
            SYS_INC_PATH => 'inc/',
870
            CONFIGURATION_PATH => 'app/config/',
871
            LIBRARY_PATH => 'inc/lib/',
872
            WEB_LIBRARY_PATH => 'inc/lib/',
873
            WEB_LIBRARY_JS_PATH => 'inc/lib/javascript/',
874
            WEB_AJAX_PATH => 'inc/ajax/',
875
            SYS_TEST_PATH => 'tests/',
876
            WEB_TEMPLATE_PATH => 'template/',
877
            SYS_TEMPLATE_PATH => 'template/',
878
            WEB_UPLOAD_PATH => 'app/upload/',
879
            WEB_PUBLIC_PATH => 'web/',
880
            SYS_PUBLIC_PATH => 'web/',
881
            WEB_FONTS_PATH => 'fonts/',
882
            SYS_FONTS_PATH => 'fonts/',
883
        ];
884
    }
885
886
    $isInitialized = [];
887
    $course_folder = isset($configuration['course_folder']) ? $configuration['course_folder'] : $course_folder;
888
    $root_rel = isset($configuration['url_append']) ? $configuration['url_append'] : '';
889
    if (!empty($root_rel)) {
890
        // Adds "/" to the root_rel
891
        $hasSlash = substr($configuration['url_append'], 0, 1);
892
        if ($hasSlash !== '/') {
893
            $root_rel = '/'.$root_rel;
894
        }
895
    }
896
897
    // Web server base and system server base.
898
    if (!array_key_exists($root_web, $isInitialized)) {
899
        // process absolute global roots
900
        if (!empty($configuration)) {
901
            $code_folder = 'main';
902
        } else {
903
            $code_folder = $paths[$root_web][REL_CODE_PATH];
904
        }
905
906
        // Support for the installation process.
907
        // Developers might use the function api_get_path() directly or indirectly (this is difficult to be traced), at the moment when
908
        // configuration has not been created yet. This is why this function should be upgraded to return correct results in this case.
909
910
        // Dealing with trailing slashes.
911
        $slashed_root_web = api_add_trailing_slash($root_web);
912
        $root_sys = api_add_trailing_slash($root_sys);
913
        $root_rel = api_add_trailing_slash($root_rel);
914
        $code_folder = api_add_trailing_slash($code_folder);
915
        $course_folder = api_add_trailing_slash($course_folder);
916
917
        // Initialization of a table that contains common-purpose paths.
918
        $paths[$root_web][REL_PATH] = $root_rel;
919
        $paths[$root_web][REL_COURSE_PATH] = $root_rel.$course_folder;
920
        $paths[$root_web][REL_CODE_PATH] = $root_rel.$code_folder;
921
        $paths[$root_web][REL_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][REL_PATH].'main/default_course_document/';
922
923
        $paths[$root_web][WEB_PATH] = $slashed_root_web;
924
        $paths[$root_web][WEB_CODE_PATH] = $paths[$root_web][WEB_PATH].$code_folder;
925
        $paths[$root_web][WEB_COURSE_PATH] = $paths[$root_web][WEB_PATH].$course_folder;
926
        $paths[$root_web][WEB_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][WEB_CODE_PATH].'default_course_document/';
927
        $paths[$root_web][WEB_APP_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_APP_PATH];
928
        $paths[$root_web][WEB_PLUGIN_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_PATH];
929
        $paths[$root_web][WEB_PLUGIN_ASSET_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_ASSET_PATH];
930
        $paths[$root_web][WEB_ARCHIVE_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_ARCHIVE_PATH];
931
932
        $paths[$root_web][WEB_CSS_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_CSS_PATH];
933
        $paths[$root_web][WEB_IMG_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_IMG_PATH];
934
        $paths[$root_web][WEB_LIBRARY_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_PATH];
935
        $paths[$root_web][WEB_LIBRARY_JS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_JS_PATH];
936
        $paths[$root_web][WEB_AJAX_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_AJAX_PATH];
937
        $paths[$root_web][WEB_FONTS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_FONTS_PATH];
938
        $paths[$root_web][WEB_TEMPLATE_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_TEMPLATE_PATH];
939
        $paths[$root_web][WEB_UPLOAD_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_UPLOAD_PATH];
940
        $paths[$root_web][WEB_PUBLIC_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PUBLIC_PATH];
941
        $paths[$root_web][WEB_HOME_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][REL_HOME_PATH];
942
943
        $paths[$root_web][SYS_PATH] = $root_sys;
944
        $paths[$root_web][SYS_CODE_PATH] = $root_sys.$code_folder;
945
        $paths[$root_web][SYS_TEST_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_TEST_PATH];
946
        $paths[$root_web][SYS_TEMPLATE_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_TEMPLATE_PATH];
947
        $paths[$root_web][SYS_PUBLIC_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PUBLIC_PATH];
948
        $paths[$root_web][SYS_CSS_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_CSS_PATH];
949
        $paths[$root_web][SYS_FONTS_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_FONTS_PATH];
950
        $paths[$root_web][SYS_ARCHIVE_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_ARCHIVE_PATH];
951
        $paths[$root_web][SYS_APP_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_APP_PATH];
952
        $paths[$root_web][SYS_COURSE_PATH] = $paths[$root_web][SYS_APP_PATH].$course_folder;
953
        $paths[$root_web][SYS_UPLOAD_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_UPLOAD_PATH];
954
        $paths[$root_web][SYS_LANG_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_LANG_PATH];
955
        $paths[$root_web][SYS_HOME_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_HOME_PATH];
956
        $paths[$root_web][SYS_PLUGIN_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PLUGIN_PATH];
957
        $paths[$root_web][SYS_INC_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_INC_PATH];
958
959
        $paths[$root_web][LIBRARY_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][LIBRARY_PATH];
960
        $paths[$root_web][CONFIGURATION_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][CONFIGURATION_PATH];
961
962
        global $virtualChamilo;
963
        if (!empty($virtualChamilo)) {
964
            $paths[$root_web][SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
965
            $paths[$root_web][SYS_HOME_PATH] = api_add_trailing_slash($virtualChamilo[SYS_HOME_PATH]);
966
            $paths[$root_web][SYS_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_COURSE_PATH]);
967
            $paths[$root_web][SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
968
969
            $paths[$root_web][WEB_HOME_PATH] = api_add_trailing_slash($virtualChamilo[WEB_HOME_PATH]);
970
            $paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
971
            $paths[$root_web][WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
972
            //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
973
974
            // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
975
976
            // RewriteEngine On
977
            // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
978
979
            //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
980
            //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
981
            //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
982
        }
983
984
        $isInitialized[$root_web] = true;
985
    }
986
987
    $path = trim($path);
988
989
    // Retrieving a common-purpose path.
990
    if (isset($paths[$root_web][$path])) {
991
        return $paths[$root_web][$path];
992
    }
993
994
    // Second purification.
995
996
    // Replacing Windows back slashes.
997
    $path = str_replace('\\', '/', $path);
998
    // Query strings sometimes mighth wrongly appear in non-URLs.
999
    // Let us check remove them from all types of paths.
1000
    if (($pos = strpos($path, '?')) !== false) {
1001
        $path = substr($path, 0, $pos);
1002
    }
1003
1004
    // Detection of the input path type. Conversion to semi-absolute type ( /chamilo/main/inc/.... ).
1005
1006
    if (preg_match(VALID_WEB_PATH, $path)) {
1007
        // A special case: When a URL points to the document download script directly, without
1008
        // mod-rewrite translation, we have to translate it into an "ordinary" web path.
1009
        // For example:
1010
        // http://localhost/chamilo/main/document/download.php?doc_url=/image.png&cDir=/
1011
        // becomes
1012
        // http://localhost/chamilo/courses/TEST/document/image.png
1013
        // TEST is a course directory name, so called "system course code".
1014
        if (strpos($path, 'download.php') !== false) { // Fast detection first.
1015
            $path = urldecode($path);
1016
            if (preg_match('/(.*)main\/document\/download.php\?doc_url=\/(.*)&cDir=\/(.*)?/', $path, $matches)) {
1017
                $sys_course_code =
1018
                    isset($_SESSION['_course']['sysCode'])  // User is inside a course?
1019
                        ? $_SESSION['_course']['sysCode']   // Yes, then use course's directory name.
1020
                        : '{SYS_COURSE_CODE}'; // No, then use a fake code, it may be processed later.
1021
                $path = $matches[1].'courses/'.$sys_course_code.'/document/'.str_replace('//', '/', $matches[3].'/'.$matches[2]);
1022
            }
1023
        }
1024
        // Replacement of the present web server base with a slash '/'.
1025
        $path = preg_replace(VALID_WEB_SERVER_BASE, '/', $path);
1026
    }
1027
1028
    // Path now is semi-absolute. It is convenient at this moment repeated slashes to be removed.
1029
    $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
1030
1031
    return $path;
1032
}
1033
1034
/**
1035
 * Gets a modified version of the path for the CDN, if defined in
1036
 * configuration.php.
1037
 *
1038
 * @param string $web_path The path of the resource without CDN
1039
 *
1040
 * @return string The path of the resource converted to CDN
1041
 *
1042
 * @author Yannick Warnier <[email protected]>
1043
 */
1044
function api_get_cdn_path($web_path)
1045
{
1046
    global $_configuration;
1047
    if (!empty($_configuration['cdn_enable'])) {
1048
        $web_root = api_get_path(WEB_PATH);
1049
        $ext = substr($web_path, strrpos($web_path, '.'));
1050
        if (isset($ext[2])) { // faster version of strlen to check if len>2
1051
            // Check for CDN definitions
1052
            if (!empty($ext)) {
1053
                foreach ($_configuration['cdn'] as $host => $exts) {
1054
                    if (in_array($ext, $exts)) {
1055
                        //Use host as defined in $_configuration['cdn'], without
1056
                        // trailing slash
1057
                        return str_replace($web_root, $host.'/', $web_path);
1058
                    }
1059
                }
1060
            }
1061
        }
1062
    }
1063
1064
    return $web_path;
1065
}
1066
1067
/**
1068
 * @return bool Return true if CAS authentification is activated
1069
 */
1070
function api_is_cas_activated()
1071
{
1072
    return api_get_setting('cas_activate') == "true";
1073
}
1074
1075
/**
1076
 * @return bool Return true if LDAP authentification is activated
1077
 */
1078
function api_is_ldap_activated()
1079
{
1080
    global $extAuthSource;
1081
1082
    return is_array($extAuthSource[LDAP_AUTH_SOURCE]);
1083
}
1084
1085
/**
1086
 * @return bool Return true if Facebook authentification is activated
1087
 */
1088
function api_is_facebook_auth_activated()
1089
{
1090
    global $_configuration;
1091
1092
    return isset($_configuration['facebook_auth']) && $_configuration['facebook_auth'] == 1;
1093
}
1094
1095
/**
1096
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
1097
 *
1098
 * @param string $path the input path
1099
 *
1100
 * @return string returns the modified path
1101
 */
1102
function api_add_trailing_slash($path)
1103
{
1104
    return substr($path, -1) == '/' ? $path : $path.'/';
1105
}
1106
1107
/**
1108
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
1109
 *
1110
 * @param string $path the input path
1111
 *
1112
 * @return string returns the modified path
1113
 */
1114
function api_remove_trailing_slash($path)
1115
{
1116
    return substr($path, -1) == '/' ? substr($path, 0, -1) : $path;
1117
}
1118
1119
/**
1120
 * Checks the RFC 3986 syntax of a given URL.
1121
 *
1122
 * @param string $url      the URL to be checked
1123
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
1124
 *
1125
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
1126
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
1127
 *
1128
 * @see http://drupal.org
1129
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
1130
 * @see http://bugs.php.net/51192
1131
 */
1132
function api_valid_url($url, $absolute = false)
1133
{
1134
    if ($absolute) {
1135
        if (preg_match("
1136
            /^                                                      # Start at the beginning of the text
1137
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
1138
            (?:                                                     # Userinfo (optional) which is typically
1139
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
1140
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
1141
            )?
1142
            (?:
1143
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
1144
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
1145
            )
1146
            (?::[0-9]+)?                                            # Server port number (optional)
1147
            (?:[\/|\?]
1148
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
1149
            *)?
1150
            $/xi", $url)) {
1151
            return $url;
1152
        }
1153
1154
        return false;
1155
    } else {
1156
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
1157
    }
1158
}
1159
1160
/**
1161
 * Checks whether a given string looks roughly like an email address.
1162
 *
1163
 * @param string $address the e-mail address to be checked
1164
 *
1165
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
1166
 */
1167
function api_valid_email($address)
1168
{
1169
    return filter_var($address, FILTER_VALIDATE_EMAIL);
1170
}
1171
1172
/* PROTECTION FUNCTIONS
1173
   Use these functions to protect your scripts. */
1174
1175
/**
1176
 * Function used to protect a course script.
1177
 * The function blocks access when
1178
 * - there is no $_SESSION["_course"] defined; or
1179
 * - $is_allowed_in_course is set to false (this depends on the course
1180
 * visibility and user status).
1181
 *
1182
 * This is only the first proposal, test and improve!
1183
 *
1184
 * @param bool Option to print headers when displaying error message. Default: false
1185
 * @param bool whether session admins should be allowed or not
1186
 * @param bool $checkTool check if tool is available for users (user, group)
1187
 *
1188
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
1189
 *
1190
 * @todo replace global variable
1191
 *
1192
 * @author Roan Embrechts
1193
 */
1194
function api_protect_course_script($print_headers = false, $allow_session_admins = false, $checkTool = '')
1195
{
1196
    $course_info = api_get_course_info();
1197
    if (empty($course_info)) {
1198
        api_not_allowed($print_headers);
1199
1200
        return false;
1201
    }
1202
1203
    if (api_is_drh()) {
1204
        return true;
1205
    }
1206
1207
    // Session admin has access to course
1208
    $sessionAccess = api_get_configuration_value('session_admins_access_all_content');
1209
    if ($sessionAccess) {
1210
        $allow_session_admins = true;
1211
    }
1212
1213
    if (api_is_platform_admin($allow_session_admins)) {
1214
        return true;
1215
    }
1216
1217
    $isAllowedInCourse = api_is_allowed_in_course();
1218
1219
    $is_visible = false;
1220
    if (isset($course_info) && isset($course_info['visibility'])) {
1221
        switch ($course_info['visibility']) {
1222
            default:
1223
            case COURSE_VISIBILITY_CLOSED:
1224
                // Completely closed: the course is only accessible to the teachers. - 0
1225
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1226
                    $is_visible = true;
1227
                }
1228
                break;
1229
            case COURSE_VISIBILITY_REGISTERED:
1230
                // Private - access authorized to course members only - 1
1231
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1232
                    $is_visible = true;
1233
                }
1234
                break;
1235
            case COURSE_VISIBILITY_OPEN_PLATFORM:
1236
                // Open - access allowed for users registered on the platform - 2
1237
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1238
                    $is_visible = true;
1239
                }
1240
                break;
1241
            case COURSE_VISIBILITY_OPEN_WORLD:
1242
                //Open - access allowed for the whole world - 3
1243
                $is_visible = true;
1244
                break;
1245
            case COURSE_VISIBILITY_HIDDEN:
1246
                //Completely closed: the course is only accessible to the teachers. - 0
1247
                if (api_is_platform_admin()) {
1248
                    $is_visible = true;
1249
                }
1250
                break;
1251
        }
1252
1253
        // If password is set and user is not registered to the course then the course is not visible.
1254
        if (false == $isAllowedInCourse &&
1255
            isset($course_info['registration_code']) && !empty($course_info['registration_code'])
1256
        ) {
1257
            $is_visible = false;
1258
        }
1259
    }
1260
1261
    if (!empty($checkTool)) {
1262
        if (!api_is_allowed_to_edit(true, true, true)) {
1263
            $toolInfo = api_get_tool_information_by_name($checkTool);
1264
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && $toolInfo['visibility'] == 0) {
1265
                api_not_allowed(true);
1266
1267
                return false;
1268
            }
1269
        }
1270
    }
1271
1272
    // Check session visibility
1273
    $session_id = api_get_session_id();
1274
1275
    if (!empty($session_id)) {
1276
        // $isAllowedInCourse was set in local.inc.php
1277
        if (!$isAllowedInCourse) {
1278
            $is_visible = false;
1279
        }
1280
1281
        // Check if course is inside session.
1282
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
1283
            $is_visible = false;
1284
        }
1285
    }
1286
1287
    if (!$is_visible) {
1288
        api_not_allowed($print_headers);
1289
1290
        return false;
1291
    }
1292
1293
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
1294
        $plugin = Positioning::create();
1295
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1296
        if ('true' === $block) {
1297
            $currentPath = $_SERVER['PHP_SELF'];
1298
            // Allowed only this course paths.
1299
            $paths = [
1300
                '/plugin/positioning/start.php',
1301
                '/plugin/positioning/start_student.php',
1302
                '/main/course_home/course_home.php',
1303
                '/main/exercise/overview.php',
1304
            ];
1305
1306
            if (!in_array($currentPath, $paths, true)) {
1307
                // Check if entering an exercise.
1308
                global $current_course_tool;
1309
                if ('quiz' !== $current_course_tool) {
1310
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1311
                    if ($initialData && isset($initialData['exercise_id'])) {
1312
                        $results = Event::getExerciseResultsByUser(
1313
                            api_get_user_id(),
1314
                            $initialData['exercise_id'],
1315
                            $course_info['real_id'],
1316
                            $session_id
1317
                        );
1318
                        if (empty($results)) {
1319
                            api_not_allowed($print_headers);
1320
1321
                            return false;
1322
                        }
1323
                    }
1324
                }
1325
            }
1326
        }
1327
    }
1328
1329
    api_block_inactive_user();
1330
1331
    return true;
1332
}
1333
1334
/**
1335
 * Function used to protect an admin script.
1336
 *
1337
 * The function blocks access when the user has no platform admin rights
1338
 * with an error message printed on default output
1339
 *
1340
 * @param bool Whether to allow session admins as well
1341
 * @param bool Whether to allow HR directors as well
1342
 * @param string An optional message (already passed through get_lang)
1343
 * @param bool Whether to allow session coach as well
1344
 *
1345
 * @return bool True if user is allowed, false otherwise.
1346
 *              The function also outputs an error message in case not allowed
1347
 *
1348
 * @author Roan Embrechts (original author)
1349
 */
1350
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null, $allow_session_coach = false)
1351
{
1352
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1353
        if (!($allow_session_coach && api_is_coach())) {
1354
            api_not_allowed(true, $message);
1355
1356
            return false;
1357
        }
1358
    }
1359
    api_block_inactive_user();
1360
1361
    return true;
1362
}
1363
1364
/**
1365
 * Blocks inactive users with a currently active session from accessing more pages "live".
1366
 *
1367
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1368
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1369
 */
1370
function api_block_inactive_user()
1371
{
1372
    $data = true;
1373
    if (api_get_configuration_value('security_block_inactive_users_immediately') != 1) {
1374
        return $data;
1375
    }
1376
1377
    $userId = api_get_user_id();
1378
    $homeUrl = api_get_path(WEB_PATH);
1379
    if ($userId == 0) {
1380
        return $data;
1381
    }
1382
1383
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1384
            WHERE id = $userId";
1385
1386
    $result = Database::query($sql);
1387
    if (Database::num_rows($result) > 0) {
1388
        $result_array = Database::fetch_array($result);
1389
        $data = (bool) $result_array['active'];
1390
    }
1391
    if ($data == false) {
1392
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1393
        $tpl->assign('hide_login_link', 1);
1394
1395
        //api_not_allowed(true, get_lang('AccountInactive'));
1396
        // we were not in a course, return to home page
1397
        $msg = Display::return_message(
1398
            get_lang('AccountInactive'),
1399
            'error',
1400
            false
1401
        );
1402
1403
        $msg .= '<p class="text-center">
1404
                 <a class="btn btn-default" href="'.$homeUrl.'">'.get_lang('BackHome').'</a></p>';
1405
1406
        if (api_is_anonymous()) {
1407
            $form = api_get_not_allowed_login_form();
1408
            $msg .= '<div class="well">';
1409
            $msg .= $form->returnForm();
1410
            $msg .= '</div>';
1411
        }
1412
1413
        $tpl->assign('content', $msg);
1414
        $tpl->display_one_col_template();
1415
        exit;
1416
    }
1417
1418
    return $data;
1419
}
1420
1421
/**
1422
 * Function used to protect a teacher script.
1423
 * The function blocks access when the user has no teacher rights.
1424
 *
1425
 * @return bool True if the current user can access the script, false otherwise
1426
 *
1427
 * @author Yoselyn Castillo
1428
 */
1429
function api_protect_teacher_script()
1430
{
1431
    if (!api_is_allowed_to_edit()) {
1432
        api_not_allowed(true);
1433
1434
        return false;
1435
    }
1436
1437
    return true;
1438
}
1439
1440
/**
1441
 * Function used to prevent anonymous users from accessing a script.
1442
 *
1443
 * @param bool|true $printHeaders
1444
 *
1445
 * @author Roan Embrechts
1446
 *
1447
 * @return bool
1448
 */
1449
function api_block_anonymous_users($printHeaders = true)
1450
{
1451
    $user = api_get_user_info();
1452
    if (!(isset($user['user_id']) && $user['user_id']) || api_is_anonymous($user['user_id'], true)) {
1453
        api_not_allowed($printHeaders);
1454
1455
        return false;
1456
    }
1457
    api_block_inactive_user();
1458
1459
    return true;
1460
}
1461
1462
/**
1463
 * Returns a rough evaluation of the browser's name and version based on very
1464
 * simple regexp.
1465
 *
1466
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1467
 */
1468
function api_get_navigator()
1469
{
1470
    $navigator = 'Unknown';
1471
    $version = 0;
1472
1473
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1474
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1475
    }
1476
1477
    if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false) {
1478
        $navigator = 'Opera';
1479
        list(, $version) = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1480
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== false) {
1481
        $navigator = 'Edge';
1482
        list(, $version) = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1483
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
1484
        $navigator = 'Internet Explorer';
1485
        list(, $version) = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1486
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false) {
1487
        $navigator = 'Chrome';
1488
        list(, $version) = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1489
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false) {
1490
        $navigator = 'Safari';
1491
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Version/') !== false) {
1492
            // If this Safari does have the "Version/" string in its user agent
1493
            // then use that as a version indicator rather than what's after
1494
            // "Safari/" which is rather a "build number" or something
1495
            list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1496
        } else {
1497
            list(, $version) = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1498
        }
1499
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== false) {
1500
        $navigator = 'Firefox';
1501
        list(, $version) = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1502
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape') !== false) {
1503
        $navigator = 'Netscape';
1504
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/') !== false) {
1505
            list(, $version) = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1506
        } else {
1507
            list(, $version) = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1508
        }
1509
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false) {
1510
        $navigator = 'Konqueror';
1511
        list(, $version) = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1512
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit') !== false) {
1513
        $navigator = 'AppleWebKit';
1514
        list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1515
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false) {
1516
        $navigator = 'Mozilla';
1517
        list(, $version) = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1518
    }
1519
1520
    // Now cut extra stuff around (mostly *after*) the version number
1521
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1522
1523
    if (strpos($version, '.') === false) {
1524
        $version = number_format(doubleval($version), 1);
1525
    }
1526
1527
    return ['name' => $navigator, 'version' => $version];
1528
}
1529
/**
1530
 * Check if it is a desktop or mobile browser.
1531
 */
1532
function api_is_browser_mobile(): bool
1533
{
1534
    if (empty($_SERVER['HTTP_USER_AGENT'])) {
1535
        static $isMobile = false;
1536
    } elseif (
1537
        strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false
1538
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false
1539
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Silk/') !== false
1540
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Kindle') !== false
1541
        || strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false
1542
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false
1543
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mobi') !== false
1544
    ) {
1545
        $isMobile = true;
1546
    } else {
1547
        $isMobile = false;
1548
    }
1549
1550
    return $isMobile;
1551
}
1552
1553
/**
1554
 * @return true if user self registration is allowed, false otherwise
1555
 */
1556
function api_is_self_registration_allowed()
1557
{
1558
    return isset($GLOBALS['allowSelfReg']) ? $GLOBALS['allowSelfReg'] : false;
1559
}
1560
1561
/**
1562
 * This function returns the id of the user which is stored in the $_user array.
1563
 *
1564
 * example: The function can be used to check if a user is logged in
1565
 *          if (api_get_user_id())
1566
 *
1567
 * @return int the id of the current user, 0 if is empty
1568
 */
1569
function api_get_user_id()
1570
{
1571
    $userInfo = Session::read('_user');
1572
    if ($userInfo && isset($userInfo['user_id'])) {
1573
        return (int) $userInfo['user_id'];
1574
    }
1575
1576
    return 0;
1577
}
1578
1579
/**
1580
 * Gets the list of courses a specific user is subscribed to.
1581
 *
1582
 * @param int       User ID
1583
 * @param bool $fetch_session Whether to get session courses or not - NOT YET IMPLEMENTED
1584
 *
1585
 * @return array Array of courses in the form [0]=>('code'=>xxx,'db'=>xxx,'dir'=>xxx,'status'=>d)
1586
 *
1587
 * @deprecated use CourseManager::get_courses_list_by_user_id()
1588
 */
1589
function api_get_user_courses($userId, $fetch_session = true)
1590
{
1591
    // Get out if not integer
1592
    if ($userId != strval(intval($userId))) {
1593
        return [];
1594
    }
1595
1596
    $t_course = Database::get_main_table(TABLE_MAIN_COURSE);
1597
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1598
1599
    $sql = "SELECT cc.id as real_id, cc.code code, cc.directory dir, cu.status status
1600
            FROM $t_course cc, $t_course_user cu
1601
            WHERE
1602
                cc.id = cu.c_id AND
1603
                cu.user_id = $userId AND
1604
                cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH;
1605
    $result = Database::query($sql);
1606
    if ($result === false) {
1607
        return [];
1608
    }
1609
1610
    $courses = [];
1611
    while ($row = Database::fetch_array($result)) {
1612
        // we only need the database name of the course
1613
        $courses[] = $row;
1614
    }
1615
1616
    return $courses;
1617
}
1618
1619
/**
1620
 * Formats user information into a standard array
1621
 * This function should be only used inside api_get_user_info().
1622
 *
1623
 * @param array Non-standard user array
1624
 * @param bool $add_password
1625
 * @param bool $loadAvatars  turn off to improve performance
1626
 *
1627
 * @return array Standard user array
1628
 */
1629
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1630
{
1631
    $result = [];
1632
1633
    $result['firstname'] = null;
1634
    $result['lastname'] = null;
1635
1636
    if (isset($user['firstname']) && isset($user['lastname'])) { // with only lowercase
1637
        $result['firstname'] = $user['firstname'];
1638
        $result['lastname'] = $user['lastname'];
1639
    } elseif (isset($user['firstName']) && isset($user['lastName'])) { // with uppercase letters
1640
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1641
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1642
    }
1643
1644
    if (isset($user['email'])) {
1645
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1646
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1647
    } else {
1648
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1649
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1650
    }
1651
1652
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1653
    $result['complete_name_with_username'] = $result['complete_name'];
1654
1655
    if (!empty($user['username']) && !api_get_configuration_value('hide_username_with_complete_name')) {
1656
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1657
    }
1658
1659
    $showEmail = api_get_setting('show_email_addresses') === 'true';
1660
    if (!empty($user['email'])) {
1661
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1662
        if ($showEmail) {
1663
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1664
        }
1665
    } else {
1666
        $result['complete_name_with_email'] = $result['complete_name'];
1667
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1668
    }
1669
1670
    // Kept for historical reasons
1671
    $result['firstName'] = $result['firstname'];
1672
    $result['lastName'] = $result['lastname'];
1673
1674
    $attributes = [
1675
        'phone',
1676
        'address',
1677
        'picture_uri',
1678
        'official_code',
1679
        'status',
1680
        'active',
1681
        'auth_source',
1682
        'username',
1683
        'theme',
1684
        'language',
1685
        'creator_id',
1686
        'registration_date',
1687
        'hr_dept_id',
1688
        'expiration_date',
1689
        'last_login',
1690
        'user_is_online',
1691
    ];
1692
1693
    if (api_get_setting('extended_profile') === 'true') {
1694
        $attributes[] = 'competences';
1695
        $attributes[] = 'diplomas';
1696
        $attributes[] = 'teach';
1697
        $attributes[] = 'openarea';
1698
    }
1699
1700
    foreach ($attributes as $attribute) {
1701
        $result[$attribute] = isset($user[$attribute]) ? $user[$attribute] : null;
1702
    }
1703
1704
    $user_id = (int) $user['user_id'];
1705
    // Maintain the user_id index for backwards compatibility
1706
    $result['user_id'] = $result['id'] = $user_id;
1707
1708
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1709
    $result['has_certificates'] = 0;
1710
    if (!empty($hasCertificates)) {
1711
        $result['has_certificates'] = 1;
1712
    }
1713
1714
    $result['icon_status'] = '';
1715
    $result['icon_status_medium'] = '';
1716
1717
    $result['is_admin'] = UserManager::is_admin($user_id);
1718
1719
    // Getting user avatar.
1720
    if ($loadAvatars) {
1721
        $result['avatar'] = '';
1722
        $result['avatar_no_query'] = '';
1723
        $result['avatar_small'] = '';
1724
        $result['avatar_medium'] = '';
1725
1726
        if (!isset($user['avatar'])) {
1727
            $originalFile = UserManager::getUserPicture(
1728
                $user_id,
1729
                USER_IMAGE_SIZE_ORIGINAL,
1730
                null,
1731
                $result
1732
            );
1733
            $result['avatar'] = $originalFile;
1734
            $avatarString = explode('?', $result['avatar']);
1735
            $result['avatar_no_query'] = reset($avatarString);
1736
        } else {
1737
            $result['avatar'] = $user['avatar'];
1738
            $avatarString = explode('?', $user['avatar']);
1739
            $result['avatar_no_query'] = reset($avatarString);
1740
        }
1741
1742
        if (!isset($user['avatar_small'])) {
1743
            $smallFile = UserManager::getUserPicture(
1744
                $user_id,
1745
                USER_IMAGE_SIZE_SMALL,
1746
                null,
1747
                $result
1748
            );
1749
            $result['avatar_small'] = $smallFile;
1750
        } else {
1751
            $result['avatar_small'] = $user['avatar_small'];
1752
        }
1753
1754
        if (!isset($user['avatar_medium'])) {
1755
            $mediumFile = UserManager::getUserPicture(
1756
                $user_id,
1757
                USER_IMAGE_SIZE_MEDIUM,
1758
                null,
1759
                $result
1760
            );
1761
            $result['avatar_medium'] = $mediumFile;
1762
        } else {
1763
            $result['avatar_medium'] = $user['avatar_medium'];
1764
        }
1765
1766
        $urlImg = api_get_path(WEB_IMG_PATH);
1767
        $iconStatus = '';
1768
        $iconStatusMedium = '';
1769
        $label = '';
1770
        switch ($result['status']) {
1771
            case STUDENT:
1772
                if ($result['has_certificates']) {
1773
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1774
                    $label = get_lang('Graduated');
1775
                } else {
1776
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1777
                    $label = get_lang('Student');
1778
                }
1779
                break;
1780
            case COURSEMANAGER:
1781
                if ($result['is_admin']) {
1782
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1783
                    $label = get_lang('Admin');
1784
                } else {
1785
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1786
                    $label = get_lang('Teacher');
1787
                }
1788
                break;
1789
            case STUDENT_BOSS:
1790
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1791
                $label = get_lang('StudentBoss');
1792
                break;
1793
        }
1794
1795
        if (!empty($iconStatus)) {
1796
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1797
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1798
        }
1799
1800
        $result['icon_status'] = $iconStatus;
1801
        $result['icon_status_label'] = $label;
1802
        $result['icon_status_medium'] = $iconStatusMedium;
1803
    }
1804
1805
    if (isset($user['user_is_online'])) {
1806
        $result['user_is_online'] = $user['user_is_online'] == true ? 1 : 0;
1807
    }
1808
    if (isset($user['user_is_online_in_chat'])) {
1809
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1810
    }
1811
1812
    if ($add_password) {
1813
        $result['password'] = $user['password'];
1814
    }
1815
1816
    if (isset($result['profile_completed'])) {
1817
        $result['profile_completed'] = $user['profile_completed'];
1818
    }
1819
1820
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1821
1822
    // Send message link
1823
    $userIdHash = UserManager::generateUserHash($user_id);
1824
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&hash='.$userIdHash;
1825
    $result['complete_name_with_message_link'] = Display::url(
1826
        $result['complete_name_with_username'],
1827
        $sendMessage,
1828
        ['class' => 'ajax']
1829
    );
1830
1831
    if (isset($user['extra'])) {
1832
        $result['extra'] = $user['extra'];
1833
    }
1834
1835
    return $result;
1836
}
1837
1838
/**
1839
 * Finds all the information about a user.
1840
 * If no parameter is passed you find all the information about the current user.
1841
 *
1842
 * @param int  $user_id
1843
 * @param bool $checkIfUserOnline
1844
 * @param bool $showPassword
1845
 * @param bool $loadExtraData
1846
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1847
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1848
 * @param bool $updateCache              update apc cache if exists
1849
 *
1850
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1851
 *
1852
 * @author Patrick Cool <[email protected]>
1853
 * @author Julio Montoya
1854
 *
1855
 * @version 21 September 2004
1856
 */
1857
function api_get_user_info(
1858
    $user_id = 0,
1859
    $checkIfUserOnline = false,
1860
    $showPassword = false,
1861
    $loadExtraData = false,
1862
    $loadOnlyVisibleExtraData = false,
1863
    $loadAvatars = true,
1864
    $updateCache = false
1865
) {
1866
    $apcVar = null;
1867
    $user = false;
1868
    $cacheAvailable = api_get_configuration_value('apc');
1869
1870
    if (empty($user_id)) {
1871
        $userFromSession = Session::read('_user');
1872
1873
        if (isset($userFromSession)) {
1874
            if ($cacheAvailable === true &&
1875
                (
1876
                    empty($userFromSession['is_anonymous']) &&
1877
                    (isset($userFromSession['status']) && $userFromSession['status'] != ANONYMOUS)
1878
                )
1879
            ) {
1880
                $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userFromSession['user_id'];
1881
                if (apcu_exists($apcVar)) {
1882
                    if ($updateCache) {
1883
                        apcu_store($apcVar, $userFromSession, 60);
1884
                    }
1885
                    $user = apcu_fetch($apcVar);
1886
                } else {
1887
                    $user = _api_format_user(
1888
                        $userFromSession,
1889
                        $showPassword,
1890
                        $loadAvatars
1891
                    );
1892
                    apcu_store($apcVar, $user, 60);
1893
                }
1894
            } else {
1895
                $user = _api_format_user(
1896
                    $userFromSession,
1897
                    $showPassword,
1898
                    $loadAvatars
1899
                );
1900
            }
1901
1902
            return $user;
1903
        }
1904
1905
        return false;
1906
    }
1907
1908
    // Make sure user_id is safe
1909
    $user_id = (int) $user_id;
1910
1911
    // Re-use user information if not stale and already stored in APCu
1912
    if ($cacheAvailable === true) {
1913
        $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1914
        if (apcu_exists($apcVar) && $updateCache == false && $checkIfUserOnline == false) {
1915
            $user = apcu_fetch($apcVar);
1916
1917
            return $user;
1918
        }
1919
    }
1920
1921
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1922
            WHERE id = $user_id";
1923
    $result = Database::query($sql);
1924
    if (Database::num_rows($result) > 0) {
1925
        $result_array = Database::fetch_array($result);
1926
        $result_array['user_is_online_in_chat'] = 0;
1927
        if ($checkIfUserOnline) {
1928
            $use_status_in_platform = user_is_online($user_id);
1929
            $result_array['user_is_online'] = $use_status_in_platform;
1930
            $user_online_in_chat = 0;
1931
            if ($use_status_in_platform) {
1932
                $user_status = UserManager::get_extra_user_data_by_field(
1933
                    $user_id,
1934
                    'user_chat_status',
1935
                    false,
1936
                    true
1937
                );
1938
                if ((int) $user_status['user_chat_status'] == 1) {
1939
                    $user_online_in_chat = 1;
1940
                }
1941
            }
1942
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1943
        }
1944
1945
        if ($loadExtraData) {
1946
            $fieldValue = new ExtraFieldValue('user');
1947
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1948
                $user_id,
1949
                $loadOnlyVisibleExtraData
1950
            );
1951
        }
1952
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1953
    }
1954
1955
    if ($cacheAvailable === true) {
1956
        apcu_store($apcVar, $user, 60);
1957
    }
1958
1959
    return $user;
1960
}
1961
1962
/**
1963
 * @param int $userId
1964
 *
1965
 * @return User
1966
 */
1967
function api_get_user_entity($userId)
1968
{
1969
    $userId = (int) $userId;
1970
    $repo = UserManager::getRepository();
1971
1972
    /** @var User $user */
1973
    $user = $repo->find($userId);
1974
1975
    return $user;
1976
}
1977
1978
/**
1979
 * Finds all the information about a user from username instead of user id.
1980
 *
1981
 * @param string $username
1982
 *
1983
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1984
 *
1985
 * @author Yannick Warnier <[email protected]>
1986
 */
1987
function api_get_user_info_from_username($username, $authSource = null)
1988
{
1989
    if (empty($username)) {
1990
        return false;
1991
    }
1992
    $username = trim($username);
1993
1994
    $andAuthSource = "";
1995
    if (isset($authSource)) {
1996
        $authSource = Database::escape_string($authSource);
1997
        $andAuthSource = " AND auth_source = '$authSource'";
1998
    }
1999
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
2000
            WHERE username='".Database::escape_string($username)."' $andAuthSource";
2001
    $result = Database::query($sql);
2002
    if (Database::num_rows($result) > 0) {
2003
        $resultArray = Database::fetch_array($result);
2004
2005
        return _api_format_user($resultArray);
2006
    }
2007
2008
    return false;
2009
}
2010
2011
/**
2012
 * Get first user with an email.
2013
 *
2014
 * @param string $email
2015
 *
2016
 * @return array|bool
2017
 */
2018
function api_get_user_info_from_email($email = '')
2019
{
2020
    if (empty($email)) {
2021
        return false;
2022
    }
2023
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
2024
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
2025
    $result = Database::query($sql);
2026
    if (Database::num_rows($result) > 0) {
2027
        $resultArray = Database::fetch_array($result);
2028
2029
        return _api_format_user($resultArray);
2030
    }
2031
2032
    return false;
2033
}
2034
2035
/**
2036
 * @return string
2037
 */
2038
function api_get_course_id()
2039
{
2040
    return Session::read('_cid', null);
2041
}
2042
2043
/**
2044
 * Returns the current course id (integer).
2045
 *
2046
 * @param string $code Optional course code
2047
 *
2048
 * @return int
2049
 */
2050
function api_get_course_int_id($code = null)
2051
{
2052
    if (!empty($code)) {
2053
        $code = Database::escape_string($code);
2054
        $row = Database::select(
2055
            'id',
2056
            Database::get_main_table(TABLE_MAIN_COURSE),
2057
            ['where' => ['code = ?' => [$code]]],
2058
            'first'
2059
        );
2060
2061
        if (is_array($row) && isset($row['id'])) {
2062
            return $row['id'];
2063
        } else {
2064
            return false;
2065
        }
2066
    }
2067
2068
    return Session::read('_real_cid', 0);
2069
}
2070
2071
/**
2072
 * Returns the current course directory.
2073
 *
2074
 * This function relies on api_get_course_info()
2075
 *
2076
 * @param string    The course code - optional (takes it from session if not given)
2077
 *
2078
 * @return string The directory where the course is located inside the Chamilo "courses" directory
2079
 *
2080
 * @author Yannick Warnier <[email protected]>
2081
 */
2082
function api_get_course_path($course_code = null)
2083
{
2084
    $info = !empty($course_code) ? api_get_course_info($course_code) : api_get_course_info();
2085
2086
    return $info['path'];
2087
}
2088
2089
/**
2090
 * Gets a course setting from the current course_setting table. Try always using integer values.
2091
 *
2092
 * @param string $settingName The name of the setting we want from the table
2093
 * @param array  $courseInfo
2094
 * @param bool   $force       force checking the value in the database
2095
 *
2096
 * @return mixed The value of that setting in that table. Return -1 if not found.
2097
 */
2098
function api_get_course_setting($settingName, $courseInfo = [], $force = false)
2099
{
2100
    if (empty($courseInfo)) {
2101
        $courseInfo = api_get_course_info();
2102
    }
2103
2104
    if (empty($courseInfo) || empty($settingName)) {
2105
        return -1;
2106
    }
2107
2108
    $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
2109
2110
    if (empty($courseId)) {
2111
        return -1;
2112
    }
2113
2114
    static $courseSettingInfo = [];
2115
2116
    if ($force) {
2117
        $courseSettingInfo = [];
2118
    }
2119
2120
    if (!isset($courseSettingInfo[$courseId])) {
2121
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
2122
        $settingName = Database::escape_string($settingName);
2123
2124
        $sql = "SELECT variable, value FROM $table
2125
                WHERE c_id = $courseId ";
2126
        $res = Database::query($sql);
2127
        if (Database::num_rows($res) > 0) {
2128
            $result = Database::store_result($res, 'ASSOC');
2129
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
2130
2131
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
2132
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
2133
                if (!is_null($value)) {
2134
                    $result = explode(',', $value);
2135
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
2136
                }
2137
            }
2138
        }
2139
    }
2140
2141
    if (isset($courseSettingInfo[$courseId]) && array_key_exists($settingName, $courseSettingInfo[$courseId])) {
2142
        return $courseSettingInfo[$courseId][$settingName];
2143
    }
2144
2145
    return -1;
2146
}
2147
2148
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
2149
{
2150
    $value = api_get_course_setting($settingName, $courseInfo, true);
2151
2152
    if (-1 === $value) {
2153
        // Check global settings
2154
        $value = api_get_plugin_setting($plugin, $settingName);
2155
        if ($value === 'true') {
2156
            return 1;
2157
        }
2158
        if ($value === 'false') {
2159
            return 0;
2160
        }
2161
        if (null === $value) {
2162
            return -1;
2163
        }
2164
    }
2165
2166
    return $value;
2167
}
2168
2169
/**
2170
 * Gets an anonymous user ID.
2171
 *
2172
 * For some tools that need tracking, like the learnpath tool, it is necessary
2173
 * to have a usable user-id to enable some kind of tracking, even if not
2174
 * perfect. An anonymous ID is taken from the users table by looking for a
2175
 * status of "6" (anonymous).
2176
 *
2177
 * @return int User ID of the anonymous user, or O if no anonymous user found
2178
 */
2179
function api_get_anonymous_id()
2180
{
2181
    // Find if another anon is connected now
2182
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2183
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
2184
    $ip = Database::escape_string(api_get_real_ip());
2185
    $max = (int) api_get_configuration_value('max_anonymous_users');
2186
    if ($max >= 2) {
2187
        $sql = "SELECT * FROM $table as TEL
2188
                JOIN $tableU as U
2189
                ON U.user_id = TEL.login_user_id
2190
                WHERE TEL.user_ip = '$ip'
2191
                    AND U.status = ".ANONYMOUS."
2192
                    AND U.user_id != 2 ";
2193
2194
        $result = Database::query($sql);
2195
        if (empty(Database::num_rows($result))) {
2196
            $login = uniqid('anon_');
2197
            $email = ' [email protected]';
2198
            if (api_get_setting('login_is_email') == 'true') {
2199
                $login = $login."@localhost.local";
2200
                $email = $login;
2201
            }
2202
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['registration_date ASC']);
2203
            if (count($anonList) >= $max) {
2204
                foreach ($anonList as $userToDelete) {
2205
                    UserManager::delete_user($userToDelete['user_id']);
2206
                    break;
2207
                }
2208
            }
2209
            // Return the user ID
2210
            return UserManager::create_user(
2211
                $login,
2212
                'anon',
2213
                ANONYMOUS,
2214
                $email,
2215
                $login,
2216
                $login
2217
            );
2218
        } else {
2219
            $row = Database::fetch_array($result, 'ASSOC');
2220
2221
            return $row['user_id'];
2222
        }
2223
    }
2224
2225
    $table = Database::get_main_table(TABLE_MAIN_USER);
2226
    $sql = "SELECT user_id
2227
            FROM $table
2228
            WHERE status = ".ANONYMOUS." ";
2229
    $res = Database::query($sql);
2230
    if (Database::num_rows($res) > 0) {
2231
        $row = Database::fetch_array($res, 'ASSOC');
2232
2233
        return $row['user_id'];
2234
    }
2235
2236
    // No anonymous user was found.
2237
    return 0;
2238
}
2239
2240
/**
2241
 * @param string $courseCode
2242
 * @param int    $sessionId
2243
 * @param int    $groupId
2244
 *
2245
 * @return string
2246
 */
2247
function api_get_cidreq_params($courseCode, $sessionId = 0, $groupId = 0)
2248
{
2249
    $courseCode = !empty($courseCode) ? htmlspecialchars($courseCode) : '';
2250
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2251
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2252
2253
    $url = 'cidReq='.$courseCode;
2254
    $url .= '&id_session='.$sessionId;
2255
    $url .= '&gidReq='.$groupId;
2256
2257
    return $url;
2258
}
2259
2260
/**
2261
 * Returns the current course url part including session, group, and gradebook params.
2262
 *
2263
 * @param bool   $addSessionId
2264
 * @param bool   $addGroupId
2265
 * @param string $origin
2266
 *
2267
 * @return string Course & session references to add to a URL
2268
 */
2269
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2270
{
2271
    $courseCode = api_get_course_id();
2272
    $url = empty($courseCode) ? '' : 'cidReq='.urlencode(htmlspecialchars($courseCode));
2273
    $origin = empty($origin) ? api_get_origin() : urlencode(Security::remove_XSS($origin));
2274
2275
    if ($addSessionId) {
2276
        if (!empty($url)) {
2277
            $url .= api_get_session_id() == 0 ? '&id_session=0' : '&id_session='.api_get_session_id();
2278
        }
2279
    }
2280
2281
    if ($addGroupId) {
2282
        if (!empty($url)) {
2283
            $url .= api_get_group_id() == 0 ? '&gidReq=0' : '&gidReq='.api_get_group_id();
2284
        }
2285
    }
2286
2287
    if (!empty($url)) {
2288
        $url .= '&gradebook='.intval(api_is_in_gradebook());
2289
        $url .= '&origin='.$origin;
2290
    }
2291
2292
    return $url;
2293
}
2294
2295
/**
2296
 * Get if we visited a gradebook page.
2297
 *
2298
 * @return bool
2299
 */
2300
function api_is_in_gradebook()
2301
{
2302
    return Session::read('in_gradebook', false);
2303
}
2304
2305
/**
2306
 * Set that we are in a page inside a gradebook.
2307
 */
2308
function api_set_in_gradebook()
2309
{
2310
    Session::write('in_gradebook', true);
2311
}
2312
2313
/**
2314
 * Remove gradebook session.
2315
 */
2316
function api_remove_in_gradebook()
2317
{
2318
    Session::erase('in_gradebook');
2319
}
2320
2321
/**
2322
 * Returns the current course info array see api_format_course_array()
2323
 * If the course_code is given, the returned array gives info about that
2324
 * particular course, if none given it gets the course info from the session.
2325
 *
2326
 * @param string $course_code
2327
 *
2328
 * @return array
2329
 */
2330
function api_get_course_info($course_code = null)
2331
{
2332
    if (!empty($course_code)) {
2333
        $course_code = Database::escape_string($course_code);
2334
        $courseId = api_get_course_int_id($course_code);
2335
2336
        if (empty($courseId)) {
2337
            return [];
2338
        }
2339
2340
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2341
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2342
        $sql = "SELECT
2343
                    course.*,
2344
                    course_category.code faCode,
2345
                    course_category.name faName
2346
                FROM $course_table
2347
                LEFT JOIN $course_cat_table
2348
                ON course.category_code = course_category.code
2349
                WHERE course.id = $courseId";
2350
        $result = Database::query($sql);
2351
        $courseInfo = [];
2352
        if (Database::num_rows($result) > 0) {
2353
            $data = Database::fetch_array($result);
2354
            $courseInfo = api_format_course_array($data);
2355
        }
2356
2357
        return $courseInfo;
2358
    }
2359
2360
    global $_course;
2361
    if ($_course == '-1') {
2362
        $_course = [];
2363
    }
2364
2365
    return $_course;
2366
}
2367
2368
/**
2369
 * @param int $courseId
2370
 *
2371
 * @return \Chamilo\CoreBundle\Entity\Course
2372
 */
2373
function api_get_course_entity($courseId = 0)
2374
{
2375
    if (empty($courseId)) {
2376
        $courseId = api_get_course_int_id();
2377
    }
2378
2379
    return Database::getManager()->getRepository('ChamiloCoreBundle:Course')->find($courseId);
2380
}
2381
2382
function api_get_group_entity($id = 0)
2383
{
2384
    if (empty($id)) {
2385
        $id = api_get_group_id();
2386
    }
2387
2388
    return Database::getManager()->getRepository('ChamiloCourseBundle:CGroupInfo')->find($id);
2389
}
2390
2391
/**
2392
 * @param int $id
2393
 *
2394
 * @return \Chamilo\CoreBundle\Entity\Session
2395
 */
2396
function api_get_session_entity($id = 0)
2397
{
2398
    if (empty($id)) {
2399
        $id = api_get_session_id();
2400
    }
2401
2402
    return Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($id);
2403
}
2404
2405
/**
2406
 * Returns the current course info array.
2407
2408
 * Now if the course_code is given, the returned array gives info about that
2409
 * particular course, not specially the current one.
2410
 *
2411
 * @param int $id Numeric ID of the course
2412
 *
2413
 * @return array The course info as an array formatted by api_format_course_array, including category.name
2414
 */
2415
function api_get_course_info_by_id($id = null)
2416
{
2417
    if (!empty($id)) {
2418
        $id = (int) $id;
2419
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2420
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2421
        $sql = "SELECT
2422
                    course.*,
2423
                    course_category.code faCode,
2424
                    course_category.name faName
2425
                FROM $course_table
2426
                LEFT JOIN $course_cat_table
2427
                ON course.category_code = course_category.code
2428
                WHERE course.id = $id";
2429
        $result = Database::query($sql);
2430
        $_course = [];
2431
        if (Database::num_rows($result) > 0) {
2432
            $row = Database::fetch_array($result);
2433
            $_course = api_format_course_array($row);
2434
        }
2435
2436
        return $_course;
2437
    }
2438
2439
    global $_course;
2440
    if ($_course == '-1') {
2441
        $_course = [];
2442
    }
2443
2444
    return $_course;
2445
}
2446
2447
/**
2448
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2449
 * to switch from 'code' to 'id' in the array. This is a legacy feature and is
2450
 * now possibly causing massive confusion as a new "id" field has been added to
2451
 * the course table in 1.9.0.
2452
 *
2453
 * @param $course_data
2454
 *
2455
 * @return array
2456
 *
2457
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2458
 */
2459
function api_format_course_array($course_data)
2460
{
2461
    if (empty($course_data)) {
2462
        return [];
2463
    }
2464
2465
    $_course = [];
2466
    $_course['id'] = $course_data['code'];
2467
    $_course['real_id'] = $course_data['id'];
2468
2469
    // Added
2470
    $_course['code'] = $course_data['code'];
2471
    $_course['name'] = $course_data['title'];
2472
    $_course['title'] = $course_data['title'];
2473
    $_course['official_code'] = $course_data['visual_code'];
2474
    $_course['visual_code'] = $course_data['visual_code'];
2475
    $_course['sysCode'] = $course_data['code'];
2476
    $_course['path'] = $course_data['directory']; // Use as key in path.
2477
    $_course['directory'] = $course_data['directory'];
2478
    $_course['creation_date'] = $course_data['creation_date'];
2479
    $_course['titular'] = $course_data['tutor_name'];
2480
    $_course['tutor_name'] = $course_data['tutor_name'];
2481
    $_course['language'] = $course_data['course_language'];
2482
    $_course['extLink']['url'] = $course_data['department_url'];
2483
    $_course['extLink']['name'] = $course_data['department_name'];
2484
    $_course['categoryCode'] = $course_data['faCode'];
2485
    $_course['category_code'] = $course_data['faCode'];
2486
    $_course['categoryName'] = $course_data['faName'];
2487
    $_course['visibility'] = $course_data['visibility'];
2488
    $_course['subscribe_allowed'] = $course_data['subscribe'];
2489
    $_course['subscribe'] = $course_data['subscribe'];
2490
    $_course['unsubscribe'] = $course_data['unsubscribe'];
2491
    $_course['course_language'] = $course_data['course_language'];
2492
    $_course['activate_legal'] = isset($course_data['activate_legal']) ? $course_data['activate_legal'] : false;
2493
    $_course['legal'] = $course_data['legal'];
2494
    $_course['show_score'] = $course_data['show_score']; //used in the work tool
2495
    $_course['department_name'] = $course_data['department_name'];
2496
    $_course['department_url'] = $course_data['department_url'];
2497
2498
    $courseSys = api_get_path(SYS_COURSE_PATH).$course_data['directory'];
2499
    $webCourseHome = api_get_path(WEB_COURSE_PATH).$course_data['directory'];
2500
2501
    // Course password
2502
    $_course['registration_code'] = !empty($course_data['registration_code']) ? sha1($course_data['registration_code']) : null;
2503
    $_course['disk_quota'] = $course_data['disk_quota'];
2504
    $_course['course_public_url'] = $webCourseHome.'/index.php';
2505
    $_course['course_sys_path'] = $courseSys.'/';
2506
2507
    if (array_key_exists('add_teachers_to_sessions_courses', $course_data)) {
2508
        $_course['add_teachers_to_sessions_courses'] = $course_data['add_teachers_to_sessions_courses'];
2509
    }
2510
2511
    // Course image
2512
    $_course['course_image_source'] = '';
2513
    if (file_exists($courseSys.'/course-pic85x85.png')) {
2514
        $url_image = $webCourseHome.'/course-pic85x85.png';
2515
        $_course['course_image_source'] = $courseSys.'/course-pic85x85.png';
2516
    } else {
2517
        $url_image = Display::return_icon(
2518
            'course.png',
2519
            null,
2520
            null,
2521
            ICON_SIZE_LARGE,
2522
            null,
2523
            true,
2524
            true
2525
        );
2526
    }
2527
    $_course['course_image'] = $url_image;
2528
2529
    // Course large image
2530
    $_course['course_image_large_source'] = '';
2531
    if (file_exists($courseSys.'/course-pic.png')) {
2532
        $url_image = $webCourseHome.'/course-pic.png';
2533
        $_course['course_image_large_source'] = $courseSys.'/course-pic.png';
2534
    } else {
2535
        $url_image = Display::return_icon(
2536
            'session_default.png',
2537
            null,
2538
            null,
2539
            null,
2540
            null,
2541
            true,
2542
            true
2543
        );
2544
    }
2545
2546
    $_course['course_image_large'] = $url_image;
2547
2548
    // email pictures
2549
    // Course image
2550
    $url_image = null;
2551
    $_course['course_email_image_source'] = '';
2552
    $mailPicture = $courseSys.'/course-email-pic-cropped.png';
2553
    if (file_exists($mailPicture)) {
2554
        $url_image = $webCourseHome.'/course-email-pic-cropped.png';
2555
        $_course['course_email_image_source'] = $mailPicture;
2556
    }
2557
    $_course['course_email_image'] = $url_image;
2558
2559
    // Course large image
2560
    $url_image = null;
2561
    $_course['course_email_image_large_source'] = '';
2562
    $mailPicture = $courseSys.'/course-email-pic.png';
2563
    if (file_exists($mailPicture)) {
2564
        $url_image = $webCourseHome.'/course-email-pic.png';
2565
        $_course['course_email_image_large_source'] = $mailPicture;
2566
    }
2567
2568
    $_course['course_email_image_large'] = $url_image;
2569
2570
    return $_course;
2571
}
2572
2573
/**
2574
 * Returns a difficult to guess password.
2575
 *
2576
 * @param int $length the length of the password
2577
 *
2578
 * @return string the generated password
2579
 */
2580
function api_generate_password($length = 8)
2581
{
2582
    if ($length < 2) {
2583
        $length = 2;
2584
    }
2585
2586
    $charactersLowerCase = Security::CHAR_LOWER;
2587
    $charactersUpperCase = Security::CHAR_UPPER;
2588
2589
    $minNumbers = 2;
2590
    $length = $length - $minNumbers;
2591
    $minLowerCase = round($length / 2);
2592
    $minUpperCase = $length - $minLowerCase;
2593
2594
    $password = '';
2595
    $passwordRequirements = api_get_configuration_value('password_requirements');
2596
2597
    $factory = new RandomLib\Factory();
2598
    $generator = $factory->getMediumStrengthGenerator();
2599
2600
    if (!empty($passwordRequirements)) {
2601
        $length = $passwordRequirements['min']['length'];
2602
        $minNumbers = $passwordRequirements['min']['numeric'];
2603
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2604
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2605
        $minSpecials = $passwordRequirements['min']['specials'];
2606
2607
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2608
        // Add the rest to fill the length requirement
2609
        if ($rest > 0) {
2610
            $password .= $generator->generateString(
2611
                $rest,
2612
                $charactersLowerCase.$charactersUpperCase
2613
            );
2614
        }
2615
2616
        $password .= $generator->generateString($minSpecials, Security::CHAR_SYMBOLS);
2617
    }
2618
2619
    // Min digits default 2
2620
    for ($i = 0; $i < $minNumbers; $i++) {
2621
        $password .= $generator->generateInt(2, 9);
2622
    }
2623
2624
    // Min lowercase
2625
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2626
2627
    // Min uppercase
2628
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2629
    $password = str_shuffle($password);
2630
2631
    return $password;
2632
}
2633
2634
/**
2635
 * Checks a password to see wether it is OK to use.
2636
 *
2637
 * @param string $password
2638
 *
2639
 * @return bool if the password is acceptable, false otherwise
2640
 *              Notes about what a password "OK to use" is:
2641
 *              1. The password should be at least 5 characters long.
2642
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2643
 *              3. The password should contain at least 3 letters.
2644
 *              4. It should contain at least 2 digits.
2645
 *              Settings will change if the configuration value is set: password_requirements
2646
 */
2647
function api_check_password($password)
2648
{
2649
    $passwordRequirements = Security::getPasswordRequirements();
2650
2651
    $minLength = $passwordRequirements['min']['length'];
2652
    $minNumbers = $passwordRequirements['min']['numeric'];
2653
    // Optional
2654
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2655
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2656
    $minSpecials = $passwordRequirements['min']['specials'];
2657
2658
    $minLetters = $minLowerCase + $minUpperCase;
2659
    $passwordLength = api_strlen($password);
2660
2661
    $conditions = [
2662
        'min_length' => $passwordLength >= $minLength,
2663
    ];
2664
2665
    $digits = 0;
2666
    $lowerCase = 0;
2667
    $upperCase = 0;
2668
    $specials = 0;
2669
2670
    for ($i = 0; $i < $passwordLength; $i++) {
2671
        $currentCharacter = api_substr($password, $i, 1);
2672
        $currentCharacterCode = api_ord($currentCharacter);
2673
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2674
            $upperCase++;
2675
        }
2676
2677
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2678
            $lowerCase++;
2679
        }
2680
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2681
            $digits++;
2682
        }
2683
2684
        if (false !== strpos(Security::CHAR_SYMBOLS, $currentCharacter)) {
2685
            $specials++;
2686
        }
2687
    }
2688
2689
    // Min number of digits
2690
    $conditions['min_numeric'] = $digits >= $minNumbers;
2691
2692
    if (!empty($minUpperCase)) {
2693
        // Uppercase
2694
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2695
    }
2696
2697
    if (!empty($minLowerCase)) {
2698
        // Lowercase
2699
        $conditions['min_lowercase'] = $lowerCase >= $minLowerCase;
2700
    }
2701
2702
    if (!empty($minSpecials)) {
2703
        $conditions['min_specials'] = $specials >= $minSpecials;
2704
    }
2705
2706
    // Min letters
2707
    $letters = $upperCase + $lowerCase;
2708
    $conditions['min_letters'] = $letters >= $minLetters;
2709
2710
    $isPasswordOk = true;
2711
    foreach ($conditions as $condition) {
2712
        if ($condition === false) {
2713
            $isPasswordOk = false;
2714
            break;
2715
        }
2716
    }
2717
2718
    if ($isPasswordOk === false) {
2719
        $output = get_lang('NewPasswordRequirementsNotMatched').'<br />';
2720
        $output .= Security::getPasswordRequirementsToString($conditions);
2721
2722
        Display::addFlash(Display::return_message($output, 'warning', false));
2723
    }
2724
2725
    return $isPasswordOk;
2726
}
2727
2728
/**
2729
 * Clears the user ID from the session if it was the anonymous user. Generally
2730
 * used on out-of-tools pages to remove a user ID that could otherwise be used
2731
 * in the wrong context.
2732
 * This function is to be used in conjunction with the api_set_anonymous()
2733
 * function to simulate the user existence in case of an anonymous visit.
2734
 *
2735
 * @param bool      database check switch - passed to api_is_anonymous()
2736
 *
2737
 * @return bool true if succesfully unregistered, false if not anonymous
2738
 */
2739
function api_clear_anonymous($db_check = false)
2740
{
2741
    global $_user;
2742
    if (isset($_user['user_id']) && api_is_anonymous($_user['user_id'], $db_check)) {
2743
        unset($_user['user_id']);
2744
        Session::erase('_uid');
2745
2746
        return true;
2747
    }
2748
2749
    return false;
2750
}
2751
2752
/**
2753
 * Returns the status string corresponding to the status code.
2754
 *
2755
 * @author Noel Dieschburg
2756
 *
2757
 * @param int $status_code The integer status code (usually in the form of a constant)
2758
 *
2759
 * @return string
2760
 */
2761
function get_status_from_code($status_code)
2762
{
2763
    switch ($status_code) {
2764
        case STUDENT:
2765
            return get_lang('Student', '');
2766
        case COURSEMANAGER:
2767
            return get_lang('Teacher', '');
2768
        case SESSIONADMIN:
2769
            return get_lang('SessionsAdmin', '');
2770
        case DRH:
2771
            return get_lang('Drh', '');
2772
        case ANONYMOUS:
2773
            return get_lang('Anonymous', '');
2774
        case PLATFORM_ADMIN:
2775
            return get_lang('Administrator', '');
2776
        case SESSION_COURSE_COACH:
2777
            return get_lang('SessionCourseCoach', '');
2778
        case SESSION_GENERAL_COACH:
2779
            return get_lang('SessionGeneralCoach', '');
2780
        case COURSE_TUTOR:
2781
            return get_lang('CourseAssistant', '');
2782
        case STUDENT_BOSS:
2783
            return get_lang('StudentBoss', '');
2784
        case INVITEE:
2785
            return get_lang('Invitee', '');
2786
    }
2787
2788
    return '';
2789
}
2790
2791
/**
2792
 * Sets the current user as anonymous if it hasn't been identified yet. This
2793
 * function should be used inside a tool only. The function api_clear_anonymous()
2794
 * acts in the opposite direction by clearing the anonymous user's data every
2795
 * time we get on a course homepage or on a neutral page (index, admin, my space).
2796
 *
2797
 * @return bool true if set user as anonymous, false if user was already logged in or anonymous id could not be found
2798
 */
2799
function api_set_anonymous()
2800
{
2801
    global $_user;
2802
2803
    if (!empty($_user['user_id'])) {
2804
        return false;
2805
    }
2806
2807
    $user_id = api_get_anonymous_id();
2808
    if ($user_id == 0) {
2809
        return false;
2810
    }
2811
2812
    if (isset($_user['is_anonymous'])) {
2813
        return false;
2814
    }
2815
2816
    Session::erase('_user');
2817
    $_user['user_id'] = $user_id;
2818
    $_user['is_anonymous'] = true;
2819
    $GLOBALS['_user'] = $_user;
2820
    Session::write('_user', $_user);
2821
2822
    return true;
2823
}
2824
2825
/**
2826
 * Gets the current Chamilo (not PHP/cookie) session ID.
2827
 *
2828
 * @return int O if no active session, the session ID otherwise
2829
 */
2830
function api_get_session_id()
2831
{
2832
    return (int) Session::read('id_session', 0);
2833
}
2834
2835
/**
2836
 * Gets the current Chamilo (not social network) group ID.
2837
 *
2838
 * @return int O if no active group, the group id otherwise
2839
 */
2840
function api_get_group_id()
2841
{
2842
    return (int) Session::read('_gid', 0);
2843
}
2844
2845
/**
2846
 * Gets the current or given session name.
2847
 *
2848
 * @param   int     Session ID (optional)
2849
 *
2850
 * @return string The session name, or null if not found
2851
 */
2852
function api_get_session_name($session_id = 0)
2853
{
2854
    if (empty($session_id)) {
2855
        $session_id = api_get_session_id();
2856
        if (empty($session_id)) {
2857
            return null;
2858
        }
2859
    }
2860
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2861
    $s = "SELECT name FROM $t WHERE id = ".(int) $session_id;
2862
    $r = Database::query($s);
2863
    $c = Database::num_rows($r);
2864
    if ($c > 0) {
2865
        //technically, there can be only one, but anyway we take the first
2866
        $rec = Database::fetch_array($r);
2867
2868
        return $rec['name'];
2869
    }
2870
2871
    return null;
2872
}
2873
2874
/**
2875
 * Gets the session info by id.
2876
 *
2877
 * @param int $id Session ID
2878
 *
2879
 * @return array information of the session
2880
 */
2881
function api_get_session_info($id)
2882
{
2883
    return SessionManager::fetch($id);
2884
}
2885
2886
/**
2887
 * Gets the session visibility by session id.
2888
 *
2889
 * @param int  $session_id
2890
 * @param int  $courseId
2891
 * @param bool $ignore_visibility_for_admins
2892
 * @param int  $userId
2893
 *
2894
 * @return int
2895
 *             0 = session still available,
2896
 *             SESSION_VISIBLE_READ_ONLY = 1,
2897
 *             SESSION_VISIBLE = 2,
2898
 *             SESSION_INVISIBLE = 3
2899
 */
2900
function api_get_session_visibility(
2901
    $session_id,
2902
    $courseId = null,
2903
    $ignore_visibility_for_admins = true,
2904
    $userId = 0
2905
) {
2906
    if (api_is_platform_admin()) {
2907
        if ($ignore_visibility_for_admins) {
2908
            return SESSION_AVAILABLE;
2909
        }
2910
    }
2911
2912
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2913
2914
    $now = time();
2915
    if (empty($session_id)) {
2916
        return 0; // Means that the session is still available.
2917
    }
2918
2919
    $session_id = (int) $session_id;
2920
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2921
2922
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2923
2924
    if (Database::num_rows($result) <= 0) {
2925
        return SESSION_INVISIBLE;
2926
    }
2927
2928
    $row = Database::fetch_array($result, 'ASSOC');
2929
    $visibility = $row['visibility'];
2930
2931
    // I don't care the session visibility.
2932
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2933
2934
        // Session duration per student.
2935
        if (isset($row['duration']) && !empty($row['duration'])) {
2936
            if (api_get_configuration_value('session_coach_access_after_duration_end') == true && api_is_teacher()) {
2937
                return SESSION_AVAILABLE;
2938
            }
2939
2940
            $duration = $row['duration'] * 24 * 60 * 60;
2941
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2942
2943
            // If there is a session duration but there is no previous
2944
            // access by the user, then the session is still available
2945
            if (0 == count($courseAccess)) {
2946
                return SESSION_AVAILABLE;
2947
            }
2948
2949
            $currentTime = time();
2950
            $firstAccess = isset($courseAccess['login_course_date'])
2951
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2952
                : 0;
2953
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2954
            $userDuration = isset($userDurationData['duration'])
2955
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2956
                : 0;
2957
2958
            $totalDuration = $firstAccess + $duration + $userDuration;
2959
2960
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2961
        }
2962
2963
        return SESSION_AVAILABLE;
2964
    }
2965
2966
    // If start date was set.
2967
    if (!empty($row['access_start_date'])) {
2968
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2969
    }
2970
2971
    // If the end date was set.
2972
    if (!empty($row['access_end_date'])) {
2973
        // Only if date_start said that it was ok
2974
        if ($visibility === SESSION_AVAILABLE) {
2975
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2976
                ? SESSION_AVAILABLE // Date still available
2977
                : $row['visibility']; // Session ends
2978
        }
2979
    }
2980
2981
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2982
    $isCoach = api_is_coach($session_id, $courseId, $userId);
2983
2984
    if ($isCoach) {
2985
        // Test start date.
2986
        if (!empty($row['coach_access_start_date'])) {
2987
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2988
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2989
        }
2990
2991
        // Test end date.
2992
        if (!empty($row['coach_access_end_date'])) {
2993
            if ($visibility === SESSION_AVAILABLE) {
2994
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2995
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2996
            }
2997
        }
2998
    }
2999
3000
    return $visibility;
3001
}
3002
3003
/**
3004
 * This function returns a (star) session icon if the session is not null and
3005
 * the user is not a student.
3006
 *
3007
 * @param int $sessionId
3008
 * @param int $statusId  User status id - if 5 (student) or in student view, will return empty
3009
 *
3010
 * @return string Session icon
3011
 */
3012
function api_get_session_image($sessionId, $statusId)
3013
{
3014
    $sessionId = (int) $sessionId;
3015
    $image = '';
3016
    $studentView = !empty($_SESSION['studentview']) && $_SESSION['studentview'] == 'studentview';
3017
    if ($statusId != STUDENT && !$studentView) {
3018
        // Check whether is not a student
3019
        if ($sessionId > 0) {
3020
            $image = '&nbsp;&nbsp;'.Display::return_icon(
3021
                'star.png',
3022
                get_lang('SessionSpecificResource'),
3023
                ['align' => 'absmiddle'],
3024
                ICON_SIZE_SMALL
3025
            );
3026
        }
3027
    }
3028
3029
    return $image;
3030
}
3031
3032
/**
3033
 * This function add an additional condition according to the session of the course.
3034
 *
3035
 * @param int    $session_id        session id
3036
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
3037
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
3038
 *                                  false for strict session condition
3039
 * @param string $session_field
3040
 *
3041
 * @return string condition of the session
3042
 */
3043
function api_get_session_condition(
3044
    $session_id,
3045
    $and = true,
3046
    $with_base_content = false,
3047
    $session_field = 'session_id'
3048
) {
3049
    $session_id = (int) $session_id;
3050
3051
    if (empty($session_field)) {
3052
        $session_field = 'session_id';
3053
    }
3054
    // Condition to show resources by session
3055
    $condition_add = $and ? ' AND ' : ' WHERE ';
3056
3057
    if ($with_base_content) {
3058
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
3059
    } else {
3060
        if (empty($session_id)) {
3061
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
3062
        } else {
3063
            $condition_session = $condition_add." $session_field = $session_id ";
3064
        }
3065
    }
3066
3067
    return $condition_session;
3068
}
3069
3070
/**
3071
 * Returns the value of a setting from the web-adjustable admin config settings.
3072
 *
3073
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
3074
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
3075
 * instead of
3076
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
3077
 *
3078
 * @param string $variable The variable name
3079
 * @param string $key      The subkey (sub-variable) if any. Defaults to NULL
3080
 *
3081
 * @return string
3082
 *
3083
 * @author René Haentjens
3084
 * @author Bart Mollet
3085
 */
3086
function api_get_setting($variable, $key = null)
3087
{
3088
    global $_setting;
3089
    if ($variable == 'header_extra_content') {
3090
        $filename = api_get_home_path().'header_extra_content.txt';
3091
        if (file_exists($filename)) {
3092
            $value = file_get_contents($filename);
3093
3094
            return $value;
3095
        } else {
3096
            return '';
3097
        }
3098
    }
3099
    if ($variable == 'footer_extra_content') {
3100
        $filename = api_get_home_path().'footer_extra_content.txt';
3101
        if (file_exists($filename)) {
3102
            $value = file_get_contents($filename);
3103
3104
            return $value;
3105
        } else {
3106
            return '';
3107
        }
3108
    }
3109
    $value = null;
3110
    if (is_null($key)) {
3111
        $value = ((isset($_setting[$variable]) && $_setting[$variable] != '') ? $_setting[$variable] : null);
3112
    } else {
3113
        if (isset($_setting[$variable][$key])) {
3114
            $value = $_setting[$variable][$key];
3115
        }
3116
    }
3117
3118
    return $value;
3119
}
3120
3121
/**
3122
 * @param string $plugin
3123
 * @param string $variable
3124
 *
3125
 * @return string
3126
 */
3127
function api_get_plugin_setting($plugin, $variable)
3128
{
3129
    $settings = api_get_configuration_value('plugin_settings');
3130
3131
    if (!empty($settings) && isset($settings[$plugin]) && isset($settings[$plugin][$variable])) {
3132
        return $settings[$plugin][$variable];
3133
    }
3134
3135
    $variableName = $plugin.'_'.$variable;
3136
    $result = api_get_setting($variableName);
3137
3138
    if (isset($result[$plugin])) {
3139
        $value = $result[$plugin];
3140
        $unSerialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
3141
3142
        if (false !== $unSerialized) {
3143
            $value = $unSerialized;
3144
        }
3145
3146
        return $value;
3147
    }
3148
3149
    return null;
3150
}
3151
3152
/**
3153
 * Returns the value of a setting from the web-adjustable admin config settings.
3154
 */
3155
function api_get_settings_params($params)
3156
{
3157
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3158
3159
    return Database::select('*', $table, ['where' => $params]);
3160
}
3161
3162
/**
3163
 * @param array $params example: [id = ? => '1']
3164
 *
3165
 * @return array
3166
 */
3167
function api_get_settings_params_simple($params)
3168
{
3169
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3170
3171
    return Database::select('*', $table, ['where' => $params], 'one');
3172
}
3173
3174
/**
3175
 * Returns the value of a setting from the web-adjustable admin config settings.
3176
 */
3177
function api_delete_settings_params($params)
3178
{
3179
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3180
    $result = Database::delete($table, $params);
3181
3182
    return $result;
3183
}
3184
3185
/**
3186
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
3187
 *
3188
 * @return string Escaped version of $_SERVER['PHP_SELF']
3189
 */
3190
function api_get_self()
3191
{
3192
    return htmlentities($_SERVER['PHP_SELF']);
3193
}
3194
3195
/* USER PERMISSIONS */
3196
3197
/**
3198
 * Checks whether current user is a platform administrator.
3199
 *
3200
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
3201
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
3202
 *
3203
 * @return bool true if the user has platform admin rights,
3204
 *              false otherwise
3205
 *
3206
 * @see usermanager::is_admin(user_id) for a user-id specific function
3207
 */
3208
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
3209
{
3210
    $isAdmin = Session::read('is_platformAdmin');
3211
    if ($isAdmin) {
3212
        return true;
3213
    }
3214
    $user = api_get_user_info();
3215
3216
    return
3217
        isset($user['status']) &&
3218
        (
3219
            ($allowSessionAdmins && $user['status'] == SESSIONADMIN) ||
3220
            ($allowDrh && $user['status'] == DRH)
3221
        );
3222
}
3223
3224
/**
3225
 * Checks whether the user given as user id is in the admin table.
3226
 *
3227
 * @param int $user_id If none provided, will use current user
3228
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
3229
 *
3230
 * @return bool True if the user is admin, false otherwise
3231
 */
3232
function api_is_platform_admin_by_id($user_id = null, $url = null)
3233
{
3234
    $user_id = (int) $user_id;
3235
    if (empty($user_id)) {
3236
        $user_id = api_get_user_id();
3237
    }
3238
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3239
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3240
    $res = Database::query($sql);
3241
    $is_admin = Database::num_rows($res) === 1;
3242
    if (!$is_admin || !isset($url)) {
3243
        return $is_admin;
3244
    }
3245
    // We get here only if $url is set
3246
    $url = (int) $url;
3247
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3248
    $sql = "SELECT * FROM $url_user_table
3249
            WHERE access_url_id = $url AND user_id = $user_id";
3250
    $res = Database::query($sql);
3251
    $result = Database::num_rows($res) === 1;
3252
3253
    return $result;
3254
}
3255
3256
/**
3257
 * Returns the user's numeric status ID from the users table.
3258
 *
3259
 * @param int $user_id If none provided, will use current user
3260
 *
3261
 * @return int User's status (1 for teacher, 5 for student, etc)
3262
 */
3263
function api_get_user_status($user_id = null)
3264
{
3265
    $user_id = (int) $user_id;
3266
    if (empty($user_id)) {
3267
        $user_id = api_get_user_id();
3268
    }
3269
    $table = Database::get_main_table(TABLE_MAIN_USER);
3270
    $sql = "SELECT status FROM $table WHERE user_id = $user_id ";
3271
    $result = Database::query($sql);
3272
    $status = null;
3273
    if (Database::num_rows($result)) {
3274
        $row = Database::fetch_array($result);
3275
        $status = $row['status'];
3276
    }
3277
3278
    return $status;
3279
}
3280
3281
/**
3282
 * Checks whether current user is allowed to create courses.
3283
 *
3284
 * @return bool true if the user has course creation rights,
3285
 *              false otherwise
3286
 */
3287
function api_is_allowed_to_create_course()
3288
{
3289
    if (api_is_platform_admin()) {
3290
        return true;
3291
    }
3292
3293
    // Teachers can only create courses
3294
    if (api_is_teacher()) {
3295
        if (api_get_setting('allow_users_to_create_courses') === 'true') {
3296
            return true;
3297
        } else {
3298
            return false;
3299
        }
3300
    }
3301
3302
    return Session::read('is_allowedCreateCourse');
3303
}
3304
3305
/**
3306
 * Checks whether the current user is a course administrator.
3307
 *
3308
 * @return bool True if current user is a course administrator
3309
 */
3310
function api_is_course_admin()
3311
{
3312
    if (api_is_platform_admin()) {
3313
        return true;
3314
    }
3315
3316
    return Session::read('is_courseAdmin');
3317
}
3318
3319
/**
3320
 * Checks whether the current user is a course coach
3321
 * Based on the presence of user in session.id_coach (session general coach).
3322
 *
3323
 * @return bool True if current user is a course coach
3324
 */
3325
function api_is_session_general_coach()
3326
{
3327
    return Session::read('is_session_general_coach');
3328
}
3329
3330
/**
3331
 * Checks whether the current user is a course tutor
3332
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
3333
 *
3334
 * @return bool True if current user is a course tutor
3335
 */
3336
function api_is_course_tutor()
3337
{
3338
    return Session::read('is_courseTutor');
3339
}
3340
3341
/**
3342
 * @param int $user_id
3343
 * @param int $courseId
3344
 * @param int $session_id
3345
 *
3346
 * @return bool
3347
 */
3348
function api_is_course_session_coach($user_id, $courseId, $session_id)
3349
{
3350
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3351
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3352
3353
    $user_id = (int) $user_id;
3354
    $session_id = (int) $session_id;
3355
    $courseId = (int) $courseId;
3356
3357
    $sql = "SELECT DISTINCT session.id
3358
            FROM $session_table
3359
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3360
            ON session.id = session_rc_ru.session_id
3361
            WHERE
3362
                session_rc_ru.user_id = '".$user_id."'  AND
3363
                session_rc_ru.c_id = '$courseId' AND
3364
                session_rc_ru.status = 2 AND
3365
                session_rc_ru.session_id = '$session_id'";
3366
    $result = Database::query($sql);
3367
3368
    return Database::num_rows($result) > 0;
3369
}
3370
3371
/**
3372
 * Checks whether the current user is a course or session coach.
3373
 *
3374
 * @param int $session_id
3375
 * @param int $courseId
3376
 * @param bool  Check whether we are in student view and, if we are, return false
3377
 * @param int $userId
3378
 *
3379
 * @return bool True if current user is a course or session coach
3380
 */
3381
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3382
{
3383
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3384
3385
    if (!empty($session_id)) {
3386
        $session_id = (int) $session_id;
3387
    } else {
3388
        $session_id = api_get_session_id();
3389
    }
3390
3391
    // The student preview was on
3392
    if ($check_student_view && api_is_student_view_active()) {
3393
        return false;
3394
    }
3395
3396
    if (!empty($courseId)) {
3397
        $courseId = (int) $courseId;
3398
    } else {
3399
        $courseId = api_get_course_int_id();
3400
    }
3401
3402
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3403
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3404
    $sessionIsCoach = [];
3405
3406
    if (!empty($courseId)) {
3407
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3408
                FROM $session_table s
3409
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3410
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3411
                WHERE
3412
                    session_rc_ru.c_id = '$courseId' AND
3413
                    session_rc_ru.status = 2 AND
3414
                    session_rc_ru.session_id = '$session_id'";
3415
        $result = Database::query($sql);
3416
        $sessionIsCoach = Database::store_result($result);
3417
    }
3418
3419
    if (!empty($session_id)) {
3420
        $sql = "SELECT DISTINCT id, name, access_start_date, access_end_date
3421
                FROM $session_table
3422
                WHERE session.id_coach = $userId AND id = $session_id
3423
                ORDER BY access_start_date, access_end_date, name";
3424
        $result = Database::query($sql);
3425
        if (!empty($sessionIsCoach)) {
3426
            $sessionIsCoach = array_merge(
3427
                $sessionIsCoach,
3428
                Database::store_result($result)
3429
            );
3430
        } else {
3431
            $sessionIsCoach = Database::store_result($result);
3432
        }
3433
    }
3434
3435
    return count($sessionIsCoach) > 0;
3436
}
3437
3438
/**
3439
 * Checks whether the current user is a session administrator.
3440
 *
3441
 * @return bool True if current user is a course administrator
3442
 */
3443
function api_is_session_admin()
3444
{
3445
    $user = api_get_user_info();
3446
3447
    return isset($user['status']) && $user['status'] == SESSIONADMIN;
3448
}
3449
3450
/**
3451
 * Checks whether the current user is a human resources manager.
3452
 *
3453
 * @return bool True if current user is a human resources manager
3454
 */
3455
function api_is_drh()
3456
{
3457
    $user = api_get_user_info();
3458
3459
    return isset($user['status']) && $user['status'] == DRH;
3460
}
3461
3462
/**
3463
 * Checks whether the current user is a student.
3464
 *
3465
 * @return bool True if current user is a human resources manager
3466
 */
3467
function api_is_student()
3468
{
3469
    $user = api_get_user_info();
3470
3471
    return isset($user['status']) && $user['status'] == STUDENT;
3472
}
3473
3474
/**
3475
 * Checks whether the current user has the status 'teacher'.
3476
 *
3477
 * @return bool True if current user is a human resources manager
3478
 */
3479
function api_is_teacher()
3480
{
3481
    $user = api_get_user_info();
3482
3483
    return isset($user['status']) && $user['status'] == COURSEMANAGER;
3484
}
3485
3486
/**
3487
 * Checks whether the current user is a invited user.
3488
 *
3489
 * @return bool
3490
 */
3491
function api_is_invitee()
3492
{
3493
    $user = api_get_user_info();
3494
3495
    return isset($user['status']) && $user['status'] == INVITEE;
3496
}
3497
3498
/**
3499
 * This function checks whether a session is assigned into a category.
3500
 *
3501
 * @param int       - session id
3502
 * @param string    - category name
3503
 *
3504
 * @return bool - true if is found, otherwise false
3505
 */
3506
function api_is_session_in_category($session_id, $category_name)
3507
{
3508
    $session_id = (int) $session_id;
3509
    $category_name = Database::escape_string($category_name);
3510
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3511
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3512
3513
    $sql = "SELECT 1
3514
            FROM $tbl_session
3515
            WHERE $session_id IN (
3516
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3517
                WHERE
3518
                  s.session_category_id = sc.id AND
3519
                  sc.name LIKE '%$category_name'
3520
            )";
3521
    $rs = Database::query($sql);
3522
3523
    if (Database::num_rows($rs) > 0) {
3524
        return true;
3525
    } else {
3526
        return false;
3527
    }
3528
}
3529
3530
/**
3531
 * Displays the title of a tool.
3532
 * Normal use: parameter is a string:
3533
 * api_display_tool_title("My Tool").
3534
 *
3535
 * Optionally, there can be a subtitle below
3536
 * the normal title, and / or a supra title above the normal title.
3537
 *
3538
 * e.g. supra title:
3539
 * group
3540
 * GROUP PROPERTIES
3541
 *
3542
 * e.g. subtitle:
3543
 * AGENDA
3544
 * calender & events tool
3545
 *
3546
 * @author Hugues Peeters <[email protected]>
3547
 *
3548
 * @param mixed $title_element - it could either be a string or an array
3549
 *                             containing 'supraTitle', 'mainTitle',
3550
 *                             'subTitle'
3551
 */
3552
function api_display_tool_title($title_element)
3553
{
3554
    if (is_string($title_element)) {
3555
        $tit = $title_element;
3556
        unset($title_element);
3557
        $title_element = [];
3558
        $title_element['mainTitle'] = $tit;
3559
    }
3560
    echo '<h3>';
3561
    if (!empty($title_element['supraTitle'])) {
3562
        echo '<small>'.$title_element['supraTitle'].'</small><br />';
3563
    }
3564
    if (!empty($title_element['mainTitle'])) {
3565
        echo $title_element['mainTitle'];
3566
    }
3567
    if (!empty($title_element['subTitle'])) {
3568
        echo '<br /><small>'.$title_element['subTitle'].'</small>';
3569
    }
3570
    echo '</h3>';
3571
}
3572
3573
/**
3574
 * Displays options for switching between student view and course manager view.
3575
 *
3576
 * Changes in version 1.2 (Patrick Cool)
3577
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3578
 * is changed explicitly
3579
 *
3580
 * Changes in version 1.1 (Patrick Cool)
3581
 * student view now works correctly in subfolders of the document tool
3582
 * student view works correctly in the new links tool
3583
 *
3584
 * Example code for using this in your tools:
3585
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3586
 * //   display_tool_view_option($isStudentView);
3587
 * //}
3588
 * //and in later sections, use api_is_allowed_to_edit()
3589
 *
3590
 * @author Roan Embrechts
3591
 * @author Patrick Cool
3592
 * @author Julio Montoya, changes added in Chamilo
3593
 *
3594
 * @version 1.2
3595
 *
3596
 * @todo rewrite code so it is easier to understand
3597
 */
3598
function api_display_tool_view_option()
3599
{
3600
    if (api_get_setting('student_view_enabled') != 'true') {
3601
        return '';
3602
    }
3603
3604
    $sourceurl = '';
3605
    $is_framed = false;
3606
    // Exceptions apply for all multi-frames pages
3607
    if (strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php') !== false) {
3608
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3609
        return '';
3610
    }
3611
3612
    // Uncomment to remove student view link from document view page
3613
    if (strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php') !== false) {
3614
        if (empty($_GET['lp_id'])) {
3615
            return '';
3616
        }
3617
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3618
        $sourceurl = str_replace(
3619
            'lp/lp_header.php',
3620
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.($_SESSION['studentview'] == 'studentview' ? 'false' : 'true'),
3621
            $sourceurl
3622
        );
3623
        //showinframes doesn't handle student view anyway...
3624
        //return '';
3625
        $is_framed = true;
3626
    }
3627
3628
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3629
    if (!$is_framed) {
3630
        if (strpos($_SERVER['REQUEST_URI'], '?') === false) {
3631
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3632
        } else {
3633
            $sourceurl = $_SERVER['REQUEST_URI'];
3634
        }
3635
    }
3636
3637
    $output_string = '';
3638
    if (!empty($_SESSION['studentview'])) {
3639
        if ($_SESSION['studentview'] == 'studentview') {
3640
            // We have to remove the isStudentView=true from the $sourceurl
3641
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3642
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3643
            $output_string .= '<a class="btn btn-primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3644
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToTeacherView').'</a>';
3645
        } elseif ($_SESSION['studentview'] == 'teacherview') {
3646
            // Switching to teacherview
3647
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3648
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3649
            $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3650
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3651
        }
3652
    } else {
3653
        $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3654
            Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3655
    }
3656
    $output_string = Security::remove_XSS($output_string);
3657
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3658
3659
    return $html;
3660
}
3661
3662
// TODO: This is for the permission section.
3663
/**
3664
 * Function that removes the need to directly use is_courseAdmin global in
3665
 * tool scripts. It returns true or false depending on the user's rights in
3666
 * this particular course.
3667
 * Optionally checking for tutor and coach roles here allows us to use the
3668
 * student_view feature altogether with these roles as well.
3669
 *
3670
 * @param bool  Whether to check if the user has the tutor role
3671
 * @param bool  Whether to check if the user has the coach role
3672
 * @param bool  Whether to check if the user has the session coach role
3673
 * @param bool  check the student view or not
3674
 *
3675
 * @author Roan Embrechts
3676
 * @author Patrick Cool
3677
 * @author Julio Montoya
3678
 *
3679
 * @version 1.1, February 2004
3680
 *
3681
 * @return bool true: the user has the rights to edit, false: he does not
3682
 */
3683
function api_is_allowed_to_edit(
3684
    $tutor = false,
3685
    $coach = false,
3686
    $session_coach = false,
3687
    $check_student_view = true
3688
) {
3689
    $allowSessionAdminEdit = api_get_configuration_value('session_admins_edit_courses_content') === true;
3690
3691
    // Admins can edit anything.
3692
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3693
        //The student preview was on
3694
        if ($check_student_view && api_is_student_view_active()) {
3695
            return false;
3696
        }
3697
3698
        return true;
3699
    }
3700
3701
    $sessionId = api_get_session_id();
3702
3703
    if ($sessionId && api_get_configuration_value('session_courses_read_only_mode')) {
3704
        $efv = new ExtraFieldValue('course');
3705
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3706
            api_get_course_int_id(),
3707
            'session_courses_read_only_mode'
3708
        );
3709
3710
        if (!empty($lockExrafieldField['value'])) {
3711
            return false;
3712
        }
3713
    }
3714
3715
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3716
    $session_visibility = api_get_session_visibility($sessionId);
3717
    $is_courseAdmin = api_is_course_admin();
3718
3719
    if (!$is_courseAdmin && $tutor) {
3720
        // If we also want to check if the user is a tutor...
3721
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3722
    }
3723
3724
    if (!$is_courseAdmin && $coach) {
3725
        // If we also want to check if the user is a coach...';
3726
        // Check if session visibility is read only for coaches.
3727
        if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3728
            $is_allowed_coach_to_edit = false;
3729
        }
3730
3731
        if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3732
            // Check if coach is allowed to edit a course.
3733
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3734
        }
3735
    }
3736
3737
    if (!$is_courseAdmin && $session_coach) {
3738
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3739
    }
3740
3741
    // Check if the student_view is enabled, and if so, if it is activated.
3742
    if (api_get_setting('student_view_enabled') == 'true') {
3743
        if (!empty($sessionId)) {
3744
            // Check if session visibility is read only for coaches.
3745
            if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3746
                $is_allowed_coach_to_edit = false;
3747
            }
3748
3749
            if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3750
                // Check if coach is allowed to edit a course.
3751
                $is_allowed = $is_allowed_coach_to_edit;
3752
            } else {
3753
                $is_allowed = false;
3754
            }
3755
            if ($check_student_view) {
3756
                $is_allowed = $is_allowed && $_SESSION['studentview'] != 'studentview';
3757
            }
3758
        } else {
3759
            if ($check_student_view) {
3760
                $is_allowed = $is_courseAdmin && $_SESSION['studentview'] != 'studentview';
3761
            } else {
3762
                $is_allowed = $is_courseAdmin;
3763
            }
3764
        }
3765
3766
        return $is_allowed;
3767
    } else {
3768
        return $is_courseAdmin;
3769
    }
3770
}
3771
3772
/**
3773
 * Returns true if user is a course coach of at least one course in session.
3774
 *
3775
 * @param int $sessionId
3776
 *
3777
 * @return bool
3778
 */
3779
function api_is_coach_of_course_in_session($sessionId)
3780
{
3781
    if (api_is_platform_admin()) {
3782
        return true;
3783
    }
3784
3785
    $userId = api_get_user_id();
3786
    $courseList = UserManager::get_courses_list_by_session(
3787
        $userId,
3788
        $sessionId
3789
    );
3790
3791
    // Session visibility.
3792
    $visibility = api_get_session_visibility(
3793
        $sessionId,
3794
        null,
3795
        false
3796
    );
3797
3798
    if ($visibility != SESSION_VISIBLE && !empty($courseList)) {
3799
        // Course Coach session visibility.
3800
        $blockedCourseCount = 0;
3801
        $closedVisibilityList = [
3802
            COURSE_VISIBILITY_CLOSED,
3803
            COURSE_VISIBILITY_HIDDEN,
3804
        ];
3805
3806
        foreach ($courseList as $course) {
3807
            // Checking session visibility
3808
            $sessionCourseVisibility = api_get_session_visibility(
3809
                $sessionId,
3810
                $course['real_id']
3811
            );
3812
3813
            $courseIsVisible = !in_array(
3814
                $course['visibility'],
3815
                $closedVisibilityList
3816
            );
3817
            if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3818
                $blockedCourseCount++;
3819
            }
3820
        }
3821
3822
        // If all courses are blocked then no show in the list.
3823
        if ($blockedCourseCount === count($courseList)) {
3824
            $visibility = SESSION_INVISIBLE;
3825
        } else {
3826
            $visibility = SESSION_VISIBLE;
3827
        }
3828
    }
3829
3830
    switch ($visibility) {
3831
        case SESSION_VISIBLE_READ_ONLY:
3832
        case SESSION_VISIBLE:
3833
        case SESSION_AVAILABLE:
3834
            return true;
3835
            break;
3836
        case SESSION_INVISIBLE:
3837
            return false;
3838
    }
3839
3840
    return false;
3841
}
3842
3843
/**
3844
 * Checks if a student can edit contents in a session depending
3845
 * on the session visibility.
3846
 *
3847
 * @param bool $tutor Whether to check if the user has the tutor role
3848
 * @param bool $coach Whether to check if the user has the coach role
3849
 *
3850
 * @return bool true: the user has the rights to edit, false: he does not
3851
 */
3852
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3853
{
3854
    if (api_is_allowed_to_edit($tutor, $coach)) {
3855
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3856
        return true;
3857
    } else {
3858
        $sessionId = api_get_session_id();
3859
3860
        if (0 == $sessionId) {
3861
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3862
            return true;
3863
        } else {
3864
            // I'm in a session and I'm a student
3865
            // Get the session visibility
3866
            $session_visibility = api_get_session_visibility($sessionId);
3867
            // if 5 the session is still available
3868
            switch ($session_visibility) {
3869
                case SESSION_VISIBLE_READ_ONLY: // 1
3870
                    return false;
3871
                case SESSION_VISIBLE:           // 2
3872
                    return true;
3873
                case SESSION_INVISIBLE:         // 3
3874
                    return false;
3875
                case SESSION_AVAILABLE:         //5
3876
                    return true;
3877
            }
3878
        }
3879
    }
3880
3881
    return false;
3882
}
3883
3884
/**
3885
 * Checks whether the user is allowed in a specific tool for a specific action.
3886
 *
3887
 * @param string $tool   the tool we are checking if the user has a certain permission
3888
 * @param string $action the action we are checking (add, edit, delete, move, visibility)
3889
 *
3890
 * @return bool
3891
 *
3892
 * @author Patrick Cool <[email protected]>, Ghent University
3893
 * @author Julio Montoya
3894
 *
3895
 * @version 1.0
3896
 */
3897
function api_is_allowed($tool, $action, $task_id = 0)
3898
{
3899
    $_user = api_get_user_info();
3900
    $_course = api_get_course_info();
3901
3902
    if (api_is_course_admin()) {
3903
        return true;
3904
    }
3905
3906
    if (is_array($_course) and count($_course) > 0) {
3907
        require_once __DIR__.'/../../permissions/permissions_functions.inc.php';
3908
3909
        // Getting the permissions of this user.
3910
        if ($task_id == 0) {
3911
            $user_permissions = get_permissions('user', $_user['user_id']);
3912
            $_SESSION['total_permissions'][$_course['code']] = $user_permissions;
3913
        }
3914
3915
        // Getting the permissions of the task.
3916
        if ($task_id != 0) {
3917
            $task_permissions = get_permissions('task', $task_id);
3918
            $_SESSION['total_permissions'][$_course['code']] = $task_permissions;
3919
        }
3920
        //print_r($_SESSION['total_permissions']);
3921
3922
        // Getting the permissions of the groups of the user
3923
        //$groups_of_user = GroupManager::get_group_ids($_course['db_name'], $_user['user_id']);
3924
3925
        //foreach($groups_of_user as $group)
3926
        //   $this_group_permissions = get_permissions('group', $group);
3927
3928
        // Getting the permissions of the courseroles of the user
3929
        $user_courserole_permissions = get_roles_permissions('user', $_user['user_id']);
3930
3931
        // Getting the permissions of the platformroles of the user
3932
        //$user_platformrole_permissions = get_roles_permissions('user', $_user['user_id'], ', platform');
3933
3934
        // Getting the permissions of the roles of the groups of the user
3935
        //foreach($groups_of_user as $group)
3936
        //    $this_group_courserole_permissions = get_roles_permissions('group', $group);
3937
3938
        // Getting the permissions of the platformroles of the groups of the user
3939
        //foreach($groups_of_user as $group)
3940
        //    $this_group_platformrole_permissions = get_roles_permissions('group', $group, 'platform');
3941
    }
3942
3943
    // If the permissions are limited, we have to map the extended ones to the limited ones.
3944
    if (api_get_setting('permissions') == 'limited') {
3945
        if ($action == 'Visibility') {
3946
            $action = 'Edit';
3947
        }
3948
        if ($action == 'Move') {
3949
            $action = 'Edit';
3950
        }
3951
    }
3952
3953
    // The session that contains all the permissions already exists for this course
3954
    // so there is no need to requery everything.
3955
    //my_print_r($_SESSION['total_permissions'][$_course['code']][$tool]);
3956
    if (is_array($_SESSION['total_permissions'][$_course['code']][$tool])) {
3957
        if (in_array($action, $_SESSION['total_permissions'][$_course['code']][$tool])) {
3958
            return true;
3959
        } else {
3960
            return false;
3961
        }
3962
    }
3963
3964
    return false;
3965
}
3966
3967
/**
3968
 * Tells whether this user is an anonymous user.
3969
 *
3970
 * @param int  $user_id  User ID (optional, will take session ID if not provided)
3971
 * @param bool $db_check Whether to check in the database (true) or simply in
3972
 *                       the session (false) to see if the current user is the anonymous user
3973
 *
3974
 * @return bool true if this user is anonymous, false otherwise
3975
 */
3976
function api_is_anonymous($user_id = null, $db_check = false)
3977
{
3978
    if (!isset($user_id)) {
3979
        $user_id = api_get_user_id();
3980
    }
3981
3982
    if ($db_check) {
3983
        $info = api_get_user_info($user_id);
3984
        if (false === $info || $info['status'] == ANONYMOUS) {
3985
            return true;
3986
        }
3987
    }
3988
3989
    $_user = api_get_user_info();
3990
3991
    if (isset($_user['status']) && $_user['status'] == ANONYMOUS) {
3992
        //if ($_user['user_id'] == 0) {
3993
        // In some cases, api_set_anonymous doesn't seem to be triggered in local.inc.php. Make sure it is.
3994
        // Occurs in agenda for admin links - YW
3995
        // it occurs when pages are opened directly without entering first the course home page. To fix it add
3996
        // $use_anonymous = true;
3997
        // before including global.inc.php in the page
3998
        global $use_anonymous;
3999
        if (isset($use_anonymous) && $use_anonymous) {
4000
            api_set_anonymous();
4001
        }
4002
4003
        return true;
4004
    }
4005
4006
    return (isset($_user['is_anonymous']) && $_user['is_anonymous'] === true) || $_user === false;
4007
}
4008
4009
/**
4010
 * Displays message "You are not allowed here..." and exits the entire script.
4011
 *
4012
 * @param bool   $print_headers Whether or not to print headers (default = false -> does not print them)
4013
 * @param string $message
4014
 * @param int    $responseCode
4015
 */
4016
function api_not_allowed(
4017
    $print_headers = false,
4018
    $message = null,
4019
    $responseCode = 0
4020
) {
4021
    if (api_get_setting('sso_authentication') === 'true') {
4022
        global $osso;
4023
        if ($osso) {
4024
            $osso->logout();
4025
        }
4026
    }
4027
    $home_url = api_get_path(WEB_PATH);
4028
    $user_id = api_get_user_id();
4029
    $course = api_get_course_id();
4030
4031
    global $this_section;
4032
4033
    if (CustomPages::enabled() && !isset($user_id)) {
4034
        if (empty($user_id)) {
4035
            // Why the CustomPages::enabled() need to be to set the request_uri
4036
            $_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
4037
        }
4038
        CustomPages::display(CustomPages::INDEX_UNLOGGED);
4039
    }
4040
4041
    $origin = api_get_origin();
4042
4043
    $msg = null;
4044
    if (isset($message)) {
4045
        $msg = $message;
4046
    } else {
4047
        $msg = Display::return_message(
4048
            get_lang('NotAllowedClickBack').'
4049
            <script>function goBack(){window.history.back();}</script>',
4050
            'error',
4051
            false
4052
        );
4053
        $msg .= '<p class="text-center">
4054
             <a onclick="goBack();" class="btn btn-default" href="'.$home_url.'">'.get_lang('GoBack').'</a>
4055
             </p>';
4056
    }
4057
4058
    $msg = Display::div($msg, ['align' => 'center']);
4059
4060
    $show_headers = 0;
4061
    if ($print_headers && $origin != 'learnpath') {
4062
        $show_headers = 1;
4063
    }
4064
4065
    $hideBreadCrumb = false;
4066
    if (api_get_configuration_value('hide_breadcrumb_if_not_allowed')) {
4067
        $hideBreadCrumb = true;
4068
    }
4069
4070
    $tpl = new Template(null, $show_headers, $show_headers, $hideBreadCrumb, true, false, true, $responseCode);
4071
    $tpl->assign('hide_login_link', 1);
4072
    $tpl->assign('content', $msg);
4073
4074
    if (($user_id != 0 && !api_is_anonymous()) &&
4075
        (!isset($course) || $course == -1) &&
4076
        empty($_GET['cidReq'])
4077
    ) {
4078
        // if the access is not authorized and there is some login information
4079
        // but the cidReq is not found, assume we are missing course data and send the user
4080
        // to the user_portal
4081
        $tpl->display_one_col_template();
4082
        exit;
4083
    }
4084
4085
    $tplPlugin = new AppPlugin();
4086
    $loginTopRegionContent = $tplPlugin->load_region('login_top', $tpl, true);
4087
    $loginBottomRegionContent = $tplPlugin->load_region('login_bottom', $tpl, true);
4088
4089
    if (!empty($_SERVER['REQUEST_URI']) &&
4090
        (
4091
            !empty($_GET['cidReq']) ||
4092
            $this_section == SECTION_MYPROFILE ||
4093
            $this_section == SECTION_PLATFORM_ADMIN
4094
        )
4095
    ) {
4096
        $courseCode = api_get_course_id();
4097
        // Only display form and return to the previous URL if there was a course ID included
4098
        if ($user_id != 0 && !api_is_anonymous()) {
4099
            //if there is a user ID, then the user is not allowed but the session is still there. Say so and exit
4100
            $tpl->assign('content', $msg);
4101
            $tpl->display_one_col_template();
4102
            exit;
4103
        }
4104
4105
        if (!is_null($courseCode)) {
4106
            api_set_firstpage_parameter($courseCode);
4107
        }
4108
4109
        // If the user has no user ID, then his session has expired
4110
        $form = api_get_not_allowed_login_form();
4111
4112
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (above)
4113
        $content = Display::return_message(get_lang('NotAllowed'), 'error', false);
4114
4115
        if (!empty($courseCode)) {
4116
            $content .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
4117
        }
4118
4119
        if (api_is_cas_activated()) {
4120
            $content .= Display::return_message(sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")), '', false);
4121
            $content .= Display::div(
4122
                Template::displayCASLoginButton(),
4123
                ['align' => 'center']
4124
            );
4125
            $content .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4126
            $content .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4127
            $content .= "<div style='display:none;'>";
4128
        }
4129
        $content .= PHP_EOL.$loginTopRegionContent;
4130
        $content .= '<div class="well">';
4131
        $content .= $form->returnForm();
4132
        $content .= '</div>';
4133
        $content .= PHP_EOL.$loginBottomRegionContent;
4134
        if (api_is_cas_activated()) {
4135
            $content .= "</div>";
4136
        }
4137
4138
        if (!empty($courseCode)) {
4139
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4140
                get_lang('ReturnToCourseHomepage').'</a></p>';
4141
        } else {
4142
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4143
                get_lang('BackHome').'</a></p>';
4144
        }
4145
4146
        $tpl->setLoginBodyClass();
4147
        $tpl->assign('content', $content);
4148
        $tpl->display_one_col_template();
4149
        exit;
4150
    }
4151
4152
    if ($user_id != 0 && !api_is_anonymous()) {
4153
        $tpl->display_one_col_template();
4154
        exit;
4155
    }
4156
4157
    $msg = null;
4158
    // The session is over and we were not in a course,
4159
    // or we try to get directly to a private course without being logged
4160
    $courseId = api_get_course_int_id();
4161
    if (!empty($courseId)) {
4162
        api_set_firstpage_parameter(api_get_course_id());
4163
        $tpl->setLoginBodyClass();
4164
4165
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (bellow)
4166
        $msg = Display::return_message(get_lang('NotAllowed'), 'error', false);
4167
        $msg .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
4168
        $casEnabled = api_is_cas_activated();
4169
        if ($casEnabled) {
4170
            $msg .= Display::return_message(
4171
                sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")),
4172
                '',
4173
                false
4174
            );
4175
            $msg .= Display::div(
4176
                Template::displayCASLoginButton(),
4177
                ['align' => 'center']
4178
            );
4179
            $msg .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4180
            $msg .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4181
            $msg .= "<div style='display:none;'>";
4182
        }
4183
        $form = api_get_not_allowed_login_form();
4184
        $msg .= PHP_EOL.$loginTopRegionContent;
4185
        $msg .= '<div class="well">';
4186
        $msg .= $form->returnForm();
4187
        $msg .= '</div>';
4188
        $msg .= PHP_EOL.$loginBottomRegionContent;
4189
        if ($casEnabled) {
4190
            $msg .= "</div>";
4191
        }
4192
    } else {
4193
        // we were not in a course, return to home page
4194
        $msg = Display::return_message(
4195
            get_lang('NotAllowed'),
4196
            'error',
4197
            false
4198
        );
4199
4200
        $msg .= '<p class="text-center">
4201
                 <a class="btn btn-default" href="'.$home_url.'">'.get_lang('BackHome').'</a>
4202
                 </p>';
4203
4204
        if (!empty($message)) {
4205
            $msg = $message;
4206
        }
4207
4208
        if (api_is_anonymous()) {
4209
            $form = api_get_not_allowed_login_form();
4210
            $msg .= PHP_EOL.$loginTopRegionContent;
4211
            $msg .= '<div class="well">';
4212
            $msg .= $form->returnForm();
4213
            $msg .= '</div>';
4214
            $msg .= PHP_EOL.$loginBottomRegionContent;
4215
        }
4216
    }
4217
4218
    $tpl->assign('content', $msg);
4219
    $tpl->display_one_col_template();
4220
    exit;
4221
}
4222
4223
/**
4224
 * @return FormValidator
4225
 */
4226
function api_get_not_allowed_login_form()
4227
{
4228
    $action = api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']);
4229
    $action = str_replace('&amp;', '&', $action);
4230
    Session::write('redirect_after_not_allow_page', $action);
4231
    $action .= '&redirect_after_not_allow_page=1';
4232
4233
    $form = new FormValidator(
4234
        'formLogin',
4235
        'post',
4236
        $action,
4237
        null,
4238
        ['class' => 'form-stacked']
4239
    );
4240
    $params = [
4241
        'placeholder' => get_lang('UserName'),
4242
        'class' => 'col-md-3',
4243
    ];
4244
    if (api_browser_support('autocapitalize')) {
4245
        $params['autocapitalize'] = 'none';
4246
    }
4247
4248
    $form->addElement(
4249
        'text',
4250
        'login',
4251
        null,
4252
        $params
4253
    );
4254
    $form->addElement(
4255
        'password',
4256
        'password',
4257
        null,
4258
        ['placeholder' => get_lang('Password'), 'class' => 'col-md-3']
4259
    ); //new
4260
    $form->addButtonNext(get_lang('LoginEnter'), 'submitAuth');
4261
4262
    return $form;
4263
}
4264
4265
/**
4266
 * Gets a UNIX timestamp from a database (MySQL) datetime format string.
4267
 *
4268
 * @param string $last_post_datetime standard output date in a sql query
4269
 *
4270
 * @return int timestamp
4271
 *
4272
 * @author Toon Van Hoecke <[email protected]>
4273
 *
4274
 * @version October 2003
4275
 * @desc convert sql date to unix timestamp
4276
 */
4277
function convert_sql_date($last_post_datetime)
4278
{
4279
    list($last_post_date, $last_post_time) = explode(' ', $last_post_datetime);
4280
    list($year, $month, $day) = explode('-', $last_post_date);
4281
    list($hour, $min, $sec) = explode(':', $last_post_time);
4282
4283
    return gmmktime((int) $hour, (int) $min, (int) $sec, (int) $month, (int) $day, (int) $year);
4284
}
4285
4286
/**
4287
 * Gets item visibility from the item_property table.
4288
 *
4289
 * Getting the visibility is done by getting the last updated visibility entry,
4290
 * using the largest session ID found if session 0 and another was found (meaning
4291
 * the only one that is actually from the session, in case there are results from
4292
 * session 0 *AND* session n).
4293
 *
4294
 * @param array  $_course  Course properties array (result of api_get_course_info())
4295
 * @param string $tool     Tool (learnpath, document, etc)
4296
 * @param int    $id       The item ID in the given tool
4297
 * @param int    $session  The session ID (optional)
4298
 * @param int    $user_id
4299
 * @param string $type
4300
 * @param string $group_id
4301
 *
4302
 * @return int -1 on error, 0 if invisible, 1 if visible
4303
 */
4304
function api_get_item_visibility(
4305
    $_course,
4306
    $tool,
4307
    $id,
4308
    $session = 0,
4309
    $user_id = null,
4310
    $type = null,
4311
    $group_id = null
4312
) {
4313
    if (!is_array($_course) || count($_course) == 0 || empty($tool) || empty($id)) {
4314
        return -1;
4315
    }
4316
4317
    $tool = Database::escape_string($tool);
4318
    $id = (int) $id;
4319
    $session = (int) $session;
4320
    $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
4321
    $course_id = (int) $_course['real_id'];
4322
4323
    $userCondition = '';
4324
    if (!empty($user_id)) {
4325
        $user_id = (int) $user_id;
4326
        $userCondition = " AND to_user_id = $user_id ";
4327
    }
4328
4329
    $typeCondition = '';
4330
    if (!empty($type)) {
4331
        $type = Database::escape_string($type);
4332
        $typeCondition = " AND lastedit_type = '$type' ";
4333
    }
4334
4335
    $groupCondition = '';
4336
    if (!empty($group_id)) {
4337
        $group_id = (int) $group_id;
4338
        $groupCondition = " AND to_group_id = '$group_id' ";
4339
    }
4340
4341
    $sql = "SELECT visibility
4342
            FROM $TABLE_ITEMPROPERTY
4343
            WHERE
4344
                c_id = $course_id AND
4345
                tool = '$tool' AND
4346
                ref = $id AND
4347
                (session_id = $session OR session_id = 0 OR session_id IS NULL)
4348
                $userCondition $typeCondition $groupCondition
4349
            ORDER BY session_id DESC, lastedit_date DESC
4350
            LIMIT 1";
4351
4352
    $res = Database::query($sql);
4353
    if ($res === false || Database::num_rows($res) == 0) {
4354
        return -1;
4355
    }
4356
    $row = Database::fetch_array($res);
4357
4358
    return (int) $row['visibility'];
4359
}
4360
4361
/**
4362
 * Delete a row in the c_item_property table.
4363
 *
4364
 * @param array  $courseInfo
4365
 * @param string $tool
4366
 * @param int    $itemId
4367
 * @param int    $userId
4368
 * @param int    $groupId    group.iid
4369
 * @param int    $sessionId
4370
 *
4371
 * @return false|null
4372
 */
4373
function api_item_property_delete(
4374
    $courseInfo,
4375
    $tool,
4376
    $itemId,
4377
    $userId,
4378
    $groupId = 0,
4379
    $sessionId = 0
4380
) {
4381
    if (empty($courseInfo)) {
4382
        return false;
4383
    }
4384
4385
    $courseId = (int) $courseInfo['real_id'];
4386
4387
    if (empty($courseId) || empty($tool) || empty($itemId)) {
4388
        return false;
4389
    }
4390
4391
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4392
    $tool = Database::escape_string($tool);
4393
    $itemId = intval($itemId);
4394
    $userId = intval($userId);
4395
    $groupId = intval($groupId);
4396
    $sessionId = intval($sessionId);
4397
4398
    $groupCondition = " AND to_group_id = $groupId ";
4399
    if (empty($groupId)) {
4400
        $groupCondition = " AND (to_group_id is NULL OR to_group_id = 0) ";
4401
    }
4402
4403
    $userCondition = " AND to_user_id = $userId ";
4404
    if (empty($userId)) {
4405
        $userCondition = " AND (to_user_id is NULL OR to_user_id = 0) ";
4406
    }
4407
    $sessionCondition = api_get_session_condition($sessionId, true, false, 'session_id');
4408
    $sql = "DELETE FROM $table
4409
            WHERE
4410
                c_id = $courseId AND
4411
                tool  = '$tool' AND
4412
                ref = $itemId
4413
                $sessionCondition
4414
                $userCondition
4415
                $groupCondition
4416
            ";
4417
4418
    Database::query($sql);
4419
}
4420
4421
/**
4422
 * Updates or adds item properties to the Item_propetry table
4423
 * Tool and lastedit_type are language independant strings (langvars->get_lang!).
4424
 *
4425
 * @param array  $_course        array with course properties
4426
 * @param string $tool           tool id, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4427
 * @param int    $item_id        id of the item itself, linked to key of every tool ('id', ...)
4428
 * @param string $last_edit_type add or update action
4429
 *                               (1) message to be translated (in trad4all) : e.g. DocumentAdded, DocumentUpdated;
4430
 *                               (2) "delete"
4431
 *                               (3) "visible"
4432
 *                               (4) "invisible"
4433
 * @param int    $user_id        id of the editing/adding user
4434
 * @param array  $groupInfo      must include group.iid/group.od
4435
 * @param int    $to_user_id     id of the intended user (always has priority over $to_group_id !), only relevant for $type (1)
4436
 * @param string $start_visible  0000-00-00 00:00:00 format
4437
 * @param string $end_visible    0000-00-00 00:00:00 format
4438
 * @param int    $session_id     The session ID, if any, otherwise will default to 0
4439
 *
4440
 * @return bool false if update fails
4441
 *
4442
 * @author Toon Van Hoecke <[email protected]>, Ghent University
4443
 *
4444
 * @version January 2005
4445
 * @desc update the item_properties table (if entry not exists, insert) of the course
4446
 */
4447
function api_item_property_update(
4448
    $_course,
4449
    $tool,
4450
    $item_id,
4451
    $last_edit_type,
4452
    $user_id,
4453
    $groupInfo = [],
4454
    $to_user_id = null,
4455
    $start_visible = '',
4456
    $end_visible = '',
4457
    $session_id = 0
4458
) {
4459
    if (empty($_course)) {
4460
        return false;
4461
    }
4462
4463
    $course_id = $_course['real_id'];
4464
4465
    if (empty($course_id)) {
4466
        return false;
4467
    }
4468
4469
    $to_group_id = 0;
4470
    if (!empty($groupInfo) && isset($groupInfo['iid'])) {
4471
        $to_group_id = (int) $groupInfo['iid'];
4472
    }
4473
4474
    $em = Database::getManager();
4475
4476
    // Definition of variables.
4477
    $tool = Database::escape_string($tool);
4478
    $item_id = (int) $item_id;
4479
    $lastEditTypeNoFilter = $last_edit_type;
4480
    $last_edit_type = Database::escape_string($last_edit_type);
4481
    $user_id = (int) $user_id;
4482
4483
    $startVisible = "NULL";
4484
    if (!empty($start_visible)) {
4485
        $start_visible = Database::escape_string($start_visible);
4486
        $startVisible = "'$start_visible'";
4487
    }
4488
4489
    $endVisible = "NULL";
4490
    if (!empty($end_visible)) {
4491
        $end_visible = Database::escape_string($end_visible);
4492
        $endVisible = "'$end_visible'";
4493
    }
4494
4495
    $to_filter = '';
4496
    $time = api_get_utc_datetime();
4497
4498
    if (!empty($session_id)) {
4499
        $session_id = (int) $session_id;
4500
    } else {
4501
        $session_id = api_get_session_id();
4502
    }
4503
4504
    // Definition of tables.
4505
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4506
4507
    if ($to_user_id <= 0) {
4508
        $to_user_id = null; // No to_user_id set
4509
    }
4510
4511
    if (!is_null($to_user_id)) {
4512
        // $to_user_id has more priority than $to_group_id
4513
        $to_user_id = (int) $to_user_id;
4514
        $to_field = 'to_user_id';
4515
        $to_value = $to_user_id;
4516
    } else {
4517
        // $to_user_id is not set.
4518
        $to_field = 'to_group_id';
4519
        $to_value = $to_group_id;
4520
    }
4521
4522
    $toValueCondition = empty($to_value) ? 'NULL' : "'$to_value'";
4523
    // Set filters for $to_user_id and $to_group_id, with priority for $to_user_id
4524
    $condition_session = " AND session_id = $session_id ";
4525
    if (empty($session_id)) {
4526
        $condition_session = ' AND (session_id = 0 OR session_id IS NULL) ';
4527
    }
4528
4529
    $filter = " c_id = $course_id AND tool = '$tool' AND ref = $item_id $condition_session ";
4530
4531
    // Check whether $to_user_id and $to_group_id are passed in the function call.
4532
    // If both are not passed (both are null) then it is a message for everybody and $to_group_id should be 0 !
4533
    if (is_null($to_user_id) && is_null($to_group_id)) {
4534
        $to_group_id = 0;
4535
    }
4536
4537
    if (!is_null($to_user_id)) {
4538
        // Set filter to intended user.
4539
        $to_filter = " AND to_user_id = $to_user_id $condition_session";
4540
    } else {
4541
        // Set filter to intended group.
4542
        if (($to_group_id != 0) && $to_group_id == strval(intval($to_group_id))) {
4543
            $to_filter = " AND to_group_id = $to_group_id $condition_session";
4544
        }
4545
    }
4546
4547
    // Adding filter if set.
4548
    $filter .= $to_filter;
4549
4550
    // Update if possible
4551
    $set_type = '';
4552
4553
    switch ($lastEditTypeNoFilter) {
4554
        case 'delete':
4555
            // delete = make item only visible for the platform admin.
4556
            $visibility = '2';
4557
            if (!empty($session_id)) {
4558
                // Check whether session id already exist into item_properties for updating visibility or add it.
4559
                $sql = "SELECT session_id FROM $tableItemProperty
4560
                        WHERE
4561
                            c_id = $course_id AND
4562
                            tool = '$tool' AND
4563
                            ref = $item_id AND
4564
                            session_id = $session_id";
4565
                $rs = Database::query($sql);
4566
                if (Database::num_rows($rs) > 0) {
4567
                    $sql = "UPDATE $tableItemProperty
4568
                            SET lastedit_type       = '".str_replace('_', '', ucwords($tool))."Deleted',
4569
                                lastedit_date       = '$time',
4570
                                lastedit_user_id    = $user_id,
4571
                                visibility          = $visibility,
4572
                                session_id          = $session_id $set_type
4573
                            WHERE $filter";
4574
                    $result = Database::query($sql);
4575
                } else {
4576
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
4577
                            VALUES ($course_id, '$tool',$item_id, '$time', $user_id, '$time', '$last_edit_type',$user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4578
                    $result = Database::query($sql);
4579
                    $id = Database::insert_id();
4580
                    if ($id) {
4581
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4582
                        Database::query($sql);
4583
                    }
4584
                }
4585
            } else {
4586
                $sql = "UPDATE $tableItemProperty
4587
                        SET
4588
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Deleted',
4589
                            lastedit_date='$time',
4590
                            lastedit_user_id = $user_id,
4591
                            visibility = $visibility $set_type
4592
                        WHERE $filter";
4593
                $result = Database::query($sql);
4594
            }
4595
            break;
4596
        case 'visible': // Change item to visible.
4597
            $visibility = '1';
4598
            if (!empty($session_id)) {
4599
                // Check whether session id already exist into item_properties for updating visibility or add it.
4600
                $sql = "SELECT session_id FROM $tableItemProperty
4601
                        WHERE
4602
                            c_id = $course_id AND
4603
                            tool = '$tool' AND
4604
                            ref = $item_id AND
4605
                            session_id = $session_id";
4606
                $rs = Database::query($sql);
4607
                if (Database::num_rows($rs) > 0) {
4608
                    $sql = "UPDATE $tableItemProperty
4609
                            SET
4610
                                lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4611
                                lastedit_date='$time',
4612
                                lastedit_user_id = $user_id,
4613
                                visibility = $visibility,
4614
                                session_id = $session_id $set_type
4615
                            WHERE $filter";
4616
                    $result = Database::query($sql);
4617
                } else {
4618
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
4619
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4620
                    $result = Database::query($sql);
4621
                    $id = Database::insert_id();
4622
                    if ($id) {
4623
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4624
                        Database::query($sql);
4625
                    }
4626
                }
4627
            } else {
4628
                $sql = "UPDATE $tableItemProperty
4629
                        SET
4630
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4631
                            lastedit_date='$time',
4632
                            lastedit_user_id = $user_id,
4633
                            visibility = $visibility $set_type
4634
                        WHERE $filter";
4635
                $result = Database::query($sql);
4636
            }
4637
            break;
4638
        case 'invisible': // Change item to invisible.
4639
            $visibility = '0';
4640
            if (!empty($session_id)) {
4641
                // Check whether session id already exist into item_properties for updating visibility or add it
4642
                $sql = "SELECT session_id FROM $tableItemProperty
4643
                        WHERE
4644
                            c_id = $course_id AND
4645
                            tool = '$tool' AND
4646
                            ref = $item_id AND
4647
                            session_id = $session_id";
4648
                $rs = Database::query($sql);
4649
                if (Database::num_rows($rs) > 0) {
4650
                    $sql = "UPDATE $tableItemProperty
4651
                            SET
4652
                                lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4653
                                lastedit_date = '$time',
4654
                                lastedit_user_id = $user_id,
4655
                                visibility = $visibility,
4656
                                session_id = $session_id $set_type
4657
                            WHERE $filter";
4658
                    $result = Database::query($sql);
4659
                } else {
4660
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id,$to_field, visibility, start_visible, end_visible, session_id)
4661
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4662
                    $result = Database::query($sql);
4663
                    $id = Database::insert_id();
4664
                    if ($id) {
4665
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4666
                        Database::query($sql);
4667
                    }
4668
                }
4669
            } else {
4670
                $sql = "UPDATE $tableItemProperty
4671
                        SET
4672
                            lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4673
                            lastedit_date = '$time',
4674
                            lastedit_user_id = $user_id,
4675
                            visibility = $visibility $set_type
4676
                        WHERE $filter";
4677
                $result = Database::query($sql);
4678
            }
4679
            break;
4680
        default: // The item will be added or updated.
4681
            $set_type = ", lastedit_type = '$last_edit_type' ";
4682
            $visibility = '1';
4683
            //$filter .= $to_filter; already added
4684
            $sql = "UPDATE $tableItemProperty
4685
                    SET
4686
                      lastedit_date = '$time',
4687
                      lastedit_user_id = $user_id $set_type
4688
                    WHERE $filter";
4689
            $result = Database::query($sql);
4690
    }
4691
4692
    // Insert if no entries are found (can only happen in case of $last_edit_type switch is 'default').
4693
    if ($result == false || Database::affected_rows($result) == 0) {
4694
        $objCourse = $em->find('ChamiloCoreBundle:Course', intval($course_id));
4695
        $objTime = new DateTime('now', new DateTimeZone('UTC'));
4696
        $objUser = api_get_user_entity($user_id);
4697
        if (empty($objUser)) {
4698
            // Use anonymous
4699
            $user_id = api_get_anonymous_id();
4700
            $objUser = api_get_user_entity($user_id);
4701
        }
4702
4703
        $objGroup = null;
4704
        if (!empty($to_group_id)) {
4705
            $objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', $to_group_id);
4706
        }
4707
4708
        $objToUser = api_get_user_entity($to_user_id);
4709
        $objSession = $em->find('ChamiloCoreBundle:Session', intval($session_id));
4710
4711
        $startVisibleDate = !empty($start_visible) ? new DateTime($start_visible, new DateTimeZone('UTC')) : null;
4712
        $endVisibleDate = !empty($endVisibleDate) ? new DateTime($endVisibleDate, new DateTimeZone('UTC')) : null;
4713
4714
        $cItemProperty = new CItemProperty($objCourse);
4715
        $cItemProperty
4716
            ->setTool($tool)
4717
            ->setRef($item_id)
4718
            ->setInsertDate($objTime)
4719
            ->setInsertUser($objUser)
4720
            ->setLasteditDate($objTime)
4721
            ->setLasteditType($last_edit_type)
4722
            ->setGroup($objGroup)
4723
            ->setToUser($objToUser)
4724
            ->setVisibility($visibility)
4725
            ->setStartVisible($startVisibleDate)
4726
            ->setEndVisible($endVisibleDate)
4727
            ->setSession($objSession);
4728
4729
        $em->persist($cItemProperty);
4730
        $em->flush();
4731
4732
        $id = $cItemProperty->getIid();
4733
4734
        if ($id) {
4735
            $cItemProperty->setId($id);
4736
            $em->merge($cItemProperty);
4737
            $em->flush();
4738
4739
            return false;
4740
        }
4741
    }
4742
4743
    return true;
4744
}
4745
4746
/**
4747
 * Gets item property by tool.
4748
 *
4749
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4750
 * @param string $course_code
4751
 * @param int    $session_id
4752
 *
4753
 * @return array All fields from c_item_property (all rows found) or empty array
4754
 */
4755
function api_get_item_property_by_tool($tool, $course_code, $session_id = null)
4756
{
4757
    $course_info = api_get_course_info($course_code);
4758
    $tool = Database::escape_string($tool);
4759
4760
    // Definition of tables.
4761
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4762
    $session_id = (int) $session_id;
4763
    $session_condition = ' AND session_id = '.$session_id;
4764
    if (empty($session_id)) {
4765
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4766
    }
4767
    $course_id = $course_info['real_id'];
4768
4769
    $sql = "SELECT * FROM $item_property_table
4770
            WHERE
4771
                c_id = $course_id AND
4772
                tool = '$tool'
4773
                $session_condition ";
4774
    $rs = Database::query($sql);
4775
    $list = [];
4776
    if (Database::num_rows($rs) > 0) {
4777
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4778
            $list[] = $row;
4779
        }
4780
    }
4781
4782
    return $list;
4783
}
4784
4785
/**
4786
 * Gets item property by tool and user.
4787
 *
4788
 * @param int $userId
4789
 * @param int $tool
4790
 * @param int $courseId
4791
 * @param int $session_id
4792
 *
4793
 * @return array
4794
 */
4795
function api_get_item_property_list_by_tool_by_user(
4796
    $userId,
4797
    $tool,
4798
    $courseId,
4799
    $session_id = 0
4800
) {
4801
    $userId = intval($userId);
4802
    $tool = Database::escape_string($tool);
4803
    $session_id = intval($session_id);
4804
    $courseId = intval($courseId);
4805
4806
    // Definition of tables.
4807
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4808
    $session_condition = ' AND session_id = '.$session_id;
4809
    if (empty($session_id)) {
4810
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4811
    }
4812
    $sql = "SELECT * FROM $item_property_table
4813
            WHERE
4814
                insert_user_id = $userId AND
4815
                c_id = $courseId AND
4816
                tool = '$tool'
4817
                $session_condition ";
4818
4819
    $rs = Database::query($sql);
4820
    $list = [];
4821
    if (Database::num_rows($rs) > 0) {
4822
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4823
            $list[] = $row;
4824
        }
4825
    }
4826
4827
    return $list;
4828
}
4829
4830
/**
4831
 * Gets item property id from tool of a course.
4832
 *
4833
 * @param string $course_code course code
4834
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4835
 * @param int    $ref         id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4836
 * @param int    $sessionId   Session ID (optional)
4837
 *
4838
 * @return int
4839
 */
4840
function api_get_item_property_id($course_code, $tool, $ref, $sessionId = 0)
4841
{
4842
    $course_info = api_get_course_info($course_code);
4843
    $tool = Database::escape_string($tool);
4844
    $ref = (int) $ref;
4845
4846
    // Definition of tables.
4847
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4848
    $course_id = $course_info['real_id'];
4849
    $sessionId = (int) $sessionId;
4850
    $sessionCondition = " AND session_id = $sessionId ";
4851
    if (empty($sessionId)) {
4852
        $sessionCondition = ' AND (session_id = 0 OR session_id IS NULL) ';
4853
    }
4854
    $sql = "SELECT id FROM $tableItemProperty
4855
            WHERE
4856
                c_id = $course_id AND
4857
                tool = '$tool' AND
4858
                ref = $ref
4859
                $sessionCondition";
4860
    $rs = Database::query($sql);
4861
    $item_property_id = '';
4862
    if (Database::num_rows($rs) > 0) {
4863
        $row = Database::fetch_array($rs);
4864
        $item_property_id = $row['id'];
4865
    }
4866
4867
    return $item_property_id;
4868
}
4869
4870
/**
4871
 * Inserts a record in the track_e_item_property table (No update).
4872
 *
4873
 * @param string $tool
4874
 * @param int    $ref
4875
 * @param string $title
4876
 * @param string $content
4877
 * @param int    $progress
4878
 *
4879
 * @return bool|int
4880
 */
4881
function api_track_item_property_update($tool, $ref, $title, $content, $progress)
4882
{
4883
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4884
    $course_id = api_get_course_int_id(); //numeric
4885
    $course_code = api_get_course_id(); //alphanumeric
4886
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4887
    if (!empty($item_property_id)) {
4888
        $sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
4889
                course_id           = '$course_id',
4890
                item_property_id    = '$item_property_id',
4891
                title               = '".Database::escape_string($title)."',
4892
                content             = '".Database::escape_string($content)."',
4893
                progress            = '".intval($progress)."',
4894
                lastedit_date       = '".api_get_utc_datetime()."',
4895
                lastedit_user_id    = '".api_get_user_id()."',
4896
                session_id          = '".api_get_session_id()."'";
4897
        $result = Database::query($sql);
4898
        $affected_rows = Database::affected_rows($result);
4899
4900
        return $affected_rows;
4901
    }
4902
4903
    return false;
4904
}
4905
4906
/**
4907
 * @param string $tool
4908
 * @param int    $ref
4909
 *
4910
 * @return array|resource
4911
 */
4912
function api_get_track_item_property_history($tool, $ref)
4913
{
4914
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4915
    $course_id = api_get_course_int_id(); //numeric
4916
    $course_code = api_get_course_id(); //alphanumeric
4917
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4918
    $sql = "SELECT * FROM $tbl_stats_item_property
4919
            WHERE item_property_id = $item_property_id AND course_id = $course_id
4920
            ORDER BY lastedit_date DESC";
4921
    $result = Database::query($sql);
4922
    if ($result === false or $result === null) {
4923
        $result = [];
4924
    } else {
4925
        $result = Database::store_result($result, 'ASSOC');
4926
    }
4927
4928
    return $result;
4929
}
4930
4931
/**
4932
 * Gets item property data from tool of a course id.
4933
 *
4934
 * @param int    $course_id
4935
 * @param string $tool       tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4936
 * @param int    $ref        id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4937
 * @param int    $session_id
4938
 * @param int    $groupId
4939
 *
4940
 * @return array with all fields from c_item_property, empty array if not found or false if course could not be found
4941
 */
4942
function api_get_item_property_info($course_id, $tool, $ref, $session_id = 0, $groupId = 0)
4943
{
4944
    $courseInfo = api_get_course_info_by_id($course_id);
4945
4946
    if (empty($courseInfo)) {
4947
        return false;
4948
    }
4949
4950
    $tool = Database::escape_string($tool);
4951
    $course_id = $courseInfo['real_id'];
4952
    $ref = (int) $ref;
4953
    $session_id = (int) $session_id;
4954
4955
    $sessionCondition = " session_id = $session_id";
4956
    if (empty($session_id)) {
4957
        $sessionCondition = ' (session_id = 0 OR session_id IS NULL) ';
4958
    }
4959
4960
    // Definition of tables.
4961
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4962
4963
    $sql = "SELECT * FROM $table
4964
            WHERE
4965
                c_id = $course_id AND
4966
                tool = '$tool' AND
4967
                ref = $ref AND
4968
                $sessionCondition ";
4969
4970
    if (!empty($groupId)) {
4971
        $groupId = (int) $groupId;
4972
        $sql .= " AND to_group_id = $groupId ";
4973
    }
4974
4975
    $rs = Database::query($sql);
4976
    $row = [];
4977
    if (Database::num_rows($rs) > 0) {
4978
        $row = Database::fetch_array($rs, 'ASSOC');
4979
    }
4980
4981
    return $row;
4982
}
4983
4984
/**
4985
 * Gets the last item property data from tool of a course id, in chronological order.
4986
 *
4987
 * @param string $tool      tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4988
 * @param int    $ref       id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4989
 * @param int    $sessionId
4990
 * @param int    $groupId
4991
 *
4992
 * @return array with all fields from c_item_property, empty array if not found or false if course could not be found
4993
 */
4994
function api_get_last_item_property_info(int $courseId, string $tool, int $ref, int $sessionId = null, int $groupId = null): array
4995
{
4996
    $tool = Database::escape_string($tool);
4997
    // Definition of tables.
4998
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4999
    $sessionCondition = " session_id = $sessionId";
5000
    if (empty($sessionId)) {
5001
        $sessionCondition = ' (session_id = 0 OR session_id IS NULL) ';
5002
    }
5003
5004
    $sql = "SELECT * FROM $table
5005
            WHERE
5006
                c_id = $courseId AND
5007
                tool = '$tool' AND
5008
                ref = $ref AND
5009
                $sessionCondition ";
5010
5011
    if (!empty($groupId)) {
5012
        $sql .= " AND to_group_id = $groupId ";
5013
    }
5014
    // Add criteria to only get the last one
5015
    $sql .= "ORDER BY lastedit_date DESC LIMIT 1";
5016
    $rs = Database::query($sql);
5017
    $row = [];
5018
    if (Database::num_rows($rs) > 0) {
5019
        $row = Database::fetch_array($rs, 'ASSOC');
5020
    }
5021
5022
    return $row;
5023
}
5024
5025
/**
5026
 * Displays a combo box so the user can select his/her preferred language.
5027
 *
5028
 * @param string The desired name= value for the select
5029
 * @param bool Whether we use the JQuery Chozen library or not
5030
 * (in some cases, like the indexing language picker, it can alter the presentation)
5031
 *
5032
 * @return string
5033
 */
5034
function api_get_languages_combo($name = 'language')
5035
{
5036
    $ret = '';
5037
    $platformLanguage = api_get_setting('platformLanguage');
5038
5039
    // Retrieve a complete list of all the languages.
5040
    $language_list = api_get_languages();
5041
5042
    if (count($language_list['name']) < 2) {
5043
        return $ret;
5044
    }
5045
5046
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
5047
    if (isset($_SESSION['user_language_choice'])) {
5048
        $default = $_SESSION['user_language_choice'];
5049
    } else {
5050
        $default = $platformLanguage;
5051
    }
5052
5053
    $languages = $language_list['name'];
5054
    $folder = $language_list['folder'];
5055
5056
    $ret .= '<select name="'.$name.'" id="language_chosen" class="selectpicker form-control">';
5057
    foreach ($languages as $key => $value) {
5058
        if ($folder[$key] == $default) {
5059
            $selected = ' selected="selected"';
5060
        } else {
5061
            $selected = '';
5062
        }
5063
        $ret .= sprintf('<option value=%s" %s>%s</option>', $folder[$key], $selected, $value);
5064
    }
5065
    $ret .= '</select>';
5066
5067
    return $ret;
5068
}
5069
5070
/**
5071
 * Displays a form (drop down menu) so the user can select his/her preferred language.
5072
 * The form works with or without javascript.
5073
 *
5074
 * @param  bool Hide form if only one language available (defaults to false = show the box anyway)
5075
 * @param bool $showAsButton
5076
 *
5077
 * @return string|null Display the box directly
5078
 */
5079
function api_display_language_form($hide_if_no_choice = false, $showAsButton = false)
5080
{
5081
    // Retrieve a complete list of all the languages.
5082
    $language_list = api_get_languages();
5083
    if (count($language_list['name']) <= 1 && $hide_if_no_choice) {
5084
        return null; //don't show any form
5085
    }
5086
5087
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
5088
    if (isset($_SESSION['user_language_choice'])) {
5089
        $user_selected_language = $_SESSION['user_language_choice'];
5090
    }
5091
    if (empty($user_selected_language)) {
5092
        $user_selected_language = api_get_setting('platformLanguage');
5093
    }
5094
5095
    $currentLanguageId = api_get_language_id($user_selected_language);
5096
    $currentLanguageInfo = api_get_language_info($currentLanguageId);
5097
5098
    $countryCode = languageCodeToCountryIsoCodeForFlags($currentLanguageInfo['isocode']);
5099
    $url = api_get_self();
5100
    if ($showAsButton) {
5101
        $html = '<div class="btn-group">
5102
              <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
5103
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
5104
                '.$currentLanguageInfo['original_name'].'
5105
                <span class="caret">
5106
                </span>
5107
              </button>';
5108
    } else {
5109
        $html = '
5110
            <a href="'.$url.'" class="dropdown-toggle" data-toggle="dropdown" role="button">
5111
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
5112
                '.$currentLanguageInfo['original_name'].'
5113
                <span class="caret"></span>
5114
            </a>
5115
            ';
5116
    }
5117
5118
    $html .= '<ul class="dropdown-menu" role="menu">';
5119
    foreach ($language_list['all'] as $key => $data) {
5120
        $urlLink = $url.'?language='.$data['english_name'];
5121
        $html .= '<li><a href="'.$urlLink.'"><span class="flag-icon flag-icon-'.languageCodeToCountryIsoCodeForFlags($data['isocode']).'"></span> '.$data['original_name'].'</a></li>';
5122
    }
5123
    $html .= '</ul>';
5124
5125
    if ($showAsButton) {
5126
        $html .= '</div>';
5127
    }
5128
5129
    return $html;
5130
}
5131
5132
/**
5133
 * Return a country code based on a language in order to show a country flag.
5134
 * Note: Showing a "language" flag is arguably a bad idea, as several countries
5135
 * share languages and the right flag cannot be shown for all of them.
5136
 *
5137
 * @param string $languageIsoCode
5138
 *
5139
 * @return string
5140
 */
5141
function languageCodeToCountryIsoCodeForFlags($languageIsoCode)
5142
{
5143
    $allow = api_get_configuration_value('language_flags_by_country');
5144
5145
    // @todo save in DB
5146
    switch ($languageIsoCode) {
5147
        case 'ar':
5148
            $country = 'ae';
5149
            break;
5150
        case 'bs':
5151
            $country = 'ba';
5152
            break;
5153
        case 'ca':
5154
            $country = 'es';
5155
            if ($allow) {
5156
                $country = 'catalan';
5157
            }
5158
            break;
5159
        case 'cs':
5160
            $country = 'cz';
5161
            break;
5162
        case 'da':
5163
            $country = 'dk';
5164
            break;
5165
        case 'el':
5166
            $country = 'gr';
5167
            break;
5168
        case 'en':
5169
            $country = 'gb';
5170
            break;
5171
        case 'eu': // Euskera
5172
            $country = 'es';
5173
            if ($allow) {
5174
                $country = 'basque';
5175
            }
5176
            break;
5177
        case 'gl': // galego
5178
            $country = 'es';
5179
            if ($allow) {
5180
                $country = 'galician';
5181
            }
5182
            break;
5183
        case 'he':
5184
            $country = 'il';
5185
            break;
5186
        case 'ja':
5187
            $country = 'jp';
5188
            break;
5189
        case 'ka':
5190
            $country = 'ge';
5191
            break;
5192
        case 'ko':
5193
            $country = 'kr';
5194
            break;
5195
        case 'ms':
5196
            $country = 'my';
5197
            break;
5198
        case 'pt-BR':
5199
            $country = 'br';
5200
            break;
5201
        case 'qu':
5202
            $country = 'pe';
5203
            break;
5204
        case 'sl':
5205
            $country = 'si';
5206
            break;
5207
        case 'sv':
5208
            $country = 'se';
5209
            break;
5210
        case 'uk': // Ukraine
5211
            $country = 'ua';
5212
            break;
5213
        case 'vi': // Vietnam - GH#4231
5214
            $country = 'vn';
5215
            break;
5216
        case 'zh-TW':
5217
        case 'zh':
5218
            $country = 'cn';
5219
            break;
5220
        default:
5221
            $country = $languageIsoCode;
5222
            break;
5223
    }
5224
    $country = strtolower($country);
5225
5226
    return $country;
5227
}
5228
5229
/**
5230
 * Returns a list of all the languages that are made available by the admin.
5231
 *
5232
 * @return array An array with all languages. Structure of the array is
5233
 *               array['name'] = An array with the name of every language
5234
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
5235
 */
5236
function api_get_languages()
5237
{
5238
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5239
    $sql = "SELECT * FROM $tbl_language WHERE available='1'
5240
            ORDER BY original_name ASC";
5241
    $result = Database::query($sql);
5242
    $language_list = [];
5243
    while ($row = Database::fetch_array($result)) {
5244
        $language_list['name'][] = $row['original_name'];
5245
        $language_list['folder'][] = $row['dokeos_folder'];
5246
        $language_list['all'][] = $row;
5247
    }
5248
5249
    return $language_list;
5250
}
5251
5252
/**
5253
 * Returns a list of all the languages that are made available by the admin.
5254
 *
5255
 * @return array
5256
 */
5257
function api_get_languages_to_array()
5258
{
5259
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5260
    $sql = "SELECT * FROM $tbl_language
5261
            WHERE available='1' ORDER BY original_name ASC";
5262
    $result = Database::query($sql);
5263
    $languages = [];
5264
    while ($row = Database::fetch_array($result)) {
5265
        $languages[$row['dokeos_folder']] = $row['original_name'];
5266
    }
5267
5268
    return $languages;
5269
}
5270
5271
/**
5272
 * Returns the id (the database id) of a language.
5273
 *
5274
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
5275
 *
5276
 * @return int id of the language
5277
 */
5278
function api_get_language_id($language)
5279
{
5280
    if (empty($language)) {
5281
        return null;
5282
    }
5283
5284
    static $staticResult = [];
5285
5286
    if (isset($staticResult[$language])) {
5287
        return $staticResult[$language];
5288
    } else {
5289
        $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5290
        $language = Database::escape_string($language);
5291
        $sql = "SELECT id FROM $table
5292
                WHERE dokeos_folder = '$language' LIMIT 1";
5293
        $result = Database::query($sql);
5294
        $row = Database::fetch_array($result);
5295
5296
        $staticResult[$language] = $row['id'];
5297
5298
        return $row['id'];
5299
    }
5300
}
5301
5302
/**
5303
 * Gets language of the requested type for the current user. Types are :
5304
 * user_profil_lang : profile language of current user
5305
 * user_select_lang : language selected by user at login
5306
 * course_lang : language of the current course
5307
 * platform_lang : default platform language.
5308
 *
5309
 * @param string $lang_type
5310
 *
5311
 * @return string
5312
 */
5313
function api_get_language_from_type($lang_type)
5314
{
5315
    $return = false;
5316
    switch ($lang_type) {
5317
        case 'platform_lang':
5318
            $temp_lang = api_get_setting('platformLanguage');
5319
            if (!empty($temp_lang)) {
5320
                $return = $temp_lang;
5321
            }
5322
            break;
5323
        case 'user_profil_lang':
5324
            $_user = api_get_user_info();
5325
            if (isset($_user['language']) && !empty($_user['language'])) {
5326
                $return = $_user['language'];
5327
            }
5328
            break;
5329
        case 'user_selected_lang':
5330
            if (isset($_SESSION['user_language_choice']) && !empty($_SESSION['user_language_choice'])) {
5331
                $return = $_SESSION['user_language_choice'];
5332
            }
5333
            break;
5334
        case 'course_lang':
5335
            global $_course;
5336
            $cidReq = null;
5337
            if (empty($_course)) {
5338
                // Code modified because the local.inc.php file it's declarated after this work
5339
                // causing the function api_get_course_info() returns a null value
5340
                $cidReq = isset($_GET["cidReq"]) ? Database::escape_string($_GET["cidReq"]) : null;
5341
                $cDir = (!empty($_GET['cDir']) ? $_GET['cDir'] : null);
5342
                if (empty($cidReq) && !empty($cDir)) {
5343
                    $c = CourseManager::getCourseCodeFromDirectory($cDir);
5344
                    if ($c) {
5345
                        $cidReq = $c;
5346
                    }
5347
                }
5348
            }
5349
            $_course = api_get_course_info($cidReq);
5350
            if (isset($_course['language']) && !empty($_course['language'])) {
5351
                $return = $_course['language'];
5352
                $showCourseInUserLanguage = api_get_course_setting('show_course_in_user_language');
5353
                if ($showCourseInUserLanguage == 1) {
5354
                    $userInfo = api_get_user_info();
5355
                    if (isset($userInfo['language'])) {
5356
                        $return = $userInfo['language'];
5357
                    }
5358
                }
5359
            }
5360
            break;
5361
        default:
5362
            $return = false;
5363
            break;
5364
    }
5365
5366
    return $return;
5367
}
5368
5369
/**
5370
 * Get the language information by its id.
5371
 *
5372
 * @param int $languageId
5373
 *
5374
 * @throws Exception
5375
 *
5376
 * @return array
5377
 */
5378
function api_get_language_info($languageId)
5379
{
5380
    if (empty($languageId)) {
5381
        return [];
5382
    }
5383
5384
    $language = Database::getManager()
5385
        ->find('ChamiloCoreBundle:Language', $languageId);
5386
5387
    if (!$language) {
5388
        return [];
5389
    }
5390
5391
    return [
5392
        'id' => $language->getId(),
5393
        'original_name' => $language->getOriginalName(),
5394
        'english_name' => $language->getEnglishName(),
5395
        'isocode' => $language->getIsocode(),
5396
        'dokeos_folder' => $language->getDokeosFolder(),
5397
        'available' => $language->getAvailable(),
5398
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
5399
    ];
5400
}
5401
5402
/**
5403
 * Returns the name of the visual (CSS) theme to be applied on the current page.
5404
 * The returned name depends on the platform, course or user -wide settings.
5405
 *
5406
 * @return string The visual theme's name, it is the name of a folder inside web/css/themes
5407
 */
5408
function api_get_visual_theme()
5409
{
5410
    static $visual_theme;
5411
5412
    $course_id = api_get_course_id();
5413
    $courseThemeAvailable = false;
5414
    // If called from CLI or from inside a course, it should be reloaded.
5415
    if ('cli' === PHP_SAPI) {
5416
        $visual_theme = null;
5417
    } elseif (!empty($course_id)) {
5418
        $courseThemeAvailable = api_get_setting('allow_course_theme') == 'true';
5419
        if ($courseThemeAvailable) {
5420
            $visual_theme = null;
5421
        }
5422
    }
5423
5424
    if (!isset($visual_theme)) {
5425
        $cacheAvailable = api_get_configuration_value('apc');
5426
        $userThemeAvailable = api_get_setting('user_selected_theme') == 'true';
5427
        // only use a shared cache if no user-based or course-based theme is allowed
5428
        $useCache = ($cacheAvailable && !$userThemeAvailable && !$courseThemeAvailable);
5429
        $apcVar = '';
5430
        if ($useCache) {
5431
            $apcVar = api_get_configuration_value('apc_prefix').'my_campus_visual_theme';
5432
            if (apcu_exists($apcVar)) {
5433
                return apcu_fetch($apcVar);
5434
            }
5435
        }
5436
5437
        $accessUrlId = api_get_current_access_url_id();
5438
        if ('cli' === PHP_SAPI) {
5439
            $accessUrlId = api_get_configuration_value('access_url');
5440
        }
5441
5442
        // Get style directly from DB
5443
        $styleFromDatabase = api_get_settings_params_simple(
5444
            [
5445
                'variable = ? AND access_url = ?' => [
5446
                    'stylesheets',
5447
                    $accessUrlId,
5448
                ],
5449
            ]
5450
        );
5451
        if ($styleFromDatabase) {
5452
            $platform_theme = $styleFromDatabase['selected_value'];
5453
        } else {
5454
            $platform_theme = api_get_setting('stylesheets');
5455
        }
5456
5457
        // Platform's theme.
5458
        $visual_theme = $platform_theme;
5459
        if ($userThemeAvailable) {
5460
            $user_info = api_get_user_info();
5461
            if (isset($user_info['theme'])) {
5462
                $user_theme = $user_info['theme'];
5463
5464
                if (!empty($user_theme)) {
5465
                    $visual_theme = $user_theme;
5466
                    // User's theme.
5467
                }
5468
            }
5469
        }
5470
5471
        if (!empty($course_id)) {
5472
            if ($courseThemeAvailable) {
5473
                $course_theme = api_get_course_setting('course_theme', api_get_course_info());
5474
5475
                if (!empty($course_theme) && $course_theme != -1) {
5476
                    if (!empty($course_theme)) {
5477
                        // Course's theme.
5478
                        $visual_theme = $course_theme;
5479
                    }
5480
                }
5481
5482
                $allow_lp_theme = api_get_course_setting('allow_learning_path_theme');
5483
                if ($allow_lp_theme == 1) {
5484
                    global $lp_theme_css, $lp_theme_config;
5485
                    // These variables come from the file lp_controller.php.
5486
                    if (!$lp_theme_config) {
5487
                        if (!empty($lp_theme_css)) {
5488
                            // LP's theme.
5489
                            $visual_theme = $lp_theme_css;
5490
                        }
5491
                    }
5492
                }
5493
            }
5494
        }
5495
5496
        if (empty($visual_theme)) {
5497
            $visual_theme = 'chamilo';
5498
        }
5499
5500
        global $lp_theme_log;
5501
        if ($lp_theme_log) {
5502
            $visual_theme = $platform_theme;
5503
        }
5504
        if ($useCache) {
5505
            apcu_store($apcVar, $visual_theme, 120);
5506
        }
5507
    }
5508
5509
    return $visual_theme;
5510
}
5511
5512
/**
5513
 * Returns a list of CSS themes currently available in the CSS folder
5514
 * The folder must have a default.css file.
5515
 *
5516
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
5517
 *
5518
 * @return array list of themes directories from the css folder
5519
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
5520
 */
5521
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
5522
{
5523
    // This configuration value is set by the vchamilo plugin
5524
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
5525
5526
    $readCssFolder = function ($dir) use ($virtualTheme) {
5527
        $finder = new Finder();
5528
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
5529
        $list = [];
5530
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
5531
        foreach ($themes as $theme) {
5532
            $folder = $theme->getFilename();
5533
            // A theme folder is consider if there's a default.css file
5534
            if (!file_exists($theme->getPathname().'/default.css')) {
5535
                continue;
5536
            }
5537
            $name = ucwords(str_replace('_', ' ', $folder));
5538
            if ($folder == $virtualTheme) {
5539
                continue;
5540
            }
5541
            $list[$folder] = $name;
5542
        }
5543
5544
        return $list;
5545
    };
5546
5547
    $dir = api_get_path(SYS_CSS_PATH).'themes/';
5548
    $list = $readCssFolder($dir);
5549
5550
    if (!empty($virtualTheme)) {
5551
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
5552
        if ($getOnlyThemeFromVirtualInstance) {
5553
            return $newList;
5554
        }
5555
        $list = $list + $newList;
5556
        asort($list);
5557
    }
5558
5559
    return $list;
5560
}
5561
5562
/**
5563
 * Find the largest sort value in a given user_course_category
5564
 * This function is used when we are moving a course to a different category
5565
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
5566
 *
5567
 * @author Patrick Cool <[email protected]>, Ghent University
5568
 *
5569
 * @param int $user_course_category the id of the user_course_category
5570
 * @param int $user_id
5571
 *
5572
 * @return int the value of the highest sort of the user_course_category
5573
 */
5574
function api_max_sort_value($user_course_category, $user_id)
5575
{
5576
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5577
    $sql = "SELECT max(sort) as max_sort FROM $tbl_course_user
5578
            WHERE
5579
                user_id='".intval($user_id)."' AND
5580
                relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
5581
                user_course_cat='".intval($user_course_category)."'";
5582
    $result_max = Database::query($sql);
5583
    if (Database::num_rows($result_max) == 1) {
5584
        $row_max = Database::fetch_array($result_max);
5585
5586
        return $row_max['max_sort'];
5587
    }
5588
5589
    return 0;
5590
}
5591
5592
/**
5593
 * Transforms a number of seconds in hh:mm:ss format.
5594
 *
5595
 * @author Julian Prud'homme
5596
 *
5597
 * @param int    $seconds      number of seconds
5598
 * @param string $space
5599
 * @param bool   $showSeconds
5600
 * @param bool   $roundMinutes
5601
 *
5602
 * @return string the formatted time
5603
 */
5604
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
5605
{
5606
    // $seconds = -1 means that we have wrong data in the db.
5607
    if ($seconds == -1) {
5608
        return
5609
            get_lang('Unknown').
5610
            Display::return_icon(
5611
                'info2.gif',
5612
                get_lang('WrongDatasForTimeSpentOnThePlatform'),
5613
                ['align' => 'absmiddle', 'hspace' => '3px']
5614
            );
5615
    }
5616
5617
    // How many hours ?
5618
    $hours = floor($seconds / 3600);
5619
5620
    // How many minutes ?
5621
    $min = floor(($seconds - ($hours * 3600)) / 60);
5622
5623
    if ($roundMinutes) {
5624
        if ($min >= 45) {
5625
            $min = 45;
5626
        }
5627
5628
        if ($min >= 30 && $min <= 44) {
5629
            $min = 30;
5630
        }
5631
5632
        if ($min >= 15 && $min <= 29) {
5633
            $min = 15;
5634
        }
5635
5636
        if ($min >= 0 && $min <= 14) {
5637
            $min = 0;
5638
        }
5639
    }
5640
5641
    // How many seconds
5642
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
5643
5644
    if ($hours < 10) {
5645
        $hours = "0$hours";
5646
    }
5647
5648
    if ($sec < 10) {
5649
        $sec = "0$sec";
5650
    }
5651
5652
    if ($min < 10) {
5653
        $min = "0$min";
5654
    }
5655
5656
    $seconds = '';
5657
    if ($showSeconds) {
5658
        $seconds = $space.$sec;
5659
    }
5660
5661
    return $hours.$space.$min.$seconds;
5662
}
5663
5664
/* FILE SYSTEM RELATED FUNCTIONS */
5665
5666
/**
5667
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5668
 * The return value is based on the platform administrator's setting
5669
 * "Administration > Configuration settings > Security > Permissions for new directories".
5670
 *
5671
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
5672
 */
5673
function api_get_permissions_for_new_directories()
5674
{
5675
    static $permissions;
5676
    if (!isset($permissions)) {
5677
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
5678
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
5679
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
5680
    }
5681
5682
    return $permissions;
5683
}
5684
5685
/**
5686
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5687
 * The return value is based on the platform administrator's setting
5688
 * "Administration > Configuration settings > Security > Permissions for new files".
5689
 *
5690
 * @return int returns the permissions in the format
5691
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
5692
 */
5693
function api_get_permissions_for_new_files()
5694
{
5695
    static $permissions;
5696
    if (!isset($permissions)) {
5697
        $permissions = trim(api_get_setting('permissions_for_new_files'));
5698
        // The default value 0666 is according to that in the platform
5699
        // administration panel after fresh system installation.
5700
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
5701
    }
5702
5703
    return $permissions;
5704
}
5705
5706
/**
5707
 * Deletes a file, or a folder and its contents.
5708
 *
5709
 * @author      Aidan Lister <[email protected]>
5710
 *
5711
 * @version     1.0.3
5712
 *
5713
 * @param string $dirname Directory to delete
5714
 * @param       bool     Deletes only the content or not
5715
 * @param bool $strict if one folder/file fails stop the loop
5716
 *
5717
 * @return bool Returns TRUE on success, FALSE on failure
5718
 *
5719
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
5720
 *
5721
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
5722
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
5723
 */
5724
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
5725
{
5726
    $res = true;
5727
    // A sanity check.
5728
    if (!file_exists($dirname)) {
5729
        return false;
5730
    }
5731
    $php_errormsg = '';
5732
    // Simple delete for a file.
5733
    if (is_file($dirname) || is_link($dirname)) {
5734
        $res = unlink($dirname);
5735
        if ($res === false) {
5736
            error_log(__FILE__.' line '.__LINE__.': '.((bool) ini_get('track_errors') ? $php_errormsg : 'Error not recorded because track_errors is off in your php.ini'), 0);
5737
        }
5738
5739
        return $res;
5740
    }
5741
5742
    // Loop through the folder.
5743
    $dir = dir($dirname);
5744
    // A sanity check.
5745
    $is_object_dir = is_object($dir);
5746
    if ($is_object_dir) {
5747
        while (false !== $entry = $dir->read()) {
5748
            // Skip pointers.
5749
            if ($entry == '.' || $entry == '..') {
5750
                continue;
5751
            }
5752
5753
            // Recurse.
5754
            if ($strict) {
5755
                $result = rmdirr("$dirname/$entry");
5756
                if ($result == false) {
5757
                    $res = false;
5758
                    break;
5759
                }
5760
            } else {
5761
                rmdirr("$dirname/$entry");
5762
            }
5763
        }
5764
    }
5765
5766
    // Clean up.
5767
    if ($is_object_dir) {
5768
        $dir->close();
5769
    }
5770
5771
    if ($delete_only_content_in_folder == false) {
5772
        $res = rmdir($dirname);
5773
        if ($res === false) {
5774
            error_log(__FILE__.' line '.__LINE__.': '.((bool) ini_get('track_errors') ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
5775
        }
5776
    }
5777
5778
    return $res;
5779
}
5780
5781
// TODO: This function is to be simplified. File access modes to be implemented.
5782
/**
5783
 * function adapted from a php.net comment
5784
 * copy recursively a folder.
5785
 *
5786
 * @param string $source       the source folder
5787
 * @param string $dest         the dest folder
5788
 * @param array  $exclude      an array of excluded file_name (without extension)
5789
 * @param array  $copied_files the returned array of copied files
5790
 */
5791
function copyr($source, $dest, $exclude = [], $copied_files = [])
5792
{
5793
    if (empty($dest)) {
5794
        return false;
5795
    }
5796
    // Simple copy for a file
5797
    if (is_file($source)) {
5798
        $path_info = pathinfo($source);
5799
        if (!in_array($path_info['filename'], $exclude)) {
5800
            copy($source, $dest);
5801
        }
5802
5803
        return true;
5804
    } elseif (!is_dir($source)) {
5805
        //then source is not a dir nor a file, return
5806
        return false;
5807
    }
5808
5809
    // Make destination directory.
5810
    if (!is_dir($dest)) {
5811
        mkdir($dest, api_get_permissions_for_new_directories());
5812
    }
5813
5814
    // Loop through the folder.
5815
    $dir = dir($source);
5816
    while (false !== $entry = $dir->read()) {
5817
        // Skip pointers
5818
        if ($entry == '.' || $entry == '..') {
5819
            continue;
5820
        }
5821
5822
        // Deep copy directories.
5823
        if ($dest !== "$source/$entry") {
5824
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
5825
        }
5826
    }
5827
    // Clean up.
5828
    $dir->close();
5829
5830
    return true;
5831
}
5832
5833
/**
5834
 * @param string $pathname
5835
 * @param string $base_path_document
5836
 * @param int    $session_id
5837
 * @param array
5838
 * @param string
5839
 *
5840
 * @return mixed True if directory already exists, false if a file already exists at
5841
 *               the destination and null if everything goes according to plan
5842
 *@todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
5843
 * Documentation header to be added here.
5844
 */
5845
function copy_folder_course_session(
5846
    $pathname,
5847
    $base_path_document,
5848
    $session_id,
5849
    $course_info,
5850
    $document,
5851
    $source_course_id,
5852
    $originalFolderNameList = [],
5853
    $originalBaseName = ''
5854
) {
5855
    // Check whether directory already exists.
5856
    if (empty($pathname) || is_dir($pathname)) {
5857
        return true;
5858
    }
5859
5860
    // Ensure that a file with the same name does not already exist.
5861
    if (is_file($pathname)) {
5862
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
5863
5864
        return false;
5865
    }
5866
5867
    //error_log('checking:');
5868
    //error_log(str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5869
    $baseNoDocument = str_replace('document', '', $originalBaseName);
5870
    $folderTitles = explode('/', $baseNoDocument);
5871
    $folderTitles = array_filter($folderTitles);
5872
5873
    //error_log($baseNoDocument);error_log(print_r($folderTitles, 1));
5874
5875
    $table = Database::get_course_table(TABLE_DOCUMENT);
5876
    $session_id = (int) $session_id;
5877
    $source_course_id = (int) $source_course_id;
5878
    $course_id = $course_info['real_id'];
5879
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5880
    $new_pathname = $base_path_document;
5881
5882
    $path = '';
5883
    foreach ($folders as $index => $folder) {
5884
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
5885
        $path .= DIRECTORY_SEPARATOR.$folder;
5886
5887
        if (!file_exists($new_pathname)) {
5888
            $path = Database::escape_string($path);
5889
            //error_log("path: $path");
5890
            $sql = "SELECT * FROM $table
5891
                    WHERE
5892
                        c_id = $source_course_id AND
5893
                        path = '$path' AND
5894
                        filetype = 'folder' AND
5895
                        session_id = '$session_id'";
5896
            $rs1 = Database::query($sql);
5897
            $num_rows = Database::num_rows($rs1);
5898
5899
            if (0 == $num_rows) {
5900
                mkdir($new_pathname, api_get_permissions_for_new_directories());
5901
                $title = basename($new_pathname);
5902
5903
                if (isset($folderTitles[$index + 1])) {
5904
                    $checkPath = $folderTitles[$index + 1];
5905
                    //error_log("check $checkPath");
5906
                    if (isset($originalFolderNameList[$checkPath])) {
5907
                        $title = $originalFolderNameList[$checkPath];
5908
                        //error_log('use this name: '.$title);
5909
                    }
5910
                }
5911
5912
                // Insert new folder with destination session_id.
5913
                $params = [
5914
                    'c_id' => $course_id,
5915
                    'path' => $path,
5916
                    'comment' => $document->comment,
5917
                    'title' => $title,
5918
                    'filetype' => 'folder',
5919
                    'size' => '0',
5920
                    'session_id' => $session_id,
5921
                ];
5922
5923
                //error_log("old $folder"); error_log("Add doc $title in $path");
5924
                $document_id = Database::insert($table, $params);
5925
                if ($document_id) {
5926
                    $sql = "UPDATE $table SET id = iid WHERE iid = $document_id";
5927
                    Database::query($sql);
5928
5929
                    api_item_property_update(
5930
                        $course_info,
5931
                        TOOL_DOCUMENT,
5932
                        $document_id,
5933
                        'FolderCreated',
5934
                        api_get_user_id(),
5935
                        0,
5936
                        0,
5937
                        null,
5938
                        null,
5939
                        $session_id
5940
                    );
5941
                }
5942
            }
5943
        }
5944
    }
5945
}
5946
5947
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
5948
/**
5949
 * @param string $path
5950
 */
5951
function api_chmod_R($path, $filemode)
5952
{
5953
    if (!is_dir($path)) {
5954
        return chmod($path, $filemode);
5955
    }
5956
5957
    $handler = opendir($path);
5958
    while ($file = readdir($handler)) {
5959
        if ($file != '.' && $file != '..') {
5960
            $fullpath = "$path/$file";
5961
            if (!is_dir($fullpath)) {
5962
                if (!chmod($fullpath, $filemode)) {
5963
                    return false;
5964
                }
5965
            } else {
5966
                if (!api_chmod_R($fullpath, $filemode)) {
5967
                    return false;
5968
                }
5969
            }
5970
        }
5971
    }
5972
5973
    closedir($handler);
5974
5975
    return chmod($path, $filemode);
5976
}
5977
5978
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
5979
/**
5980
 * Parse info file format. (e.g: file.info).
5981
 *
5982
 * Files should use an ini-like format to specify values.
5983
 * White-space generally doesn't matter, except inside values.
5984
 * e.g.
5985
 *
5986
 * @verbatim
5987
 *   key = value
5988
 *   key = "value"
5989
 *   key = 'value'
5990
 *   key = "multi-line
5991
 *
5992
 *   value"
5993
 *   key = 'multi-line
5994
 *
5995
 *   value'
5996
 *   key
5997
 *   =
5998
 *   'value'
5999
 * @endverbatim
6000
 *
6001
 * Arrays are created using a GET-like syntax:
6002
 *
6003
 * @verbatim
6004
 *   key[] = "numeric array"
6005
 *   key[index] = "associative array"
6006
 *   key[index][] = "nested numeric array"
6007
 *   key[index][index] = "nested associative array"
6008
 * @endverbatim
6009
 *
6010
 * PHP constants are substituted in, but only when used as the entire value:
6011
 *
6012
 * Comments should start with a semi-colon at the beginning of a line.
6013
 *
6014
 * This function is NOT for placing arbitrary module-specific settings. Use
6015
 * variable_get() and variable_set() for that.
6016
 *
6017
 * Information stored in the module.info file:
6018
 * - name: The real name of the module for display purposes.
6019
 * - description: A brief description of the module.
6020
 * - dependencies: An array of shortnames of other modules this module depends on.
6021
 * - package: The name of the package of modules this module belongs to.
6022
 *
6023
 * Example of .info file:
6024
 * <code>
6025
 * @verbatim
6026
 *   name = Forum
6027
 *   description = Enables threaded discussions about general topics.
6028
 *   dependencies[] = taxonomy
6029
 *   dependencies[] = comment
6030
 *   package = Core - optional
6031
 *   version = VERSION
6032
 * @endverbatim
6033
 * </code>
6034
 *
6035
 * @param string $filename
6036
 *                         The file we are parsing. Accepts file with relative or absolute path.
6037
 *
6038
 * @return
6039
 *   The info array
6040
 */
6041
function api_parse_info_file($filename)
6042
{
6043
    $info = [];
6044
6045
    if (!file_exists($filename)) {
6046
        return $info;
6047
    }
6048
6049
    $data = file_get_contents($filename);
6050
    if (preg_match_all('
6051
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
6052
        ((?:
6053
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
6054
          \[[^\[\]]*\]                  # unless they are balanced and not nested
6055
        )+?)
6056
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
6057
        (?:
6058
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
6059
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
6060
          ([^\r\n]*?)                   # Non-quoted string
6061
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
6062
        @msx', $data, $matches, PREG_SET_ORDER)) {
6063
        $key = $value1 = $value2 = $value3 = '';
6064
        foreach ($matches as $match) {
6065
            // Fetch the key and value string.
6066
            $i = 0;
6067
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
6068
                $$var = isset($match[++$i]) ? $match[$i] : '';
6069
            }
6070
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
6071
6072
            // Parse array syntax.
6073
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
6074
            $last = array_pop($keys);
6075
            $parent = &$info;
6076
6077
            // Create nested arrays.
6078
            foreach ($keys as $key) {
6079
                if ($key == '') {
6080
                    $key = count($parent);
6081
                }
6082
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
6083
                    $parent[$key] = [];
6084
                }
6085
                $parent = &$parent[$key];
6086
            }
6087
6088
            // Handle PHP constants.
6089
            if (defined($value)) {
6090
                $value = constant($value);
6091
            }
6092
6093
            // Insert actual value.
6094
            if ($last == '') {
6095
                $last = count($parent);
6096
            }
6097
            $parent[$last] = $value;
6098
        }
6099
    }
6100
6101
    return $info;
6102
}
6103
6104
/**
6105
 * Gets Chamilo version from the configuration files.
6106
 *
6107
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
6108
 */
6109
function api_get_version()
6110
{
6111
    return (string) api_get_configuration_value('system_version');
6112
}
6113
6114
/**
6115
 * Gets the software name (the name/brand of the Chamilo-based customized system).
6116
 *
6117
 * @return string
6118
 */
6119
function api_get_software_name()
6120
{
6121
    $name = api_get_configuration_value('software_name');
6122
    if (!empty($name)) {
6123
        return $name;
6124
    } else {
6125
        return 'Chamilo';
6126
    }
6127
}
6128
6129
/**
6130
 * Checks whether status given in parameter exists in the platform.
6131
 *
6132
 * @param mixed the status (can be either int either string)
6133
 *
6134
 * @return bool if the status exists, else returns false
6135
 */
6136
function api_status_exists($status_asked)
6137
{
6138
    global $_status_list;
6139
6140
    return in_array($status_asked, $_status_list) ? true : isset($_status_list[$status_asked]);
6141
}
6142
6143
/**
6144
 * Checks whether status given in parameter exists in the platform. The function
6145
 * returns the status ID or false if it does not exist, but given the fact there
6146
 * is no "0" status, the return value can be checked against
6147
 * if(api_status_key()) to know if it exists.
6148
 *
6149
 * @param   mixed   The status (can be either int or string)
6150
 *
6151
 * @return mixed Status ID if exists, false otherwise
6152
 */
6153
function api_status_key($status)
6154
{
6155
    global $_status_list;
6156
6157
    return isset($_status_list[$status]) ? $status : array_search($status, $_status_list);
6158
}
6159
6160
/**
6161
 * Gets the status langvars list.
6162
 *
6163
 * @return string[] the list of status with their translations
6164
 */
6165
function api_get_status_langvars()
6166
{
6167
    return [
6168
        COURSEMANAGER => get_lang('Teacher', ''),
6169
        SESSIONADMIN => get_lang('SessionsAdmin', ''),
6170
        DRH => get_lang('Drh', ''),
6171
        STUDENT => get_lang('Student', ''),
6172
        ANONYMOUS => get_lang('Anonymous', ''),
6173
        STUDENT_BOSS => get_lang('RoleStudentBoss', ''),
6174
        INVITEE => get_lang('Invited'),
6175
    ];
6176
}
6177
6178
/**
6179
 * The function that retrieves all the possible settings for a certain config setting.
6180
 *
6181
 * @author Patrick Cool <[email protected]>, Ghent University
6182
 */
6183
function api_get_settings_options($var)
6184
{
6185
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6186
    $var = Database::escape_string($var);
6187
    $sql = "SELECT * FROM $table_settings_options
6188
            WHERE variable = '$var'
6189
            ORDER BY id";
6190
    $result = Database::query($sql);
6191
    $settings_options_array = [];
6192
    while ($row = Database::fetch_array($result, 'ASSOC')) {
6193
        $settings_options_array[] = $row;
6194
    }
6195
6196
    return $settings_options_array;
6197
}
6198
6199
/**
6200
 * @param array $params
6201
 */
6202
function api_set_setting_option($params)
6203
{
6204
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6205
    if (empty($params['id'])) {
6206
        Database::insert($table, $params);
6207
    } else {
6208
        Database::update($table, $params, ['id = ? ' => $params['id']]);
6209
    }
6210
}
6211
6212
/**
6213
 * @param array $params
6214
 */
6215
function api_set_setting_simple($params)
6216
{
6217
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6218
    $url_id = api_get_current_access_url_id();
6219
6220
    if (empty($params['id'])) {
6221
        $params['access_url'] = $url_id;
6222
        Database::insert($table, $params);
6223
    } else {
6224
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
6225
    }
6226
}
6227
6228
/**
6229
 * @param int $id
6230
 */
6231
function api_delete_setting_option($id)
6232
{
6233
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6234
    if (!empty($id)) {
6235
        Database::delete($table, ['id = ? ' => $id]);
6236
    }
6237
}
6238
6239
/**
6240
 * Sets a platform configuration setting to a given value.
6241
 *
6242
 * @param string    The variable we want to update
6243
 * @param string    The value we want to record
6244
 * @param string    The sub-variable if any (in most cases, this will remain null)
6245
 * @param string    The category if any (in most cases, this will remain null)
6246
 * @param int       The access_url for which this parameter is valid
6247
 * @param string $cat
6248
 *
6249
 * @return bool|null
6250
 */
6251
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
6252
{
6253
    if (empty($var)) {
6254
        return false;
6255
    }
6256
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6257
    $var = Database::escape_string($var);
6258
    $value = Database::escape_string($value);
6259
    $access_url = (int) $access_url;
6260
    if (empty($access_url)) {
6261
        $access_url = 1;
6262
    }
6263
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
6264
    if (!empty($subvar)) {
6265
        $subvar = Database::escape_string($subvar);
6266
        $select .= " AND subkey = '$subvar'";
6267
    }
6268
    if (!empty($cat)) {
6269
        $cat = Database::escape_string($cat);
6270
        $select .= " AND category = '$cat'";
6271
    }
6272
    if ($access_url > 1) {
6273
        $select .= " AND access_url = $access_url";
6274
    } else {
6275
        $select .= " AND access_url = 1 ";
6276
    }
6277
6278
    $res = Database::query($select);
6279
    if (Database::num_rows($res) > 0) {
6280
        // Found item for this access_url.
6281
        $row = Database::fetch_array($res);
6282
        $sql = "UPDATE $t_settings SET selected_value = '$value'
6283
                WHERE id = ".$row['id'];
6284
        Database::query($sql);
6285
    } else {
6286
        // Item not found for this access_url, we have to check if it exist with access_url = 1
6287
        $select = "SELECT * FROM $t_settings
6288
                   WHERE variable = '$var' AND access_url = 1 ";
6289
        // Just in case
6290
        if ($access_url == 1) {
6291
            if (!empty($subvar)) {
6292
                $select .= " AND subkey = '$subvar'";
6293
            }
6294
            if (!empty($cat)) {
6295
                $select .= " AND category = '$cat'";
6296
            }
6297
            $res = Database::query($select);
6298
            if (Database::num_rows($res) > 0) {
6299
                // We have a setting for access_url 1, but none for the current one, so create one.
6300
                $row = Database::fetch_array($res);
6301
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
6302
                        VALUES
6303
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6304
                        "'".$row['type']."','".$row['category']."',".
6305
                        "'$value','".$row['title']."',".
6306
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6307
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
6308
                Database::query($insert);
6309
            } else {
6310
                // Such a setting does not exist.
6311
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
6312
            }
6313
        } else {
6314
            // Other access url.
6315
            if (!empty($subvar)) {
6316
                $select .= " AND subkey = '$subvar'";
6317
            }
6318
            if (!empty($cat)) {
6319
                $select .= " AND category = '$cat'";
6320
            }
6321
            $res = Database::query($select);
6322
6323
            if (Database::num_rows($res) > 0) {
6324
                // We have a setting for access_url 1, but none for the current one, so create one.
6325
                $row = Database::fetch_array($res);
6326
                if ($row['access_url_changeable'] == 1) {
6327
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
6328
                            ('".$row['variable']."',".
6329
                            (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6330
                            "'".$row['type']."','".$row['category']."',".
6331
                            "'$value','".$row['title']."',".
6332
                            "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
6333
                            (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6334
                            "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
6335
                    Database::query($insert);
6336
                }
6337
            } else { // Such a setting does not exist.
6338
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
6339
            }
6340
        }
6341
    }
6342
}
6343
6344
/**
6345
 * Sets a whole category of settings to one specific value.
6346
 *
6347
 * @param string    Category
6348
 * @param string    Value
6349
 * @param int       Access URL. Optional. Defaults to 1
6350
 * @param array     Optional array of filters on field type
6351
 * @param string $category
6352
 * @param string $value
6353
 *
6354
 * @return bool
6355
 */
6356
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
6357
{
6358
    if (empty($category)) {
6359
        return false;
6360
    }
6361
    $category = Database::escape_string($category);
6362
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6363
    $access_url = (int) $access_url;
6364
    if (empty($access_url)) {
6365
        $access_url = 1;
6366
    }
6367
    if (isset($value)) {
6368
        $value = Database::escape_string($value);
6369
        $sql = "UPDATE $t_s SET selected_value = '$value'
6370
                WHERE category = '$category' AND access_url = $access_url";
6371
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6372
            $sql .= " AND ( ";
6373
            $i = 0;
6374
            foreach ($fieldtype as $type) {
6375
                if ($i > 0) {
6376
                    $sql .= ' OR ';
6377
                }
6378
                $type = Database::escape_string($type);
6379
                $sql .= " type='".$type."' ";
6380
                $i++;
6381
            }
6382
            $sql .= ")";
6383
        }
6384
        $res = Database::query($sql);
6385
6386
        return $res !== false;
6387
    } else {
6388
        $sql = "UPDATE $t_s SET selected_value = NULL
6389
                WHERE category = '$category' AND access_url = $access_url";
6390
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6391
            $sql .= " AND ( ";
6392
            $i = 0;
6393
            foreach ($fieldtype as $type) {
6394
                if ($i > 0) {
6395
                    $sql .= ' OR ';
6396
                }
6397
                $type = Database::escape_string($type);
6398
                $sql .= " type='".$type."' ";
6399
                $i++;
6400
            }
6401
            $sql .= ")";
6402
        }
6403
        $res = Database::query($sql);
6404
6405
        return $res !== false;
6406
    }
6407
}
6408
6409
/**
6410
 * Gets all available access urls in an array (as in the database).
6411
 *
6412
 * @return array An array of database records
6413
 */
6414
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
6415
{
6416
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6417
    $from = (int) $from;
6418
    $to = (int) $to;
6419
    $order = Database::escape_string($order);
6420
    $direction = Database::escape_string($direction);
6421
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
6422
6423
    $sql = "SELECT id, url, description, active, created_by, tms
6424
            FROM $table
6425
            ORDER BY `$order` $direction
6426
            LIMIT $to OFFSET $from";
6427
    $res = Database::query($sql);
6428
6429
    return Database::store_result($res);
6430
}
6431
6432
/**
6433
 * Gets the access url info in an array.
6434
 *
6435
 * @param int  $id            Id of the access url
6436
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
6437
 *
6438
 * @return array All the info (url, description, active, created_by, tms)
6439
 *               from the access_url table
6440
 *
6441
 * @author Julio Montoya
6442
 */
6443
function api_get_access_url($id, $returnDefault = true)
6444
{
6445
    static $staticResult;
6446
    $id = (int) $id;
6447
6448
    if (isset($staticResult[$id])) {
6449
        $result = $staticResult[$id];
6450
    } else {
6451
        // Calling the Database:: library dont work this is handmade.
6452
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6453
        $sql = "SELECT url, description, active, created_by, tms
6454
                FROM $table_access_url WHERE id = '$id' ";
6455
        $res = Database::query($sql);
6456
        $result = @Database::fetch_array($res);
6457
        $staticResult[$id] = $result;
6458
    }
6459
6460
    // If the result url is 'http://localhost/' (the default) and the root_web
6461
    // (=current url) is different, and the $id is = 1 (which might mean
6462
    // api_get_current_access_url_id() returned 1 by default), then return the
6463
    // root_web setting instead of the current URL
6464
    // This is provided as an option to avoid breaking the storage of URL-specific
6465
    // homepages in home/localhost/
6466
    if ($id === 1 && $returnDefault === false) {
6467
        $currentUrl = api_get_current_access_url_id();
6468
        // only do this if we are on the main URL (=1), otherwise we could get
6469
        // information on another URL instead of the one asked as parameter
6470
        if ($currentUrl === 1) {
6471
            $rootWeb = api_get_path(WEB_PATH);
6472
            $default = 'http://localhost/';
6473
            if ($result['url'] === $default && $rootWeb != $default) {
6474
                $result['url'] = $rootWeb;
6475
            }
6476
        }
6477
    }
6478
6479
    return $result;
6480
}
6481
6482
/**
6483
 * Gets all the current settings for a specific access url.
6484
 *
6485
 * @param string    The category, if any, that we want to get
6486
 * @param string    Whether we want a simple list (display a category) or
6487
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
6488
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
6489
 *
6490
 * @return array Array of database results for the current settings of the current access URL
6491
 */
6492
function api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
6493
{
6494
    // Try getting settings from cache first (avoids query w/ ~375 rows result)
6495
    $apcVarName = '';
6496
    $apcVar = [];
6497
    $cacheAvailable = api_get_configuration_value('apc');
6498
    if ($cacheAvailable) {
6499
        $apcVarName = api_get_configuration_value('apc_prefix').'settings';
6500
        $catName = (empty($cat) ? 'global' : $cat);
6501
6502
        if (apcu_exists($apcVarName)) {
6503
            $apcVar = apcu_fetch($apcVarName);
6504
            if (!empty($apcVar[$catName]) && !empty($apcVar[$catName][$ordering]) && isset($apcVar[$catName][$ordering][$url_changeable])) {
6505
                return $apcVar[$catName][$ordering][$url_changeable];
6506
            }
6507
        }
6508
    }
6509
    // Could not find settings in cache (or already expired), so query DB
6510
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6511
    $access_url = (int) $access_url;
6512
    $where_condition = '';
6513
    if ($url_changeable == 1) {
6514
        $where_condition = " AND access_url_changeable= '1' ";
6515
    }
6516
    if (empty($access_url) || $access_url == -1) {
6517
        $access_url = 1;
6518
    }
6519
    $sql = "SELECT * FROM $table
6520
            WHERE access_url = $access_url  $where_condition ";
6521
6522
    if (!empty($cat)) {
6523
        $cat = Database::escape_string($cat);
6524
        $sql .= " AND category='$cat' ";
6525
    }
6526
    if ($ordering == 'group') {
6527
        $sql .= " ORDER BY id ASC";
6528
    } else {
6529
        $sql .= " ORDER BY 1,2 ASC";
6530
    }
6531
    $result = Database::query($sql);
6532
    if ($result === null) {
6533
        return [];
6534
    }
6535
    $result = Database::store_result($result, 'ASSOC');
6536
6537
    if ($cacheAvailable) {
6538
        // If we got here, it means cache is available but the settings
6539
        // were not recently stored, so now we have them, let's store them
6540
        if (empty($apcVar[$catName])) {
6541
            $apcVar[$catName] = [];
6542
        }
6543
        if (empty($apcVar[$catName][$ordering])) {
6544
            $apcVar[$catName][$ordering] = [];
6545
        }
6546
        $apcVar[$catName][$ordering][$url_changeable] = $result;
6547
        apcu_store($apcVarName, $apcVar, 600);
6548
    }
6549
6550
    return $result;
6551
}
6552
6553
/**
6554
 * @param string $value       The value we want to record
6555
 * @param string $variable    The variable name we want to insert
6556
 * @param string $subKey      The subkey for the variable we want to insert
6557
 * @param string $type        The type for the variable we want to insert
6558
 * @param string $category    The category for the variable we want to insert
6559
 * @param string $title       The title
6560
 * @param string $comment     The comment
6561
 * @param string $scope       The scope
6562
 * @param string $subKeyText  The subkey text
6563
 * @param int    $accessUrlId The access_url for which this parameter is valid
6564
 * @param int    $visibility  The changeability of this setting for non-master urls
6565
 *
6566
 * @return int The setting ID
6567
 */
6568
function api_add_setting(
6569
    $value,
6570
    $variable,
6571
    $subKey = '',
6572
    $type = 'textfield',
6573
    $category = '',
6574
    $title = '',
6575
    $comment = '',
6576
    $scope = '',
6577
    $subKeyText = '',
6578
    $accessUrlId = 1,
6579
    $visibility = 0
6580
) {
6581
    $em = Database::getManager();
6582
    $settingRepo = $em->getRepository('ChamiloCoreBundle:SettingsCurrent');
6583
    $accessUrlId = (int) $accessUrlId ?: 1;
6584
6585
    if (is_array($value)) {
6586
        $value = serialize($value);
6587
    } else {
6588
        $value = trim($value);
6589
    }
6590
6591
    $criteria = ['variable' => $variable, 'accessUrl' => $accessUrlId];
6592
6593
    if (!empty($subKey)) {
6594
        $criteria['subkey'] = $subKey;
6595
    }
6596
6597
    // Check if this variable doesn't exist already
6598
    /** @var SettingsCurrent $setting */
6599
    $setting = $settingRepo->findOneBy($criteria);
6600
6601
    if ($setting) {
6602
        $setting->setSelectedValue($value);
6603
6604
        $em->persist($setting);
6605
        $em->flush();
6606
6607
        return $setting->getId();
6608
    }
6609
6610
    // Item not found for this access_url, we have to check if the whole thing is missing
6611
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
6612
    $setting = new SettingsCurrent();
6613
    $setting
6614
        ->setVariable($variable)
6615
        ->setSelectedValue($value)
6616
        ->setType($type)
6617
        ->setCategory($category)
6618
        ->setSubkey($subKey)
6619
        ->setTitle($title)
6620
        ->setComment($comment)
6621
        ->setScope($scope)
6622
        ->setSubkeytext($subKeyText)
6623
        ->setAccessUrl($accessUrlId)
6624
        ->setAccessUrlChangeable($visibility);
6625
6626
    $em->persist($setting);
6627
    $em->flush();
6628
6629
    return $setting->getId();
6630
}
6631
6632
/**
6633
 * Checks wether a user can or can't view the contents of a course.
6634
 *
6635
 * @deprecated use CourseManager::is_user_subscribed_in_course
6636
 *
6637
 * @param int $userid User id or NULL to get it from $_SESSION
6638
 * @param int $cid    course id to check whether the user is allowed
6639
 *
6640
 * @return bool
6641
 */
6642
function api_is_course_visible_for_user($userid = null, $cid = null)
6643
{
6644
    if ($userid === null) {
6645
        $userid = api_get_user_id();
6646
    }
6647
    if (empty($userid) || strval(intval($userid)) != $userid) {
6648
        if (api_is_anonymous()) {
6649
            $userid = api_get_anonymous_id();
6650
        } else {
6651
            return false;
6652
        }
6653
    }
6654
    $cid = Database::escape_string($cid);
6655
6656
    $courseInfo = api_get_course_info($cid);
6657
    $courseId = $courseInfo['real_id'];
6658
    $is_platformAdmin = api_is_platform_admin();
6659
6660
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
6661
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
6662
6663
    $sql = "SELECT
6664
                $course_table.category_code,
6665
                $course_table.visibility,
6666
                $course_table.code,
6667
                $course_cat_table.code
6668
            FROM $course_table
6669
            LEFT JOIN $course_cat_table
6670
                ON $course_table.category_code = $course_cat_table.code
6671
            WHERE
6672
                $course_table.code = '$cid'
6673
            LIMIT 1";
6674
6675
    $result = Database::query($sql);
6676
6677
    if (Database::num_rows($result) > 0) {
6678
        $visibility = Database::fetch_array($result);
6679
        $visibility = $visibility['visibility'];
6680
    } else {
6681
        $visibility = 0;
6682
    }
6683
    // Shortcut permissions in case the visibility is "open to the world".
6684
    if ($visibility === COURSE_VISIBILITY_OPEN_WORLD) {
6685
        return true;
6686
    }
6687
6688
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6689
6690
    $sql = "SELECT
6691
                is_tutor, status
6692
            FROM $tbl_course_user
6693
            WHERE
6694
                user_id  = '$userid' AND
6695
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
6696
                c_id = $courseId
6697
            LIMIT 1";
6698
6699
    $result = Database::query($sql);
6700
6701
    if (Database::num_rows($result) > 0) {
6702
        // This user has got a recorded state for this course.
6703
        $cuData = Database::fetch_array($result);
6704
        $is_courseMember = true;
6705
        $is_courseAdmin = ($cuData['status'] == 1);
6706
    }
6707
6708
    if (!$is_courseAdmin) {
6709
        // This user has no status related to this course.
6710
        // Is it the session coach or the session admin?
6711
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
6712
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
6713
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6714
6715
        $sql = "SELECT
6716
                    session.id_coach, session_admin_id, session.id
6717
                FROM
6718
                    $tbl_session as session
6719
                INNER JOIN $tbl_session_course
6720
                    ON session_rel_course.session_id = session.id
6721
                    AND session_rel_course.c_id = '$courseId'
6722
                LIMIT 1";
6723
6724
        $result = Database::query($sql);
6725
        $row = Database::store_result($result);
6726
6727
        if ($row[0]['id_coach'] == $userid) {
6728
            $is_courseMember = true;
6729
            $is_courseAdmin = false;
6730
        } elseif ($row[0]['session_admin_id'] == $userid) {
6731
            $is_courseMember = false;
6732
            $is_courseAdmin = false;
6733
        } else {
6734
            // Check if the current user is the course coach.
6735
            $sql = "SELECT 1
6736
                    FROM $tbl_session_course
6737
                    WHERE session_rel_course.c_id = '$courseId'
6738
                    AND session_rel_course.id_coach = '$userid'
6739
                    LIMIT 1";
6740
6741
            $result = Database::query($sql);
6742
6743
            //if ($row = Database::fetch_array($result)) {
6744
            if (Database::num_rows($result) > 0) {
6745
                $is_courseMember = true;
6746
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
6747
6748
                $sql = "SELECT status FROM $tbl_user
6749
                        WHERE user_id = $userid
6750
                        LIMIT 1";
6751
6752
                $result = Database::query($sql);
6753
6754
                if (Database::result($result, 0, 0) == 1) {
6755
                    $is_courseAdmin = true;
6756
                } else {
6757
                    $is_courseAdmin = false;
6758
                }
6759
            } else {
6760
                // Check if the user is a student is this session.
6761
                $sql = "SELECT  id
6762
                        FROM $tbl_session_course_user
6763
                        WHERE
6764
                            user_id  = '$userid' AND
6765
                            c_id = '$courseId'
6766
                        LIMIT 1";
6767
6768
                if (Database::num_rows($result) > 0) {
6769
                    // This user haa got a recorded state for this course.
6770
                    while ($row = Database::fetch_array($result)) {
6771
                        $is_courseMember = true;
6772
                        $is_courseAdmin = false;
6773
                    }
6774
                }
6775
            }
6776
        }
6777
    }
6778
6779
    switch ($visibility) {
6780
        case COURSE_VISIBILITY_OPEN_WORLD:
6781
            return true;
6782
        case COURSE_VISIBILITY_OPEN_PLATFORM:
6783
            return isset($userid);
6784
        case COURSE_VISIBILITY_REGISTERED:
6785
        case COURSE_VISIBILITY_CLOSED:
6786
            return $is_platformAdmin || $is_courseMember || $is_courseAdmin;
6787
        case COURSE_VISIBILITY_HIDDEN:
6788
            return $is_platformAdmin;
6789
    }
6790
6791
    return false;
6792
}
6793
6794
/**
6795
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
6796
 *
6797
 * @param string the tool of the element
6798
 * @param int the element id in database
6799
 * @param int the session_id to compare with element session id
6800
 *
6801
 * @return bool true if the element is in the session, false else
6802
 */
6803
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
6804
{
6805
    if (is_null($session_id)) {
6806
        $session_id = api_get_session_id();
6807
    }
6808
6809
    $element_id = (int) $element_id;
6810
6811
    if (empty($element_id)) {
6812
        return false;
6813
    }
6814
6815
    // Get information to build query depending of the tool.
6816
    switch ($tool) {
6817
        case TOOL_SURVEY:
6818
            $table_tool = Database::get_course_table(TABLE_SURVEY);
6819
            $key_field = 'survey_id';
6820
            break;
6821
        case TOOL_ANNOUNCEMENT:
6822
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
6823
            $key_field = 'id';
6824
            break;
6825
        case TOOL_AGENDA:
6826
            $table_tool = Database::get_course_table(TABLE_AGENDA);
6827
            $key_field = 'id';
6828
            break;
6829
        case TOOL_GROUP:
6830
            $table_tool = Database::get_course_table(TABLE_GROUP);
6831
            $key_field = 'id';
6832
            break;
6833
        default:
6834
            return false;
6835
    }
6836
    $course_id = api_get_course_int_id();
6837
6838
    $sql = "SELECT session_id FROM $table_tool
6839
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
6840
    $rs = Database::query($sql);
6841
    if ($element_session_id = Database::result($rs, 0, 0)) {
6842
        if ($element_session_id == intval($session_id)) {
6843
            // The element belongs to the session.
6844
            return true;
6845
        }
6846
    }
6847
6848
    return false;
6849
}
6850
6851
/**
6852
 * Replaces "forbidden" characters in a filename string.
6853
 *
6854
 * @param string $filename
6855
 * @param bool   $treat_spaces_as_hyphens
6856
 *
6857
 * @return string
6858
 */
6859
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
6860
{
6861
    // Some non-properly encoded file names can cause the whole file to be
6862
    // skipped when uploaded. Avoid this by detecting the encoding and
6863
    // converting to UTF-8, setting the source as ASCII (a reasonably
6864
    // limited characters set) if nothing could be found (BT#
6865
    $encoding = api_detect_encoding($filename);
6866
    if (empty($encoding)) {
6867
        $encoding = 'ASCII';
6868
        if (!api_is_valid_ascii($filename)) {
6869
            // try iconv and try non standard ASCII a.k.a CP437
6870
            // see BT#15022
6871
            if (function_exists('iconv')) {
6872
                $result = iconv('CP437', 'UTF-8', $filename);
6873
                if (api_is_valid_utf8($result)) {
6874
                    $filename = $result;
6875
                    $encoding = 'UTF-8';
6876
                }
6877
            }
6878
        }
6879
    }
6880
6881
    $filename = api_to_system_encoding($filename, $encoding);
6882
6883
    $url = URLify::filter(
6884
        $filename,
6885
        250,
6886
        '',
6887
        true,
6888
        false,
6889
        false,
6890
        false,
6891
        $treat_spaces_as_hyphens
6892
    );
6893
6894
    // Replace multiple dots at the end.
6895
    $regex = "/\.+$/";
6896
    $url = preg_replace($regex, '', $url);
6897
6898
    return $url;
6899
}
6900
6901
/**
6902
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
6903
 *
6904
 * @author Ivan Tcholakov, 28-JUN-2006.
6905
 */
6906
function api_request_uri()
6907
{
6908
    if (!empty($_SERVER['REQUEST_URI'])) {
6909
        return $_SERVER['REQUEST_URI'];
6910
    }
6911
    $uri = $_SERVER['SCRIPT_NAME'];
6912
    if (!empty($_SERVER['QUERY_STRING'])) {
6913
        $uri .= '?'.$_SERVER['QUERY_STRING'];
6914
    }
6915
    $_SERVER['REQUEST_URI'] = $uri;
6916
6917
    return $uri;
6918
}
6919
6920
/**
6921
 * Gets the current access_url id of the Chamilo Platform.
6922
 *
6923
 * @return int access_url_id of the current Chamilo Installation or 1 if multiple_access_urls is not enabled
6924
 *
6925
 * @author Julio Montoya <[email protected]>
6926
 */
6927
function api_get_current_access_url_id()
6928
{
6929
    if ('cli' === PHP_SAPI) {
6930
        $accessUrlId = api_get_configuration_value('access_url');
6931
        if (!empty($accessUrlId)) {
6932
            return $accessUrlId;
6933
        }
6934
    }
6935
6936
    static $id;
6937
    if (!empty($id)) {
6938
        return (int) $id;
6939
    }
6940
6941
    if (!api_get_multiple_access_url()) {
6942
        // If the feature is not enabled, assume 1 and return before querying
6943
        // the database
6944
        return 1;
6945
    }
6946
6947
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6948
    $path = Database::escape_string(api_get_path(WEB_PATH));
6949
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
6950
    $result = Database::query($sql);
6951
    if (Database::num_rows($result) > 0) {
6952
        $id = Database::result($result, 0, 0);
6953
        if ($id === false) {
6954
            return -1;
6955
        }
6956
6957
        return (int) $id;
6958
    }
6959
6960
    $id = 1;
6961
6962
    //if the url in WEB_PATH was not found, it can only mean that there is
6963
    // either a configuration problem or the first URL has not been defined yet
6964
    // (by default it is http://localhost/). Thus the more sensible thing we can
6965
    // do is return 1 (the main URL) as the user cannot hack this value anyway
6966
    return 1;
6967
}
6968
6969
/**
6970
 * Gets the registered urls from a given user id.
6971
 *
6972
 * @param int $user_id
6973
 * @param int $checkCourseId the course id to check url access
6974
 *
6975
 * @return array
6976
 *
6977
 * @author Julio Montoya <[email protected]>
6978
 */
6979
function api_get_access_url_from_user($user_id, $checkCourseId = null)
6980
{
6981
    $user_id = (int) $user_id;
6982
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
6983
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6984
    $includeIds = "";
6985
    if (isset($checkCourseId)) {
6986
        $cid = (int) $checkCourseId;
6987
        $tblUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
6988
        $sql = "SELECT access_url_id
6989
            FROM $tblUrlCourse url_rel_course
6990
            INNER JOIN $table_url u
6991
            ON (url_rel_course.access_url_id = u.id)
6992
            WHERE c_id = $cid";
6993
        $rs = Database::query($sql);
6994
        $courseUrlIds = [];
6995
        if (Database::num_rows($rs) > 0) {
6996
            while ($rowC = Database::fetch_array($rs, 'ASSOC')) {
6997
                $courseUrlIds[] = $rowC['access_url_id'];
6998
            }
6999
        }
7000
        if (!empty($courseUrlIds)) {
7001
            $includeIds = " AND access_url_id IN (".implode(',', $courseUrlIds).")";
7002
        }
7003
    }
7004
7005
    $sql = "SELECT access_url_id
7006
            FROM $table_url_rel_user url_rel_user
7007
            INNER JOIN $table_url u
7008
            ON (url_rel_user.access_url_id = u.id)
7009
            WHERE user_id = $user_id $includeIds
7010
            ORDER BY access_url_id";
7011
    $result = Database::query($sql);
7012
    $list = [];
7013
    while ($row = Database::fetch_array($result, 'ASSOC')) {
7014
        $list[] = $row['access_url_id'];
7015
    }
7016
7017
    return $list;
7018
}
7019
7020
/**
7021
 * Gets the status of a user in a course.
7022
 *
7023
 * @param int $user_id
7024
 * @param int $courseId
7025
 *
7026
 * @return int user status
7027
 */
7028
function api_get_status_of_user_in_course($user_id, $courseId)
7029
{
7030
    $tbl_rel_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
7031
    if (!empty($user_id) && !empty($courseId)) {
7032
        $user_id = intval($user_id);
7033
        $courseId = intval($courseId);
7034
        $sql = 'SELECT status
7035
                FROM '.$tbl_rel_course_user.'
7036
                WHERE user_id='.$user_id.' AND c_id = '.$courseId;
7037
        $result = Database::query($sql);
7038
        $row_status = Database::fetch_array($result, 'ASSOC');
7039
7040
        return $row_status['status'];
7041
    } else {
7042
        return 0;
7043
    }
7044
}
7045
7046
/**
7047
 * Checks whether the curent user is in a group or not.
7048
 *
7049
 * @param string        The group id - optional (takes it from session if not given)
7050
 * @param string        The course code - optional (no additional check by course if course code is not given)
7051
 *
7052
 * @return bool
7053
 *
7054
 * @author Ivan Tcholakov
7055
 */
7056
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
7057
{
7058
    if (!empty($courseCodeParam)) {
7059
        $courseCode = api_get_course_id();
7060
        if (!empty($courseCode)) {
7061
            if ($courseCodeParam != $courseCode) {
7062
                return false;
7063
            }
7064
        } else {
7065
            return false;
7066
        }
7067
    }
7068
7069
    $groupId = api_get_group_id();
7070
7071
    if (!empty($groupId)) {
7072
        if (!empty($groupIdParam)) {
7073
            return $groupIdParam == $groupId;
7074
        } else {
7075
            return true;
7076
        }
7077
    }
7078
7079
    return false;
7080
}
7081
7082
/**
7083
 * Checks whether a secret key is valid.
7084
 *
7085
 * @param string $original_key_secret - secret key from (webservice) client
7086
 * @param string $security_key        - security key from Chamilo
7087
 *
7088
 * @return bool - true if secret key is valid, false otherwise
7089
 */
7090
function api_is_valid_secret_key($original_key_secret, $security_key)
7091
{
7092
    if (empty($original_key_secret) || empty($security_key)) {
7093
        return false;
7094
    }
7095
7096
    return (string) $original_key_secret === sha1($security_key);
7097
}
7098
7099
/**
7100
 * Checks whether a user is into course.
7101
 *
7102
 * @param int $course_id - the course id
7103
 * @param int $user_id   - the user id
7104
 *
7105
 * @return bool
7106
 */
7107
function api_is_user_of_course($course_id, $user_id)
7108
{
7109
    $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
7110
    $sql = 'SELECT user_id FROM '.$tbl_course_rel_user.'
7111
            WHERE
7112
                c_id ="'.intval($course_id).'" AND
7113
                user_id = "'.intval($user_id).'" AND
7114
                relation_type <> '.COURSE_RELATION_TYPE_RRHH.' ';
7115
    $result = Database::query($sql);
7116
7117
    return Database::num_rows($result) == 1;
7118
}
7119
7120
/**
7121
 * Checks whether the server's operating system is Windows (TM).
7122
 *
7123
 * @return bool - true if the operating system is Windows, false otherwise
7124
 */
7125
function api_is_windows_os()
7126
{
7127
    if (function_exists('php_uname')) {
7128
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
7129
        // We expect that this function will always work for Chamilo 1.8.x.
7130
        $os = php_uname();
7131
    }
7132
    // The following methods are not needed, but let them stay, just in case.
7133
    elseif (isset($_ENV['OS'])) {
7134
        // Sometimes $_ENV['OS'] may not be present (bugs?)
7135
        $os = $_ENV['OS'];
7136
    } elseif (defined('PHP_OS')) {
7137
        // PHP_OS means on which OS PHP was compiled, this is why
7138
        // using PHP_OS is the last choice for detection.
7139
        $os = PHP_OS;
7140
    } else {
7141
        return false;
7142
    }
7143
7144
    return strtolower(substr((string) $os, 0, 3)) == 'win';
7145
}
7146
7147
/**
7148
 * This function informs whether the sent request is XMLHttpRequest.
7149
 */
7150
function api_is_xml_http_request()
7151
{
7152
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
7153
}
7154
7155
/**
7156
 * This wrapper function has been implemented for avoiding some known problems about the function getimagesize().
7157
 *
7158
 * @see http://php.net/manual/en/function.getimagesize.php
7159
 * @see http://www.dokeos.com/forum/viewtopic.php?t=12345
7160
 * @see http://www.dokeos.com/forum/viewtopic.php?t=16355
7161
 *
7162
 * @return int
7163
 */
7164
function api_getimagesize($path)
7165
{
7166
    $image = new Image($path);
7167
7168
    return $image->get_image_size();
7169
}
7170
7171
/**
7172
 * This function resizes an image, with preserving its proportions (or aspect ratio).
7173
 *
7174
 * @author Ivan Tcholakov, MAY-2009.
7175
 *
7176
 * @param int $image         System path or URL of the image
7177
 * @param int $target_width  Targeted width
7178
 * @param int $target_height Targeted height
7179
 *
7180
 * @return array Calculated new width and height
7181
 */
7182
function api_resize_image($image, $target_width, $target_height)
7183
{
7184
    $image_properties = api_getimagesize($image);
7185
7186
    return api_calculate_image_size(
7187
        $image_properties['width'],
7188
        $image_properties['height'],
7189
        $target_width,
7190
        $target_height
7191
    );
7192
}
7193
7194
/**
7195
 * This function calculates new image size, with preserving image's proportions (or aspect ratio).
7196
 *
7197
 * @author Ivan Tcholakov, MAY-2009.
7198
 * @author The initial idea has been taken from code by Patrick Cool, MAY-2004.
7199
 *
7200
 * @param int $image_width   Initial width
7201
 * @param int $image_height  Initial height
7202
 * @param int $target_width  Targeted width
7203
 * @param int $target_height Targeted height
7204
 *
7205
 * @return array Calculated new width and height
7206
 */
7207
function api_calculate_image_size(
7208
    $image_width,
7209
    $image_height,
7210
    $target_width,
7211
    $target_height
7212
) {
7213
    // Only maths is here.
7214
    $result = ['width' => $image_width, 'height' => $image_height];
7215
    if ($image_width <= 0 || $image_height <= 0) {
7216
        return $result;
7217
    }
7218
    $resize_factor_width = $target_width / $image_width;
7219
    $resize_factor_height = $target_height / $image_height;
7220
    $delta_width = $target_width - $image_width * $resize_factor_height;
7221
    $delta_height = $target_height - $image_height * $resize_factor_width;
7222
    if ($delta_width > $delta_height) {
7223
        $result['width'] = ceil($image_width * $resize_factor_height);
7224
        $result['height'] = ceil($image_height * $resize_factor_height);
7225
    } elseif ($delta_width < $delta_height) {
7226
        $result['width'] = ceil($image_width * $resize_factor_width);
7227
        $result['height'] = ceil($image_height * $resize_factor_width);
7228
    } else {
7229
        $result['width'] = ceil($target_width);
7230
        $result['height'] = ceil($target_height);
7231
    }
7232
7233
    return $result;
7234
}
7235
7236
/**
7237
 * Returns a list of Chamilo's tools or
7238
 * checks whether a given identificator is a valid Chamilo's tool.
7239
 *
7240
 * @author Isaac flores paz
7241
 *
7242
 * @param string The tool name to filter
7243
 *
7244
 * @return mixed Filtered string or array
7245
 */
7246
function api_get_tools_lists($my_tool = null)
7247
{
7248
    $tools_list = [
7249
        TOOL_DOCUMENT,
7250
        TOOL_THUMBNAIL,
7251
        TOOL_HOTPOTATOES,
7252
        TOOL_CALENDAR_EVENT,
7253
        TOOL_LINK,
7254
        TOOL_COURSE_DESCRIPTION,
7255
        TOOL_SEARCH,
7256
        TOOL_LEARNPATH,
7257
        TOOL_ANNOUNCEMENT,
7258
        TOOL_FORUM,
7259
        TOOL_THREAD,
7260
        TOOL_POST,
7261
        TOOL_DROPBOX,
7262
        TOOL_QUIZ,
7263
        TOOL_USER,
7264
        TOOL_GROUP,
7265
        TOOL_BLOGS,
7266
        TOOL_CHAT,
7267
        TOOL_STUDENTPUBLICATION,
7268
        TOOL_TRACKING,
7269
        TOOL_HOMEPAGE_LINK,
7270
        TOOL_COURSE_SETTING,
7271
        TOOL_BACKUP,
7272
        TOOL_COPY_COURSE_CONTENT,
7273
        TOOL_RECYCLE_COURSE,
7274
        TOOL_COURSE_HOMEPAGE,
7275
        TOOL_COURSE_RIGHTS_OVERVIEW,
7276
        TOOL_UPLOAD,
7277
        TOOL_COURSE_MAINTENANCE,
7278
        TOOL_SURVEY,
7279
        TOOL_WIKI,
7280
        TOOL_GLOSSARY,
7281
        TOOL_GRADEBOOK,
7282
        TOOL_NOTEBOOK,
7283
        TOOL_ATTENDANCE,
7284
        TOOL_COURSE_PROGRESS,
7285
    ];
7286
    if (empty($my_tool)) {
7287
        return $tools_list;
7288
    }
7289
7290
    return in_array($my_tool, $tools_list) ? $my_tool : '';
7291
}
7292
7293
/**
7294
 * Checks whether we already approved the last version term and condition.
7295
 *
7296
 * @param int user id
7297
 *
7298
 * @return bool true if we pass false otherwise
7299
 */
7300
function api_check_term_condition($userId)
7301
{
7302
    if (api_get_setting('allow_terms_conditions') === 'true') {
7303
        // Check if exists terms and conditions
7304
        if (LegalManager::count() == 0) {
7305
            return true;
7306
        }
7307
7308
        $extraFieldValue = new ExtraFieldValue('user');
7309
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
7310
            $userId,
7311
            'legal_accept'
7312
        );
7313
7314
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
7315
            $result = $data['value'];
7316
            $user_conditions = explode(':', $result);
7317
            $version = $user_conditions[0];
7318
            $langId = $user_conditions[1];
7319
            $realVersion = LegalManager::get_last_version($langId);
7320
7321
            return $version >= $realVersion;
7322
        }
7323
7324
        return false;
7325
    }
7326
7327
    return false;
7328
}
7329
7330
/**
7331
 * Gets all information of a tool into course.
7332
 *
7333
 * @param int The tool id
7334
 *
7335
 * @return array
7336
 */
7337
function api_get_tool_information_by_name($name)
7338
{
7339
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
7340
    $course_id = api_get_course_int_id();
7341
    $sql = "SELECT * FROM $t_tool
7342
            WHERE c_id = $course_id  AND name = '".Database::escape_string($name)."' ";
7343
    $rs = Database::query($sql);
7344
7345
    return Database::fetch_array($rs, 'ASSOC');
7346
}
7347
7348
/**
7349
 * Function used to protect a "global" admin script.
7350
 * The function blocks access when the user has no global platform admin rights.
7351
 * Global admins are the admins that are registered in the main.admin table
7352
 * AND the users who have access to the "principal" portal.
7353
 * That means that there is a record in the main.access_url_rel_user table
7354
 * with his user id and the access_url_id=1.
7355
 *
7356
 * @author Julio Montoya
7357
 *
7358
 * @param int $user_id
7359
 *
7360
 * @return bool
7361
 */
7362
function api_is_global_platform_admin($user_id = null)
7363
{
7364
    $user_id = (int) $user_id;
7365
    if (empty($user_id)) {
7366
        $user_id = api_get_user_id();
7367
    }
7368
    if (api_is_platform_admin_by_id($user_id)) {
7369
        $urlList = api_get_access_url_from_user($user_id);
7370
        // The admin is registered in the first "main" site with access_url_id = 1
7371
        if (in_array(1, $urlList)) {
7372
            return true;
7373
        } else {
7374
            return false;
7375
        }
7376
    }
7377
7378
    return false;
7379
}
7380
7381
/**
7382
 * @param int  $admin_id_to_check
7383
 * @param int  $my_user_id
7384
 * @param bool $allow_session_admin
7385
 *
7386
 * @return bool
7387
 */
7388
function api_global_admin_can_edit_admin(
7389
    $admin_id_to_check,
7390
    $my_user_id = null,
7391
    $allow_session_admin = false
7392
) {
7393
    if (empty($my_user_id)) {
7394
        $my_user_id = api_get_user_id();
7395
    }
7396
7397
    $iam_a_global_admin = api_is_global_platform_admin($my_user_id);
7398
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
7399
7400
    if ($iam_a_global_admin) {
7401
        // Global admin can edit everything
7402
        return true;
7403
    } else {
7404
        // If i'm a simple admin
7405
        $is_platform_admin = api_is_platform_admin_by_id($my_user_id);
7406
7407
        if ($allow_session_admin) {
7408
            $is_platform_admin = api_is_platform_admin_by_id($my_user_id) || (api_get_user_status($my_user_id) == SESSIONADMIN);
7409
        }
7410
7411
        if ($is_platform_admin) {
7412
            if ($user_is_global_admin) {
7413
                return false;
7414
            } else {
7415
                return true;
7416
            }
7417
        } else {
7418
            return false;
7419
        }
7420
    }
7421
}
7422
7423
/**
7424
 * @param int  $admin_id_to_check
7425
 * @param int  $my_user_id
7426
 * @param bool $allow_session_admin
7427
 *
7428
 * @return bool|null
7429
 */
7430
function api_protect_super_admin($admin_id_to_check, $my_user_id = null, $allow_session_admin = false)
7431
{
7432
    if (api_global_admin_can_edit_admin($admin_id_to_check, $my_user_id, $allow_session_admin)) {
7433
        return true;
7434
    } else {
7435
        api_not_allowed();
7436
    }
7437
}
7438
7439
/**
7440
 * Function used to protect a global admin script.
7441
 * The function blocks access when the user has no global platform admin rights.
7442
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
7443
 *
7444
 * @author Julio Montoya
7445
 */
7446
function api_protect_global_admin_script()
7447
{
7448
    if (!api_is_global_platform_admin()) {
7449
        api_not_allowed();
7450
7451
        return false;
7452
    }
7453
7454
    return true;
7455
}
7456
7457
/**
7458
 * Get active template.
7459
 *
7460
 * @param string    theme type (optional: default)
7461
 * @param string    path absolute(abs) or relative(rel) (optional:rel)
7462
 *
7463
 * @return string actived template path
7464
 */
7465
function api_get_template($path_type = 'rel')
7466
{
7467
    $path_types = ['rel', 'abs'];
7468
    $template_path = '';
7469
    if (in_array($path_type, $path_types)) {
7470
        if ($path_type == 'rel') {
7471
            $template_path = api_get_path(SYS_TEMPLATE_PATH);
7472
        } else {
7473
            $template_path = api_get_path(WEB_TEMPLATE_PATH);
7474
        }
7475
    }
7476
    $actived_theme = 'default';
7477
    if (api_get_setting('active_template')) {
7478
        $actived_theme = api_get_setting('active_template');
7479
    }
7480
    $actived_theme_path = $template_path.$actived_theme.DIRECTORY_SEPARATOR;
7481
7482
    return $actived_theme_path;
7483
}
7484
7485
/**
7486
 * Check browser support for specific file types or features
7487
 * This function checks if the user's browser supports a file format or given
7488
 * feature, or returns the current browser and major version when
7489
 * $format=check_browser. Only a limited number of formats and features are
7490
 * checked by this method. Make sure you check its definition first.
7491
 *
7492
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
7493
 *
7494
 * @return bool or return text array if $format=check_browser
7495
 *
7496
 * @author Juan Carlos Raña Trabado
7497
 */
7498
function api_browser_support($format = '')
7499
{
7500
    $browser = new Browser();
7501
    $current_browser = $browser->getBrowser();
7502
    $a_versiontemp = explode('.', $browser->getVersion());
7503
    $current_majorver = $a_versiontemp[0];
7504
7505
    static $result;
7506
7507
    if (isset($result[$format])) {
7508
        return $result[$format];
7509
    }
7510
7511
    // Native svg support
7512
    if ($format == 'svg') {
7513
        if (($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7514
            ($current_browser == 'Firefox' && $current_majorver > 1) ||
7515
            ($current_browser == 'Safari' && $current_majorver >= 4) ||
7516
            ($current_browser == 'Chrome' && $current_majorver >= 1) ||
7517
            ($current_browser == 'Opera' && $current_majorver >= 9)
7518
        ) {
7519
            $result[$format] = true;
7520
7521
            return true;
7522
        } else {
7523
            $result[$format] = false;
7524
7525
            return false;
7526
        }
7527
    } elseif ($format == 'pdf') {
7528
        // native pdf support
7529
        if (($current_browser == 'Chrome' && $current_majorver >= 6) ||
7530
            ('Firefox' === $current_browser && $current_majorver >= 15)
7531
        ) {
7532
            $result[$format] = true;
7533
7534
            return true;
7535
        } else {
7536
            $result[$format] = false;
7537
7538
            return false;
7539
        }
7540
    } elseif ($format == 'tif' || $format == 'tiff') {
7541
        //native tif support
7542
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7543
            $result[$format] = true;
7544
7545
            return true;
7546
        } else {
7547
            $result[$format] = false;
7548
7549
            return false;
7550
        }
7551
    } elseif ($format == 'ogg' || $format == 'ogx' || $format == 'ogv' || $format == 'oga') {
7552
        //native ogg, ogv,oga support
7553
        if (($current_browser == 'Firefox' && $current_majorver >= 3) ||
7554
            ($current_browser == 'Chrome' && $current_majorver >= 3) ||
7555
            ($current_browser == 'Opera' && $current_majorver >= 9)) {
7556
            $result[$format] = true;
7557
7558
            return true;
7559
        } else {
7560
            $result[$format] = false;
7561
7562
            return false;
7563
        }
7564
    } elseif ($format == 'mpg' || $format == 'mpeg') {
7565
        //native mpg support
7566
        if (($current_browser == 'Safari' && $current_majorver >= 5)) {
7567
            $result[$format] = true;
7568
7569
            return true;
7570
        } else {
7571
            $result[$format] = false;
7572
7573
            return false;
7574
        }
7575
    } elseif ($format == 'mp4') {
7576
        //native mp4 support (TODO: Android, iPhone)
7577
        if ($current_browser == 'Android' || $current_browser == 'iPhone') {
7578
            $result[$format] = true;
7579
7580
            return true;
7581
        } else {
7582
            $result[$format] = false;
7583
7584
            return false;
7585
        }
7586
    } elseif ($format == 'mov') {
7587
        //native mov support( TODO:check iPhone)
7588
        if ($current_browser == 'Safari' && $current_majorver >= 5 || $current_browser == 'iPhone') {
7589
            $result[$format] = true;
7590
7591
            return true;
7592
        } else {
7593
            $result[$format] = false;
7594
7595
            return false;
7596
        }
7597
    } elseif ($format == 'avi') {
7598
        //native avi support
7599
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7600
            $result[$format] = true;
7601
7602
            return true;
7603
        } else {
7604
            $result[$format] = false;
7605
7606
            return false;
7607
        }
7608
    } elseif ($format == 'wmv') {
7609
        //native wmv support
7610
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7611
            $result[$format] = true;
7612
7613
            return true;
7614
        } else {
7615
            $result[$format] = false;
7616
7617
            return false;
7618
        }
7619
    } elseif ($format == 'webm') {
7620
        //native webm support (TODO:check IE9, Chrome9, Android)
7621
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7622
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7623
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7624
            ($current_browser == 'Chrome' && $current_majorver >= 9) ||
7625
            $current_browser == 'Android'
7626
        ) {
7627
            $result[$format] = true;
7628
7629
            return true;
7630
        } else {
7631
            $result[$format] = false;
7632
7633
            return false;
7634
        }
7635
    } elseif ($format == 'wav') {
7636
        //native wav support (only some codecs !)
7637
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7638
            ($current_browser == 'Safari' && $current_majorver >= 5) ||
7639
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7640
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7641
            ($current_browser == 'Chrome' && $current_majorver > 9) ||
7642
            $current_browser == 'Android' ||
7643
            $current_browser == 'iPhone'
7644
        ) {
7645
            $result[$format] = true;
7646
7647
            return true;
7648
        } else {
7649
            $result[$format] = false;
7650
7651
            return false;
7652
        }
7653
    } elseif ($format == 'mid' || $format == 'kar') {
7654
        //native midi support (TODO:check Android)
7655
        if ($current_browser == 'Opera' && $current_majorver >= 9 || $current_browser == 'Android') {
7656
            $result[$format] = true;
7657
7658
            return true;
7659
        } else {
7660
            $result[$format] = false;
7661
7662
            return false;
7663
        }
7664
    } elseif ($format == 'wma') {
7665
        //native wma support
7666
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7667
            $result[$format] = true;
7668
7669
            return true;
7670
        } else {
7671
            $result[$format] = false;
7672
7673
            return false;
7674
        }
7675
    } elseif ($format == 'au') {
7676
        //native au support
7677
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7678
            $result[$format] = true;
7679
7680
            return true;
7681
        } else {
7682
            $result[$format] = false;
7683
7684
            return false;
7685
        }
7686
    } elseif ($format == 'mp3') {
7687
        //native mp3 support (TODO:check Android, iPhone)
7688
        if (($current_browser == 'Safari' && $current_majorver >= 5) ||
7689
            ($current_browser == 'Chrome' && $current_majorver >= 6) ||
7690
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7691
            $current_browser == 'Android' ||
7692
            $current_browser == 'iPhone' ||
7693
            $current_browser == 'Firefox'
7694
        ) {
7695
            $result[$format] = true;
7696
7697
            return true;
7698
        } else {
7699
            $result[$format] = false;
7700
7701
            return false;
7702
        }
7703
    } elseif ($format == 'autocapitalize') {
7704
        // Help avoiding showing the autocapitalize option if the browser doesn't
7705
        // support it: this attribute is against the HTML5 standard
7706
        if ($current_browser == 'Safari' || $current_browser == 'iPhone') {
7707
            return true;
7708
        } else {
7709
            return false;
7710
        }
7711
    } elseif ($format == "check_browser") {
7712
        $array_check_browser = [$current_browser, $current_majorver];
7713
7714
        return $array_check_browser;
7715
    } else {
7716
        $result[$format] = false;
7717
7718
        return false;
7719
    }
7720
}
7721
7722
/**
7723
 * This function checks if exist path and file browscap.ini
7724
 * In order for this to work, your browscap configuration setting in php.ini
7725
 * must point to the correct location of the browscap.ini file on your system
7726
 * http://php.net/manual/en/function.get-browser.php.
7727
 *
7728
 * @return bool
7729
 *
7730
 * @author Juan Carlos Raña Trabado
7731
 */
7732
function api_check_browscap()
7733
{
7734
    $setting = ini_get('browscap');
7735
    if ($setting) {
7736
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
7737
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
7738
            return true;
7739
        }
7740
    }
7741
7742
    return false;
7743
}
7744
7745
/**
7746
 * Returns the <script> HTML tag.
7747
 */
7748
function api_get_js($file)
7749
{
7750
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
7751
}
7752
7753
/**
7754
 * Returns the <script> HTML tag.
7755
 *
7756
 * @return string
7757
 */
7758
function api_get_asset($file)
7759
{
7760
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
7761
}
7762
7763
/**
7764
 * Returns the <script> HTML tag.
7765
 *
7766
 * @param string $file
7767
 * @param string $media
7768
 *
7769
 * @return string
7770
 */
7771
function api_get_css_asset($file, $media = 'screen')
7772
{
7773
    return '<link href="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7774
}
7775
7776
/**
7777
 * Returns the <link> HTML tag.
7778
 *
7779
 * @param string $file
7780
 * @param string $media
7781
 */
7782
function api_get_css($file, $media = 'screen')
7783
{
7784
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7785
}
7786
7787
/**
7788
 * Returns the js header to include the jquery library.
7789
 */
7790
function api_get_jquery_js()
7791
{
7792
    return api_get_asset('jquery/dist/jquery.min.js');
7793
}
7794
7795
/**
7796
 * Returns the jquery path.
7797
 *
7798
 * @return string
7799
 */
7800
function api_get_jquery_web_path()
7801
{
7802
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/dist/jquery.min.js';
7803
}
7804
7805
/**
7806
 * @return string
7807
 */
7808
function api_get_jquery_ui_js_web_path()
7809
{
7810
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
7811
}
7812
7813
/**
7814
 * @return string
7815
 */
7816
function api_get_jquery_ui_css_web_path()
7817
{
7818
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
7819
}
7820
7821
/**
7822
 * Returns the jquery-ui library js headers.
7823
 *
7824
 * @param   bool    add the jqgrid library
7825
 *
7826
 * @return string html tags
7827
 */
7828
function api_get_jquery_ui_js($include_jqgrid = false)
7829
{
7830
    $libraries = [];
7831
    if ($include_jqgrid) {
7832
        $libraries[] = 'jqgrid';
7833
    }
7834
7835
    return api_get_jquery_libraries_js($libraries);
7836
}
7837
7838
function api_get_jqgrid_js()
7839
{
7840
    return api_get_jquery_libraries_js(['jqgrid']);
7841
}
7842
7843
/**
7844
 * Returns the jquery library js and css headers.
7845
 *
7846
 * @param   array   list of jquery libraries supported jquery-ui, jqgrid
7847
 * @param   bool    add the jquery library
7848
 *
7849
 * @return string html tags
7850
 */
7851
function api_get_jquery_libraries_js($libraries)
7852
{
7853
    $js = '';
7854
    $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
7855
7856
    //jqgrid js and css
7857
    if (in_array('jqgrid', $libraries)) {
7858
        $languaje = 'en';
7859
        $platform_isocode = strtolower(api_get_language_isocode());
7860
7861
        //languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7862
        $jqgrid_langs = [
7863
            'bg', 'bg1251', 'cat', 'cn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fr', 'gl', 'he', 'hu', 'is', 'it', 'ja', 'nl', 'no', 'pl', 'pt-br', 'pt', 'ro', 'ru', 'sk', 'sr', 'sv', 'tr', 'ua',
7864
        ];
7865
7866
        if (in_array($platform_isocode, $jqgrid_langs)) {
7867
            $languaje = $platform_isocode;
7868
        }
7869
        //$js .= '<link rel="stylesheet" href="'.$js_path.'jqgrid/css/ui.jqgrid.css" type="text/css">';
7870
        $js .= api_get_css($js_path.'jqgrid/css/ui.jqgrid.css');
7871
        $js .= api_get_js('jqgrid/js/i18n/grid.locale-'.$languaje.'.js');
7872
        $js .= api_get_js('jqgrid/js/jquery.jqGrid.min.js');
7873
    }
7874
7875
    //Document multiple upload funcionality
7876
    if (in_array('jquery-upload', $libraries)) {
7877
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
7878
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
7879
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
7880
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
7881
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
7882
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
7883
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
7884
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
7885
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
7886
7887
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
7888
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
7889
    }
7890
7891
    // jquery datepicker
7892
    if (in_array('datepicker', $libraries)) {
7893
        $languaje = 'en-GB';
7894
        $platform_isocode = strtolower(api_get_language_isocode());
7895
7896
        // languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7897
        $datapicker_langs = [
7898
            'af', 'ar', 'ar-DZ', 'az', 'bg', 'bs', 'ca', 'cs', 'cy-GB', 'da', 'de', 'el', 'en-AU', 'en-GB', 'en-NZ', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fo', 'fr', 'fr-CH', 'gl', 'he', 'hi', 'hr', 'hu', 'hy', 'id', 'is', 'it', 'ja', 'ka', 'kk', 'km', 'ko', 'lb', 'lt', 'lv', 'mk', 'ml', 'ms', 'nl', 'nl-BE', 'no', 'pl', 'pt', 'pt-BR', 'rm', 'ro', 'ru', 'sk', 'sl', 'sq', 'sr', 'sr-SR', 'sv', 'ta', 'th', 'tj', 'tr', 'uk', 'vi', 'zh-CN', 'zh-HK', 'zh-TW',
7899
        ];
7900
        if (in_array($platform_isocode, $datapicker_langs)) {
7901
            $languaje = $platform_isocode;
7902
        }
7903
7904
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
7905
        $script = '<script>
7906
        $(function(){
7907
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
7908
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
7909
        });
7910
        </script>
7911
        ';
7912
        $js .= $script;
7913
    }
7914
7915
    return $js;
7916
}
7917
7918
/**
7919
 * Returns the URL to the course or session, removing the complexity of the URL
7920
 * building piece by piece.
7921
 *
7922
 * This function relies on api_get_course_info()
7923
 *
7924
 * @param string $courseCode The course code - optional (takes it from context if not given)
7925
 * @param int    $sessionId  The session ID  - optional (takes it from context if not given)
7926
 * @param int    $groupId    The group ID - optional (takes it from context if not given)
7927
 *
7928
 * @return string The URL to a course, a session, or empty string if nothing works e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
7929
 *
7930
 * @author  Julio Montoya <[email protected]>
7931
 */
7932
function api_get_course_url($courseCode = null, $sessionId = null, $groupId = null)
7933
{
7934
    $courseDirectory = '';
7935
    $url = '';
7936
    // If courseCode not set, get context or []
7937
    if (empty($courseCode)) {
7938
        $courseInfo = api_get_course_info();
7939
    } else {
7940
        $courseInfo = api_get_course_info($courseCode);
7941
    }
7942
7943
    // If course defined, get directory, otherwise keep empty string
7944
    if (!empty($courseInfo['directory'])) {
7945
        $courseDirectory = $courseInfo['directory'];
7946
    }
7947
7948
    // If sessionId not set, get context or 0
7949
    if (empty($sessionId)) {
7950
        $sessionId = api_get_session_id();
7951
    }
7952
7953
    // If groupId not set, get context or 0
7954
    if (empty($groupId)) {
7955
        $groupId = api_get_group_id();
7956
    }
7957
7958
    // Build the URL
7959
    if (!empty($courseDirectory)) {
7960
        // directory not empty, so we do have a course
7961
        $url = api_get_path(WEB_COURSE_PATH).$courseDirectory.'/index.php?id_session='.$sessionId.'&gidReq='.$groupId;
7962
    } elseif (!empty($sessionId) && api_get_configuration_value('remove_session_url') !== true) {
7963
        // if the course was unset and the session was set, send directly to the session
7964
        $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
7965
    }
7966
    // if not valid combination was found, return an empty string
7967
    return $url;
7968
}
7969
7970
/**
7971
 * Check if the current portal has the $_configuration['multiple_access_urls'] parameter on.
7972
 *
7973
 * @return bool true if multi site is enabled
7974
 */
7975
function api_get_multiple_access_url()
7976
{
7977
    global $_configuration;
7978
    if (isset($_configuration['multiple_access_urls']) && $_configuration['multiple_access_urls']) {
7979
        return true;
7980
    }
7981
7982
    return false;
7983
}
7984
7985
/**
7986
 * Just a synonym for api_get_multiple_access_url().
7987
 *
7988
 * @return bool
7989
 */
7990
function api_is_multiple_url_enabled()
7991
{
7992
    return api_get_multiple_access_url();
7993
}
7994
7995
/**
7996
 * Returns a md5 unique id.
7997
 *
7998
 * @todo add more parameters
7999
 */
8000
function api_get_unique_id()
8001
{
8002
    $id = md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
8003
8004
    return $id;
8005
}
8006
8007
/**
8008
 * Get home path.
8009
 *
8010
 * @return string
8011
 */
8012
function api_get_home_path()
8013
{
8014
    // FIX : Start the routing determination from central path definition
8015
    $home = api_get_path(SYS_HOME_PATH);
8016
    if (api_get_multiple_access_url()) {
8017
        $access_url_id = api_get_current_access_url_id();
8018
        $url_info = api_get_access_url($access_url_id);
8019
        $url = api_remove_trailing_slash(preg_replace('/https?:\/\//i', '', $url_info['url']));
8020
        $clean_url = api_replace_dangerous_char($url);
8021
        $clean_url = str_replace('/', '-', $clean_url);
8022
        $clean_url .= '/';
8023
        if ($clean_url != 'localhost/') {
8024
            // means that the multiple URL was not well configured we don't rename the $home variable
8025
            return "{$home}{$clean_url}";
8026
        }
8027
    }
8028
8029
    return $home;
8030
}
8031
8032
/**
8033
 * @param int Course id
8034
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
8035
 * @param int the item id (tool id, exercise id, lp id)
8036
 *
8037
 * @return bool
8038
 */
8039
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
8040
{
8041
    if (api_is_platform_admin()) {
8042
        return false;
8043
    }
8044
    if (api_get_setting('gradebook_locking_enabled') == 'true') {
8045
        if (empty($course_code)) {
8046
            $course_code = api_get_course_id();
8047
        }
8048
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
8049
        $item_id = intval($item_id);
8050
        $link_type = intval($link_type);
8051
        $course_code = Database::escape_string($course_code);
8052
        $sql = "SELECT locked FROM $table
8053
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
8054
        $result = Database::query($sql);
8055
        if (Database::num_rows($result)) {
8056
            return true;
8057
        }
8058
    }
8059
8060
    return false;
8061
}
8062
8063
/**
8064
 * Blocks a page if the item was added in a gradebook.
8065
 *
8066
 * @param int       exercise id, work id, thread id,
8067
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
8068
 * see gradebook/lib/be/linkfactory
8069
 * @param string    course code
8070
 *
8071
 * @return false|null
8072
 */
8073
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
8074
{
8075
    if (api_is_platform_admin()) {
8076
        return false;
8077
    }
8078
8079
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
8080
        $message = Display::return_message(get_lang('ResourceLockedByGradebook'), 'warning');
8081
        api_not_allowed(true, $message);
8082
    }
8083
}
8084
8085
/**
8086
 * Checks the PHP version installed is enough to run Chamilo.
8087
 *
8088
 * @param string Include path (used to load the error page)
8089
 */
8090
function api_check_php_version($my_inc_path = null)
8091
{
8092
    if (!function_exists('version_compare') || version_compare(phpversion(), REQUIRED_PHP_VERSION, '<')) {
8093
        $global_error_code = 1;
8094
        // Incorrect PHP version
8095
        $global_page = $my_inc_path.'global_error_message.inc.php';
8096
        if (file_exists($global_page)) {
8097
            require $global_page;
8098
        }
8099
        exit;
8100
    }
8101
}
8102
8103
/**
8104
 * Checks whether the Archive directory is present and writeable. If not,
8105
 * prints a warning message.
8106
 */
8107
function api_check_archive_dir()
8108
{
8109
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
8110
        $message = Display::return_message(get_lang('ArchivesDirectoryNotWriteableContactAdmin'), 'warning');
8111
        api_not_allowed(true, $message);
8112
    }
8113
}
8114
8115
/**
8116
 * Returns an array of global configuration settings which should be ignored
8117
 * when printing the configuration settings screens.
8118
 *
8119
 * @return array Array of strings, each identifying one of the excluded settings
8120
 */
8121
function api_get_locked_settings()
8122
{
8123
    return [
8124
        'server_type',
8125
        'permanently_remove_deleted_files',
8126
        'account_valid_duration',
8127
        'service_ppt2lp',
8128
        'wcag_anysurfer_public_pages',
8129
        'upload_extensions_list_type',
8130
        'upload_extensions_blacklist',
8131
        'upload_extensions_whitelist',
8132
        'upload_extensions_skip',
8133
        'upload_extensions_replace_by',
8134
        'hide_dltt_markup',
8135
        'split_users_upload_directory',
8136
        'permissions_for_new_directories',
8137
        'permissions_for_new_files',
8138
        'platform_charset',
8139
        'ldap_description',
8140
        'cas_activate',
8141
        'cas_server',
8142
        'cas_server_uri',
8143
        'cas_port',
8144
        'cas_protocol',
8145
        'cas_add_user_activate',
8146
        'update_user_info_cas_with_ldap',
8147
        'languagePriority1',
8148
        'languagePriority2',
8149
        'languagePriority3',
8150
        'languagePriority4',
8151
        'login_is_email',
8152
        'chamilo_database_version',
8153
    ];
8154
}
8155
8156
/**
8157
 * Checks if the user is corrently logged in. Returns the user ID if he is, or
8158
 * false if he isn't. If the user ID is given and is an integer, then the same
8159
 * ID is simply returned.
8160
 *
8161
 * @param  int User ID
8162
 *
8163
 * @return bool Integer User ID is logged in, or false otherwise
8164
 */
8165
function api_user_is_login($user_id = null)
8166
{
8167
    $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id;
8168
8169
    return $user_id && !api_is_anonymous();
8170
}
8171
8172
/**
8173
 * Guess the real ip for register in the database, even in reverse proxy cases.
8174
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
8175
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
8176
 * Note: the result of this function is not SQL-safe. Please escape it before
8177
 * inserting in a database.
8178
 *
8179
 * @return string the user's real ip (unsafe - escape it before inserting to db)
8180
 *
8181
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
8182
 *
8183
 * @version CEV CHANGE 24APR2012
8184
 */
8185
function api_get_real_ip()
8186
{
8187
    $ip = trim($_SERVER['REMOTE_ADDR']);
8188
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
8189
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
8190
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
8191
        } else {
8192
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
8193
        }
8194
        $ip = trim($ip1);
8195
    }
8196
8197
    return $ip;
8198
}
8199
8200
/**
8201
 * Checks whether an IP is included inside an IP range.
8202
 *
8203
 * @param string IP address
8204
 * @param string IP range
8205
 * @param string $ip
8206
 *
8207
 * @return bool True if IP is in the range, false otherwise
8208
 *
8209
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
8210
 * @author Yannick Warnier for improvements and managment of multiple ranges
8211
 *
8212
 * @todo check for IPv6 support
8213
 */
8214
function api_check_ip_in_range($ip, $range)
8215
{
8216
    if (empty($ip) or empty($range)) {
8217
        return false;
8218
    }
8219
    $ip_ip = ip2long($ip);
8220
    // divide range param into array of elements
8221
    if (strpos($range, ',') !== false) {
8222
        $ranges = explode(',', $range);
8223
    } else {
8224
        $ranges = [$range];
8225
    }
8226
    foreach ($ranges as $range) {
0 ignored issues
show
$range is overwriting one of the parameters of this function.
Loading history...
8227
        $range = trim($range);
8228
        if (empty($range)) {
8229
            continue;
8230
        }
8231
        if (strpos($range, '/') === false) {
8232
            if (strcmp($ip, $range) === 0) {
8233
                return true; // there is a direct IP match, return OK
8234
            }
8235
            continue; //otherwise, get to the next range
8236
        }
8237
        // the range contains a "/", so analyse completely
8238
        list($net, $mask) = explode("/", $range);
8239
8240
        $ip_net = ip2long($net);
8241
        // mask binary magic
8242
        $ip_mask = ~((1 << (32 - $mask)) - 1);
8243
8244
        $ip_ip_net = $ip_ip & $ip_mask;
8245
        if ($ip_ip_net == $ip_net) {
8246
            return true;
8247
        }
8248
    }
8249
8250
    return false;
8251
}
8252
8253
function api_check_user_access_to_legal($courseInfo)
8254
{
8255
    if (empty($courseInfo)) {
8256
        return false;
8257
    }
8258
8259
    $visibility = (int) $courseInfo['visibility'];
8260
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
8261
8262
    return
8263
        in_array($visibility, $visibilityList) ||
8264
        api_is_drh() ||
8265
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
8266
}
8267
8268
/**
8269
 * Checks if the global chat is enabled or not.
8270
 *
8271
 * @return bool
8272
 */
8273
function api_is_global_chat_enabled()
8274
{
8275
    return
8276
        !api_is_anonymous() &&
8277
        api_get_setting('allow_global_chat') === 'true' &&
8278
        api_get_setting('allow_social_tool') === 'true';
8279
}
8280
8281
/**
8282
 * @todo Fix tool_visible_by_default_at_creation labels
8283
 *
8284
 * @param int   $item_id
8285
 * @param int   $tool_id
8286
 * @param int   $group_id   id
8287
 * @param array $courseInfo
8288
 * @param int   $sessionId
8289
 * @param int   $userId
8290
 */
8291
function api_set_default_visibility(
8292
    $item_id,
8293
    $tool_id,
8294
    $group_id = 0,
8295
    $courseInfo = [],
8296
    $sessionId = 0,
8297
    $userId = 0
8298
) {
8299
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
8300
    $courseId = $courseInfo['real_id'];
8301
    $courseCode = $courseInfo['code'];
8302
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
8303
    $userId = empty($userId) ? api_get_user_id() : $userId;
8304
8305
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
8306
    if (is_null($group_id)) {
8307
        $group_id = 0;
8308
    } else {
8309
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
8310
    }
8311
8312
    $groupInfo = [];
8313
    if (!empty($group_id)) {
8314
        $groupInfo = GroupManager::get_group_properties($group_id);
8315
    }
8316
    $original_tool_id = $tool_id;
8317
8318
    switch ($tool_id) {
8319
        case TOOL_LINK:
8320
        case TOOL_LINK_CATEGORY:
8321
            $tool_id = 'links';
8322
            break;
8323
        case TOOL_DOCUMENT:
8324
            $tool_id = 'documents';
8325
            break;
8326
        case TOOL_LEARNPATH:
8327
            $tool_id = 'learning';
8328
            break;
8329
        case TOOL_ANNOUNCEMENT:
8330
            $tool_id = 'announcements';
8331
            break;
8332
        case TOOL_FORUM:
8333
        case TOOL_FORUM_CATEGORY:
8334
        case TOOL_FORUM_THREAD:
8335
            $tool_id = 'forums';
8336
            break;
8337
        case TOOL_QUIZ:
8338
            $tool_id = 'quiz';
8339
            break;
8340
    }
8341
    $setting = api_get_setting('tool_visible_by_default_at_creation');
8342
8343
    if (isset($setting[$tool_id])) {
8344
        $visibility = 'invisible';
8345
        if ($setting[$tool_id] == 'true') {
8346
            $visibility = 'visible';
8347
        }
8348
8349
        // Read the portal and course default visibility
8350
        if ($tool_id === 'documents') {
8351
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
8352
        }
8353
8354
        api_item_property_update(
8355
            $courseInfo,
8356
            $original_tool_id,
8357
            $item_id,
8358
            $visibility,
8359
            $userId,
8360
            $groupInfo,
8361
            null,
8362
            null,
8363
            null,
8364
            $sessionId
8365
        );
8366
8367
        // Fixes default visibility for tests
8368
        switch ($original_tool_id) {
8369
            case TOOL_QUIZ:
8370
                if (empty($sessionId)) {
8371
                    $objExerciseTmp = new Exercise($courseId);
8372
                    $objExerciseTmp->read($item_id);
8373
                    if ($visibility == 'visible') {
8374
                        $objExerciseTmp->enable();
8375
                        $objExerciseTmp->save();
8376
                    } else {
8377
                        $objExerciseTmp->disable();
8378
                        $objExerciseTmp->save();
8379
                    }
8380
                }
8381
                break;
8382
        }
8383
    }
8384
}
8385
8386
/**
8387
 * @return string
8388
 */
8389
function api_get_security_key()
8390
{
8391
    return api_get_configuration_value('security_key');
8392
}
8393
8394
/**
8395
 * @param int $user_id
8396
 * @param int $courseId
8397
 * @param int $session_id
8398
 *
8399
 * @return array
8400
 */
8401
function api_detect_user_roles($user_id, $courseId, $session_id = 0)
8402
{
8403
    $user_roles = [];
8404
    $courseInfo = api_get_course_info_by_id($courseId);
8405
    $course_code = $courseInfo['code'];
8406
8407
    $url_id = api_get_current_access_url_id();
8408
    if (api_is_platform_admin_by_id($user_id, $url_id)) {
8409
        $user_roles[] = PLATFORM_ADMIN;
8410
    }
8411
8412
    /*if (api_is_drh()) {
8413
        $user_roles[] = DRH;
8414
    }*/
8415
8416
    if (!empty($session_id)) {
8417
        if (SessionManager::user_is_general_coach($user_id, $session_id)) {
8418
            $user_roles[] = SESSION_GENERAL_COACH;
8419
        }
8420
    }
8421
8422
    if (!empty($course_code)) {
8423
        if (empty($session_id)) {
8424
            if (CourseManager::is_course_teacher($user_id, $course_code)) {
8425
                $user_roles[] = COURSEMANAGER;
8426
            }
8427
            if (CourseManager::get_tutor_in_course_status($user_id, $courseInfo['real_id'])) {
8428
                $user_roles[] = COURSE_TUTOR;
8429
            }
8430
8431
            if (CourseManager::is_user_subscribed_in_course($user_id, $course_code)) {
8432
                $user_roles[] = COURSE_STUDENT;
8433
            }
8434
        } else {
8435
            $user_status_in_session = SessionManager::get_user_status_in_course_session(
8436
                $user_id,
8437
                $courseId,
8438
                $session_id
8439
            );
8440
8441
            if (!empty($user_status_in_session)) {
8442
                if ($user_status_in_session == 0) {
8443
                    $user_roles[] = SESSION_STUDENT;
8444
                }
8445
                if ($user_status_in_session == 2) {
8446
                    $user_roles[] = SESSION_COURSE_COACH;
8447
                }
8448
            }
8449
8450
            /*if (api_is_course_session_coach($user_id, $course_code, $session_id)) {
8451
               $user_roles[] = SESSION_COURSE_COACH;
8452
            }*/
8453
        }
8454
    }
8455
8456
    return $user_roles;
8457
}
8458
8459
/**
8460
 * @param int $courseId
8461
 * @param int $session_id
8462
 *
8463
 * @return bool
8464
 */
8465
function api_coach_can_edit_view_results($courseId = null, $session_id = null)
8466
{
8467
    if (api_is_platform_admin()) {
8468
        return true;
8469
    }
8470
8471
    $user_id = api_get_user_id();
8472
8473
    if (empty($courseId)) {
8474
        $courseId = api_get_course_int_id();
8475
    }
8476
8477
    if (empty($session_id)) {
8478
        $session_id = api_get_session_id();
8479
    }
8480
8481
    $roles = api_detect_user_roles($user_id, $courseId, $session_id);
8482
8483
    if (in_array(SESSION_COURSE_COACH, $roles)) {
8484
        //return api_get_setting('session_tutor_reports_visibility') == 'true';
8485
        return true;
8486
    } else {
8487
        if (in_array(COURSEMANAGER, $roles)) {
8488
            return true;
8489
        }
8490
8491
        return false;
8492
    }
8493
}
8494
8495
/**
8496
 * @param string $file
8497
 *
8498
 * @return string
8499
 */
8500
function api_get_js_simple($file)
8501
{
8502
    return '<script src="'.$file.'"></script>'."\n";
8503
}
8504
8505
function api_set_settings_and_plugins()
8506
{
8507
    global $_configuration;
8508
    $_setting = [];
8509
    $_plugins = [];
8510
8511
    // access_url == 1 is the default chamilo location
8512
    $settings_by_access_list = [];
8513
    $access_url_id = api_get_current_access_url_id();
8514
    if ($access_url_id != 1) {
8515
        $url_info = api_get_access_url($_configuration['access_url']);
8516
        if ($url_info['active'] == 1) {
8517
            $settings_by_access = api_get_settings(null, 'list', $_configuration['access_url'], 1);
8518
            foreach ($settings_by_access as &$row) {
8519
                if (empty($row['variable'])) {
8520
                    $row['variable'] = 0;
8521
                }
8522
                if (empty($row['subkey'])) {
8523
                    $row['subkey'] = 0;
8524
                }
8525
                if (empty($row['category'])) {
8526
                    $row['category'] = 0;
8527
                }
8528
                $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']] = $row;
8529
            }
8530
        }
8531
    }
8532
8533
    $result = api_get_settings(null, 'list', 1);
8534
8535
    foreach ($result as &$row) {
8536
        if ($access_url_id != 1) {
8537
            if ($url_info['active'] == 1) {
8538
                $var = empty($row['variable']) ? 0 : $row['variable'];
8539
                $subkey = empty($row['subkey']) ? 0 : $row['subkey'];
8540
                $category = empty($row['category']) ? 0 : $row['category'];
8541
            }
8542
8543
            if ($row['access_url_changeable'] == 1 && $url_info['active'] == 1) {
8544
                if (isset($settings_by_access_list[$var]) &&
8545
                    $settings_by_access_list[$var][$subkey][$category]['selected_value'] != '') {
8546
                    if ($row['subkey'] == null) {
8547
                        $_setting[$row['variable']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8548
                    } else {
8549
                        $_setting[$row['variable']][$row['subkey']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8550
                    }
8551
                } else {
8552
                    if ($row['subkey'] == null) {
8553
                        $_setting[$row['variable']] = $row['selected_value'];
8554
                    } else {
8555
                        $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8556
                    }
8557
                }
8558
            } else {
8559
                if ($row['subkey'] == null) {
8560
                    $_setting[$row['variable']] = $row['selected_value'];
8561
                } else {
8562
                    $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8563
                }
8564
            }
8565
        } else {
8566
            if ($row['subkey'] == null) {
8567
                $_setting[$row['variable']] = $row['selected_value'];
8568
            } else {
8569
                $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8570
            }
8571
        }
8572
    }
8573
8574
    $result = api_get_settings('Plugins', 'list', $access_url_id);
8575
    $_plugins = [];
8576
    foreach ($result as &$row) {
8577
        $key = &$row['variable'];
8578
        if (is_string($_setting[$key])) {
8579
            $_setting[$key] = [];
8580
        }
8581
        $_setting[$key][] = $row['selected_value'];
8582
        $_plugins[$key][] = $row['selected_value'];
8583
    }
8584
8585
    $_SESSION['_setting'] = $_setting;
8586
    $_SESSION['_plugins'] = $_plugins;
8587
}
8588
8589
/**
8590
 * Modify default memory_limit and max_execution_time limits
8591
 * Needed when processing long tasks.
8592
 */
8593
function api_set_more_memory_and_time_limits()
8594
{
8595
    if (function_exists('ini_set')) {
8596
        api_set_memory_limit('2048M');
8597
        ini_set('max_execution_time', 3600);
8598
    }
8599
}
8600
8601
/**
8602
 * Tries to set memory limit, if authorized and new limit is higher than current.
8603
 *
8604
 * @param string $mem New memory limit
8605
 *
8606
 * @return bool True on success, false on failure or current is higher than suggested
8607
 * @assert (null) === false
8608
 * @assert (-1) === false
8609
 * @assert (0) === true
8610
 * @assert ('1G') === true
8611
 */
8612
function api_set_memory_limit($mem)
8613
{
8614
    //if ini_set() not available, this function is useless
8615
    if (!function_exists('ini_set') || is_null($mem) || $mem == -1) {
8616
        return false;
8617
    }
8618
8619
    $memory_limit = ini_get('memory_limit');
8620
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
8621
        ini_set('memory_limit', $mem);
8622
8623
        return true;
8624
    }
8625
8626
    return false;
8627
}
8628
8629
/**
8630
 * Gets memory limit in bytes.
8631
 *
8632
 * @param string The memory size (128M, 1G, 1000K, etc)
8633
 *
8634
 * @return int
8635
 * @assert (null) === false
8636
 * @assert ('1t')  === 1099511627776
8637
 * @assert ('1g')  === 1073741824
8638
 * @assert ('1m')  === 1048576
8639
 * @assert ('100k') === 102400
8640
 */
8641
function api_get_bytes_memory_limit($mem)
8642
{
8643
    $size = strtolower(substr($mem, -1));
8644
8645
    switch ($size) {
8646
        case 't':
8647
            $mem = intval(substr($mem, -1)) * 1024 * 1024 * 1024 * 1024;
8648
            break;
8649
        case 'g':
8650
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024 * 1024;
8651
            break;
8652
        case 'm':
8653
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024;
8654
            break;
8655
        case 'k':
8656
            $mem = intval(substr($mem, 0, -1)) * 1024;
8657
            break;
8658
        default:
8659
            // we assume it's integer only
8660
            $mem = intval($mem);
8661
            break;
8662
    }
8663
8664
    return $mem;
8665
}
8666
8667
/**
8668
 * Finds all the information about a user from username instead of user id.
8669
 *
8670
 * @param string $officialCode
8671
 *
8672
 * @return array $user_info user_id, lastname, firstname, username, email, ...
8673
 *
8674
 * @author Yannick Warnier <[email protected]>
8675
 */
8676
function api_get_user_info_from_official_code($officialCode)
8677
{
8678
    if (empty($officialCode)) {
8679
        return false;
8680
    }
8681
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
8682
            WHERE official_code ='".Database::escape_string($officialCode)."'";
8683
    $result = Database::query($sql);
8684
    if (Database::num_rows($result) > 0) {
8685
        $result_array = Database::fetch_array($result);
8686
8687
        return _api_format_user($result_array);
8688
    }
8689
8690
    return false;
8691
}
8692
8693
/**
8694
 * @param string $usernameInputId
8695
 * @param string $passwordInputId
8696
 *
8697
 * @return string|null
8698
 */
8699
function api_get_password_checker_js($usernameInputId, $passwordInputId)
8700
{
8701
    $checkPass = api_get_setting('allow_strength_pass_checker');
8702
    $useStrengthPassChecker = $checkPass === 'true';
8703
8704
    if ($useStrengthPassChecker === false) {
8705
        return null;
8706
    }
8707
8708
    $minRequirements = Security::getPasswordRequirements()['min'];
8709
8710
    $options = [
8711
        'rules' => [],
8712
    ];
8713
8714
    if ($minRequirements['length'] > 0) {
8715
        $options['rules'][] = [
8716
            'minChar' => $minRequirements['length'],
8717
            'pattern' => '.',
8718
            'helpText' => sprintf(
8719
                get_lang('NewPasswordRequirementMinXLength'),
8720
                $minRequirements['length']
8721
            ),
8722
        ];
8723
    }
8724
8725
    if ($minRequirements['lowercase'] > 0) {
8726
        $options['rules'][] = [
8727
            'minChar' => $minRequirements['lowercase'],
8728
            'pattern' => '[a-z]',
8729
            'helpText' => sprintf(
8730
                get_lang('NewPasswordRequirementMinXLowercase'),
8731
                $minRequirements['lowercase']
8732
            ),
8733
        ];
8734
    }
8735
8736
    if ($minRequirements['uppercase'] > 0) {
8737
        $options['rules'][] = [
8738
            'minChar' => $minRequirements['uppercase'],
8739
            'pattern' => '[A-Z]',
8740
            'helpText' => sprintf(
8741
                get_lang('NewPasswordRequirementMinXUppercase'),
8742
                $minRequirements['uppercase']
8743
            ),
8744
        ];
8745
    }
8746
8747
    if ($minRequirements['numeric'] > 0) {
8748
        $options['rules'][] = [
8749
            'minChar' => $minRequirements['numeric'],
8750
            'pattern' => '[0-9]',
8751
            'helpText' => sprintf(
8752
                get_lang('NewPasswordRequirementMinXNumeric'),
8753
                $minRequirements['numeric']
8754
            ),
8755
        ];
8756
    }
8757
8758
    if ($minRequirements['specials'] > 0) {
8759
        $options['rules'][] = [
8760
            'minChar' => $minRequirements['specials'],
8761
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
8762
            'helpText' => sprintf(
8763
                get_lang('NewPasswordRequirementMinXSpecials'),
8764
                $minRequirements['specials']
8765
            ),
8766
        ];
8767
    }
8768
8769
    $js = api_get_js('password-checker/password-checker.js');
8770
    $js .= "<script>
8771
    $(function() {
8772
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
8773
    });
8774
    </script>";
8775
8776
    return $js;
8777
}
8778
8779
/**
8780
 * create an user extra field called 'captcha_blocked_until_date'.
8781
 *
8782
 * @param string $username
8783
 *
8784
 * @return bool
8785
 */
8786
function api_block_account_captcha($username)
8787
{
8788
    $userInfo = api_get_user_info_from_username($username);
8789
    if (empty($userInfo)) {
8790
        return false;
8791
    }
8792
    $minutesToBlock = api_get_setting('captcha_time_to_block');
8793
    $time = time() + $minutesToBlock * 60;
8794
    UserManager::update_extra_field_value(
8795
        $userInfo['user_id'],
8796
        'captcha_blocked_until_date',
8797
        api_get_utc_datetime($time)
8798
    );
8799
8800
    return true;
8801
}
8802
8803
/**
8804
 * @param string $username
8805
 *
8806
 * @return bool
8807
 */
8808
function api_clean_account_captcha($username)
8809
{
8810
    $userInfo = api_get_user_info_from_username($username);
8811
    if (empty($userInfo)) {
8812
        return false;
8813
    }
8814
    Session::erase('loginFailedCount');
8815
    UserManager::update_extra_field_value(
8816
        $userInfo['user_id'],
8817
        'captcha_blocked_until_date',
8818
        null
8819
    );
8820
8821
    return true;
8822
}
8823
8824
/**
8825
 * @param string $username
8826
 *
8827
 * @return bool
8828
 */
8829
function api_get_user_blocked_by_captcha($username)
8830
{
8831
    $userInfo = api_get_user_info_from_username($username);
8832
    if (empty($userInfo)) {
8833
        return false;
8834
    }
8835
    $data = UserManager::get_extra_user_data_by_field(
8836
        $userInfo['user_id'],
8837
        'captcha_blocked_until_date'
8838
    );
8839
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
8840
        return $data['captcha_blocked_until_date'];
8841
    }
8842
8843
    return false;
8844
}
8845
8846
/**
8847
 * Remove tags from HTML anf return the $in_number_char first non-HTML char
8848
 * Postfix the text with "..." if it has been truncated.
8849
 *
8850
 * @param string $text
8851
 * @param int    $number
8852
 *
8853
 * @return string
8854
 *
8855
 * @author hubert borderiou
8856
 */
8857
function api_get_short_text_from_html($text, $number)
8858
{
8859
    // Delete script and style tags
8860
    $text = preg_replace('/(<(script|style)\b[^>]*>).*?(<\/\2>)/is', "$1$3", $text);
8861
    $text = api_html_entity_decode($text);
8862
    $out_res = api_remove_tags_with_space($text, false);
8863
    $postfix = "...";
8864
    if (strlen($out_res) > $number) {
8865
        $out_res = substr($out_res, 0, $number).$postfix;
8866
    }
8867
8868
    return $out_res;
8869
}
8870
8871
/**
8872
 * Replace tags with a space in a text.
8873
 * If $in_double_quote_replace, replace " with '' (for HTML attribute purpose, for exemple).
8874
 *
8875
 * @return string
8876
 *
8877
 * @author hubert borderiou
8878
 */
8879
function api_remove_tags_with_space($in_html, $in_double_quote_replace = true)
8880
{
8881
    $out_res = $in_html;
8882
    if ($in_double_quote_replace) {
8883
        $out_res = str_replace('"', "''", $out_res);
8884
    }
8885
    // avoid text stuck together when tags are removed, adding a space after >
8886
    $out_res = str_replace(">", "> ", $out_res);
8887
    $out_res = strip_tags($out_res);
8888
8889
    return $out_res;
8890
}
8891
8892
/**
8893
 * If true, the drh can access all content (courses, users) inside a session.
8894
 *
8895
 * @return bool
8896
 */
8897
function api_drh_can_access_all_session_content()
8898
{
8899
    return api_get_setting('drh_can_access_all_session_content') === 'true';
8900
}
8901
8902
/**
8903
 * @param string $tool
8904
 * @param string $setting
8905
 * @param int    $defaultValue
8906
 *
8907
 * @return string
8908
 */
8909
function api_get_default_tool_setting($tool, $setting, $defaultValue)
8910
{
8911
    global $_configuration;
8912
    if (isset($_configuration[$tool]) &&
8913
        isset($_configuration[$tool]['default_settings']) &&
8914
        isset($_configuration[$tool]['default_settings'][$setting])
8915
    ) {
8916
        return $_configuration[$tool]['default_settings'][$setting];
8917
    }
8918
8919
    return $defaultValue;
8920
}
8921
8922
/**
8923
 * Checks if user can login as another user.
8924
 *
8925
 * @param int $loginAsUserId the user id to log in
8926
 * @param int $userId        my user id
8927
 *
8928
 * @return bool
8929
 */
8930
function api_can_login_as($loginAsUserId, $userId = null)
8931
{
8932
    if (empty($userId)) {
8933
        $userId = api_get_user_id();
8934
    }
8935
    if ($loginAsUserId == $userId) {
8936
        return false;
8937
    }
8938
8939
    if (empty($loginAsUserId)) {
8940
        return false;
8941
    }
8942
8943
    if ($loginAsUserId != strval(intval($loginAsUserId))) {
8944
        return false;
8945
    }
8946
8947
    // Check if the user to login is an admin
8948
    if (api_is_platform_admin_by_id($loginAsUserId)) {
8949
        // Only super admins can login to admin accounts
8950
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
8951
            return false;
8952
        }
8953
    }
8954
8955
    $userInfo = api_get_user_info($loginAsUserId);
8956
    $isDrh = function () use ($loginAsUserId) {
8957
        if (api_is_drh()) {
8958
            if (api_drh_can_access_all_session_content()) {
8959
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
8960
                    'drh_all',
8961
                    api_get_user_id()
8962
                );
8963
                $userList = [];
8964
                if (is_array($users)) {
8965
                    foreach ($users as $user) {
8966
                        $userList[] = $user['user_id'];
8967
                    }
8968
                }
8969
                if (in_array($loginAsUserId, $userList)) {
8970
                    return true;
8971
                }
8972
            } else {
8973
                if (api_is_drh() &&
8974
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
8975
                ) {
8976
                    return true;
8977
                }
8978
            }
8979
        }
8980
8981
        return false;
8982
    };
8983
8984
    $loginAsStatusForSessionAdmins = [STUDENT];
8985
8986
    if (api_get_configuration_value('allow_session_admin_login_as_teacher')) {
8987
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
8988
    }
8989
8990
    return api_is_platform_admin() ||
8991
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
8992
        $isDrh();
8993
}
8994
8995
/**
8996
 * @return bool
8997
 */
8998
function api_is_allowed_in_course()
8999
{
9000
    if (api_is_platform_admin()) {
9001
        return true;
9002
    }
9003
9004
    return Session::read('is_allowed_in_course');
9005
}
9006
9007
function api_set_cookie($name, $value, $expires = 0)
9008
{
9009
    $expires = (int) $expires;
9010
    setcookie($name, $value, $expires, '', '', api_is_https(), true);
9011
}
9012
9013
/**
9014
 * Set the cookie to go directly to the course code $in_firstpage
9015
 * after login.
9016
 *
9017
 * @param string $value is the course code of the course to go
9018
 */
9019
function api_set_firstpage_parameter($value)
9020
{
9021
    api_set_cookie('GotoCourse', $value);
9022
}
9023
9024
/**
9025
 * Delete the cookie to go directly to the course code $in_firstpage
9026
 * after login.
9027
 */
9028
function api_delete_firstpage_parameter()
9029
{
9030
    api_set_cookie('GotoCourse', '', time() - 3600);
9031
}
9032
9033
/**
9034
 * @return bool if course_code for direct course access after login is set
9035
 */
9036
function exist_firstpage_parameter()
9037
{
9038
    return isset($_COOKIE['GotoCourse']) && $_COOKIE['GotoCourse'] != '';
9039
}
9040
9041
/**
9042
 * @return string return the course_code of the course where user login
9043
 */
9044
function api_get_firstpage_parameter()
9045
{
9046
    return $_COOKIE['GotoCourse'];
9047
}
9048
9049
/**
9050
 * Return true on https install.
9051
 *
9052
 * @return bool
9053
 */
9054
function api_is_https()
9055
{
9056
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
9057
        $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto'])
9058
    ) {
9059
        $isSecured = true;
9060
    } else {
9061
        if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
9062
            $isSecured = true;
9063
        } else {
9064
            $isSecured = false;
9065
            // last chance
9066
            if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
9067
                $isSecured = true;
9068
            }
9069
        }
9070
    }
9071
9072
    return $isSecured;
9073
}
9074
9075
/**
9076
 * Return protocol (http or https).
9077
 *
9078
 * @return string
9079
 */
9080
function api_get_protocol()
9081
{
9082
    return api_is_https() ? 'https' : 'http';
9083
}
9084
9085
/**
9086
 * Return a string where " are replaced with 2 '
9087
 * It is useful when you pass a PHP variable in a Javascript browser dialog
9088
 * e.g. : alert("<?php get_lang('Message') ?>");
9089
 * and message contains character ".
9090
 *
9091
 * @param string $in_text
9092
 *
9093
 * @return string
9094
 */
9095
function convert_double_quote_to_single($in_text)
9096
{
9097
    return api_preg_replace('/"/', "''", $in_text);
9098
}
9099
9100
/**
9101
 * Get origin.
9102
 *
9103
 * @param string
9104
 *
9105
 * @return string
9106
 */
9107
function api_get_origin()
9108
{
9109
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
9110
}
9111
9112
/**
9113
 * Warns an user that the portal reach certain limit.
9114
 *
9115
 * @param string $limitName
9116
 */
9117
function api_warn_hosting_contact($limitName)
9118
{
9119
    $hostingParams = api_get_configuration_value(1);
9120
    $email = null;
9121
9122
    if (!empty($hostingParams)) {
9123
        if (isset($hostingParams['hosting_contact_mail'])) {
9124
            $email = $hostingParams['hosting_contact_mail'];
9125
        }
9126
    }
9127
9128
    if (!empty($email)) {
9129
        $subject = get_lang('HostingWarningReached');
9130
        $body = get_lang('PortalName').': '.api_get_path(WEB_PATH)." \n ";
9131
        $body .= get_lang('PortalLimitType').': '.$limitName." \n ";
9132
        if (isset($hostingParams[$limitName])) {
9133
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
9134
        }
9135
        api_mail_html(null, $email, $subject, $body);
9136
    }
9137
}
9138
9139
/**
9140
 * Gets value of a variable from app/config/configuration.php
9141
 * Variables that are not set in the configuration.php file but set elsewhere:
9142
 * - virtual_css_theme_folder (vchamilo plugin)
9143
 * - access_url (global.inc.php)
9144
 * - apc/apc_prefix (global.inc.php).
9145
 *
9146
 * @param string $variable
9147
 *
9148
 * @return bool|mixed
9149
 */
9150
function api_get_configuration_value($variable)
9151
{
9152
    global $_configuration;
9153
    // Check the current url id, id = 1 by default
9154
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
9155
9156
    $variable = trim($variable);
9157
9158
    // Check if variable exists
9159
    if (isset($_configuration[$variable])) {
9160
        if (is_array($_configuration[$variable]) && api_is_multiple_url_enabled() && is_int(array_keys($_configuration[$variable])[0])) {
9161
            // It has been configured for at least one sub URL, so we will not return the complete variable
9162
            /*
9163
             * The idea is that if the first level key of the configuration variable is an int
9164
             * then it is a multiURL configuration and if it's a string then it's a configuration that is not multiURL.
9165
             * For example if in app/config/configuration.php you have set :
9166
             * $_configuration['ticket_project_user_roles'] = [
9167
             *     'permissions' => [
9168
             *         1 => [17] // project_id = 1, STUDENT_BOSS = 17
9169
             *     ]
9170
             * ];
9171
             * You do not want to enter this block even if multiURL is activated because the option is configured globally
9172
             * and you want to return the full array.
9173
             * The is_int is to consider only the option that are array and configured for multiURL
9174
             * which means there is an int as the first level key of the array.
9175
             */
9176
            // Check if it exists for the sub portal
9177
            if (array_key_exists($urlId, $_configuration[$variable])) {
9178
                return $_configuration[$variable][$urlId];
9179
            } else {
9180
                // Try to found element with id = 1 (master portal)
9181
                if (array_key_exists(1, $_configuration[$variable])) {
9182
                    return $_configuration[$variable][1];
9183
                } else {
9184
                    // The value was there for other URLs but not the main URL nor the current URL
9185
                    return null;
9186
                }
9187
            }
9188
        }
9189
9190
        return $_configuration[$variable];
9191
    }
9192
9193
    return false;
9194
}
9195
9196
/**
9197
 * Gets value of a variable from app/config/mail.conf.php.
9198
 *
9199
 * @param string $variable
9200
 *
9201
 * @return bool|mixed
9202
 */
9203
function api_get_mail_configuration_value($variable)
9204
{
9205
    global $_configuration;
9206
    global $platform_email;
9207
9208
    // Check the current url id, id = 1 by default
9209
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
9210
9211
    $variable = trim($variable);
9212
9213
    // Check if variable exists for the sub portal
9214
    if (api_is_multiple_url_enabled() && isset($platform_email[$urlId]) && isset($platform_email[$urlId][$variable])) {
9215
        return $platform_email[$urlId][$variable];
9216
    } elseif (isset($platform_email[1]) && isset($platform_email[1][$variable])) {
9217
        // Try to find element with id = 1 (master portal)
9218
        return $platform_email[1][$variable];
9219
    } elseif (isset($platform_email[$variable])) {
9220
        // If variable is not found for the sub portal or master portal, try to find the default element
9221
        return $platform_email[$variable];
9222
    }
9223
9224
    return false;
9225
}
9226
9227
/**
9228
 * Retreives and returns a value in a hierarchical configuration array
9229
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
9230
 *
9231
 * @param string $path      the successive array keys, separated by the separator
9232
 * @param mixed  $default   value to be returned if not found, null by default
9233
 * @param string $separator '/' by default
9234
 * @param array  $array     the active configuration array by default
9235
 *
9236
 * @return mixed the found value or $default
9237
 */
9238
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
9239
{
9240
    $pos = strpos($path, $separator);
9241
    if (false === $pos) {
9242
        if (is_null($array)) {
9243
            return api_get_configuration_value($path);
9244
        }
9245
        if (is_array($array) && array_key_exists($path, $array)) {
9246
            return $array[$path];
9247
        }
9248
9249
        return $default;
9250
    }
9251
    $key = substr($path, 0, $pos);
9252
    if (is_null($array)) {
9253
        $newArray = api_get_configuration_value($key);
9254
    } elseif (is_array($array) && array_key_exists($key, $array)) {
9255
        $newArray = $array[$key];
9256
    } else {
9257
        return $default;
9258
    }
9259
    if (is_array($newArray)) {
9260
        $newPath = substr($path, $pos + 1);
9261
9262
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
9263
    }
9264
9265
    return $default;
9266
}
9267
9268
/**
9269
 * Retrieves and returns a value in a hierarchical configuration array
9270
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
9271
 *
9272
 * @param array  $array     the recursive array that contains the value to be returned (or not)
9273
 * @param string $path      the successive array keys, separated by the separator
9274
 * @param mixed  $default   the value to be returned if not found
9275
 * @param string $separator the separator substring
9276
 *
9277
 * @return mixed the found value or $default
9278
 */
9279
function api_array_sub_value($array, $path, $default = null, $separator = '/')
9280
{
9281
    $pos = strpos($path, $separator);
9282
    if (false === $pos) {
9283
        if (is_array($array) && array_key_exists($path, $array)) {
9284
            return $array[$path];
9285
        }
9286
9287
        return $default;
9288
    }
9289
    $key = substr($path, 0, $pos);
9290
    if (is_array($array) && array_key_exists($key, $array)) {
9291
        $newArray = $array[$key];
9292
    } else {
9293
        return $default;
9294
    }
9295
    if (is_array($newArray)) {
9296
        $newPath = substr($path, $pos + 1);
9297
9298
        return api_array_sub_value($newArray, $newPath, $default);
9299
    }
9300
9301
    return $default;
9302
}
9303
9304
/**
9305
 * Returns supported image extensions in the portal.
9306
 *
9307
 * @param bool $supportVectors Whether vector images should also be accepted or not
9308
 *
9309
 * @return array Supported image extensions in the portal
9310
 */
9311
function api_get_supported_image_extensions($supportVectors = true)
9312
{
9313
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
9314
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
9315
    if ($supportVectors) {
9316
        array_push($supportedImageExtensions, 'svg');
9317
    }
9318
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
9319
        array_push($supportedImageExtensions, 'webp');
9320
    }
9321
9322
    return $supportedImageExtensions;
9323
}
9324
9325
/**
9326
 * This setting changes the registration status for the campus.
9327
 *
9328
 * @author Patrick Cool <[email protected]>, Ghent University
9329
 *
9330
 * @version August 2006
9331
 *
9332
 * @param bool $listCampus Whether we authorize
9333
 *
9334
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
9335
 */
9336
function api_register_campus($listCampus = true)
9337
{
9338
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
9339
9340
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
9341
    Database::query($sql);
9342
9343
    if (!$listCampus) {
9344
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
9345
        Database::query($sql);
9346
    }
9347
}
9348
9349
/**
9350
 * Checks whether current user is a student boss.
9351
 *
9352
 * @global array $_user
9353
 *
9354
 * @return bool
9355
 */
9356
function api_is_student_boss()
9357
{
9358
    $_user = api_get_user_info();
9359
9360
    return isset($_user['status']) && $_user['status'] == STUDENT_BOSS;
9361
}
9362
9363
/**
9364
 * Check whether the user type should be exclude.
9365
 * Such as invited or anonymous users.
9366
 *
9367
 * @param bool $checkDB Optional. Whether check the user status
9368
 * @param int  $userId  Options. The user id
9369
 *
9370
 * @return bool
9371
 */
9372
function api_is_excluded_user_type($checkDB = false, $userId = 0)
9373
{
9374
    if ($checkDB) {
9375
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
9376
9377
        if ($userId == 0) {
9378
            return true;
9379
        }
9380
9381
        $userInfo = api_get_user_info($userId);
9382
9383
        switch ($userInfo['status']) {
9384
            case INVITEE:
9385
            case ANONYMOUS:
9386
                return true;
9387
            default:
9388
                return false;
9389
        }
9390
    }
9391
9392
    $isInvited = api_is_invitee();
9393
    $isAnonymous = api_is_anonymous();
9394
9395
    if ($isInvited || $isAnonymous) {
9396
        return true;
9397
    }
9398
9399
    return false;
9400
}
9401
9402
/**
9403
 * Get the user status to ignore in reports.
9404
 *
9405
 * @param string $format Optional. The result type (array or string)
9406
 *
9407
 * @return array|string
9408
 */
9409
function api_get_users_status_ignored_in_reports($format = 'array')
9410
{
9411
    $excludedTypes = [
9412
        INVITEE,
9413
        ANONYMOUS,
9414
    ];
9415
9416
    if ($format == 'string') {
9417
        return implode(', ', $excludedTypes);
9418
    }
9419
9420
    return $excludedTypes;
9421
}
9422
9423
/**
9424
 * Set the Site Use Cookie Warning for 1 year.
9425
 */
9426
function api_set_site_use_cookie_warning_cookie()
9427
{
9428
    api_set_cookie('ChamiloUsesCookies', 'ok', time() + 31556926);
9429
}
9430
9431
/**
9432
 * Return true if the Site Use Cookie Warning Cookie warning exists.
9433
 *
9434
 * @return bool
9435
 */
9436
function api_site_use_cookie_warning_cookie_exist()
9437
{
9438
    return isset($_COOKIE['ChamiloUsesCookies']);
9439
}
9440
9441
/**
9442
 * Given a number of seconds, format the time to show hours, minutes and seconds.
9443
 *
9444
 * @param int    $time         The time in seconds
9445
 * @param string $originFormat Optional.
9446
 * PHP (used for scorm)
9447
 * JS (used in most cases and understood by excel)
9448
 * LANG (used to present unit in the user language)
9449
 *
9450
 * @return string (00h00'00")
9451
 */
9452
function api_format_time($time, $originFormat = 'php')
9453
{
9454
    $h = get_lang('h');
9455
    $hours = $time / 3600;
9456
    $mins = ($time % 3600) / 60;
9457
    $secs = ($time % 60);
9458
9459
    if ($time < 0) {
9460
        $hours = 0;
9461
        $mins = 0;
9462
        $secs = 0;
9463
    }
9464
9465
    if ($originFormat == 'js') {
9466
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
9467
    } elseif ($originFormat == 'lang') {
9468
        $formattedTime = trim(sprintf(get_lang('HoursMinutesSeconds'), $hours, $mins, $secs));
9469
    } else {
9470
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
9471
    }
9472
9473
    return $formattedTime;
9474
}
9475
9476
/**
9477
 * Create a new empty directory with index.html file.
9478
 *
9479
 * @param string $name            The new directory name
9480
 * @param string $parentDirectory Directory parent directory name
9481
 *
9482
 * @return bool Return true if the directory was create. Otherwise return false
9483
 */
9484
function api_create_protected_dir($name, $parentDirectory)
9485
{
9486
    $isCreated = false;
9487
9488
    if (!is_writable($parentDirectory)) {
9489
        return false;
9490
    }
9491
9492
    $fullPath = $parentDirectory.api_replace_dangerous_char($name);
9493
9494
    if (mkdir($fullPath, api_get_permissions_for_new_directories(), true)) {
9495
        $fp = fopen($fullPath.'/index.html', 'w');
9496
9497
        if ($fp) {
9498
            if (fwrite($fp, '<html><head><title></title></head><body></body></html>')) {
9499
                $isCreated = true;
9500
            }
9501
        }
9502
9503
        fclose($fp);
9504
    }
9505
9506
    return $isCreated;
9507
}
9508
9509
/**
9510
 * Sends an HTML email using the phpmailer class (and multipart/alternative to downgrade gracefully)
9511
 * Sender name and email can be specified, if not specified
9512
 * name and email of the platform admin are used.
9513
 *
9514
 * @author Bert Vanderkimpen ICT&O UGent
9515
 * @author Yannick Warnier <[email protected]>
9516
 *
9517
 * @param string    name of recipient
9518
 * @param string    email of recipient
9519
 * @param string    email subject
9520
 * @param string    email body
9521
 * @param string    sender name
9522
 * @param string    sender e-mail
9523
 * @param array  $extra_headers        in form $headers = array($name => $value) to allow parsing
9524
 * @param array  $data_file            (path and filename)
9525
 * @param bool   $embedded_image       True for attaching a embedded file inside content html (optional)
9526
 * @param array  $additionalParameters
9527
 * @param string $sendErrorTo          If there's an error while sending the email, $sendErrorTo will receive a notification
9528
 *
9529
 * @return int true if mail was sent
9530
 *
9531
 * @see             PHPMailer.php
9532
 */
9533
function api_mail_html(
9534
    $recipient_name,
9535
    $recipient_email,
9536
    $subject,
9537
    $message,
9538
    $senderName = '',
9539
    $senderEmail = '',
9540
    $extra_headers = [],
9541
    $data_file = [],
9542
    $embedded_image = false,
9543
    $additionalParameters = [],
9544
    $sendErrorTo = ''
9545
) {
9546
    if (true === api_get_configuration_value('disable_send_mail')) {
9547
        return true;
9548
    }
9549
9550
    $mail = new PHPMailer();
9551
9552
    if (!empty(api_get_mail_configuration_value('XOAUTH2_METHOD'))) {
9553
        $provider = new GenericProvider([
9554
            'clientId' => api_get_mail_configuration_value('XOAUTH2_CLIENT_ID'),
9555
            'clientSecret' => api_get_mail_configuration_value('XOAUTH2_CLIENT_SECRET'),
9556
            'urlAuthorize' => api_get_mail_configuration_value('XOAUTH2_URL_AUTHORIZE'),
9557
            'urlAccessToken' => api_get_mail_configuration_value('XOAUTH2_URL_ACCES_TOKEN'),
9558
            'urlResourceOwnerDetails' => api_get_mail_configuration_value('XOAUTH2_URL_RESOURCE_OWNER_DETAILS'),
9559
            'scopes' => api_get_mail_configuration_value('XOAUTH2_SCOPES'),
9560
        ]);
9561
        $mail->AuthType = 'XOAUTH2';
9562
        $mail->setOAuth(
9563
            new OAuth([
9564
                'provider' => $provider,
9565
                'clientId' => api_get_mail_configuration_value('XOAUTH2_CLIENT_ID'),
9566
                'clientSecret' => api_get_mail_configuration_value('XOAUTH2_CLIENT_SECRET'),
9567
                'refreshToken' => api_get_mail_configuration_value('XOAUTH2_REFRESH_TOKEN'),
9568
                'userName' => api_get_mail_configuration_value('SMTP_USER'),
9569
            ])
9570
        );
9571
    }
9572
9573
    $mail->Mailer = api_get_mail_configuration_value('SMTP_MAILER');
9574
    $mail->Host = api_get_mail_configuration_value('SMTP_HOST');
9575
    $mail->Port = api_get_mail_configuration_value('SMTP_PORT');
9576
    $mail->CharSet = api_get_mail_configuration_value('SMTP_CHARSET') ?: 'UTF-8';
9577
    // Stay far below SMTP protocol 980 chars limit.
9578
    $mail->WordWrap = 200;
9579
    $mail->SMTPOptions = api_get_mail_configuration_value('SMTPOptions') ?: [];
9580
9581
    if (api_get_mail_configuration_value('SMTP_AUTH')) {
9582
        $mail->SMTPAuth = 1;
9583
        $mail->Username = api_get_mail_configuration_value('SMTP_USER');
9584
        $mail->Password = api_get_mail_configuration_value('SMTP_PASS');
9585
        if (api_get_mail_configuration_value('SMTP_SECURE')) {
9586
            $mail->SMTPSecure = api_get_mail_configuration_value('SMTP_SECURE');
9587
        }
9588
    }
9589
    $mail->SMTPDebug = api_get_mail_configuration_value('SMTP_DEBUG') ?: 0;
9590
9591
    // 5 = low, 1 = high
9592
    $mail->Priority = 3;
9593
    $mail->SMTPKeepAlive = true;
9594
9595
    api_set_noreply_and_from_address_to_mailer(
9596
        $mail,
9597
        ['name' => $senderName, 'email' => $senderEmail],
9598
        !empty($extra_headers['reply_to']) ? $extra_headers['reply_to'] : []
9599
    );
9600
9601
    if (!empty($sendErrorTo) && PHPMailer::ValidateAddress($sendErrorTo)) {
9602
        $mail->AddCustomHeader('Errors-To', $sendErrorTo);
9603
    }
9604
9605
    unset($extra_headers['reply_to']);
9606
9607
    $mail->Subject = $subject;
9608
    $mail->AltBody = strip_tags(
9609
        str_replace('<br />', "\n", api_html_entity_decode($message))
9610
    );
9611
9612
    $list = api_get_configuration_value('send_all_emails_to');
9613
    if (!empty($list) && isset($list['emails'])) {
9614
        foreach ($list['emails'] as $email) {
9615
            $mail->AddAddress($email);
9616
        }
9617
    }
9618
9619
    // Send embedded image.
9620
    if ($embedded_image) {
9621
        // Get all images html inside content.
9622
        preg_match_all("/<img\s+.*?src=[\"\']?([^\"\' >]*)[\"\']?[^>]*>/i", $message, $m);
9623
        // Prepare new tag images.
9624
        $new_images_html = [];
9625
        $i = 1;
9626
        if (!empty($m[1])) {
9627
            foreach ($m[1] as $image_path) {
9628
                $real_path = realpath($image_path);
9629
                $filename = basename($image_path);
9630
                $image_cid = $filename.'_'.$i;
9631
                $encoding = 'base64';
9632
                $image_type = mime_content_type($real_path);
9633
                $mail->AddEmbeddedImage(
9634
                    $real_path,
9635
                    $image_cid,
9636
                    $filename,
9637
                    $encoding,
9638
                    $image_type
9639
                );
9640
                $new_images_html[] = '<img src="cid:'.$image_cid.'" />';
9641
                $i++;
9642
            }
9643
        }
9644
9645
        // Replace origin image for new embedded image html.
9646
        $x = 0;
9647
        if (!empty($m[0])) {
9648
            foreach ($m[0] as $orig_img) {
9649
                $message = str_replace($orig_img, $new_images_html[$x], $message);
9650
                $x++;
9651
            }
9652
        }
9653
    }
9654
9655
    $extendedFooterMessageConfig = api_get_configuration_value('notifications_extended_footer_message');
9656
    if ($extendedFooterMessageConfig) {
9657
        $platformLanguage = api_get_interface_language();
9658
        $extendedFooterMessage = api_get_configuration_value('notifications_extended_footer_message')[$platformLanguage];
9659
9660
        if ($extendedFooterMessage) {
9661
            $message .= '<br /><hr><i>'.'<p>'.implode('<br/><br/>', $extendedFooterMessage['paragraphs']).'</p>';
9662
        }
9663
    }
9664
9665
    $mailView = new Template(null, false, false, false, false, false, false);
9666
9667
    $noReply = api_get_setting('noreply_email_address');
9668
    if (!empty($noReply)) {
9669
        $message .= '<br />'.get_lang('ThisIsAutomaticEmailNoReply');
9670
    }
9671
    $mailView->assign('content', $message);
9672
9673
    if (isset($additionalParameters['link'])) {
9674
        $mailView->assign('link', $additionalParameters['link']);
9675
    }
9676
    if (isset($additionalParameters['logo'])) {
9677
        $mailView->assign('logo', $additionalParameters['logo']);
9678
    } elseif (api_get_configuration_value('email_logo') == true) {
9679
        $logoSubPath = 'themes/'.api_get_visual_theme().'/images/email-logo.png';
9680
        $logoSysPath = api_get_path(SYS_PATH).'web/css/'.$logoSubPath;
9681
        if (file_exists($logoSysPath)) {
9682
            $logoWebPath = api_get_path(WEB_CSS_PATH).$logoSubPath;
9683
            $imgTag = \Display::img(
9684
                $logoWebPath,
9685
                api_get_setting('siteName'),
9686
                [
9687
                    'id' => 'header-logo',
9688
                    'class' => 'img-responsive'
9689
                ]
9690
            );
9691
            $logoTag = \Display::url($imgTag, api_get_path(WEB_PATH));
9692
            $mailView->assign('logo', $logoTag);
9693
        }
9694
    }
9695
    $mailView->assign('mail_header_style', api_get_configuration_value('mail_header_style'));
9696
    $mailView->assign('mail_content_style', api_get_configuration_value('mail_content_style'));
9697
    $mailView->assign('include_ldjson', (empty(api_get_mail_configuration_value('EXCLUDE_JSON')) ? true : false));
9698
    $layout = $mailView->get_template('mail/mail.tpl');
9699
    $mail->Body = $mailView->fetch($layout);
9700
9701
    if (isset($additionalParameters['checkUrls'])) {
9702
        $useMultipleUrl = api_get_configuration_value('multiple_access_urls');
9703
        if ($useMultipleUrl) {
9704
            $accessConfig = [];
9705
            $accessUrls = api_get_access_url_from_user($additionalParameters['userId'], $additionalParameters['courseId']);
9706
            if (!empty($accessUrls)) {
9707
                $accessConfig['multiple_access_urls'] = true;
9708
                $accessConfig['access_url'] = (int) $accessUrls[0];
9709
                $params = ['variable = ? AND access_url = ?' => ['stylesheets', $accessConfig['access_url']]];
9710
                $settings = api_get_settings_params_simple($params);
9711
                if (!empty($settings['selected_value'])) {
9712
                    $accessConfig['theme_dir'] = \Template::getThemeDir($settings['selected_value']);
9713
                }
9714
            }
9715
            // To replace the current urls by access url user
9716
            $mail->Body = str_replace(api_get_path(WEB_PATH), api_get_path(WEB_PATH, $accessConfig), $mail->Body);
9717
            if (!empty($accessConfig['theme_dir'])) {
9718
                $mail->Body = str_replace('themes/chamilo/', $accessConfig['theme_dir'], $mail->Body);
9719
            }
9720
        }
9721
    }
9722
    // Attachment.
9723
    if (!empty($data_file)) {
9724
        foreach ($data_file as $file_attach) {
9725
            if (!empty($file_attach['path']) && !empty($file_attach['filename'])) {
9726
                $mail->AddAttachment($file_attach['path'], $file_attach['filename']);
9727
            }
9728
        }
9729
    }
9730
9731
    // Only valid addresses are accepted.
9732
    if (is_array($recipient_email)) {
9733
        foreach ($recipient_email as $dest) {
9734
            if (api_valid_email($dest)) {
9735
                if (UserManager::isEmailingAllowed($dest)) {
9736
                    // Do not send if user is not active = 1
9737
                    $mail->AddAddress($dest, $recipient_name);
9738
                }
9739
            } else {
9740
                // error_log('e-mail recipient '.$dest.' is not valid.');
9741
                return 0;
9742
            }
9743
        }
9744
    } else {
9745
        if (api_valid_email($recipient_email)) {
9746
            if (UserManager::isEmailingAllowed($recipient_email)) {
9747
                // Do not send if user is not active = 1
9748
                $mail->AddAddress($recipient_email, $recipient_name);
9749
            }
9750
        } else {
9751
            // error_log('e-mail recipient '.$recipient_email.' is not valid.');
9752
            return 0;
9753
        }
9754
    }
9755
    if (empty($mail->getAllRecipientAddresses())) {
9756
        // error_log('No valid and active destination e-mail in api_mail_html() with address '.print_r($recipient_email, 1).'. Not sending.');
9757
        return 0;
9758
    }
9759
9760
    if (is_array($extra_headers) && count($extra_headers) > 0) {
9761
        foreach ($extra_headers as $key => $value) {
9762
            switch (strtolower($key)) {
9763
                case 'encoding':
9764
                case 'content-transfer-encoding':
9765
                    $mail->Encoding = $value;
9766
                    break;
9767
                case 'charset':
9768
                    $mail->CharSet = $value;
9769
                    break;
9770
                case 'contenttype':
9771
                case 'content-type':
9772
                    $mail->ContentType = $value;
9773
                    break;
9774
                default:
9775
                    $mail->AddCustomHeader($key, $value);
9776
                    break;
9777
            }
9778
        }
9779
    } else {
9780
        if (!empty($extra_headers)) {
9781
            $mail->AddCustomHeader($extra_headers);
9782
        }
9783
    }
9784
9785
    // WordWrap the html body (phpMailer only fixes AltBody) FS#2988
9786
    $mail->Body = $mail->WrapText($mail->Body, $mail->WordWrap);
9787
9788
    if (!empty(api_get_mail_configuration_value('DKIM')) &&
9789
        !empty(api_get_mail_configuration_value('DKIM_SELECTOR')) &&
9790
        !empty(api_get_mail_configuration_value('DKIM_DOMAIN')) &&
9791
        (!empty(api_get_mail_configuration_value('DKIM_PRIVATE_KEY_STRING')) || !empty(api_get_mail_configuration_value('DKIM_PRIVATE_KEY')))) {
9792
        $mail->DKIM_selector = api_get_mail_configuration_value('DKIM_SELECTOR');
9793
        $mail->DKIM_domain = api_get_mail_configuration_value('DKIM_DOMAIN');
9794
        if (!empty(api_get_mail_configuration_value('SMTP_UNIQUE_SENDER'))) {
9795
            $mail->DKIM_identity = api_get_mail_configuration_value('SMTP_FROM_EMAIL');
9796
        }
9797
        $mail->DKIM_private_string = api_get_mail_configuration_value('DKIM_PRIVATE_KEY_STRING');
9798
        $mail->DKIM_private = api_get_mail_configuration_value('DKIM_PRIVATE_KEY');
9799
        if (!empty(api_get_mail_configuration_value['DKIM_PASSPHRASE'])) {
9800
            $mail->DKIM_passphrase = api_get_mail_configuration_value['DKIM_PASSPHRASE'];
9801
        }
9802
    }
9803
9804
    // Send the mail message.
9805
    $sent = $mail->Send();
9806
    if (!$sent) {
9807
        error_log('ERROR: mail not sent to '.$recipient_name.' ('.$recipient_email.') because of '.$mail->ErrorInfo.'<br />');
9808
    }
9809
9810
    if ($mail->SMTPDebug >= 1) {
9811
        error_log(
9812
            "Mail debug:: ".
9813
            "Protocol: ".$mail->Mailer.' :: '.
9814
            "Host/Port: ".$mail->Host.':'.$mail->Port.' :: '.
9815
            "Authent/Open: ".($mail->SMTPAuth ? 'Authent' : 'Open').' :: '.
9816
            ($mail->SMTPAuth ? "  User/Pass: ".$mail->Username.':'.$mail->Password : '').' :: '.
9817
            "Sender: ".$mail->Sender.
9818
            "Recipient email: ".$recipient_email.
9819
            "Subject: ".$subject
9820
        );
9821
    }
9822
9823
    if (!$sent) {
9824
        return 0;
9825
    }
9826
9827
    if (!empty($additionalParameters)) {
9828
        $plugin = new AppPlugin();
9829
        $smsPlugin = $plugin->getSMSPluginLibrary();
9830
        if ($smsPlugin) {
9831
            $smsPlugin->send($additionalParameters);
9832
        }
9833
    }
9834
9835
    // Clear all the addresses.
9836
    $mail->ClearAddresses();
9837
9838
    // Clear all attachments
9839
    $mail->ClearAttachments();
9840
9841
    return 1;
9842
}
9843
9844
/**
9845
 * Checks access to a course group.
9846
 *
9847
 * @param string $tool       Possible values: GroupManager::GROUP_TOOL_*
9848
 * @param bool   $showHeader
9849
 */
9850
function api_protect_course_group($tool, $showHeader = true)
9851
{
9852
    $groupId = api_get_group_id();
9853
    if (!empty($groupId)) {
9854
        if (api_is_platform_admin()) {
9855
            return true;
9856
        }
9857
9858
        if (api_is_allowed_to_edit(false, true, true)) {
9859
            return true;
9860
        }
9861
9862
        $userId = api_get_user_id();
9863
        $sessionId = api_get_session_id();
9864
        if (!empty($sessionId)) {
9865
            if (api_is_coach($sessionId, api_get_course_int_id())) {
9866
                return true;
9867
            }
9868
9869
            if (api_is_drh()) {
9870
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
9871
                    return true;
9872
                }
9873
            }
9874
        }
9875
9876
        $groupInfo = GroupManager::get_group_properties($groupId);
9877
9878
        // Group doesn't exists
9879
        if (empty($groupInfo)) {
9880
            api_not_allowed($showHeader);
9881
        }
9882
9883
        // Check group access
9884
        $allow = GroupManager::user_has_access(
9885
            $userId,
9886
            $groupInfo['iid'],
9887
            $tool
9888
        );
9889
9890
        if (!$allow) {
9891
            api_not_allowed($showHeader);
9892
        }
9893
    }
9894
9895
    return false;
9896
}
9897
9898
/**
9899
 * Check if a date is in a date range.
9900
 *
9901
 * @param datetime $startDate
9902
 * @param datetime $endDate
9903
 * @param datetime $currentDate
9904
 *
9905
 * @return bool true if date is in rage, false otherwise
9906
 */
9907
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
9908
{
9909
    $startDate = strtotime(api_get_local_time($startDate));
9910
    $endDate = strtotime(api_get_local_time($endDate));
9911
    $currentDate = strtotime(api_get_local_time($currentDate));
9912
9913
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
9914
        return true;
9915
    }
9916
9917
    return false;
9918
}
9919
9920
/**
9921
 * Eliminate the duplicates of a multidimensional array by sending the key.
9922
 *
9923
 * @param array $array multidimensional array
9924
 * @param int   $key   key to find to compare
9925
 *
9926
 * @return array
9927
 */
9928
function api_unique_multidim_array($array, $key)
9929
{
9930
    $temp_array = [];
9931
    $i = 0;
9932
    $key_array = [];
9933
9934
    foreach ($array as $val) {
9935
        if (!in_array($val[$key], $key_array)) {
9936
            $key_array[$i] = $val[$key];
9937
            $temp_array[$i] = $val;
9938
        }
9939
        $i++;
9940
    }
9941
9942
    return $temp_array;
9943
}
9944
9945
/**
9946
 * Limit the access to Session Admins when the limit_session_admin_role
9947
 * configuration variable is set to true.
9948
 */
9949
function api_protect_limit_for_session_admin()
9950
{
9951
    $limitAdmin = api_get_setting('limit_session_admin_role');
9952
    if (api_is_session_admin() && $limitAdmin === 'true') {
9953
        api_not_allowed(true);
9954
    }
9955
}
9956
9957
/**
9958
 * Limits that a session admin has access to list users.
9959
 * When limit_session_admin_list_users configuration variable is set to true.
9960
 */
9961
function api_protect_session_admin_list_users()
9962
{
9963
    $limitAdmin = api_get_configuration_value('limit_session_admin_list_users');
9964
9965
    if (api_is_session_admin() && true === $limitAdmin) {
9966
        api_not_allowed(true);
9967
    }
9968
}
9969
9970
/**
9971
 * @return bool
9972
 */
9973
function api_is_student_view_active()
9974
{
9975
    $studentView = Session::read('studentview');
9976
9977
    return $studentView == 'studentview';
9978
}
9979
9980
/**
9981
 * Adds a file inside the upload/$type/id.
9982
 *
9983
 * @param string $type
9984
 * @param array  $file
9985
 * @param int    $itemId
9986
 * @param string $cropParameters
9987
 *
9988
 * @return array|bool
9989
 */
9990
function api_upload_file($type, $file, $itemId, $cropParameters = '')
9991
{
9992
    $upload = process_uploaded_file($file);
9993
    if ($upload) {
9994
        $name = api_replace_dangerous_char($file['name']);
9995
9996
        // No "dangerous" files
9997
        $name = disable_dangerous_file($name);
9998
9999
        $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
10000
        $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
10001
10002
        if (!is_dir($path)) {
10003
            mkdir($path, api_get_permissions_for_new_directories(), true);
10004
        }
10005
10006
        $pathToSave = $path.$name;
10007
        $result = moveUploadedFile($file, $pathToSave);
10008
10009
        if ($result) {
10010
            if (!empty($cropParameters)) {
10011
                $image = new Image($pathToSave);
10012
                $image->crop($cropParameters);
10013
            }
10014
10015
            return ['path_to_save' => $pathId.$name];
10016
        }
10017
    }
10018
10019
    return false;
10020
}
10021
10022
/**
10023
 * @param string $type
10024
 * @param int    $itemId
10025
 * @param string $file
10026
 *
10027
 * @return bool
10028
 */
10029
function api_get_uploaded_web_url($type, $itemId, $file)
10030
{
10031
    return api_get_uploaded_file($type, $itemId, $file, true);
10032
}
10033
10034
/**
10035
 * @param string $type
10036
 * @param int    $itemId
10037
 * @param string $file
10038
 * @param bool   $getUrl
10039
 *
10040
 * @return bool
10041
 */
10042
function api_get_uploaded_file($type, $itemId, $file, $getUrl = false)
10043
{
10044
    $itemId = (int) $itemId;
10045
    $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
10046
    $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
10047
    $file = basename($file);
10048
    $file = $path.'/'.$file;
10049
    if (Security::check_abs_path($file, $path) && is_file($file) && file_exists($file)) {
10050
        if ($getUrl) {
10051
            return str_replace(api_get_path(SYS_UPLOAD_PATH), api_get_path(WEB_UPLOAD_PATH), $file);
10052
        }
10053
10054
        return $file;
10055
    }
10056
10057
    return false;
10058
}
10059
10060
/**
10061
 * @param string $type
10062
 * @param int    $itemId
10063
 * @param string $file
10064
 * @param string $title
10065
 */
10066
function api_download_uploaded_file($type, $itemId, $file, $title = '')
10067
{
10068
    $file = api_get_uploaded_file($type, $itemId, $file);
10069
    if ($file) {
10070
        if (Security::check_abs_path($file, api_get_path(SYS_UPLOAD_PATH).$type)) {
10071
            DocumentManager::file_send_for_download($file, true, $title);
10072
            exit;
10073
        }
10074
    }
10075
    api_not_allowed(true);
10076
}
10077
10078
/**
10079
 * @param string $type
10080
 * @param string $file
10081
 */
10082
function api_remove_uploaded_file($type, $file)
10083
{
10084
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
10085
    $path = $typePath.'/'.$file;
10086
    if (Security::check_abs_path($path, $typePath) && file_exists($path) && is_file($path)) {
10087
        unlink($path);
10088
    }
10089
}
10090
10091
/**
10092
 * @param string $type
10093
 * @param int    $itemId
10094
 * @param string $file
10095
 *
10096
 * @return bool
10097
 */
10098
function api_remove_uploaded_file_by_id($type, $itemId, $file)
10099
{
10100
    $file = api_get_uploaded_file($type, $itemId, $file, false);
10101
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
10102
    if (Security::check_abs_path($file, $typePath) && file_exists($file) && is_file($file)) {
10103
        unlink($file);
10104
10105
        return true;
10106
    }
10107
10108
    return false;
10109
}
10110
10111
/**
10112
 * Converts string value to float value.
10113
 *
10114
 * 3.141516 => 3.141516
10115
 * 3,141516 => 3.141516
10116
 *
10117
 * @todo WIP
10118
 *
10119
 * @param string $number
10120
 *
10121
 * @return float
10122
 */
10123
function api_float_val($number)
10124
{
10125
    $number = (float) str_replace(',', '.', trim($number));
10126
10127
    return $number;
10128
}
10129
10130
/**
10131
 * Converts float values
10132
 * Example if $decimals = 2.
10133
 *
10134
 * 3.141516 => 3.14
10135
 * 3,141516 => 3,14
10136
 *
10137
 * @param string $number            number in iso code
10138
 * @param int    $decimals
10139
 * @param string $decimalSeparator
10140
 * @param string $thousandSeparator
10141
 *
10142
 * @return bool|string
10143
 */
10144
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
10145
{
10146
    $number = api_float_val($number);
10147
10148
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
10149
}
10150
10151
/**
10152
 * Set location url with a exit break by default.
10153
 *
10154
 * @param string $url
10155
 * @param bool   $exit
10156
 */
10157
function api_location($url, $exit = true)
10158
{
10159
    header('Location: '.$url);
10160
10161
    if ($exit) {
10162
        exit;
10163
    }
10164
}
10165
10166
/**
10167
 * @return string
10168
 */
10169
function api_get_web_url()
10170
{
10171
    if (api_get_setting('server_type') === 'test') {
10172
        return api_get_path(WEB_PATH).'web/app_dev.php/';
10173
    } else {
10174
        return api_get_path(WEB_PATH).'web/';
10175
    }
10176
}
10177
10178
/**
10179
 * @param string $from
10180
 * @param string $to
10181
 *
10182
 * @return string
10183
 */
10184
function api_get_relative_path($from, $to)
10185
{
10186
    // some compatibility fixes for Windows paths
10187
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
10188
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
10189
    $from = str_replace('\\', '/', $from);
10190
    $to = str_replace('\\', '/', $to);
10191
10192
    $from = explode('/', $from);
10193
    $to = explode('/', $to);
10194
    $relPath = $to;
10195
10196
    foreach ($from as $depth => $dir) {
10197
        // find first non-matching dir
10198
        if ($dir === $to[$depth]) {
10199
            // ignore this directory
10200
            array_shift($relPath);
10201
        } else {
10202
            // get number of remaining dirs to $from
10203
            $remaining = count($from) - $depth;
10204
            if ($remaining > 1) {
10205
                // add traversals up to first matching dir
10206
                $padLength = (count($relPath) + $remaining - 1) * -1;
10207
                $relPath = array_pad($relPath, $padLength, '..');
10208
                break;
10209
            } else {
10210
                $relPath[0] = './'.$relPath[0];
10211
            }
10212
        }
10213
    }
10214
10215
    return implode('/', $relPath);
10216
}
10217
10218
/**
10219
 * Unserialize content using Brummann\Polyfill\Unserialize.
10220
 *
10221
 * @param string $type
10222
 * @param string $serialized
10223
 *
10224
 * @return mixed
10225
 */
10226
function api_unserialize_content($type, $serialized, $ignoreErrors = false)
10227
{
10228
    switch ($type) {
10229
        case 'career':
10230
        case 'sequence_graph':
10231
            $allowedClasses = [Graph::class, VerticesMap::class, Vertices::class, Edges::class];
10232
            break;
10233
        case 'lp':
10234
            $allowedClasses = [
10235
                learnpath::class,
10236
                learnpathItem::class,
10237
                aicc::class,
10238
                aiccBlock::class,
10239
                aiccItem::class,
10240
                aiccObjective::class,
10241
                aiccResource::class,
10242
                scorm::class,
10243
                scormItem::class,
10244
                scormMetadata::class,
10245
                scormOrganization::class,
10246
                scormResource::class,
10247
                Link::class,
10248
                LpItem::class,
10249
            ];
10250
            break;
10251
        case 'course':
10252
            $allowedClasses = [
10253
                Course::class,
10254
                Announcement::class,
10255
                Attendance::class,
10256
                CalendarEvent::class,
10257
                CourseCopyLearnpath::class,
10258
                CourseCopyTestCategory::class,
10259
                CourseDescription::class,
10260
                CourseSession::class,
10261
                Document::class,
10262
                Forum::class,
10263
                ForumCategory::class,
10264
                ForumPost::class,
10265
                ForumTopic::class,
10266
                Glossary::class,
10267
                GradeBookBackup::class,
10268
                Link::class,
10269
                LinkCategory::class,
10270
                Quiz::class,
10271
                QuizQuestion::class,
10272
                QuizQuestionOption::class,
10273
                ScormDocument::class,
10274
                Survey::class,
10275
                SurveyInvitation::class,
10276
                SurveyQuestion::class,
10277
                Thematic::class,
10278
                ToolIntro::class,
10279
                Wiki::class,
10280
                Work::class,
10281
                stdClass::class,
10282
            ];
10283
            break;
10284
        case 'not_allowed_classes':
10285
        default:
10286
            $allowedClasses = false;
10287
    }
10288
10289
    if ($ignoreErrors) {
10290
        return @Unserialize::unserialize(
10291
            $serialized,
10292
            ['allowed_classes' => $allowedClasses]
10293
        );
10294
    }
10295
10296
    return Unserialize::unserialize(
10297
        $serialized,
10298
        ['allowed_classes' => $allowedClasses]
10299
    );
10300
}
10301
10302
/**
10303
 * Set the From and ReplyTo properties to PHPMailer instance.
10304
 *
10305
 * @throws \PHPMailer\PHPMailer\Exception
10306
 */
10307
function api_set_noreply_and_from_address_to_mailer(PHPMailer $mailer, array $sender, array $replyToAddress = [])
10308
{
10309
    $noReplyAddress = api_get_setting('noreply_email_address');
10310
    $avoidReplyToAddress = false;
10311
10312
    if (!empty($noReplyAddress)) {
10313
        $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
10314
    }
10315
10316
    $notification = new Notification();
10317
    // If the parameter is set don't use the admin.
10318
    $senderName = !empty($sender['name']) ? $sender['name'] : $notification->getDefaultPlatformSenderName();
10319
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $notification->getDefaultPlatformSenderEmail();
10320
10321
    // Send errors to the platform admin
10322
    $adminEmail = api_get_setting('emailAdministrator');
10323
    if (PHPMailer::ValidateAddress($adminEmail)) {
10324
        $mailer->AddCustomHeader('Errors-To: '.$adminEmail);
10325
    }
10326
10327
    // Reply to first
10328
    if (!$avoidReplyToAddress) {
10329
        if (
10330
            !empty($replyToAddress) &&
10331
            PHPMailer::ValidateAddress($replyToAddress['mail'])
10332
        ) {
10333
            $mailer->AddReplyTo($replyToAddress['mail'], $replyToAddress['name']);
10334
            //$mailer->Sender = $replyToAddress['mail'];
10335
        }
10336
    }
10337
10338
    //If the SMTP configuration only accept one sender
10339
    if (
10340
        !empty(api_get_mail_configuration_value('SMTP_UNIQUE_SENDER')) &&
10341
        api_get_mail_configuration_value('SMTP_UNIQUE_SENDER')
10342
    ) {
10343
        $senderName = $notification->getDefaultPlatformSenderName();
10344
        $senderEmail = $notification->getDefaultPlatformSenderEmail();
10345
10346
        if (PHPMailer::ValidateAddress($senderEmail)) {
10347
            //force-set Sender to $senderEmail, otherwise SetFrom only does it if it is currently empty
10348
            $mailer->Sender = $senderEmail;
10349
        }
10350
    }
10351
10352
    $mailer->SetFrom($senderEmail, $senderName, !$avoidReplyToAddress);
10353
}
10354
10355
/**
10356
 * @param string $template
10357
 *
10358
 * @return string
10359
 */
10360
function api_find_template($template)
10361
{
10362
    return Template::findTemplateFilePath($template);
10363
}
10364
10365
/**
10366
 * Returns an array of languages (English names like "english", "french", etc)
10367
 * to ISO 639-1 codes (fr, es, etc) for use (for example) to show flags
10368
 * Note: 'english' is returned as 'gb'.
10369
 *
10370
 * @return array
10371
 */
10372
function api_get_language_list_for_flag()
10373
{
10374
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
10375
    $sql = "SELECT english_name, isocode FROM $table
10376
            ORDER BY original_name ASC";
10377
    static $languages = [];
10378
    if (empty($languages)) {
10379
        $result = Database::query($sql);
10380
        while ($row = Database::fetch_array($result)) {
10381
            $languages[$row['english_name']] = $row['isocode'];
10382
        }
10383
        $languages['english'] = 'gb';
10384
    }
10385
10386
    return $languages;
10387
}
10388
10389
/**
10390
 * Generate the Javascript required for the on-page translation of
10391
 * multi-language strings.
10392
 *
10393
 * @throws Exception
10394
 *
10395
 * @return string
10396
 */
10397
function api_get_language_translate_html()
10398
{
10399
    $translate = api_get_configuration_value('translate_html');
10400
10401
    if (!$translate) {
10402
        return '';
10403
    }
10404
10405
    $languageList = api_get_languages();
10406
    $hideAll = '';
10407
    foreach ($languageList['all'] as $language) {
10408
        $hideAll .= '
10409
        $("span:lang('.$language['isocode'].')").filter(
10410
            function(e, val) {
10411
                // Only find the spans if they have set the lang
10412
                if ($(this).attr("lang") == null) {
10413
                    return false;
10414
                }
10415
10416
                // Ignore ckeditor classes
10417
                return !this.className.match(/cke(.*)/);
10418
        }).hide();'."\n";
10419
    }
10420
10421
    $userInfo = api_get_user_info();
10422
    $languageId = 0;
10423
    if (!empty($userInfo['language'])) {
10424
        $languageId = api_get_language_id($userInfo['language']);
10425
    } elseif (!empty($_GET['language'])) {
10426
        $languageId = api_get_language_id($_GET['language']);
10427
    }
10428
    $languageInfo = api_get_language_info($languageId);
10429
    $isoCode = 'en';
10430
10431
    if (!empty($languageInfo)) {
10432
        $isoCode = $languageInfo['isocode'];
10433
    }
10434
10435
    return '
10436
            $(function() {
10437
                '.$hideAll.'
10438
                var defaultLanguageFromUser = "'.$isoCode.'";
10439
10440
                $("span:lang('.$isoCode.')").filter(
10441
                    function() {
10442
                        // Ignore ckeditor classes
10443
                        return !this.className.match(/cke(.*)/);
10444
                }).show();
10445
10446
                var defaultLanguage = "";
10447
                var langFromUserFound = false;
10448
10449
                $(this).find("span").filter(
10450
                    function() {
10451
                        // Ignore ckeditor classes
10452
                        return !this.className.match(/cke(.*)/);
10453
                }).each(function() {
10454
                    defaultLanguage = $(this).attr("lang");
10455
                    if (defaultLanguage) {
10456
                        $(this).before().next("br").remove();
10457
                        if (defaultLanguageFromUser == defaultLanguage) {
10458
                            langFromUserFound = true;
10459
                        }
10460
                    }
10461
                });
10462
10463
                // Show default language
10464
                if (langFromUserFound == false && defaultLanguage) {
10465
                    $("span:lang("+defaultLanguage+")").filter(
10466
                    function() {
10467
                            // Ignore ckeditor classes
10468
                            return !this.className.match(/cke(.*)/);
10469
                    }).show();
10470
                }
10471
            });
10472
    ';
10473
}
10474
10475
/**
10476
 * Filter a multi-language HTML string (for the multi-language HTML
10477
 * feature) into the given language (strip the rest).
10478
 *
10479
 * @param string $htmlString The HTML string to "translate". Usually <p><span lang="en">Some string</span></p><p><span lang="fr">Une chaîne</span></p>
10480
 * @param string $language   The language in which we want to get the
10481
 *
10482
 * @throws Exception
10483
 *
10484
 * @return string The filtered string in the given language, or the full string if no translated string was identified
10485
 */
10486
function api_get_filtered_multilingual_HTML_string($htmlString, $language = null)
10487
{
10488
    if (api_get_configuration_value('translate_html') != true) {
10489
        return $htmlString;
10490
    }
10491
    $userInfo = api_get_user_info();
10492
    $languageId = 0;
10493
    if (!empty($language)) {
10494
        $languageId = api_get_language_id($language);
10495
    } elseif (!empty($userInfo['language'])) {
10496
        $languageId = api_get_language_id($userInfo['language']);
10497
    }
10498
    $languageInfo = api_get_language_info($languageId);
10499
    $isoCode = 'en';
10500
10501
    if (!empty($languageInfo)) {
10502
        $isoCode = $languageInfo['isocode'];
10503
    }
10504
10505
    // Split HTML in the separate language strings
10506
    // Note: some strings might look like <p><span ..>...</span></p> but others might be like combine 2 <span> in 1 <p>
10507
    if (!preg_match('/<span.*?lang="(\w\w)">/is', $htmlString)) {
10508
        return $htmlString;
10509
    }
10510
    $matches = [];
10511
    preg_match_all('/<span.*?lang="(\w\w)">(.*?)<\/span>/is', $htmlString, $matches);
10512
    if (!empty($matches)) {
10513
        // matches[0] are the full string
10514
        // matches[1] are the languages
10515
        // matches[2] are the strings
10516
        foreach ($matches[1] as $id => $match) {
10517
            if ($match == $isoCode) {
10518
                return $matches[2][$id];
10519
            }
10520
        }
10521
        // Could find the pattern but could not find our language. Return the first language found.
10522
        return $matches[2][0];
10523
    }
10524
    // Could not find pattern. Just return the whole string. We shouldn't get here.
10525
    return $htmlString;
10526
}
10527
10528
/**
10529
 * Get the print.css file for current theme.
10530
 * Only the file path or the file contents when $getFileContents is true.
10531
 */
10532
function api_get_print_css(bool $getFileContents = true, bool $useWebPath = false): string
10533
{
10534
    $sysCssPath = api_get_path(SYS_CSS_PATH);
10535
    $cssFile = $sysCssPath.'themes/'.api_get_visual_theme().'/print.css';
10536
10537
    if (!file_exists($cssFile)) {
10538
        $cssFile = $sysCssPath.'print.css';
10539
    }
10540
10541
    if ($getFileContents) {
10542
        return file_get_contents($cssFile);
10543
    }
10544
10545
    if ($useWebPath) {
10546
        return str_replace($sysCssPath, api_get_path(WEB_CSS_PATH), $cssFile);
10547
    }
10548
10549
    return $cssFile;
10550
}
10551
10552
function api_protect_webservices()
10553
{
10554
    if (api_get_configuration_value('disable_webservices')) {
10555
        echo "Webservices are disabled. \n";
10556
        echo "To enable, add \$_configuration['disable_webservices'] = false; in configuration.php";
10557
        exit;
10558
    }
10559
}
10560
10561
function api_filename_has_blacklisted_stream_wrapper(string $filename)
10562
{
10563
    if (strpos($filename, '://') > 0) {
10564
        $wrappers = stream_get_wrappers();
10565
        $allowedWrappers = ['http', 'https', 'file'];
10566
10567
        foreach ($wrappers as $wrapper) {
10568
            if (in_array($wrapper, $allowedWrappers)) {
10569
                continue;
10570
            }
10571
10572
            if (stripos($filename, $wrapper.'://') === 0) {
10573
                return true;
10574
            }
10575
        }
10576
    }
10577
10578
    return false;
10579
}
10580
10581
/**
10582
 * Calculate the percent between two numbers.
10583
 *
10584
 * @return string
10585
 */
10586
function api_calculate_increment_percent(int $newValue, int $oldValue)
10587
{
10588
    if ($oldValue <= 0) {
10589
        $result = " - ";
10590
    } else {
10591
        $result = ' '.round(100 * (($newValue / $oldValue) - 1), 2).' %';
10592
    }
10593
10594
    return $result;
10595
}
10596
10597
/**
10598
 * Erase settings from cache (because of some update) if applicable.
10599
 *
10600
 * @param int $url_id The ID of the present URL
10601
 */
10602
function api_flush_settings_cache(int $url_id): bool
10603
{
10604
    global $_configuration;
10605
    $cacheAvailable = api_get_configuration_value('apc');
10606
    if (!$cacheAvailable) {
10607
        return false;
10608
    }
10609
    $apcRootVarName = api_get_configuration_value('apc_prefix');
10610
    // Delete the APCu-stored settings array, if present
10611
    $apcVarName = $apcRootVarName.'settings';
10612
    apcu_delete($apcVarName);
10613
    if (api_is_multiple_url_enabled() && $url_id === 1) {
10614
        // if we are on the main URL of a multi-url portal, we must
10615
        // invalidate the cache for all other URLs as well as some
10616
        // main settings span multiple URLs
10617
        $urls = api_get_access_urls();
10618
        foreach ($urls as $i => $row) {
10619
            if ($row['id'] == 1) {
10620
                continue;
10621
            }
10622
            $apcVarName = $_configuration['main_database'].'_'.$row['id'].'_settings';
10623
            apcu_delete($apcVarName);
10624
        }
10625
    }
10626
10627
    return true;
10628
}
10629
10630
/**
10631
 * Decrypt sent data with encoded secret defined in app/config/configuration.php
10632
 * in the variable $_configuration['ldap_admin_password_salt'].
10633
 *
10634
 * @param $encryptedText The text to be decrypted
10635
 */
10636
function api_decrypt_ldap_password(string $encryptedText): string
10637
{
10638
    if (!empty(api_get_configuration_value('ldap_admin_password_salt'))) {
10639
        $secret = api_get_configuration_value('ldap_admin_password_salt');
10640
    } else {
10641
        return false;
10642
    }
10643
10644
    return api_decrypt_hash($encryptedText,$secret);
10645
}
10646
10647
/**
10648
 * Decrypt sent hash encoded with secret
10649
 *
10650
 * @param $encryptedText The hash text to be decrypted
10651
 * @param $secret        The secret used to encoded the hash
10652
 *
10653
 * @return string The decrypted text or false
10654
 */
10655
function api_decrypt_hash(string $encryptedHash, string $secret): string
10656
{
10657
    $iv = base64_decode(substr($encryptedHash, 0, 16), true);
10658
    $data = base64_decode(substr($encryptedHash, 16), true);
10659
    $tag = substr($data, strlen($data) - 16);
10660
    $data = substr($data, 0, strlen($data) - 16);
10661
10662
    try {
10663
        return openssl_decrypt(
10664
        $data,
10665
        'aes-256-gcm',
10666
        $secret,
10667
        OPENSSL_RAW_DATA,
10668
        $iv,
10669
        $tag
10670
      );
10671
    } catch (\Exception $e) {
10672
        return false;
10673
    }
10674
}
10675
10676
/**
10677
 * Encrypt sent data with secret
10678
 *
10679
 * @param $data   The text to be encrypted
10680
 * @param $secret The secret to use encode data
10681
 *
10682
 * @return string The encrypted text or false
10683
 */
10684
function api_encrypt_hash($data, $secret)
10685
{
10686
  $iv = random_bytes(12);
10687
  $tag = '';
10688
10689
  $encrypted = openssl_encrypt(
10690
    $data,
10691
    'aes-256-gcm',
10692
    $secret,
10693
    OPENSSL_RAW_DATA,
10694
    $iv,
10695
    $tag,
10696
    '',
10697
    16
10698
  );
10699
10700
  return base64_encode($iv) . base64_encode($encrypted . $tag);
10701
}
10702