Issues (2037)

main/inc/lib/api.lib.php (2 issues)

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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $GLOB...'allowSelfReg'] : false could also return false which is incompatible with the documented return type true. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
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;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_courseMember does not seem to be defined for all execution paths leading up to this point.
Loading history...
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) {
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