Passed
Push — master ( 65d552...4599b6 )
by Angel Fernando Quiroz
10:41
created

api_is_multiple_url_enabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\AccessUrl;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\Language;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\SettingsCurrent;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\UserCourseCategory;
12
use Chamilo\CoreBundle\Exception\NotAllowedException;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
15
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
16
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
17
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
18
use Chamilo\CourseBundle\Entity\CGroup;
19
use Chamilo\CourseBundle\Entity\CLp;
20
use ChamiloSession as Session;
21
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
22
use Symfony\Component\Finder\Finder;
23
use Symfony\Component\Mime\Address;
24
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Validator\Constraints as Assert;
27
use ZipStream\Option\Archive;
28
use ZipStream\ZipStream;
29
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
30
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
31
32
/**
33
 * This is a code library for Chamilo.
34
 * It is included by default in every Chamilo file (through including the global.inc.php)
35
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
36
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
37
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
38
 */
39
40
// PHP version requirement.
41
define('REQUIRED_PHP_VERSION', '8.2');
42
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
43
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
44
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
45
46
// USER STATUS CONSTANTS
47
/** global status of a user: student */
48
define('STUDENT', 5);
49
/** global status of a user: course manager */
50
define('COURSEMANAGER', 1);
51
/** global status of a user: session admin */
52
define('SESSIONADMIN', 3);
53
/** global status of a user: human resources manager */
54
define('DRH', 4);
55
/** global status of a user: anonymous visitor */
56
define('ANONYMOUS', 6);
57
/** global status of a user: low security, necessary for inserting data from
58
 * the teacher through HTMLPurifier */
59
define('COURSEMANAGERLOWSECURITY', 10);
60
// Soft user status
61
define('PLATFORM_ADMIN', 11);
62
define('SESSION_COURSE_COACH', 12);
63
define('SESSION_GENERAL_COACH', 13);
64
define('COURSE_STUDENT', 14); //student subscribed in a course
65
define('SESSION_STUDENT', 15); //student subscribed in a session course
66
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
67
define('STUDENT_BOSS', 17); // student is boss
68
define('INVITEE', 20);
69
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
70
71
// COURSE VISIBILITY CONSTANTS
72
/** only visible for course admin */
73
define('COURSE_VISIBILITY_CLOSED', 0);
74
/** only visible for users registered in the course */
75
define('COURSE_VISIBILITY_REGISTERED', 1);
76
/** Open for all registered users on the platform */
77
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
78
/** Open for the whole world */
79
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
80
/** Invisible to all except admin */
81
define('COURSE_VISIBILITY_HIDDEN', 4);
82
83
define('COURSE_REQUEST_PENDING', 0);
84
define('COURSE_REQUEST_ACCEPTED', 1);
85
define('COURSE_REQUEST_REJECTED', 2);
86
define('DELETE_ACTION_ENABLED', false);
87
88
// EMAIL SENDING RECIPIENT CONSTANTS
89
define('SEND_EMAIL_EVERYONE', 1);
90
define('SEND_EMAIL_STUDENTS', 2);
91
define('SEND_EMAIL_TEACHERS', 3);
92
93
// SESSION VISIBILITY CONSTANTS
94
define('SESSION_VISIBLE_READ_ONLY', 1);
95
define('SESSION_VISIBLE', 2);
96
define('SESSION_INVISIBLE', 3); // not available
97
define('SESSION_AVAILABLE', 4);
98
99
define('SESSION_LINK_TARGET', '_self');
100
101
define('SUBSCRIBE_ALLOWED', 1);
102
define('SUBSCRIBE_NOT_ALLOWED', 0);
103
define('UNSUBSCRIBE_ALLOWED', 1);
104
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
105
106
// SURVEY VISIBILITY CONSTANTS
107
define('SURVEY_VISIBLE_TUTOR', 0);
108
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
109
define('SURVEY_VISIBLE_PUBLIC', 2);
110
111
// CONSTANTS defining all tools, using the english version
112
/* When you add a new tool you must add it into function api_get_tools_lists() too */
113
define('TOOL_DOCUMENT', 'document');
114
define('TOOL_LP_FINAL_ITEM', 'final_item');
115
define('TOOL_READOUT_TEXT', 'readout_text');
116
define('TOOL_THUMBNAIL', 'thumbnail');
117
define('TOOL_HOTPOTATOES', 'hotpotatoes');
118
define('TOOL_CALENDAR_EVENT', 'calendar_event');
119
define('TOOL_LINK', 'link');
120
define('TOOL_LINK_CATEGORY', 'link_category');
121
define('TOOL_COURSE_DESCRIPTION', 'course_description');
122
define('TOOL_SEARCH', 'search');
123
define('TOOL_LEARNPATH', 'learnpath');
124
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
125
define('TOOL_AGENDA', 'agenda');
126
define('TOOL_ANNOUNCEMENT', 'announcement');
127
define('TOOL_FORUM', 'forum');
128
define('TOOL_FORUM_CATEGORY', 'forum_category');
129
define('TOOL_FORUM_THREAD', 'forum_thread');
130
define('TOOL_FORUM_POST', 'forum_post');
131
define('TOOL_FORUM_ATTACH', 'forum_attachment');
132
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
133
define('TOOL_THREAD', 'thread');
134
define('TOOL_POST', 'post');
135
define('TOOL_DROPBOX', 'dropbox');
136
define('TOOL_QUIZ', 'quiz');
137
define('TOOL_TEST_CATEGORY', 'test_category');
138
define('TOOL_USER', 'user');
139
define('TOOL_GROUP', 'group');
140
define('TOOL_BLOGS', 'blog_management');
141
define('TOOL_CHAT', 'chat');
142
define('TOOL_STUDENTPUBLICATION', 'student_publication');
143
define('TOOL_TRACKING', 'tracking');
144
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
145
define('TOOL_COURSE_SETTING', 'course_setting');
146
define('TOOL_BACKUP', 'backup');
147
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
148
define('TOOL_RECYCLE_COURSE', 'recycle_course');
149
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
150
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
151
define('TOOL_UPLOAD', 'file_upload');
152
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
153
define('TOOL_SURVEY', 'survey');
154
//define('TOOL_WIKI', 'wiki');
155
define('TOOL_GLOSSARY', 'glossary');
156
define('TOOL_GRADEBOOK', 'gradebook');
157
define('TOOL_NOTEBOOK', 'notebook');
158
define('TOOL_ATTENDANCE', 'attendance');
159
define('TOOL_COURSE_PROGRESS', 'course_progress');
160
define('TOOL_PORTFOLIO', 'portfolio');
161
define('TOOL_PLAGIARISM', 'compilatio');
162
define('TOOL_XAPI', 'xapi');
163
164
// CONSTANTS defining Chamilo interface sections
165
define('SECTION_CAMPUS', 'mycampus');
166
define('SECTION_COURSES', 'mycourses');
167
define('SECTION_CATALOG', 'catalog');
168
define('SECTION_MYPROFILE', 'myprofile');
169
define('SECTION_MYAGENDA', 'myagenda');
170
define('SECTION_COURSE_ADMIN', 'course_admin');
171
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
172
define('SECTION_MYGRADEBOOK', 'mygradebook');
173
define('SECTION_TRACKING', 'session_my_space');
174
define('SECTION_SOCIAL', 'social-network');
175
define('SECTION_DASHBOARD', 'dashboard');
176
define('SECTION_REPORTS', 'reports');
177
define('SECTION_GLOBAL', 'global');
178
define('SECTION_INCLUDE', 'include');
179
define('SECTION_CUSTOMPAGE', 'custompage');
180
181
// CONSTANT name for local authentication source
182
define('PLATFORM_AUTH_SOURCE', 'platform');
183
define('CAS_AUTH_SOURCE', 'cas');
184
define('LDAP_AUTH_SOURCE', 'extldap');
185
186
// event logs types
187
define('LOG_COURSE_DELETE', 'course_deleted');
188
define('LOG_COURSE_CREATE', 'course_created');
189
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
190
191
// @todo replace 'soc_gr' with social_group
192
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
193
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
194
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
195
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
196
197
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
198
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
199
200
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
201
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
202
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
203
204
define('LOG_MESSAGE_DATA', 'message_data');
205
define('LOG_MESSAGE_DELETE', 'msg_deleted');
206
207
define('LOG_USER_DELETE', 'user_deleted');
208
define('LOG_USER_PREDELETE', 'user_predeleted');
209
define('LOG_USER_CREATE', 'user_created');
210
define('LOG_USER_UPDATE', 'user_updated');
211
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
212
define('LOG_USER_ENABLE', 'user_enable');
213
define('LOG_USER_DISABLE', 'user_disable');
214
define('LOG_USER_ANONYMIZE', 'user_anonymized');
215
define('LOG_USER_FIELD_CREATE', 'user_field_created');
216
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
217
define('LOG_SESSION_CREATE', 'session_created');
218
define('LOG_SESSION_DELETE', 'session_deleted');
219
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
220
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
221
define('LOG_SESSION_ADD_USER', 'session_add_user');
222
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
223
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
224
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
225
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
226
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
227
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
228
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
229
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
230
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
231
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
232
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
233
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
234
define('LOG_PROMOTION_CREATE', 'promotion_created');
235
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
236
define('LOG_CAREER_CREATE', 'career_created');
237
define('LOG_CAREER_DELETE', 'career_deleted');
238
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
239
//define('LOG_WIKI_ACCESS', 'wiki_page_view');
240
// All results from an exercise
241
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
242
// Logs only the one attempt
243
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
244
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
245
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
246
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
247
248
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
249
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
250
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
251
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
252
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
253
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
254
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
255
256
// Event logs data types (max 20 chars)
257
define('LOG_COURSE_CODE', 'course_code');
258
define('LOG_COURSE_ID', 'course_id');
259
define('LOG_USER_ID', 'user_id');
260
define('LOG_USER_OBJECT', 'user_object');
261
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
262
define('LOG_SESSION_ID', 'session_id');
263
264
define('LOG_QUESTION_ID', 'question_id');
265
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
266
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
267
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
268
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
269
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
270
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
271
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
272
define('LOG_CAREER_ID', 'career_id');
273
define('LOG_PROMOTION_ID', 'promotion_id');
274
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
275
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
276
define('LOG_GRADEBOOK_ID', 'gradebook_id');
277
//define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
278
define('LOG_EXERCISE_ID', 'exercise_id');
279
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
280
define('LOG_LP_ID', 'lp_id');
281
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
282
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
283
284
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
285
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
286
define('LOG_WORK_DATA', 'work_data_array');
287
288
define('LOG_MY_FOLDER_PATH', 'path');
289
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
290
291
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
292
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
293
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
294
295
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
296
297
define('LOG_QUESTION_CREATED', 'question_created');
298
define('LOG_QUESTION_UPDATED', 'question_updated');
299
define('LOG_QUESTION_DELETED', 'question_deleted');
300
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
301
302
define('LOG_SURVEY_ID', 'survey_id');
303
define('LOG_SURVEY_CREATED', 'survey_created');
304
define('LOG_SURVEY_DELETED', 'survey_deleted');
305
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
306
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
307
308
//used when login_is_email setting is true
309
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
310
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
311
312
// This constant is a result of Windows OS detection, it has a boolean value:
313
// true whether the server runs on Windows OS, false otherwise.
314
define('IS_WINDOWS_OS', api_is_windows_os());
315
316
// Patterns for processing paths. Examples.
317
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
318
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
319
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
320
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
321
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
322
// basic (leaf elements)
323
define('REL_CODE_PATH', 'REL_CODE_PATH');
324
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
325
define('REL_HOME_PATH', 'REL_HOME_PATH');
326
327
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
328
define('WEB_PATH', 'WEB_PATH');
329
define('SYS_PATH', 'SYS_PATH');
330
define('SYMFONY_SYS_PATH', 'SYMFONY_SYS_PATH');
331
332
define('REL_PATH', 'REL_PATH');
333
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
334
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
335
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
336
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
337
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
338
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
339
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
340
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
341
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
342
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
343
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
344
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
345
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
346
define('LIBRARY_PATH', 'LIBRARY_PATH');
347
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
348
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
349
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
350
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
351
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
352
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
353
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
354
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
355
356
// Relations type with Course manager
357
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
358
359
// Relations type with Human resources manager
360
define('COURSE_RELATION_TYPE_RRHH', 1);
361
362
// User image sizes
363
define('USER_IMAGE_SIZE_ORIGINAL', 1);
364
define('USER_IMAGE_SIZE_BIG', 2);
365
define('USER_IMAGE_SIZE_MEDIUM', 3);
366
define('USER_IMAGE_SIZE_SMALL', 4);
367
368
// Gradebook link constants
369
// Please do not change existing values, they are used in the database !
370
define('GRADEBOOK_ITEM_LIMIT', 1000);
371
372
define('LINK_EXERCISE', 1);
373
define('LINK_DROPBOX', 2);
374
define('LINK_STUDENTPUBLICATION', 3);
375
define('LINK_LEARNPATH', 4);
376
define('LINK_FORUM_THREAD', 5);
377
//define('LINK_WORK',6);
378
define('LINK_ATTENDANCE', 7);
379
define('LINK_SURVEY', 8);
380
define('LINK_HOTPOTATOES', 9);
381
define('LINK_PORTFOLIO', 10);
382
383
// Score display types constants
384
define('SCORE_DIV', 1); // X / Y
385
define('SCORE_PERCENT', 2); // XX %
386
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
387
define('SCORE_AVERAGE', 4); // XX %
388
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
389
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
390
define('SCORE_SIMPLE', 7); // X
391
define('SCORE_IGNORE_SPLIT', 8); //  ??
392
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
393
define('SCORE_CUSTOM', 10); // Good!
394
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
395
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
396
define('SCORE_ONLY_SCORE', 13); // X - Good!
397
define('SCORE_NUMERIC', 14);
398
399
define('SCORE_BOTH', 1);
400
define('SCORE_ONLY_DEFAULT', 2);
401
define('SCORE_ONLY_CUSTOM', 3);
402
403
// From display.lib.php
404
405
define('MAX_LENGTH_BREADCRUMB', 100);
406
define('ICON_SIZE_ATOM', 8);
407
define('ICON_SIZE_TINY', 16);
408
define('ICON_SIZE_SMALL', 22);
409
define('ICON_SIZE_MEDIUM', 32);
410
define('ICON_SIZE_LARGE', 48);
411
define('ICON_SIZE_BIG', 64);
412
define('ICON_SIZE_HUGE', 128);
413
define('SHOW_TEXT_NEAR_ICONS', false);
414
415
// Session catalog
416
define('CATALOG_COURSES', 0);
417
define('CATALOG_SESSIONS', 1);
418
define('CATALOG_COURSES_SESSIONS', 2);
419
420
// Hook type events, pre-process and post-process.
421
// All means to be executed for both hook event types
422
define('HOOK_EVENT_TYPE_PRE', 0);
423
define('HOOK_EVENT_TYPE_POST', 1);
424
define('HOOK_EVENT_TYPE_ALL', 10);
425
426
// Group permissions
427
define('GROUP_PERMISSION_OPEN', '1');
428
define('GROUP_PERMISSION_CLOSED', '2');
429
430
// Group user permissions
431
define('GROUP_USER_PERMISSION_ADMIN', 1); // the admin of a group
432
define('GROUP_USER_PERMISSION_READER', 2); // a normal user
433
define('GROUP_USER_PERMISSION_PENDING_INVITATION', 3); // When an admin/moderator invites a user
434
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', 4); // an user joins a group
435
define('GROUP_USER_PERMISSION_MODERATOR', 5); // a moderator
436
define('GROUP_USER_PERMISSION_ANONYMOUS', 6); // an anonymous user
437
define('GROUP_USER_PERMISSION_HRM', 7); // a human resources manager
438
439
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
440
define('GROUP_IMAGE_SIZE_BIG', 2);
441
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
442
define('GROUP_IMAGE_SIZE_SMALL', 4);
443
define('GROUP_TITLE_LENGTH', 50);
444
445
// Exercise
446
// @todo move into a class
447
define('ALL_ON_ONE_PAGE', 1);
448
define('ONE_PER_PAGE', 2);
449
450
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
451
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
452
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
453
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
454
455
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
456
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
457
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
458
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
459
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
460
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
461
define('RESULT_DISABLE_RANKING', 6);
462
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
463
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
464
define('RESULT_DISABLE_RADAR', 9);
465
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
466
467
define('EXERCISE_MAX_NAME_SIZE', 80);
468
469
// Question types (edit next array as well when adding values)
470
// @todo move into a class
471
define('UNIQUE_ANSWER', 1);
472
define('MULTIPLE_ANSWER', 2);
473
define('FILL_IN_BLANKS', 3);
474
define('MATCHING', 4);
475
define('FREE_ANSWER', 5);
476
define('HOT_SPOT', 6);
477
define('HOT_SPOT_ORDER', 7);
478
define('HOT_SPOT_DELINEATION', 8);
479
define('MULTIPLE_ANSWER_COMBINATION', 9);
480
define('UNIQUE_ANSWER_NO_OPTION', 10);
481
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
482
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
483
define('ORAL_EXPRESSION', 13);
484
define('GLOBAL_MULTIPLE_ANSWER', 14);
485
define('MEDIA_QUESTION', 15);
486
define('CALCULATED_ANSWER', 16);
487
define('UNIQUE_ANSWER_IMAGE', 17);
488
define('DRAGGABLE', 18);
489
define('MATCHING_DRAGGABLE', 19);
490
define('ANNOTATION', 20);
491
define('READING_COMPREHENSION', 21);
492
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
493
494
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
495
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
496
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
497
498
// Question selection type
499
define('EX_Q_SELECTION_ORDERED', 1);
500
define('EX_Q_SELECTION_RANDOM', 2);
501
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
502
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
503
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
504
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
505
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
506
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
507
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
508
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
509
510
// Used to save the skill_rel_item table
511
define('ITEM_TYPE_EXERCISE', 1);
512
define('ITEM_TYPE_HOTPOTATOES', 2);
513
define('ITEM_TYPE_LINK', 3);
514
define('ITEM_TYPE_LEARNPATH', 4);
515
define('ITEM_TYPE_GRADEBOOK', 5);
516
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
517
//define('ITEM_TYPE_FORUM', 7);
518
define('ITEM_TYPE_ATTENDANCE', 8);
519
define('ITEM_TYPE_SURVEY', 9);
520
define('ITEM_TYPE_FORUM_THREAD', 10);
521
define('ITEM_TYPE_PORTFOLIO', 11);
522
523
// Course description blocks.
524
define('ADD_BLOCK', 8);
525
526
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
527
define(
528
    'QUESTION_TYPES',
529
    UNIQUE_ANSWER.':'.
530
    MULTIPLE_ANSWER.':'.
531
    FILL_IN_BLANKS.':'.
532
    MATCHING.':'.
533
    FREE_ANSWER.':'.
534
    HOT_SPOT.':'.
535
    HOT_SPOT_ORDER.':'.
536
    HOT_SPOT_DELINEATION.':'.
537
    MULTIPLE_ANSWER_COMBINATION.':'.
538
    UNIQUE_ANSWER_NO_OPTION.':'.
539
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
540
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
541
    ORAL_EXPRESSION.':'.
542
    GLOBAL_MULTIPLE_ANSWER.':'.
543
    MEDIA_QUESTION.':'.
544
    CALCULATED_ANSWER.':'.
545
    UNIQUE_ANSWER_IMAGE.':'.
546
    DRAGGABLE.':'.
547
    MATCHING_DRAGGABLE.':'.
548
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
549
    ANNOTATION
550
);
551
552
//Some alias used in the QTI exports
553
define('MCUA', 1);
554
define('TF', 1);
555
define('MCMA', 2);
556
define('FIB', 3);
557
558
// Message
559
define('MESSAGE_STATUS_INVITATION_PENDING', 5);
560
define('MESSAGE_STATUS_INVITATION_ACCEPTED', 6);
561
define('MESSAGE_STATUS_INVITATION_DENIED', 7);
562
define('MESSAGE_STATUS_WALL', 8);
563
564
define('MESSAGE_STATUS_WALL_DELETE', 9);
565
define('MESSAGE_STATUS_WALL_POST', 10);
566
567
define('MESSAGE_STATUS_FORUM', 12);
568
define('MESSAGE_STATUS_PROMOTED', 13);
569
570
// Images
571
define('IMAGE_WALL_SMALL_SIZE', 200);
572
define('IMAGE_WALL_MEDIUM_SIZE', 500);
573
define('IMAGE_WALL_BIG_SIZE', 2000);
574
define('IMAGE_WALL_SMALL', 'small');
575
define('IMAGE_WALL_MEDIUM', 'medium');
576
define('IMAGE_WALL_BIG', 'big');
577
578
// Social PLUGIN PLACES
579
define('SOCIAL_LEFT_PLUGIN', 1);
580
define('SOCIAL_CENTER_PLUGIN', 2);
581
define('SOCIAL_RIGHT_PLUGIN', 3);
582
define('CUT_GROUP_NAME', 50);
583
584
/**
585
 * FormValidator Filter.
586
 */
587
define('NO_HTML', 1);
588
define('STUDENT_HTML', 2);
589
define('TEACHER_HTML', 3);
590
define('STUDENT_HTML_FULLPAGE', 4);
591
define('TEACHER_HTML_FULLPAGE', 5);
592
593
// Timeline
594
define('TIMELINE_STATUS_ACTIVE', '1');
595
define('TIMELINE_STATUS_INACTIVE', '2');
596
597
// Event email template class
598
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
599
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
600
601
// Course home
602
define('SHORTCUTS_HORIZONTAL', 0);
603
define('SHORTCUTS_VERTICAL', 1);
604
605
// Course copy
606
define('FILE_SKIP', 1);
607
define('FILE_RENAME', 2);
608
define('FILE_OVERWRITE', 3);
609
define('UTF8_CONVERT', false); //false by default
610
611
define('DOCUMENT', 'file');
612
define('FOLDER', 'folder');
613
614
define('RESOURCE_ASSET', 'asset');
615
define('RESOURCE_DOCUMENT', 'document');
616
define('RESOURCE_GLOSSARY', 'glossary');
617
define('RESOURCE_EVENT', 'calendar_event');
618
define('RESOURCE_LINK', 'link');
619
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
620
define('RESOURCE_LEARNPATH', 'learnpath');
621
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
622
define('RESOURCE_ANNOUNCEMENT', 'announcement');
623
define('RESOURCE_FORUM', 'forum');
624
define('RESOURCE_FORUMTOPIC', 'thread');
625
define('RESOURCE_FORUMPOST', 'post');
626
define('RESOURCE_QUIZ', 'quiz');
627
define('RESOURCE_TEST_CATEGORY', 'test_category');
628
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
629
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
630
define('RESOURCE_LINKCATEGORY', 'Link_Category');
631
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
632
define('RESOURCE_SCORM', 'Scorm');
633
define('RESOURCE_SURVEY', 'survey');
634
define('RESOURCE_SURVEYQUESTION', 'survey_question');
635
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
636
//define('RESOURCE_WIKI', 'wiki');
637
define('RESOURCE_THEMATIC', 'thematic');
638
define('RESOURCE_ATTENDANCE', 'attendance');
639
define('RESOURCE_WORK', 'work');
640
define('RESOURCE_SESSION_COURSE', 'session_course');
641
define('RESOURCE_GRADEBOOK', 'gradebook');
642
define('ADD_THEMATIC_PLAN', 6);
643
644
// Max online users to show per page (whoisonline)
645
define('MAX_ONLINE_USERS', 12);
646
647
define('TOOL_AUTHORING', 'toolauthoring');
648
define('TOOL_INTERACTION', 'toolinteraction');
649
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
650
define('TOOL_ADMIN', 'tooladmin');
651
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
652
define('TOOL_DRH', 'tool_drh');
653
define('TOOL_STUDENT_VIEW', 'toolstudentview');
654
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
655
656
// Search settings (from main/inc/lib/search/IndexableChunk.class.php )
657
// some constants to avoid serialize string keys on serialized data array
658
define('SE_COURSE_ID', 0);
659
define('SE_TOOL_ID', 1);
660
define('SE_DATA', 2);
661
define('SE_USER', 3);
662
663
// in some cases we need top differenciate xapian documents of the same tool
664
define('SE_DOCTYPE_EXERCISE_EXERCISE', 0);
665
define('SE_DOCTYPE_EXERCISE_QUESTION', 1);
666
667
// xapian prefixes
668
define('XAPIAN_PREFIX_COURSEID', 'C');
669
define('XAPIAN_PREFIX_TOOLID', 'O');
670
671
// User active field constants
672
define('USER_ACTIVE', 1);
673
define('USER_INACTIVE', 0);
674
define('USER_INACTIVE_AUTOMATIC', -1);
675
define('USER_SOFT_DELETED', -2);
676
677
/**
678
 * Returns a path to a certain resource within Chamilo.
679
 *
680
 * @param string $path A path which type is to be converted. Also, it may be a defined constant for a path.
681
 *
682
 * @return string the requested path or the converted path
683
 *
684
 * Notes about the current behaviour model:
685
 * 1. Windows back-slashes are converted to slashes in the result.
686
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
687
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
688
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
689
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
690
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
691
 * It has not been identified as needed yet.
692
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
693
 *
694
 * Vchamilo changes : allow using an alternate configuration
695
 * to get vchamilo  instance paths
696
 */
697
function api_get_path($path = '', $configuration = [])
698
{
699
    global $paths;
700
701
    // get proper configuration data if exists
702
    global $_configuration;
703
704
    $emptyConfigurationParam = false;
705
    if (empty($configuration)) {
706
        $configuration = (array) $_configuration;
707
        $emptyConfigurationParam = true;
708
    }
709
710
    $root_sys = Container::getProjectDir();
711
    $root_web = '';
712
    if (isset(Container::$container)) {
713
        $root_web = Container::$container->get('router')->generate(
714
            'index',
715
            [],
716
            UrlGeneratorInterface::ABSOLUTE_URL
717
        );
718
    }
719
720
    /*if (api_get_multiple_access_url()) {
721
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the main_api.lib.php
722
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
723
            // We look into the DB the function api_get_access_url
724
            $urlInfo = api_get_access_url($configuration['access_url']);
725
            // Avoid default value
726
            $defaultValues = ['http://localhost/', 'https://localhost/'];
727
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
728
                $root_web = 1 == $urlInfo['active'] ? $urlInfo['url'] : $configuration['root_web'];
729
            }
730
        }
731
    }*/
732
733
    $paths = [
734
        WEB_PATH => $root_web,
735
        SYMFONY_SYS_PATH => $root_sys,
736
        SYS_PATH => $root_sys.'public/',
737
        REL_PATH => '',
738
        CONFIGURATION_PATH => 'app/config/',
739
        LIBRARY_PATH => $root_sys.'public/main/inc/lib/',
740
741
        REL_COURSE_PATH => '',
742
        REL_CODE_PATH => '/main/',
743
744
        SYS_CODE_PATH => $root_sys.'public/main/',
745
        SYS_CSS_PATH => $root_sys.'public/build/css/',
746
        SYS_PLUGIN_PATH => $root_sys.'public/plugin/',
747
        SYS_ARCHIVE_PATH => $root_sys.'var/cache/',
748
        SYS_TEST_PATH => $root_sys.'tests/',
749
        SYS_TEMPLATE_PATH => $root_sys.'public/main/template/',
750
        SYS_PUBLIC_PATH => $root_sys.'public/',
751
        SYS_FONTS_PATH => $root_sys.'public/fonts/',
752
753
        WEB_CODE_PATH => $root_web.'main/',
754
        WEB_PLUGIN_ASSET_PATH => $root_web.'plugins/',
755
        WEB_COURSE_PATH => $root_web.'course/',
756
        WEB_IMG_PATH => $root_web.'img/',
757
        WEB_CSS_PATH => $root_web.'build/css/',
758
        WEB_AJAX_PATH => $root_web.'main/inc/ajax/',
759
        WEB_LIBRARY_PATH => $root_web.'main/inc/lib/',
760
        WEB_LIBRARY_JS_PATH => $root_web.'main/inc/lib/javascript/',
761
        WEB_PLUGIN_PATH => $root_web.'plugin/',
762
        WEB_PUBLIC_PATH => $root_web,
763
    ];
764
765
    $root_rel = '';
766
767
    global $virtualChamilo;
768
    if (!empty($virtualChamilo)) {
769
        $paths[SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
770
        //$paths[SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
771
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
772
        $paths[WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
773
        //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
774
775
        // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
776
777
        // RewriteEngine On
778
        // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
779
780
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
781
        //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
782
        //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
783
    }
784
785
    $path = trim($path);
786
787
    // Retrieving a common-purpose path.
788
    if (isset($paths[$path])) {
789
        return $paths[$path];
790
    }
791
792
    return false;
793
}
794
795
/**
796
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
797
 *
798
 * @param string $path the input path
799
 *
800
 * @return string returns the modified path
801
 */
802
function api_add_trailing_slash($path)
803
{
804
    return '/' === substr($path, -1) ? $path : $path.'/';
805
}
806
807
/**
808
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
809
 *
810
 * @param string $path the input path
811
 *
812
 * @return string returns the modified path
813
 */
814
function api_remove_trailing_slash($path)
815
{
816
    return '/' === substr($path, -1) ? substr($path, 0, -1) : $path;
817
}
818
819
/**
820
 * Checks the RFC 3986 syntax of a given URL.
821
 *
822
 * @param string $url      the URL to be checked
823
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
824
 *
825
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
826
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
827
 *
828
 * @see http://drupal.org
829
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
830
 * @see http://bugs.php.net/51192
831
 */
832
function api_valid_url($url, $absolute = false)
833
{
834
    if ($absolute) {
835
        if (preg_match("
836
            /^                                                      # Start at the beginning of the text
837
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
838
            (?:                                                     # Userinfo (optional) which is typically
839
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
840
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
841
            )?
842
            (?:
843
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
844
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
845
            )
846
            (?::[0-9]+)?                                            # Server port number (optional)
847
            (?:[\/|\?]
848
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
849
            *)?
850
            $/xi", $url)) {
851
            return $url;
852
        }
853
854
        return false;
855
    } else {
856
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
857
    }
858
}
859
860
/**
861
 * Checks whether a given string looks roughly like an email address.
862
 *
863
 * @param string $address the e-mail address to be checked
864
 *
865
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
866
 */
867
function api_valid_email($address)
868
{
869
    return filter_var($address, FILTER_VALIDATE_EMAIL);
870
}
871
872
/**
873
 * Function used to protect a course script.
874
 * The function blocks access when
875
 * - there is no $_SESSION["_course"] defined; or
876
 * - $is_allowed_in_course is set to false (this depends on the course
877
 * visibility and user status).
878
 *
879
 * This is only the first proposal, test and improve!
880
 *
881
 * @param bool Option to print headers when displaying error message. Default: false
882
 * @param bool whether session admins should be allowed or not
883
 * @param string $checkTool check if tool is available for users (user, group)
884
 *
885
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
886
 *
887
 * @todo replace global variable
888
 *
889
 * @author Roan Embrechts
890
 */
891
function api_protect_course_script($print_headers = false, $allow_session_admins = false, string $checkTool = '', $cid = null): bool
892
{
893
    $course_info = api_get_course_info();
894
    if (empty($course_info) && isset($_REQUEST['cid'])) {
895
        $course_info = api_get_course_info_by_id((int) $_REQUEST['cid']);
896
    }
897
898
    if (isset($cid)) {
899
        $course_info = api_get_course_info_by_id($cid);
900
    }
901
902
    if (empty($course_info)) {
903
        api_not_allowed($print_headers);
904
905
        return false;
906
    }
907
908
    if (api_is_drh()) {
909
        return true;
910
    }
911
912
    // Session admin has access to course
913
    $sessionAccess = ('true' === api_get_setting('session.session_admins_access_all_content'));
914
    if ($sessionAccess) {
915
        $allow_session_admins = true;
916
    }
917
918
    if (api_is_platform_admin($allow_session_admins)) {
919
        return true;
920
    }
921
922
    $isAllowedInCourse = api_is_allowed_in_course();
923
    $is_visible = false;
924
    if (isset($course_info) && isset($course_info['visibility'])) {
925
        switch ($course_info['visibility']) {
926
            default:
927
            case Course::CLOSED:
928
                // Completely closed: the course is only accessible to the teachers. - 0
929
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
930
                    $is_visible = true;
931
                }
932
                break;
933
            case Course::REGISTERED:
934
                // Private - access authorized to course members only - 1
935
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
936
                    $is_visible = true;
937
                }
938
                break;
939
            case Course::OPEN_PLATFORM:
940
                // Open - access allowed for users registered on the platform - 2
941
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
942
                    $is_visible = true;
943
                }
944
                break;
945
            case Course::OPEN_WORLD:
946
                //Open - access allowed for the whole world - 3
947
                $is_visible = true;
948
                break;
949
            case Course::HIDDEN:
950
                //Completely closed: the course is only accessible to the teachers. - 0
951
                if (api_is_platform_admin()) {
952
                    $is_visible = true;
953
                }
954
                break;
955
        }
956
957
        //If password is set and user is not registered to the course then the course is not visible
958
        if (false === $isAllowedInCourse &&
959
            isset($course_info['registration_code']) &&
960
            !empty($course_info['registration_code'])
961
        ) {
962
            $is_visible = false;
963
        }
964
    }
965
966
    if (!empty($checkTool)) {
967
        if (!api_is_allowed_to_edit(true, true, true)) {
968
            $toolInfo = api_get_tool_information_by_name($checkTool);
969
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && 0 == $toolInfo['visibility']) {
970
                api_not_allowed(true);
971
972
                return false;
973
            }
974
        }
975
    }
976
977
    // Check session visibility
978
    $session_id = api_get_session_id();
979
980
    if (!empty($session_id)) {
981
        // $isAllowedInCourse was set in local.inc.php
982
        if (!$isAllowedInCourse) {
983
            $is_visible = false;
984
        }
985
        // Check if course is inside session.
986
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
987
            $is_visible = false;
988
        }
989
    }
990
991
    if (!$is_visible) {
992
        api_not_allowed($print_headers);
993
994
        return false;
995
    }
996
997
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
998
        $plugin = Positioning::create();
999
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1000
        if ('true' === $block) {
1001
            $currentPath = $_SERVER['PHP_SELF'];
1002
            // Allowed only this course paths.
1003
            $paths = [
1004
                '/plugin/positioning/start.php',
1005
                '/plugin/positioning/start_student.php',
1006
                '/main/course_home/course_home.php',
1007
                '/main/exercise/overview.php',
1008
            ];
1009
1010
            if (!in_array($currentPath, $paths, true)) {
1011
                // Check if entering an exercise.
1012
                // @todo remove global $current_course_tool
1013
                /*global $current_course_tool;
1014
                if ('quiz' !== $current_course_tool) {
1015
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1016
                    if ($initialData && isset($initialData['exercise_id'])) {
1017
                        $results = Event::getExerciseResultsByUser(
1018
                            api_get_user_id(),
1019
                            $initialData['exercise_id'],
1020
                            $course_info['real_id'],
1021
                            $session_id
1022
                        );
1023
                        if (empty($results)) {
1024
                            api_not_allowed($print_headers);
1025
1026
                            return false;
1027
                        }
1028
                    }
1029
                }*/
1030
            }
1031
        }
1032
    }
1033
1034
    api_block_inactive_user();
1035
1036
    return true;
1037
}
1038
1039
/**
1040
 * Function used to protect an admin script.
1041
 *
1042
 * The function blocks access when the user has no platform admin rights
1043
 * with an error message printed on default output
1044
 *
1045
 * @param bool Whether to allow session admins as well
1046
 * @param bool Whether to allow HR directors as well
1047
 * @param string An optional message (already passed through get_lang)
1048
 *
1049
 * @return bool True if user is allowed, false otherwise.
1050
 *              The function also outputs an error message in case not allowed
1051
 *
1052
 * @author Roan Embrechts (original author)
1053
 */
1054
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1055
{
1056
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1057
        api_not_allowed(true, $message);
1058
1059
        return false;
1060
    }
1061
    api_block_inactive_user();
1062
1063
    return true;
1064
}
1065
1066
/**
1067
 * Blocks inactive users with a currently active session from accessing more pages "live".
1068
 *
1069
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1070
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1071
 */
1072
function api_block_inactive_user()
1073
{
1074
    $data = true;
1075
    if ('true' !== api_get_setting('security.security_block_inactive_users_immediately')) {
1076
        return $data;
1077
    }
1078
1079
    $userId = api_get_user_id();
1080
    $homeUrl = api_get_path(WEB_PATH);
1081
    if (0 == $userId) {
1082
        return $data;
1083
    }
1084
1085
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1086
            WHERE id = $userId";
1087
1088
    $result = Database::query($sql);
1089
    if (Database::num_rows($result) > 0) {
1090
        $result_array = Database::fetch_array($result);
1091
        $data = (bool) $result_array['active'];
1092
    }
1093
    if (false == $data) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1094
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1095
        $tpl->assign('hide_login_link', 1);
1096
1097
        //api_not_allowed(true, get_lang('Account inactive'));
1098
        // we were not in a course, return to home page
1099
        $msg = Display::return_message(
1100
            get_lang('Account inactive'),
1101
            'error',
1102
            false
1103
        );
1104
1105
        $msg .= '<p class="text-center">
1106
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1107
1108
        $tpl->assign('content', $msg);
1109
        $tpl->display_one_col_template();
1110
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1111
    }
1112
1113
    return $data;
1114
}
1115
1116
/**
1117
 * Function used to protect a teacher script.
1118
 * The function blocks access when the user has no teacher rights.
1119
 *
1120
 * @return bool True if the current user can access the script, false otherwise
1121
 *
1122
 * @author Yoselyn Castillo
1123
 */
1124
function api_protect_teacher_script()
1125
{
1126
    if (!api_is_allowed_to_edit()) {
1127
        api_not_allowed(true);
1128
1129
        return false;
1130
    }
1131
1132
    return true;
1133
}
1134
1135
/**
1136
 * Function used to prevent anonymous users from accessing a script.
1137
 *
1138
 * @param bool $printHeaders
1139
 *
1140
 * @return bool
1141
 */
1142
function api_block_anonymous_users($printHeaders = true)
1143
{
1144
    $isAuth = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
1145
1146
    if (false === $isAuth) {
1147
        api_not_allowed($printHeaders);
1148
1149
        return false;
1150
    }
1151
1152
    api_block_inactive_user();
1153
1154
    return true;
1155
}
1156
1157
/**
1158
 * Returns a rough evaluation of the browser's name and version based on very
1159
 * simple regexp.
1160
 *
1161
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1162
 */
1163
function api_get_navigator()
1164
{
1165
    $navigator = 'Unknown';
1166
    $version = 0;
1167
1168
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1169
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1170
    }
1171
1172
    if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
1173
        $navigator = 'Opera';
1174
        [, $version] = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1175
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
1176
        $navigator = 'Edge';
1177
        [, $version] = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1178
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
1179
        $navigator = 'Internet Explorer';
1180
        [, $version] = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1181
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
1182
        $navigator = 'Chrome';
1183
        [, $version] = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1184
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
1185
        $navigator = 'Safari';
1186
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Version/')) {
1187
            // If this Safari does have the "Version/" string in its user agent
1188
            // then use that as a version indicator rather than what's after
1189
            // "Safari/" which is rather a "build number" or something
1190
            [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1191
        } else {
1192
            [, $version] = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1193
        }
1194
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
1195
        $navigator = 'Firefox';
1196
        [, $version] = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1197
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape')) {
1198
        $navigator = 'Netscape';
1199
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/')) {
1200
            [, $version] = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1201
        } else {
1202
            [, $version] = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1203
        }
1204
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror')) {
1205
        $navigator = 'Konqueror';
1206
        [, $version] = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1207
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit')) {
1208
        $navigator = 'AppleWebKit';
1209
        [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1210
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko')) {
1211
        $navigator = 'Mozilla';
1212
        [, $version] = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1213
    }
1214
1215
    // Now cut extra stuff around (mostly *after*) the version number
1216
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1217
1218
    if (false === strpos($version, '.')) {
1219
        $version = number_format(doubleval($version), 1);
1220
    }
1221
1222
    return ['name' => $navigator, 'version' => $version];
1223
}
1224
1225
/**
1226
 * This function returns the id of the user which is stored in the $_user array.
1227
 *
1228
 * example: The function can be used to check if a user is logged in
1229
 *          if (api_get_user_id())
1230
 *
1231
 * @return int the id of the current user, 0 if is empty
1232
 */
1233
function api_get_user_id()
1234
{
1235
    $userInfo = Session::read('_user');
1236
    if ($userInfo && isset($userInfo['user_id'])) {
1237
        return (int) $userInfo['user_id'];
1238
    }
1239
1240
    return 0;
1241
}
1242
1243
/**
1244
 * Formats user information into a standard array
1245
 * This function should be only used inside api_get_user_info().
1246
 *
1247
 * @param array Non-standard user array
0 ignored issues
show
Documentation Bug introduced by
The doc comment Non-standard at position 0 could not be parsed: Unknown type name 'Non-standard' at position 0 in Non-standard.
Loading history...
1248
 * @param bool $add_password
1249
 * @param bool $loadAvatars  turn off to improve performance
1250
 *
1251
 * @return array Standard user array
1252
 */
1253
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1254
{
1255
    $result = [];
1256
1257
    if (!isset($user['id'])) {
1258
        return [];
1259
    }
1260
1261
    $result['firstname'] = null;
1262
    $result['lastname'] = null;
1263
1264
    if (isset($user['firstname']) && isset($user['lastname'])) {
1265
        // with only lowercase
1266
        $result['firstname'] = $user['firstname'];
1267
        $result['lastname'] = $user['lastname'];
1268
    } elseif (isset($user['firstName']) && isset($user['lastName'])) {
1269
        // with uppercase letters
1270
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1271
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1272
    }
1273
1274
    if (isset($user['email'])) {
1275
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1276
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1277
    } else {
1278
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1279
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1280
    }
1281
1282
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1283
    $result['complete_name_with_username'] = $result['complete_name'];
1284
1285
    if (!empty($user['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1286
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1287
    }
1288
1289
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1290
    if (!empty($user['email'])) {
1291
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1292
        if ($showEmail) {
1293
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1294
        }
1295
    } else {
1296
        $result['complete_name_with_email'] = $result['complete_name'];
1297
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1298
    }
1299
1300
    // Kept for historical reasons
1301
    $result['firstName'] = $result['firstname'];
1302
    $result['lastName'] = $result['lastname'];
1303
1304
    $attributes = [
1305
        'phone',
1306
        'address',
1307
        'picture_uri',
1308
        'official_code',
1309
        'status',
1310
        'active',
1311
        'auth_source',
1312
        'username',
1313
        'theme',
1314
        'language',
1315
        'locale',
1316
        'creator_id',
1317
        'registration_date',
1318
        'hr_dept_id',
1319
        'expiration_date',
1320
        'last_login',
1321
        'user_is_online',
1322
        'profile_completed',
1323
    ];
1324
1325
    if ('true' === api_get_setting('extended_profile')) {
1326
        $attributes[] = 'competences';
1327
        $attributes[] = 'diplomas';
1328
        $attributes[] = 'teach';
1329
        $attributes[] = 'openarea';
1330
    }
1331
1332
    foreach ($attributes as $attribute) {
1333
        $result[$attribute] = $user[$attribute] ?? null;
1334
    }
1335
1336
    $user_id = (int) $user['id'];
1337
    // Maintain the user_id index for backwards compatibility
1338
    $result['user_id'] = $result['id'] = $user_id;
1339
1340
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1341
    $result['has_certificates'] = 0;
1342
    if (!empty($hasCertificates)) {
1343
        $result['has_certificates'] = 1;
1344
    }
1345
1346
    $result['icon_status'] = '';
1347
    $result['icon_status_medium'] = '';
1348
    $result['is_admin'] = UserManager::is_admin($user_id);
1349
1350
    // Getting user avatar.
1351
    if ($loadAvatars) {
1352
        $result['avatar'] = '';
1353
        $result['avatar_no_query'] = '';
1354
        $result['avatar_small'] = '';
1355
        $result['avatar_medium'] = '';
1356
1357
        if (empty($user['avatar'])) {
1358
            $originalFile = UserManager::getUserPicture(
1359
                $user_id,
1360
                USER_IMAGE_SIZE_ORIGINAL,
1361
                null,
1362
                $result
1363
            );
1364
            $result['avatar'] = $originalFile;
1365
            $avatarString = explode('?', $result['avatar']);
1366
            $result['avatar_no_query'] = reset($avatarString);
1367
        } else {
1368
            $result['avatar'] = $user['avatar'];
1369
            $avatarString = explode('?', $user['avatar']);
1370
            $result['avatar_no_query'] = reset($avatarString);
1371
        }
1372
1373
        if (!isset($user['avatar_small'])) {
1374
            $smallFile = UserManager::getUserPicture(
1375
                $user_id,
1376
                USER_IMAGE_SIZE_SMALL,
1377
                null,
1378
                $result
1379
            );
1380
            $result['avatar_small'] = $smallFile;
1381
        } else {
1382
            $result['avatar_small'] = $user['avatar_small'];
1383
        }
1384
1385
        if (!isset($user['avatar_medium'])) {
1386
            $mediumFile = UserManager::getUserPicture(
1387
                $user_id,
1388
                USER_IMAGE_SIZE_MEDIUM,
1389
                null,
1390
                $result
1391
            );
1392
            $result['avatar_medium'] = $mediumFile;
1393
        } else {
1394
            $result['avatar_medium'] = $user['avatar_medium'];
1395
        }
1396
1397
        $urlImg = api_get_path(WEB_IMG_PATH);
1398
        $iconStatus = '';
1399
        $iconStatusMedium = '';
1400
        $label = '';
1401
1402
        switch ($result['status']) {
1403
            case STUDENT:
1404
                if ($result['has_certificates']) {
1405
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1406
                    $label = get_lang('Graduated');
1407
                } else {
1408
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1409
                    $label = get_lang('Student');
1410
                }
1411
                break;
1412
            case COURSEMANAGER:
1413
                if ($result['is_admin']) {
1414
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1415
                    $label = get_lang('Admin');
1416
                } else {
1417
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1418
                    $label = get_lang('Teacher');
1419
                }
1420
                break;
1421
            case STUDENT_BOSS:
1422
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1423
                $label = get_lang('StudentBoss');
1424
                break;
1425
        }
1426
1427
        if (!empty($iconStatus)) {
1428
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1429
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1430
        }
1431
1432
        $result['icon_status'] = $iconStatus;
1433
        $result['icon_status_label'] = $label;
1434
        $result['icon_status_medium'] = $iconStatusMedium;
1435
    }
1436
1437
    if (isset($user['user_is_online'])) {
1438
        $result['user_is_online'] = true == $user['user_is_online'] ? 1 : 0;
1439
    }
1440
    if (isset($user['user_is_online_in_chat'])) {
1441
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1442
    }
1443
1444
    if ($add_password) {
1445
        $result['password'] = $user['password'];
1446
    }
1447
1448
    if (isset($result['profile_completed'])) {
1449
        $result['profile_completed'] = $user['profile_completed'];
1450
    }
1451
1452
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1453
1454
    // Send message link
1455
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1456
    $result['complete_name_with_message_link'] = Display::url(
1457
        $result['complete_name_with_username'],
1458
        $sendMessage,
1459
        ['class' => 'ajax']
1460
    );
1461
1462
    if (isset($user['extra'])) {
1463
        $result['extra'] = $user['extra'];
1464
    }
1465
1466
    return $result;
1467
}
1468
1469
/**
1470
 * Finds all the information about a user.
1471
 * If no parameter is passed you find all the information about the current user.
1472
 *
1473
 * @param int  $user_id
1474
 * @param bool $checkIfUserOnline
1475
 * @param bool $showPassword
1476
 * @param bool $loadExtraData
1477
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1478
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1479
 * @param bool $updateCache              update apc cache if exists
1480
 *
1481
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1482
 *
1483
 * @author Patrick Cool <[email protected]>
1484
 * @author Julio Montoya
1485
 *
1486
 * @version 21 September 2004
1487
 */
1488
function api_get_user_info(
1489
    $user_id = 0,
1490
    $checkIfUserOnline = false,
1491
    $showPassword = false,
1492
    $loadExtraData = false,
1493
    $loadOnlyVisibleExtraData = false,
1494
    $loadAvatars = true,
1495
    $updateCache = false
1496
) {
1497
    // Make sure user_id is safe
1498
    $user_id = (int) $user_id;
1499
    $user = false;
1500
    if (empty($user_id)) {
1501
        $userFromSession = Session::read('_user');
1502
        if (isset($userFromSession) && !empty($userFromSession)) {
1503
            return $userFromSession;
1504
            /*
1505
            return _api_format_user(
1506
                $userFromSession,
1507
                $showPassword,
1508
                $loadAvatars
1509
            );*/
1510
        }
1511
1512
        return false;
1513
    }
1514
1515
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1516
            WHERE id = $user_id";
1517
    $result = Database::query($sql);
1518
    if (Database::num_rows($result) > 0) {
1519
        $result_array = Database::fetch_array($result);
1520
        $result_array['user_is_online_in_chat'] = 0;
1521
        if ($checkIfUserOnline) {
1522
            $use_status_in_platform = user_is_online($user_id);
1523
            $result_array['user_is_online'] = $use_status_in_platform;
1524
            $user_online_in_chat = 0;
1525
            if ($use_status_in_platform) {
1526
                $user_status = UserManager::get_extra_user_data_by_field(
1527
                    $user_id,
1528
                    'user_chat_status',
1529
                    false,
1530
                    true
1531
                );
1532
                if (1 == (int) $user_status['user_chat_status']) {
1533
                    $user_online_in_chat = 1;
1534
                }
1535
            }
1536
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1537
        }
1538
1539
        if ($loadExtraData) {
1540
            $fieldValue = new ExtraFieldValue('user');
1541
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1542
                $user_id,
1543
                $loadOnlyVisibleExtraData
1544
            );
1545
        }
1546
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1547
    }
1548
1549
    return $user;
1550
}
1551
1552
function api_get_user_info_from_entity(
1553
    User $user,
1554
    $checkIfUserOnline = false,
1555
    $showPassword = false,
1556
    $loadExtraData = false,
1557
    $loadOnlyVisibleExtraData = false,
1558
    $loadAvatars = true,
1559
    $loadCertificate = false
1560
) {
1561
    if (!$user instanceof UserInterface) {
1562
        return false;
1563
    }
1564
1565
    // Make sure user_id is safe
1566
    $user_id = (int) $user->getId();
1567
1568
    if (empty($user_id)) {
1569
        $userFromSession = Session::read('_user');
1570
1571
        if (isset($userFromSession) && !empty($userFromSession)) {
1572
            return $userFromSession;
1573
        }
1574
1575
        return false;
1576
    }
1577
1578
    $result = [];
1579
    $result['user_is_online_in_chat'] = 0;
1580
    if ($checkIfUserOnline) {
1581
        $use_status_in_platform = user_is_online($user_id);
1582
        $result['user_is_online'] = $use_status_in_platform;
1583
        $user_online_in_chat = 0;
1584
        if ($use_status_in_platform) {
1585
            $user_status = UserManager::get_extra_user_data_by_field(
1586
                $user_id,
1587
                'user_chat_status',
1588
                false,
1589
                true
1590
            );
1591
            if (1 == (int) $user_status['user_chat_status']) {
1592
                $user_online_in_chat = 1;
1593
            }
1594
        }
1595
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1596
    }
1597
1598
    if ($loadExtraData) {
1599
        $fieldValue = new ExtraFieldValue('user');
1600
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1601
            $user_id,
1602
            $loadOnlyVisibleExtraData
1603
        );
1604
    }
1605
1606
    $result['username'] = $user->getUsername();
1607
    $result['status'] = $user->getStatus();
1608
    $result['firstname'] = $user->getFirstname();
1609
    $result['lastname'] = $user->getLastname();
1610
    $result['email'] = $result['mail'] = $user->getEmail();
1611
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1612
    $result['complete_name_with_username'] = $result['complete_name'];
1613
1614
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1615
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1616
    }
1617
1618
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1619
    if (!empty($result['email'])) {
1620
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1621
        if ($showEmail) {
1622
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1623
        }
1624
    } else {
1625
        $result['complete_name_with_email'] = $result['complete_name'];
1626
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1627
    }
1628
1629
    // Kept for historical reasons
1630
    $result['firstName'] = $result['firstname'];
1631
    $result['lastName'] = $result['lastname'];
1632
1633
    $attributes = [
1634
        'picture_uri',
1635
        'last_login',
1636
        'user_is_online',
1637
    ];
1638
1639
    $result['phone'] = $user->getPhone();
1640
    $result['address'] = $user->getAddress();
1641
    $result['official_code'] = $user->getOfficialCode();
1642
    $result['active'] = $user->isActive();
1643
    $result['auth_source'] = $user->getAuthSource();
1644
    $result['language'] = $user->getLocale();
1645
    $result['creator_id'] = $user->getCreatorId();
1646
    $result['registration_date'] = $user->getRegistrationDate()->format('Y-m-d H:i:s');
1647
    $result['hr_dept_id'] = $user->getHrDeptId();
1648
    $result['expiration_date'] = '';
1649
    if ($user->getExpirationDate()) {
1650
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1651
    }
1652
1653
    $result['last_login'] = null;
1654
    if ($user->getLastLogin()) {
1655
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1656
    }
1657
1658
    $result['competences'] = $user->getCompetences();
1659
    $result['diplomas'] = $user->getDiplomas();
1660
    $result['teach'] = $user->getTeach();
1661
    $result['openarea'] = $user->getOpenarea();
1662
    $user_id = (int) $user->getId();
1663
1664
    // Maintain the user_id index for backwards compatibility
1665
    $result['user_id'] = $result['id'] = $user_id;
1666
1667
    if ($loadCertificate) {
1668
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1669
        $result['has_certificates'] = 0;
1670
        if (!empty($hasCertificates)) {
1671
            $result['has_certificates'] = 1;
1672
        }
1673
    }
1674
1675
    $result['icon_status'] = '';
1676
    $result['icon_status_medium'] = '';
1677
    $result['is_admin'] = UserManager::is_admin($user_id);
1678
1679
    // Getting user avatar.
1680
    if ($loadAvatars) {
1681
        $result['avatar'] = '';
1682
        $result['avatar_no_query'] = '';
1683
        $result['avatar_small'] = '';
1684
        $result['avatar_medium'] = '';
1685
        $urlImg = '/';
1686
        $iconStatus = '';
1687
        $iconStatusMedium = '';
1688
1689
        switch ($user->getStatus()) {
1690
            case STUDENT:
1691
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1692
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1693
                } else {
1694
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1695
                }
1696
                break;
1697
            case COURSEMANAGER:
1698
                if ($result['is_admin']) {
1699
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1700
                } else {
1701
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1702
                }
1703
                break;
1704
            case STUDENT_BOSS:
1705
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1706
                break;
1707
        }
1708
1709
        if (!empty($iconStatus)) {
1710
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1711
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1712
        }
1713
1714
        $result['icon_status'] = $iconStatus;
1715
        $result['icon_status_medium'] = $iconStatusMedium;
1716
    }
1717
1718
    if (isset($result['user_is_online'])) {
1719
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1720
    }
1721
    if (isset($result['user_is_online_in_chat'])) {
1722
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1723
    }
1724
1725
    $result['password'] = '';
1726
    if ($showPassword) {
1727
        $result['password'] = $user->getPassword();
1728
    }
1729
1730
    if (isset($result['profile_completed'])) {
1731
        $result['profile_completed'] = $result['profile_completed'];
1732
    }
1733
1734
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1735
1736
    // Send message link
1737
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1738
    $result['complete_name_with_message_link'] = Display::url(
1739
        $result['complete_name_with_username'],
1740
        $sendMessage,
1741
        ['class' => 'ajax']
1742
    );
1743
1744
    if (isset($result['extra'])) {
1745
        $result['extra'] = $result['extra'];
1746
    }
1747
1748
    return $result;
1749
}
1750
1751
function api_get_lp_entity(int $id): ?CLp
1752
{
1753
    return Database::getManager()->getRepository(CLp::class)->find($id);
1754
}
1755
1756
function api_get_user_entity(int $userId = 0): ?User
1757
{
1758
    $userId = $userId ?: api_get_user_id();
1759
    $repo = Container::getUserRepository();
1760
1761
    return $repo->find($userId);
1762
}
1763
1764
function api_get_current_user(): ?User
1765
{
1766
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1767
    if (false === $isLoggedIn) {
1768
        return null;
1769
    }
1770
1771
    $token = Container::getTokenStorage()->getToken();
1772
1773
    if (null !== $token) {
1774
        return $token->getUser();
1775
    }
1776
1777
    return null;
1778
}
1779
1780
/**
1781
 * Finds all the information about a user from username instead of user id.
1782
 *
1783
 * @param string $username
1784
 *
1785
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1786
 *
1787
 * @author Yannick Warnier <[email protected]>
1788
 */
1789
function api_get_user_info_from_username($username)
1790
{
1791
    if (empty($username)) {
1792
        return false;
1793
    }
1794
    $username = trim($username);
1795
1796
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1797
            WHERE username='".Database::escape_string($username)."'";
1798
    $result = Database::query($sql);
1799
    if (Database::num_rows($result) > 0) {
1800
        $resultArray = Database::fetch_array($result);
1801
1802
        return _api_format_user($resultArray);
1803
    }
1804
1805
    return false;
1806
}
1807
1808
/**
1809
 * Get first user with an email.
1810
 *
1811
 * @param string $email
1812
 *
1813
 * @return array|bool
1814
 */
1815
function api_get_user_info_from_email($email = '')
1816
{
1817
    if (empty($email)) {
1818
        return false;
1819
    }
1820
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1821
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1822
    $result = Database::query($sql);
1823
    if (Database::num_rows($result) > 0) {
1824
        $resultArray = Database::fetch_array($result);
1825
1826
        return _api_format_user($resultArray);
1827
    }
1828
1829
    return false;
1830
}
1831
1832
/**
1833
 * @return string
1834
 */
1835
function api_get_course_id()
1836
{
1837
    return Session::read('_cid', null);
1838
}
1839
1840
/**
1841
 * Returns the current course id (integer).
1842
 *
1843
 * @param ?string $code Optional course code
1844
 *
1845
 * @return int
1846
 */
1847
function api_get_course_int_id(?string $code = null): int
1848
{
1849
    if (!empty($code)) {
1850
        $code = Database::escape_string($code);
1851
        $row = Database::select(
1852
            'id',
1853
            Database::get_main_table(TABLE_MAIN_COURSE),
1854
            ['where' => ['code = ?' => [$code]]],
1855
            'first'
1856
        );
1857
1858
        if (is_array($row) && isset($row['id'])) {
1859
            return $row['id'];
1860
        } else {
1861
            return 0;
1862
        }
1863
    }
1864
1865
    $cid = Session::read('_real_cid', 0);
1866
    if (empty($cid) && isset($_REQUEST['cid'])) {
1867
        $cid = (int) $_REQUEST['cid'];
1868
    }
1869
1870
    return $cid;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cid could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
1871
}
1872
1873
/**
1874
 * Gets a course setting from the current course_setting table. Try always using integer values.
1875
 *
1876
 * @param string       $settingName The name of the setting we want from the table
1877
 * @param Course|array $courseInfo
1878
 * @param bool         $force       force checking the value in the database
1879
 *
1880
 * @return mixed The value of that setting in that table. Return -1 if not found.
1881
 */
1882
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1883
{
1884
    if (empty($courseInfo)) {
1885
        $courseInfo = api_get_course_info();
1886
    }
1887
1888
    if (empty($courseInfo) || empty($settingName)) {
1889
        return -1;
1890
    }
1891
1892
    if ($courseInfo instanceof Course) {
1893
        $courseId = $courseInfo->getId();
1894
    } else {
1895
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1896
    }
1897
1898
    if (empty($courseId)) {
1899
        return -1;
1900
    }
1901
1902
    static $courseSettingInfo = [];
1903
1904
    if ($force) {
1905
        $courseSettingInfo = [];
1906
    }
1907
1908
    if (!isset($courseSettingInfo[$courseId])) {
1909
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1910
        $settingName = Database::escape_string($settingName);
1911
1912
        $sql = "SELECT variable, value FROM $table
1913
                WHERE c_id = $courseId ";
1914
        $res = Database::query($sql);
1915
        if (Database::num_rows($res) > 0) {
1916
            $result = Database::store_result($res, 'ASSOC');
1917
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1918
1919
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1920
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1921
                if (!is_null($value)) {
1922
                    $result = explode(',', $value);
1923
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1924
                }
1925
            }
1926
        }
1927
    }
1928
1929
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1930
        return $courseSettingInfo[$courseId][$settingName];
1931
    }
1932
1933
    return -1;
1934
}
1935
1936
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1937
{
1938
    $value = api_get_course_setting($settingName, $courseInfo, true);
1939
1940
    if (-1 === $value) {
1941
        // Check global settings
1942
        $value = api_get_plugin_setting($plugin, $settingName);
1943
        if ('true' === $value) {
1944
            return 1;
1945
        }
1946
        if ('false' === $value) {
1947
            return 0;
1948
        }
1949
        if (null === $value) {
1950
            return -1;
1951
        }
1952
    }
1953
1954
    return $value;
1955
}
1956
1957
/**
1958
 * Gets an anonymous user ID.
1959
 *
1960
 * For some tools that need tracking, like the learnpath tool, it is necessary
1961
 * to have a usable user-id to enable some kind of tracking, even if not
1962
 * perfect. An anonymous ID is taken from the users table by looking for a
1963
 * status of "6" (anonymous).
1964
 *
1965
 * @return int User ID of the anonymous user, or O if no anonymous user found
1966
 */
1967
function api_get_anonymous_id()
1968
{
1969
    // Find if another anon is connected now
1970
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1971
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1972
    $ip = Database::escape_string(api_get_real_ip());
1973
    $max = (int) api_get_setting('admin.max_anonymous_users');
1974
    if ($max >= 2) {
1975
        $sql = "SELECT * FROM $table as TEL
1976
                JOIN $tableU as U
1977
                ON U.id = TEL.login_user_id
1978
                WHERE TEL.user_ip = '$ip'
1979
                    AND U.status = ".ANONYMOUS."
1980
                    AND U.id != 2 ";
1981
1982
        $result = Database::query($sql);
1983
        if (empty(Database::num_rows($result))) {
1984
            $login = uniqid('anon_');
1985
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['registration_date ASC']);
1986
            if (count($anonList) >= $max) {
1987
                foreach ($anonList as $userToDelete) {
1988
                    UserManager::delete_user($userToDelete['user_id']);
1989
                    break;
1990
                }
1991
            }
1992
1993
            return UserManager::create_user(
0 ignored issues
show
Bug Best Practice introduced by
The expression return UserManager::crea...lhost', $login, $login) could also return false which is incompatible with the documented return type integer. 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...
1994
                $login,
1995
                'anon',
1996
                ANONYMOUS,
1997
                ' anonymous@localhost',
1998
                $login,
1999
                $login
2000
            );
2001
        } else {
2002
            $row = Database::fetch_assoc($result);
2003
2004
            return $row['id'];
2005
        }
2006
    }
2007
2008
    $table = Database::get_main_table(TABLE_MAIN_USER);
2009
    $sql = "SELECT id
2010
            FROM $table
2011
            WHERE status = ".ANONYMOUS." ";
2012
    $res = Database::query($sql);
2013
    if (Database::num_rows($res) > 0) {
2014
        $row = Database::fetch_assoc($res);
2015
2016
        return $row['id'];
2017
    }
2018
2019
    // No anonymous user was found.
2020
    return 0;
2021
}
2022
2023
/**
2024
 * @param int $courseId
2025
 * @param int $sessionId
2026
 * @param int $groupId
2027
 *
2028
 * @return string
2029
 */
2030
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2031
{
2032
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2033
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2034
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2035
2036
    $url = 'cid='.$courseId;
2037
    $url .= '&sid='.$sessionId;
2038
    $url .= '&gid='.$groupId;
2039
2040
    return $url;
2041
}
2042
2043
/**
2044
 * Returns the current course url part including session, group, and gradebook params.
2045
 *
2046
 * @param bool   $addSessionId
2047
 * @param bool   $addGroupId
2048
 * @param string $origin
2049
 *
2050
 * @return string Course & session references to add to a URL
2051
 */
2052
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2053
{
2054
    $courseId = api_get_course_int_id();
2055
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2056
        $courseId = (int) $_REQUEST['cid'];
2057
    }
2058
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2059
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2060
2061
    if ($addSessionId) {
2062
        if (!empty($url)) {
2063
            $sessionId = api_get_session_id();
2064
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2065
                $sessionId = (int) $_REQUEST['sid'];
2066
            }
2067
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2068
        }
2069
    }
2070
2071
    if ($addGroupId) {
2072
        if (!empty($url)) {
2073
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2074
        }
2075
    }
2076
2077
    if (!empty($url)) {
2078
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2079
        if (false !== $origin) {
2080
            $url .= '&origin=' . $origin;
0 ignored issues
show
Bug introduced by
Are you sure $origin of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2080
            $url .= '&origin=' . /** @scrutinizer ignore-type */ $origin;
Loading history...
2081
        }
2082
    }
2083
2084
    return $url;
2085
}
2086
2087
/**
2088
 * Get if we visited a gradebook page.
2089
 *
2090
 * @return bool
2091
 */
2092
function api_is_in_gradebook()
2093
{
2094
    return Session::read('in_gradebook', false);
2095
}
2096
2097
/**
2098
 * Set that we are in a page inside a gradebook.
2099
 */
2100
function api_set_in_gradebook()
2101
{
2102
    Session::write('in_gradebook', true);
2103
}
2104
2105
/**
2106
 * Remove gradebook session.
2107
 */
2108
function api_remove_in_gradebook()
2109
{
2110
    Session::erase('in_gradebook');
2111
}
2112
2113
/**
2114
 * Returns the current course info array see api_format_course_array()
2115
 * If the course_code is given, the returned array gives info about that
2116
 * particular course, if none given it gets the course info from the session.
2117
 *
2118
 * @param string $courseCode
2119
 *
2120
 * @return array
2121
 */
2122
function api_get_course_info($courseCode = null)
2123
{
2124
    if (!empty($courseCode)) {
2125
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2126
2127
        return api_format_course_array($course);
2128
    }
2129
2130
    $course = Session::read('_course');
2131
    if ('-1' == $course) {
2132
        $course = [];
2133
    }
2134
2135
    if (empty($course) && isset($_REQUEST['cid'])) {
2136
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2137
    }
2138
2139
    return $course;
2140
}
2141
2142
/**
2143
 * @param int $courseId
2144
 */
2145
function api_get_course_entity($courseId = 0): ?Course
2146
{
2147
    if (empty($courseId)) {
2148
        $courseId = api_get_course_int_id();
2149
    }
2150
2151
    if (empty($courseId)) {
2152
        return null;
2153
    }
2154
2155
    return Container::getCourseRepository()->find($courseId);
2156
}
2157
2158
/**
2159
 * @param int $id
2160
 */
2161
function api_get_session_entity($id = 0): ?SessionEntity
2162
{
2163
    if (empty($id)) {
2164
        $id = api_get_session_id();
2165
    }
2166
2167
    if (empty($id)) {
2168
        return null;
2169
    }
2170
2171
    return Container::getSessionRepository()->find($id);
2172
}
2173
2174
/**
2175
 * @param int $id
2176
 */
2177
function api_get_group_entity($id = 0): ?CGroup
2178
{
2179
    if (empty($id)) {
2180
        $id = api_get_group_id();
2181
    }
2182
2183
    return Container::getGroupRepository()->find($id);
2184
}
2185
2186
/**
2187
 * @param int $id
2188
 */
2189
function api_get_url_entity($id = 0): ?AccessUrl
2190
{
2191
    if (empty($id)) {
2192
        $id = api_get_current_access_url_id();
2193
    }
2194
2195
    return Container::getAccessUrlRepository()->find($id);
2196
}
2197
2198
/**
2199
 * Returns the current course info array.
2200
2201
 * Now if the course_code is given, the returned array gives info about that
2202
 * particular course, not specially the current one.
2203
 *
2204
 * @param int $id Numeric ID of the course
2205
 *
2206
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2207
 */
2208
function api_get_course_info_by_id(?int $id = 0)
2209
{
2210
    if (empty($id)) {
2211
        $course = Session::read('_course', []);
2212
2213
        return $course;
2214
    }
2215
2216
    $course = Container::getCourseRepository()->find($id);
2217
    if (empty($course)) {
2218
        return [];
2219
    }
2220
2221
    return api_format_course_array($course);
2222
}
2223
2224
/**
2225
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2226
 * to switch from 'code' to 'id' in the array.
2227
 *
2228
 * @return array
2229
 *
2230
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2231
 */
2232
function api_format_course_array(Course $course = null)
2233
{
2234
    if (empty($course)) {
2235
        return [];
2236
    }
2237
2238
    $courseData = [];
2239
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2240
2241
    // Added
2242
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2243
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2244
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2245
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2246
    $courseData['titular'] = $course->getTutorName();
2247
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2248
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2249
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2250
2251
    $courseData['visibility'] = $course->getVisibility();
2252
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2253
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2254
    $courseData['activate_legal'] = $course->getActivateLegal();
2255
    $courseData['legal'] = $course->getLegal();
2256
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2257
    $courseData['video_url'] = $course->getVideoUrl();
2258
    $courseData['sticky'] = (int) $course->isSticky();
2259
2260
    $coursePath = '/course/';
2261
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2262
2263
    // Course password
2264
    $courseData['registration_code'] = $course->getRegistrationCode();
2265
    $courseData['disk_quota'] = $course->getDiskQuota();
2266
    $courseData['course_public_url'] = $webCourseHome;
2267
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2268
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2269
2270
    $image = Display::getMdiIcon(
2271
        ObjectIcon::COURSE,
2272
        'ch-tool-icon',
2273
        null,
2274
        ICON_SIZE_BIG
2275
    );
2276
2277
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2278
    if (!empty($illustration)) {
2279
        $image = $illustration;
2280
    }
2281
2282
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2283
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2284
2285
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2286
        $courseData['duration'] = $course->getDuration();
2287
    }
2288
2289
    return $courseData;
2290
}
2291
2292
/**
2293
 * Returns a difficult to guess password.
2294
 */
2295
function api_generate_password(int $length = 8, $useRequirements = true): string
2296
{
2297
    if ($length < 2) {
2298
        $length = 2;
2299
    }
2300
2301
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2302
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2303
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2304
    $minNumbers = 2;
2305
    $length = $length - $minNumbers;
2306
    $minLowerCase = round($length / 2);
2307
    $minUpperCase = $length - $minLowerCase;
2308
    $minSpecials = 1; // Default minimum special characters
2309
2310
    $password = '';
2311
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2312
2313
    $factory = new RandomLib\Factory();
2314
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2315
2316
    if (!empty($passwordRequirements)) {
2317
        $length = $passwordRequirements['min']['length'];
2318
        $minNumbers = $passwordRequirements['min']['numeric'];
2319
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2320
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2321
        $minSpecials = $passwordRequirements['min']['specials'];
2322
2323
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2324
        // Add the rest to fill the length requirement
2325
        if ($rest > 0) {
2326
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2327
        }
2328
    }
2329
2330
    // Min digits default 2
2331
    for ($i = 0; $i < $minNumbers; $i++) {
2332
        $password .= $generator->generateInt(2, 9);
2333
    }
2334
2335
    // Min lowercase
2336
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2337
2338
    // Min uppercase
2339
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2340
2341
    // Min special characters
2342
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2343
2344
    // Shuffle the password to ensure randomness
2345
    $password = str_shuffle($password);
2346
2347
    return $password;
2348
}
2349
2350
/**
2351
 * Checks a password to see wether it is OK to use.
2352
 *
2353
 * @param string $password
2354
 *
2355
 * @return bool if the password is acceptable, false otherwise
2356
 *              Notes about what a password "OK to use" is:
2357
 *              1. The password should be at least 5 characters long.
2358
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2359
 *              3. The password should contain at least 3 letters.
2360
 *              4. It should contain at least 2 digits.
2361
 *              Settings will change if the configuration value is set: password_requirements
2362
 */
2363
function api_check_password($password)
2364
{
2365
    $passwordRequirements = Security::getPasswordRequirements();
2366
2367
    $minLength = $passwordRequirements['min']['length'];
2368
    $minNumbers = $passwordRequirements['min']['numeric'];
2369
    // Optional
2370
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2371
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2372
2373
    $minLetters = $minLowerCase + $minUpperCase;
2374
    $passwordLength = api_strlen($password);
2375
2376
    $conditions = [
2377
        'min_length' => $passwordLength >= $minLength,
2378
    ];
2379
2380
    $digits = 0;
2381
    $lowerCase = 0;
2382
    $upperCase = 0;
2383
2384
    for ($i = 0; $i < $passwordLength; $i++) {
2385
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2386
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2387
            $upperCase++;
2388
        }
2389
2390
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2391
            $lowerCase++;
2392
        }
2393
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2394
            $digits++;
2395
        }
2396
    }
2397
2398
    // Min number of digits
2399
    $conditions['min_numeric'] = $digits >= $minNumbers;
2400
2401
    if (!empty($minUpperCase)) {
2402
        // Uppercase
2403
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2404
    }
2405
2406
    if (!empty($minLowerCase)) {
2407
        // Lowercase
2408
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2409
    }
2410
2411
    // Min letters
2412
    $letters = $upperCase + $lowerCase;
2413
    $conditions['min_letters'] = $letters >= $minLetters;
2414
2415
    $isPasswordOk = true;
2416
    foreach ($conditions as $condition) {
2417
        if (false === $condition) {
2418
            $isPasswordOk = false;
2419
            break;
2420
        }
2421
    }
2422
2423
    if (false === $isPasswordOk) {
2424
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2425
        $output .= Security::getPasswordRequirementsToString($conditions);
2426
2427
        Display::addFlash(Display::return_message($output, 'warning', false));
2428
    }
2429
2430
    return $isPasswordOk;
2431
}
2432
2433
/**
2434
 * Gets the current Chamilo (not PHP/cookie) session ID.
2435
 *
2436
 * @return int O if no active session, the session ID otherwise
2437
 */
2438
function api_get_session_id()
2439
{
2440
    return (int) Session::read('sid', 0);
2441
}
2442
2443
/**
2444
 * Gets the current Chamilo (not social network) group ID.
2445
 *
2446
 * @return int O if no active session, the session ID otherwise
2447
 */
2448
function api_get_group_id()
2449
{
2450
    return Session::read('gid', 0);
2451
}
2452
2453
/**
2454
 * Gets the current or given session name.
2455
 *
2456
 * @param   int     Session ID (optional)
2457
 *
2458
 * @return string The session name, or null if not found
2459
 */
2460
function api_get_session_name($session_id = 0)
2461
{
2462
    if (empty($session_id)) {
2463
        $session_id = api_get_session_id();
2464
        if (empty($session_id)) {
2465
            return null;
2466
        }
2467
    }
2468
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2469
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2470
    $r = Database::query($s);
2471
    $c = Database::num_rows($r);
2472
    if ($c > 0) {
2473
        //technically, there can be only one, but anyway we take the first
2474
        $rec = Database::fetch_array($r);
2475
2476
        return $rec['title'];
2477
    }
2478
2479
    return null;
2480
}
2481
2482
/**
2483
 * Gets the session info by id.
2484
 *
2485
 * @param int $id Session ID
2486
 *
2487
 * @return array information of the session
2488
 */
2489
function api_get_session_info($id)
2490
{
2491
    return SessionManager::fetch($id);
2492
}
2493
2494
/**
2495
 * Gets the session visibility by session id.
2496
 *
2497
 * @param int  $session_id
2498
 * @param int  $courseId
2499
 * @param bool $ignore_visibility_for_admins
2500
 *
2501
 * @return int
2502
 *             0 = session still available,
2503
 *             SESSION_VISIBLE_READ_ONLY = 1,
2504
 *             SESSION_VISIBLE = 2,
2505
 *             SESSION_INVISIBLE = 3
2506
 */
2507
function api_get_session_visibility(
2508
    $session_id,
2509
    $courseId = null,
2510
    $ignore_visibility_for_admins = true,
2511
    $userId = 0
2512
) {
2513
    if (api_is_platform_admin()) {
2514
        if ($ignore_visibility_for_admins) {
2515
            return SESSION_AVAILABLE;
2516
        }
2517
    }
2518
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2519
2520
    $now = time();
2521
    if (empty($session_id)) {
2522
        return 0; // Means that the session is still available.
2523
    }
2524
2525
    $session_id = (int) $session_id;
2526
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2527
2528
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2529
2530
    if (Database::num_rows($result) <= 0) {
2531
        return SESSION_INVISIBLE;
2532
    }
2533
2534
    $row = Database::fetch_assoc($result);
2535
    $visibility = $row['visibility'];
2536
2537
    // I don't care the session visibility.
2538
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2539
        // Session duration per student.
2540
        if (isset($row['duration']) && !empty($row['duration'])) {
2541
            $duration = $row['duration'] * 24 * 60 * 60;
2542
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2543
2544
            // If there is a session duration but there is no previous
2545
            // access by the user, then the session is still available
2546
            if (0 == count($courseAccess)) {
2547
                return SESSION_AVAILABLE;
2548
            }
2549
2550
            $currentTime = time();
2551
            $firstAccess = isset($courseAccess['login_course_date'])
2552
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2553
                : 0;
2554
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2555
            $userDuration = isset($userDurationData['duration'])
2556
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2557
                : 0;
2558
2559
            $totalDuration = $firstAccess + $duration + $userDuration;
2560
2561
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2562
        }
2563
2564
        return SESSION_AVAILABLE;
2565
    }
2566
2567
    // If start date was set.
2568
    if (!empty($row['access_start_date'])) {
2569
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2570
    } else {
2571
        // If there's no start date, assume it's available until the end date
2572
        $visibility = SESSION_AVAILABLE;
2573
    }
2574
2575
    // If the end date was set.
2576
    if (!empty($row['access_end_date'])) {
2577
        // Only if date_start said that it was ok
2578
        if (SESSION_AVAILABLE === $visibility) {
2579
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2580
                ? SESSION_AVAILABLE // Date still available
2581
                : $row['visibility']; // Session ends
2582
        }
2583
    }
2584
2585
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2586
    $isCoach = api_is_coach($session_id, $courseId);
2587
2588
    if ($isCoach) {
2589
        // Test start date.
2590
        if (!empty($row['coach_access_start_date'])) {
2591
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2592
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2593
        }
2594
2595
        // Test end date.
2596
        if (!empty($row['coach_access_end_date'])) {
2597
            if (SESSION_AVAILABLE === $visibility) {
2598
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2599
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2600
            }
2601
        }
2602
    }
2603
2604
    return $visibility;
2605
}
2606
2607
/**
2608
 * This function returns a (star) session icon if the session is not null and
2609
 * the user is not a student.
2610
 *
2611
 * @param int $sessionId
2612
 * @param int $statusId  User status id - if 5 (student), will return empty
2613
 *
2614
 * @return string Session icon
2615
 */
2616
function api_get_session_image($sessionId, User $user)
2617
{
2618
    $sessionId = (int) $sessionId;
2619
    $image = '';
2620
    if (!$user->hasRole('ROLE_STUDENT')) {
2621
        // Check whether is not a student
2622
        if ($sessionId > 0) {
2623
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2624
                ObjectIcon::STAR,
2625
                'ch-tool-icon',
2626
                'align:absmiddle;',
2627
                ICON_SIZE_SMALL,
2628
                get_lang('Session-specific resource')
2629
            );
2630
        }
2631
    }
2632
2633
    return $image;
2634
}
2635
2636
/**
2637
 * This function add an additional condition according to the session of the course.
2638
 *
2639
 * @param int    $session_id        session id
2640
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2641
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2642
 *                                  false for strict session condition
2643
 * @param string $session_field
2644
 *
2645
 * @return string condition of the session
2646
 */
2647
function api_get_session_condition(
2648
    $session_id,
2649
    $and = true,
2650
    $with_base_content = false,
2651
    $session_field = 'session_id'
2652
) {
2653
    $session_id = (int) $session_id;
2654
2655
    if (empty($session_field)) {
2656
        $session_field = 'session_id';
2657
    }
2658
    // Condition to show resources by session
2659
    $condition_add = $and ? ' AND ' : ' WHERE ';
2660
2661
    if ($with_base_content) {
2662
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2663
    } else {
2664
        if (empty($session_id)) {
2665
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2666
        } else {
2667
            $condition_session = $condition_add." $session_field = $session_id ";
2668
        }
2669
    }
2670
2671
    return $condition_session;
2672
}
2673
2674
/**
2675
 * Returns the value of a setting from the web-adjustable admin config settings.
2676
 *
2677
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2678
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2679
 * instead of
2680
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2681
 *
2682
 * @param string $variable The variable name
2683
 *
2684
 * @return string|array
2685
 */
2686
function api_get_setting($variable, $isArray = false, $key = null)
2687
{
2688
    $settingsManager = Container::getSettingsManager();
2689
    if (empty($settingsManager)) {
2690
        return '';
2691
    }
2692
    $variable = trim($variable);
2693
2694
    switch ($variable) {
2695
        case 'server_type':
2696
            $test = ['dev', 'test'];
2697
            $environment = Container::getEnvironment();
2698
            if (in_array($environment, $test)) {
2699
                return 'test';
2700
            }
2701
2702
            return 'prod';
2703
        // deprecated settings
2704
        // no break
2705
        case 'openid_authentication':
2706
        case 'service_ppt2lp':
2707
        case 'formLogin_hide_unhide_label':
2708
            return false;
2709
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2710
        case 'tool_visible_by_default_at_creation':
2711
            $values = $settingsManager->getSetting($variable);
2712
            $newResult = [];
2713
            foreach ($values as $parameter) {
2714
                $newResult[$parameter] = 'true';
2715
            }
2716
2717
            return $newResult;
2718
            break;
2719
        default:
2720
            $settingValue = $settingsManager->getSetting($variable, true);
2721
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2722
                // Check if the value is a valid JSON string
2723
                $decodedValue = json_decode($settingValue, true);
2724
2725
                // If it's a valid JSON string and the result is an array, return it
2726
                if (is_array($decodedValue)) {
2727
                    return $decodedValue;
2728
                }
2729
2730
                // If it's not an array, continue with the normal flow
2731
                // Optional: If you need to evaluate the value using eval
2732
                $strArrayValue = rtrim($settingValue, ';');
2733
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2734
                if (is_array($value)) {
2735
                    return $value;
2736
                }
2737
            }
2738
2739
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2740
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2741
                return $settingValue[$variable][$key];
2742
            }
2743
2744
            return $settingValue;
2745
            break;
2746
    }
2747
}
2748
2749
/**
2750
 * @param string $variable
2751
 * @param string $option
2752
 *
2753
 * @return bool
2754
 */
2755
function api_get_setting_in_list($variable, $option)
2756
{
2757
    $value = api_get_setting($variable);
2758
2759
    return in_array($option, $value);
2760
}
2761
2762
/**
2763
 * @param string $plugin
2764
 * @param string $variable
2765
 *
2766
 * @return string
2767
 */
2768
function api_get_plugin_setting($plugin, $variable)
2769
{
2770
    $variableName = $plugin.'_'.$variable;
2771
    //$result = api_get_setting($variableName);
2772
    $params = [
2773
        'category = ? AND subkey = ? AND variable = ?' => [
2774
            'Plugins',
2775
            $plugin,
2776
            $variableName,
2777
        ],
2778
    ];
2779
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2780
    $result = Database::select(
2781
        'selected_value',
2782
        $table,
2783
        ['where' => $params],
2784
        'one'
2785
    );
2786
    if ($result) {
2787
        $value = $result['selected_value'];
2788
        $serializedValue = @unserialize($result['selected_value'], []);
2789
        if (false !== $serializedValue) {
2790
            $value = $serializedValue;
2791
        }
2792
2793
        return $value;
2794
    }
2795
2796
    return null;
2797
    /// Old code
2798
2799
    $variableName = $plugin.'_'.$variable;
0 ignored issues
show
Unused Code introduced by
$variableName = $plugin . '_' . $variable is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2800
    $result = api_get_setting($variableName);
2801
2802
    if (isset($result[$plugin])) {
2803
        $value = $result[$plugin];
2804
2805
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2806
2807
        if (false !== $unserialized) {
2808
            $value = $unserialized;
2809
        }
2810
2811
        return $value;
2812
    }
2813
2814
    return null;
2815
}
2816
2817
/**
2818
 * Returns the value of a setting from the web-adjustable admin config settings.
2819
 */
2820
function api_get_settings_params($params)
2821
{
2822
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2823
2824
    return Database::select('*', $table, ['where' => $params]);
2825
}
2826
2827
/**
2828
 * @param array $params example: [id = ? => '1']
2829
 *
2830
 * @return array
2831
 */
2832
function api_get_settings_params_simple($params)
2833
{
2834
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2835
2836
    return Database::select('*', $table, ['where' => $params], 'one');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::select(...re' => $params), 'one') also could return the type integer which is incompatible with the documented return type array.
Loading history...
2837
}
2838
2839
/**
2840
 * Returns the value of a setting from the web-adjustable admin config settings.
2841
 */
2842
function api_delete_settings_params($params)
2843
{
2844
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2845
2846
    return Database::delete($table, $params);
2847
}
2848
2849
/**
2850
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2851
 *
2852
 * @return string Escaped version of $_SERVER['PHP_SELF']
2853
 */
2854
function api_get_self()
2855
{
2856
    return htmlentities($_SERVER['PHP_SELF']);
2857
}
2858
2859
/**
2860
 * Checks whether current user is a platform administrator.
2861
 *
2862
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2863
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2864
 *
2865
 * @return bool true if the user has platform admin rights,
2866
 *              false otherwise
2867
 *
2868
 * @see usermanager::is_admin(user_id) for a user-id specific function
2869
 */
2870
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2871
{
2872
    $currentUser = api_get_current_user();
2873
2874
    if (null === $currentUser) {
2875
        return false;
2876
    }
2877
2878
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2879
2880
    if ($isAdmin) {
2881
        return true;
2882
    }
2883
2884
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2885
        return true;
2886
    }
2887
2888
    if ($allowDrh && $currentUser->hasRole('ROLE_HR')) {
2889
        return true;
2890
    }
2891
2892
    return false;
2893
}
2894
2895
/**
2896
 * Checks whether the user given as user id is in the admin table.
2897
 *
2898
 * @param int $user_id If none provided, will use current user
2899
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2900
 *
2901
 * @return bool True if the user is admin, false otherwise
2902
 */
2903
function api_is_platform_admin_by_id($user_id = null, $url = null)
2904
{
2905
    $user_id = (int) $user_id;
2906
    if (empty($user_id)) {
2907
        $user_id = api_get_user_id();
2908
    }
2909
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2910
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2911
    $res = Database::query($sql);
2912
    $is_admin = 1 === Database::num_rows($res);
2913
    if (!$is_admin || !isset($url)) {
2914
        return $is_admin;
2915
    }
2916
    // We get here only if $url is set
2917
    $url = (int) $url;
2918
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2919
    $sql = "SELECT * FROM $url_user_table
2920
            WHERE access_url_id = $url AND user_id = $user_id";
2921
    $res = Database::query($sql);
2922
2923
    return 1 === Database::num_rows($res);
2924
}
2925
2926
/**
2927
 * Checks whether current user is allowed to create courses.
2928
 *
2929
 * @return bool true if the user has course creation rights,
2930
 *              false otherwise
2931
 */
2932
function api_is_allowed_to_create_course()
2933
{
2934
    if (api_is_platform_admin()) {
2935
        return true;
2936
    }
2937
2938
    // Teachers can only create courses
2939
    if (api_is_teacher()) {
2940
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2941
            return true;
2942
        } else {
2943
            return false;
2944
        }
2945
    }
2946
2947
    return Session::read('is_allowedCreateCourse');
2948
}
2949
2950
/**
2951
 * Checks whether the current user is a course administrator.
2952
 *
2953
 * @return bool True if current user is a course administrator
2954
 */
2955
function api_is_course_admin()
2956
{
2957
    if (api_is_platform_admin()) {
2958
        return true;
2959
    }
2960
2961
    $user = api_get_current_user();
2962
    if ($user) {
2963
        if (
2964
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2965
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2966
        ) {
2967
            return true;
2968
        }
2969
    }
2970
2971
    return false;
2972
}
2973
2974
/**
2975
 * Checks whether the current user is a course coach
2976
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2977
 *
2978
 * @return bool True if current user is a course coach
2979
 */
2980
function api_is_session_general_coach()
2981
{
2982
    return Session::read('is_session_general_coach');
2983
}
2984
2985
/**
2986
 * Checks whether the current user is a course tutor
2987
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2988
 *
2989
 * @return bool True if current user is a course tutor
2990
 */
2991
function api_is_course_tutor()
2992
{
2993
    return Session::read('is_courseTutor');
2994
}
2995
2996
/**
2997
 * @param int $user_id
2998
 * @param int $courseId
2999
 * @param int $session_id
3000
 *
3001
 * @return bool
3002
 */
3003
function api_is_course_session_coach($user_id, $courseId, $session_id)
3004
{
3005
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3006
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3007
3008
    $user_id = (int) $user_id;
3009
    $session_id = (int) $session_id;
3010
    $courseId = (int) $courseId;
3011
3012
    $sql = "SELECT DISTINCT session.id
3013
            FROM $session_table
3014
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3015
            ON session.id = session_rc_ru.session_id
3016
            WHERE
3017
                session_rc_ru.user_id = '".$user_id."'  AND
3018
                session_rc_ru.c_id = '$courseId' AND
3019
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3020
                session_rc_ru.session_id = '$session_id'";
3021
    $result = Database::query($sql);
3022
3023
    return Database::num_rows($result) > 0;
3024
}
3025
3026
/**
3027
 * Checks whether the current user is a course or session coach.
3028
 *
3029
 * @param int $session_id
3030
 * @param int $courseId
3031
 * @param bool  Check whether we are in student view and, if we are, return false
3032
 * @param int $userId
3033
 *
3034
 * @return bool True if current user is a course or session coach
3035
 */
3036
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3037
{
3038
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3039
3040
    if (!empty($session_id)) {
3041
        $session_id = (int) $session_id;
3042
    } else {
3043
        $session_id = api_get_session_id();
3044
    }
3045
3046
    // The student preview was on
3047
    if ($check_student_view && api_is_student_view_active()) {
3048
        return false;
3049
    }
3050
3051
    if (!empty($courseId)) {
3052
        $courseId = (int) $courseId;
3053
    } else {
3054
        $courseId = api_get_course_int_id();
3055
    }
3056
3057
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3058
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3059
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3060
    $sessionIsCoach = [];
3061
3062
    if (!empty($courseId)) {
3063
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3064
                FROM $session_table s
3065
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3066
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3067
                WHERE
3068
                    session_rc_ru.c_id = '$courseId' AND
3069
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3070
                    session_rc_ru.session_id = '$session_id'";
3071
        $result = Database::query($sql);
3072
        $sessionIsCoach = Database::store_result($result);
3073
    }
3074
3075
    if (!empty($session_id)) {
3076
        $sql = "SELECT DISTINCT s.id
3077
                FROM $session_table AS s
3078
                INNER JOIN $tblSessionRelUser sru
3079
                ON s.id = sru.session_id
3080
                WHERE
3081
                    sru.user_id = $userId AND
3082
                    s.id = $session_id AND
3083
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3084
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3085
        $result = Database::query($sql);
3086
        if (!empty($sessionIsCoach)) {
3087
            $sessionIsCoach = array_merge(
3088
                $sessionIsCoach,
3089
                Database::store_result($result)
3090
            );
3091
        } else {
3092
            $sessionIsCoach = Database::store_result($result);
3093
        }
3094
    }
3095
3096
    return count($sessionIsCoach) > 0;
3097
}
3098
3099
function api_user_has_role(string $role, ?User $user = null): bool
3100
{
3101
    if (null === $user) {
3102
        $user = api_get_current_user();
3103
    }
3104
3105
    if (null === $user) {
3106
        return false;
3107
    }
3108
3109
    return $user->hasRole($role);
3110
}
3111
3112
function api_is_allowed_in_course(): bool
3113
{
3114
    if (api_is_platform_admin()) {
3115
        return true;
3116
    }
3117
3118
    $user = api_get_current_user();
3119
    if ($user instanceof User) {
3120
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3121
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3122
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3123
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3124
        ) {
3125
            return true;
3126
        }
3127
    }
3128
3129
    return false;
3130
}
3131
3132
/**
3133
 * Checks whether current user is a student boss.
3134
 */
3135
function api_is_student_boss(?User $user = null): bool
3136
{
3137
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3138
}
3139
3140
/**
3141
 * Checks whether the current user is a session administrator.
3142
 *
3143
 * @return bool True if current user is a course administrator
3144
 */
3145
function api_is_session_admin(?User $user = null)
3146
{
3147
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3148
}
3149
3150
/**
3151
 * Checks whether the current user is a human resources manager.
3152
 *
3153
 * @return bool True if current user is a human resources manager
3154
 */
3155
function api_is_drh()
3156
{
3157
    return api_user_has_role('ROLE_HR');
3158
}
3159
3160
/**
3161
 * Checks whether the current user is a student.
3162
 *
3163
 * @return bool True if current user is a human resources manager
3164
 */
3165
function api_is_student()
3166
{
3167
    return api_user_has_role('ROLE_STUDENT');
3168
}
3169
3170
/**
3171
 * Checks whether the current user has the status 'teacher'.
3172
 *
3173
 * @return bool True if current user is a human resources manager
3174
 */
3175
function api_is_teacher()
3176
{
3177
    return api_user_has_role('ROLE_TEACHER');
3178
}
3179
3180
/**
3181
 * Checks whether the current user is a invited user.
3182
 *
3183
 * @return bool
3184
 */
3185
function api_is_invitee()
3186
{
3187
    return api_user_has_role('ROLE_INVITEE');
3188
}
3189
3190
/**
3191
 * This function checks whether a session is assigned into a category.
3192
 *
3193
 * @param int       - session id
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
3194
 * @param string    - category name
3195
 *
3196
 * @return bool - true if is found, otherwise false
3197
 */
3198
function api_is_session_in_category($session_id, $category_name)
3199
{
3200
    $session_id = (int) $session_id;
3201
    $category_name = Database::escape_string($category_name);
3202
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3203
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3204
3205
    $sql = "SELECT 1
3206
            FROM $tbl_session
3207
            WHERE $session_id IN (
3208
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3209
                WHERE
3210
                  s.session_category_id = sc.id AND
3211
                  sc.name LIKE '%$category_name'
3212
            )";
3213
    $rs = Database::query($sql);
3214
3215
    if (Database::num_rows($rs) > 0) {
3216
        return true;
3217
    }
3218
3219
    return false;
3220
}
3221
3222
/**
3223
 * Displays options for switching between student view and course manager view.
3224
 *
3225
 * Changes in version 1.2 (Patrick Cool)
3226
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3227
 * is changed explicitly
3228
 *
3229
 * Changes in version 1.1 (Patrick Cool)
3230
 * student view now works correctly in subfolders of the document tool
3231
 * student view works correctly in the new links tool
3232
 *
3233
 * Example code for using this in your tools:
3234
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3235
 * //   display_tool_view_option($isStudentView);
3236
 * //}
3237
 * //and in later sections, use api_is_allowed_to_edit()
3238
 *
3239
 * @author Roan Embrechts
3240
 * @author Patrick Cool
3241
 * @author Julio Montoya, changes added in Chamilo
3242
 *
3243
 * @version 1.2
3244
 *
3245
 * @todo rewrite code so it is easier to understand
3246
 */
3247
function api_display_tool_view_option()
3248
{
3249
    if ('true' != api_get_setting('student_view_enabled')) {
3250
        return '';
3251
    }
3252
3253
    $sourceurl = '';
3254
    $is_framed = false;
3255
    // Exceptions apply for all multi-frames pages
3256
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3257
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3258
        return '';
3259
    }
3260
3261
    // Uncomment to remove student view link from document view page
3262
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3263
        if (empty($_GET['lp_id'])) {
3264
            return '';
3265
        }
3266
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3267
        $sourceurl = str_replace(
3268
            'lp/lp_header.php',
3269
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3270
            $sourceurl
3271
        );
3272
        //showinframes doesn't handle student view anyway...
3273
        //return '';
3274
        $is_framed = true;
3275
    }
3276
3277
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3278
    if (!$is_framed) {
3279
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3280
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3281
        } else {
3282
            $sourceurl = $_SERVER['REQUEST_URI'];
3283
        }
3284
    }
3285
3286
    $output_string = '';
3287
    if (!empty($_SESSION['studentview'])) {
3288
        if ('studentview' == $_SESSION['studentview']) {
3289
            // We have to remove the isStudentView=true from the $sourceurl
3290
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3291
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3292
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3293
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3294
        } elseif ('teacherview' == $_SESSION['studentview']) {
3295
            // Switching to teacherview
3296
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3297
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3298
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3299
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3300
        }
3301
    } else {
3302
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3303
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3304
    }
3305
    $output_string = Security::remove_XSS($output_string);
3306
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3307
3308
    return $html;
3309
}
3310
3311
/**
3312
 * Function that removes the need to directly use is_courseAdmin global in
3313
 * tool scripts. It returns true or false depending on the user's rights in
3314
 * this particular course.
3315
 * Optionally checking for tutor and coach roles here allows us to use the
3316
 * student_view feature altogether with these roles as well.
3317
 *
3318
 * @param bool  Whether to check if the user has the tutor role
3319
 * @param bool  Whether to check if the user has the coach role
3320
 * @param bool  Whether to check if the user has the session coach role
3321
 * @param bool  check the student view or not
3322
 *
3323
 * @author Roan Embrechts
3324
 * @author Patrick Cool
3325
 * @author Julio Montoya
3326
 *
3327
 * @version 1.1, February 2004
3328
 *
3329
 * @return bool true: the user has the rights to edit, false: he does not
3330
 */
3331
function api_is_allowed_to_edit(
3332
    $tutor = false,
3333
    $coach = false,
3334
    $session_coach = false,
3335
    $check_student_view = true
3336
) {
3337
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3338
    // Admins can edit anything.
3339
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3340
        //The student preview was on
3341
        if ($check_student_view && api_is_student_view_active()) {
3342
            return false;
3343
        }
3344
3345
        return true;
3346
    }
3347
3348
    $sessionId = api_get_session_id();
3349
3350
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3351
        $efv = new ExtraFieldValue('course');
3352
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3353
            api_get_course_int_id(),
3354
            'session_courses_read_only_mode'
3355
        );
3356
3357
        if (!empty($lockExrafieldField['value'])) {
3358
            return false;
3359
        }
3360
    }
3361
3362
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3363
    $session_visibility = api_get_session_visibility($sessionId);
3364
    $is_courseAdmin = api_is_course_admin();
3365
3366
    if (!$is_courseAdmin && $tutor) {
3367
        // If we also want to check if the user is a tutor...
3368
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3369
    }
3370
3371
    if (!$is_courseAdmin && $coach) {
3372
        // If we also want to check if the user is a coach...';
3373
        // Check if session visibility is read only for coaches.
3374
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3375
            $is_allowed_coach_to_edit = false;
3376
        }
3377
3378
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3379
            // Check if coach is allowed to edit a course.
3380
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3381
        }
3382
    }
3383
3384
    if (!$is_courseAdmin && $session_coach) {
3385
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3386
    }
3387
3388
    // Check if the student_view is enabled, and if so, if it is activated.
3389
    if ('true' === api_get_setting('student_view_enabled')) {
3390
        $studentView = api_is_student_view_active();
3391
        if (!empty($sessionId)) {
3392
            // Check if session visibility is read only for coaches.
3393
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3394
                $is_allowed_coach_to_edit = false;
3395
            }
3396
3397
            $is_allowed = false;
3398
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3399
                // Check if coach is allowed to edit a course.
3400
                $is_allowed = $is_allowed_coach_to_edit;
3401
            }
3402
            if ($check_student_view) {
3403
                $is_allowed = $is_allowed && false === $studentView;
3404
            }
3405
        } else {
3406
            $is_allowed = $is_courseAdmin;
3407
            if ($check_student_view) {
3408
                $is_allowed = $is_courseAdmin && false === $studentView;
3409
            }
3410
        }
3411
3412
        return $is_allowed;
3413
    } else {
3414
        return $is_courseAdmin;
3415
    }
3416
}
3417
3418
/**
3419
 * Returns true if user is a course coach of at least one course in session.
3420
 *
3421
 * @param int $sessionId
3422
 *
3423
 * @return bool
3424
 */
3425
function api_is_coach_of_course_in_session($sessionId)
3426
{
3427
    if (api_is_platform_admin()) {
3428
        return true;
3429
    }
3430
3431
    $userId = api_get_user_id();
3432
    $courseList = UserManager::get_courses_list_by_session(
3433
        $userId,
3434
        $sessionId
3435
    );
3436
3437
    // Session visibility.
3438
    $visibility = api_get_session_visibility(
3439
        $sessionId,
3440
        null,
3441
        false
3442
    );
3443
3444
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3445
        // Course Coach session visibility.
3446
        $blockedCourseCount = 0;
3447
        $closedVisibilityList = [
3448
            COURSE_VISIBILITY_CLOSED,
3449
            COURSE_VISIBILITY_HIDDEN,
3450
        ];
3451
3452
        foreach ($courseList as $course) {
3453
            // Checking session visibility
3454
            $sessionCourseVisibility = api_get_session_visibility(
3455
                $sessionId,
3456
                $course['real_id']
3457
            );
3458
3459
            $courseIsVisible = !in_array(
3460
                $course['visibility'],
3461
                $closedVisibilityList
3462
            );
3463
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3464
                $blockedCourseCount++;
3465
            }
3466
        }
3467
3468
        // If all courses are blocked then no show in the list.
3469
        if ($blockedCourseCount === count($courseList)) {
3470
            $visibility = SESSION_INVISIBLE;
3471
        } else {
3472
            $visibility = SESSION_VISIBLE;
3473
        }
3474
    }
3475
3476
    switch ($visibility) {
3477
        case SESSION_VISIBLE_READ_ONLY:
3478
        case SESSION_VISIBLE:
3479
        case SESSION_AVAILABLE:
3480
            return true;
3481
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3482
        case SESSION_INVISIBLE:
3483
            return false;
3484
    }
3485
3486
    return false;
3487
}
3488
3489
/**
3490
 * Checks if a student can edit contents in a session depending
3491
 * on the session visibility.
3492
 *
3493
 * @param bool $tutor Whether to check if the user has the tutor role
3494
 * @param bool $coach Whether to check if the user has the coach role
3495
 *
3496
 * @return bool true: the user has the rights to edit, false: he does not
3497
 */
3498
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3499
{
3500
    if (api_is_allowed_to_edit($tutor, $coach)) {
3501
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3502
        return true;
3503
    } else {
3504
        $sessionId = api_get_session_id();
3505
3506
        if (0 == $sessionId) {
3507
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3508
            return true;
3509
        } else {
3510
            // I'm in a session and I'm a student
3511
            // Get the session visibility
3512
            $session_visibility = api_get_session_visibility($sessionId);
3513
            // if 5 the session is still available
3514
            switch ($session_visibility) {
3515
                case SESSION_VISIBLE_READ_ONLY: // 1
3516
                    return false;
3517
                case SESSION_VISIBLE:           // 2
3518
                    return true;
3519
                case SESSION_INVISIBLE:         // 3
3520
                    return false;
3521
                case SESSION_AVAILABLE:         //5
3522
                    return true;
3523
            }
3524
        }
3525
    }
3526
3527
    return false;
3528
}
3529
3530
/**
3531
 * Current user is anon?
3532
 *
3533
 * @return bool true if this user is anonymous, false otherwise
3534
 */
3535
function api_is_anonymous()
3536
{
3537
    return !Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
3538
}
3539
3540
/**
3541
 * Displays message "You are not allowed here..." and exits the entire script.
3542
 *
3543
 * @param bool $print_headers Whether to print headers (default = false -> does not print them)
3544
 * @param string $message
3545
 * @param int $responseCode
3546
 *
3547
 * @throws Exception
3548
 */
3549
function api_not_allowed(
3550
    $print_headers = false,
3551
    $message = null,
3552
    $responseCode = 0
3553
): never {
3554
    throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode);
3555
}
3556
3557
/**
3558
 * @param string $languageIsoCode
3559
 *
3560
 * @return string
3561
 */
3562
function languageToCountryIsoCode($languageIsoCode)
3563
{
3564
    $allow = ('true' === api_get_setting('language.language_flags_by_country'));
3565
3566
    // @todo save in DB
3567
    switch ($languageIsoCode) {
3568
        case 'ar':
3569
            $country = 'ae';
3570
            break;
3571
        case 'bs':
3572
            $country = 'ba';
3573
            break;
3574
        case 'ca':
3575
            $country = 'es';
3576
            if ($allow) {
3577
                $country = 'catalan';
3578
            }
3579
            break;
3580
        case 'cs':
3581
            $country = 'cz';
3582
            break;
3583
        case 'da':
3584
            $country = 'dk';
3585
            break;
3586
        case 'el':
3587
            $country = 'ae';
3588
            break;
3589
        case 'en':
3590
            $country = 'gb';
3591
            break;
3592
        case 'eu': // Euskera
3593
            $country = 'es';
3594
            if ($allow) {
3595
                $country = 'basque';
3596
            }
3597
            break;
3598
        case 'gl': // galego
3599
            $country = 'es';
3600
            if ($allow) {
3601
                $country = 'galician';
3602
            }
3603
            break;
3604
        case 'he':
3605
            $country = 'il';
3606
            break;
3607
        case 'ja':
3608
            $country = 'jp';
3609
            break;
3610
        case 'ka':
3611
            $country = 'ge';
3612
            break;
3613
        case 'ko':
3614
            $country = 'kr';
3615
            break;
3616
        case 'ms':
3617
            $country = 'my';
3618
            break;
3619
        case 'pt-BR':
3620
            $country = 'br';
3621
            break;
3622
        case 'qu':
3623
            $country = 'pe';
3624
            break;
3625
        case 'sl':
3626
            $country = 'si';
3627
            break;
3628
        case 'sv':
3629
            $country = 'se';
3630
            break;
3631
        case 'uk': // Ukraine
3632
            $country = 'ua';
3633
            break;
3634
        case 'zh-TW':
3635
        case 'zh':
3636
            $country = 'cn';
3637
            break;
3638
        default:
3639
            $country = $languageIsoCode;
3640
            break;
3641
    }
3642
    $country = strtolower($country);
3643
3644
    return $country;
3645
}
3646
3647
/**
3648
 * Returns a list of all the languages that are made available by the admin.
3649
 *
3650
 * @return array An array with all languages. Structure of the array is
3651
 *               array['name'] = An array with the name of every language
3652
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
3653
 */
3654
function api_get_languages()
3655
{
3656
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3657
    $sql = "SELECT * FROM $table WHERE available='1'
3658
            ORDER BY original_name ASC";
3659
    $result = Database::query($sql);
3660
    $languages = [];
3661
    while ($row = Database::fetch_assoc($result)) {
3662
        $languages[$row['isocode']] = $row['original_name'];
3663
    }
3664
3665
    return $languages;
3666
}
3667
3668
/**
3669
 * Returns the id (the database id) of a language.
3670
 *
3671
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
3672
 *
3673
 * @return int id of the language
3674
 */
3675
function api_get_language_id($language)
3676
{
3677
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3678
    if (empty($language)) {
3679
        return null;
3680
    }
3681
3682
    // We check the language by iscocode
3683
    $langInfo = api_get_language_from_iso($language);
3684
    if (null !== $langInfo && !empty($langInfo->getId())) {
3685
        return $langInfo->getId();
3686
    }
3687
3688
    $language = Database::escape_string($language);
3689
    $sql = "SELECT id FROM $tbl_language
3690
            WHERE english_name = '$language' LIMIT 1";
3691
    $result = Database::query($sql);
3692
    $row = Database::fetch_array($result);
3693
3694
    return $row['id'];
3695
}
3696
3697
/**
3698
 * Get the language information by its id.
3699
 *
3700
 * @param int $languageId
3701
 *
3702
 * @throws Exception
3703
 *
3704
 * @return array
3705
 */
3706
function api_get_language_info($languageId)
3707
{
3708
    if (empty($languageId)) {
3709
        return [];
3710
    }
3711
3712
    $language = Database::getManager()->find(Language::class, $languageId);
3713
3714
    if (!$language) {
3715
        return [];
3716
    }
3717
3718
    return [
3719
        'id' => $language->getId(),
3720
        'original_name' => $language->getOriginalName(),
3721
        'english_name' => $language->getEnglishName(),
3722
        'isocode' => $language->getIsocode(),
3723
        'available' => $language->getAvailable(),
3724
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
3725
    ];
3726
}
3727
3728
/**
3729
 * @param string $code
3730
 *
3731
 * @return Language
3732
 */
3733
function api_get_language_from_iso($code)
3734
{
3735
    $em = Database::getManager();
3736
3737
    return $em->getRepository(Language::class)->findOneBy(['isocode' => $code]);
3738
}
3739
3740
/**
3741
 * Shortcut to ThemeHelper::getVisualTheme()
3742
 */
3743
function api_get_visual_theme(): string
3744
{
3745
    $themeHelper = Container::$container->get(ThemeHelper::class);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3745
    /** @scrutinizer ignore-call */ 
3746
    $themeHelper = Container::$container->get(ThemeHelper::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3746
3747
    return $themeHelper->getVisualTheme();
3748
}
3749
3750
/**
3751
 * Returns a list of CSS themes currently available in the CSS folder
3752
 * The folder must have a default.css file.
3753
 *
3754
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
3755
 *
3756
 * @return array list of themes directories from the css folder
3757
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
3758
 */
3759
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
3760
{
3761
    // This configuration value is set by the vchamilo plugin
3762
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
3763
3764
    $readCssFolder = function ($dir) use ($virtualTheme) {
3765
        $finder = new Finder();
3766
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
3767
        $list = [];
3768
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
3769
        foreach ($themes as $theme) {
3770
            $folder = $theme->getFilename();
3771
            // A theme folder is consider if there's a default.css file
3772
            if (!file_exists($theme->getPathname().'/default.css')) {
3773
                continue;
3774
            }
3775
            $name = ucwords(str_replace('_', ' ', $folder));
3776
            if ($folder == $virtualTheme) {
3777
                continue;
3778
            }
3779
            $list[$folder] = $name;
3780
        }
3781
3782
        return $list;
3783
    };
3784
3785
    $dir = Container::getProjectDir().'var/themes/';
3786
    $list = $readCssFolder($dir);
3787
3788
    if (!empty($virtualTheme)) {
3789
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
3790
        if ($getOnlyThemeFromVirtualInstance) {
3791
            return $newList;
3792
        }
3793
        $list = $list + $newList;
3794
        asort($list);
3795
    }
3796
3797
    return $list;
3798
}
3799
3800
/**
3801
 * Find the largest sort value in a given user_course_category
3802
 * This function is used when we are moving a course to a different category
3803
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
3804
 *
3805
 * @param int $courseCategoryId the id of the user_course_category
3806
 * @param int $userId
3807
 *
3808
 * @return int the value of the highest sort of the user_course_category
3809
 */
3810
function api_max_sort_value($courseCategoryId, $userId)
3811
{
3812
    $user = api_get_user_entity($userId);
3813
    $userCourseCategory = Database::getManager()->getRepository(UserCourseCategory::class)->find($courseCategoryId);
3814
3815
    return null === $user ? 0 : $user->getMaxSortValue($userCourseCategory);
3816
}
3817
3818
/**
3819
 * Transforms a number of seconds in hh:mm:ss format.
3820
 *
3821
 * @author Julian Prud'homme
3822
 *
3823
 * @param int    $seconds      number of seconds
3824
 * @param string $space
3825
 * @param bool   $showSeconds
3826
 * @param bool   $roundMinutes
3827
 *
3828
 * @return string the formatted time
3829
 */
3830
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
3831
{
3832
    // $seconds = -1 means that we have wrong data in the db.
3833
    if (-1 == $seconds) {
3834
        return
3835
            get_lang('Unknown').
3836
            Display::getMdiIcon(
3837
                ActionIcon::INFORMATION,
3838
                'ch-tool-icon',
3839
                'align: absmiddle; hspace: 3px',
3840
                ICON_SIZE_SMALL,
3841
                get_lang('The datas about this user were registered when the calculation of time spent on the platform wasn\'t possible.')
3842
            );
3843
    }
3844
3845
    // How many hours ?
3846
    $hours = floor($seconds / 3600);
3847
3848
    // How many minutes ?
3849
    $min = floor(($seconds - ($hours * 3600)) / 60);
3850
3851
    if ($roundMinutes) {
3852
        if ($min >= 45) {
3853
            $min = 45;
3854
        }
3855
3856
        if ($min >= 30 && $min <= 44) {
3857
            $min = 30;
3858
        }
3859
3860
        if ($min >= 15 && $min <= 29) {
3861
            $min = 15;
3862
        }
3863
3864
        if ($min >= 0 && $min <= 14) {
3865
            $min = 0;
3866
        }
3867
    }
3868
3869
    // How many seconds
3870
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
3871
3872
    if ($hours < 10) {
3873
        $hours = "0$hours";
3874
    }
3875
3876
    if ($sec < 10) {
3877
        $sec = "0$sec";
3878
    }
3879
3880
    if ($min < 10) {
3881
        $min = "0$min";
3882
    }
3883
3884
    $seconds = '';
3885
    if ($showSeconds) {
3886
        $seconds = $space.$sec;
3887
    }
3888
3889
    return $hours.$space.$min.$seconds;
3890
}
3891
3892
/**
3893
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3894
 * The return value is based on the platform administrator's setting
3895
 * "Administration > Configuration settings > Security > Permissions for new directories".
3896
 *
3897
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
3898
 */
3899
function api_get_permissions_for_new_directories()
3900
{
3901
    static $permissions;
3902
    if (!isset($permissions)) {
3903
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
3904
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
3905
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
3906
    }
3907
3908
    return $permissions;
3909
}
3910
3911
/**
3912
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3913
 * The return value is based on the platform administrator's setting
3914
 * "Administration > Configuration settings > Security > Permissions for new files".
3915
 *
3916
 * @return int returns the permissions in the format
3917
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
3918
 */
3919
function api_get_permissions_for_new_files()
3920
{
3921
    static $permissions;
3922
    if (!isset($permissions)) {
3923
        $permissions = trim(api_get_setting('permissions_for_new_files'));
3924
        // The default value 0666 is according to that in the platform
3925
        // administration panel after fresh system installation.
3926
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
3927
    }
3928
3929
    return $permissions;
3930
}
3931
3932
/**
3933
 * Deletes a file, or a folder and its contents.
3934
 *
3935
 * @author      Aidan Lister <[email protected]>
3936
 *
3937
 * @version     1.0.3
3938
 *
3939
 * @param string $dirname Directory to delete
3940
 * @param       bool     Deletes only the content or not
3941
 * @param bool $strict if one folder/file fails stop the loop
3942
 *
3943
 * @return bool Returns TRUE on success, FALSE on failure
3944
 *
3945
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
3946
 *
3947
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
3948
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
3949
 */
3950
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
3951
{
3952
    $res = true;
3953
    // A sanity check.
3954
    if (!file_exists($dirname)) {
3955
        return false;
3956
    }
3957
    $php_errormsg = '';
3958
    // Simple delete for a file.
3959
    if (is_file($dirname) || is_link($dirname)) {
3960
        $res = unlink($dirname);
3961
        if (false === $res) {
3962
            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);
3963
        }
3964
3965
        return $res;
3966
    }
3967
3968
    // Loop through the folder.
3969
    $dir = dir($dirname);
3970
    // A sanity check.
3971
    $is_object_dir = is_object($dir);
3972
    if ($is_object_dir) {
3973
        while (false !== $entry = $dir->read()) {
3974
            // Skip pointers.
3975
            if ('.' == $entry || '..' == $entry) {
3976
                continue;
3977
            }
3978
3979
            // Recurse.
3980
            if ($strict) {
3981
                $result = rmdirr("$dirname/$entry");
3982
                if (false == $result) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
3983
                    $res = false;
3984
                    break;
3985
                }
3986
            } else {
3987
                rmdirr("$dirname/$entry");
3988
            }
3989
        }
3990
    }
3991
3992
    // Clean up.
3993
    if ($is_object_dir) {
3994
        $dir->close();
3995
    }
3996
3997
    if (false == $delete_only_content_in_folder) {
3998
        $res = rmdir($dirname);
3999
        if (false === $res) {
4000
            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);
4001
        }
4002
    }
4003
4004
    return $res;
4005
}
4006
4007
// TODO: This function is to be simplified. File access modes to be implemented.
4008
/**
4009
 * function adapted from a php.net comment
4010
 * copy recursively a folder.
4011
 *
4012
 * @param the source folder
4013
 * @param the dest folder
4014
 * @param an array of excluded file_name (without extension)
4015
 * @param copied_files the returned array of copied files
4016
 * @param string $source
4017
 * @param string $dest
4018
 */
4019
function copyr($source, $dest, $exclude = [], $copied_files = [])
4020
{
4021
    if (empty($dest)) {
4022
        return false;
4023
    }
4024
    // Simple copy for a file
4025
    if (is_file($source)) {
4026
        $path_info = pathinfo($source);
4027
        if (!in_array($path_info['filename'], $exclude)) {
4028
            copy($source, $dest);
4029
        }
4030
4031
        return true;
4032
    } elseif (!is_dir($source)) {
4033
        //then source is not a dir nor a file, return
4034
        return false;
4035
    }
4036
4037
    // Make destination directory.
4038
    if (!is_dir($dest)) {
4039
        mkdir($dest, api_get_permissions_for_new_directories());
4040
    }
4041
4042
    // Loop through the folder.
4043
    $dir = dir($source);
4044
    while (false !== $entry = $dir->read()) {
4045
        // Skip pointers
4046
        if ('.' == $entry || '..' == $entry) {
4047
            continue;
4048
        }
4049
4050
        // Deep copy directories.
4051
        if ($dest !== "$source/$entry") {
4052
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
4053
        }
4054
    }
4055
    // Clean up.
4056
    $dir->close();
4057
4058
    return true;
4059
}
4060
4061
/**
4062
 * @todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
4063
 * Documentation header to be added here.
4064
 *
4065
 * @param string $pathname
4066
 * @param string $base_path_document
4067
 * @param int    $session_id
4068
 *
4069
 * @return mixed True if directory already exists, false if a file already exists at
4070
 *               the destination and null if everything goes according to plan
4071
 */
4072
function copy_folder_course_session(
4073
    $pathname,
4074
    $base_path_document,
4075
    $session_id,
4076
    $course_info,
4077
    $document,
4078
    $source_course_id
4079
) {
4080
    $table = Database::get_course_table(TABLE_DOCUMENT);
4081
    $session_id = intval($session_id);
4082
    $source_course_id = intval($source_course_id);
4083
4084
    // Check whether directory already exists.
4085
    if (is_dir($pathname) || empty($pathname)) {
4086
        return true;
4087
    }
4088
4089
    // Ensure that a file with the same name does not already exist.
4090
    if (is_file($pathname)) {
4091
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
4092
4093
        return false;
4094
    }
4095
4096
    $course_id = $course_info['real_id'];
4097
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
4098
    $new_pathname = $base_path_document;
4099
    $path = '';
4100
4101
    foreach ($folders as $folder) {
4102
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
4103
        $path .= DIRECTORY_SEPARATOR.$folder;
4104
4105
        if (!file_exists($new_pathname)) {
4106
            $path = Database::escape_string($path);
4107
4108
            $sql = "SELECT * FROM $table
4109
                    WHERE
4110
                        c_id = $source_course_id AND
4111
                        path = '$path' AND
4112
                        filetype = 'folder' AND
4113
                        session_id = '$session_id'";
4114
            $rs1 = Database::query($sql);
4115
            $num_rows = Database::num_rows($rs1);
4116
4117
            if (0 == $num_rows) {
4118
                mkdir($new_pathname, api_get_permissions_for_new_directories());
4119
4120
                // Insert new folder with destination session_id.
4121
                $params = [
4122
                    'c_id' => $course_id,
4123
                    'path' => $path,
4124
                    'comment' => $document->comment,
4125
                    'title' => basename($new_pathname),
4126
                    'filetype' => 'folder',
4127
                    'size' => '0',
4128
                    'session_id' => $session_id,
4129
                ];
4130
                Database::insert($table, $params);
4131
            }
4132
        }
4133
    } // en foreach
4134
}
4135
4136
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
4137
/**
4138
 * @param string $path
4139
 */
4140
function api_chmod_R($path, $filemode)
4141
{
4142
    if (!is_dir($path)) {
4143
        return chmod($path, $filemode);
4144
    }
4145
4146
    $handler = opendir($path);
4147
    while ($file = readdir($handler)) {
4148
        if ('.' != $file && '..' != $file) {
4149
            $fullpath = "$path/$file";
4150
            if (!is_dir($fullpath)) {
4151
                if (!chmod($fullpath, $filemode)) {
4152
                    return false;
4153
                }
4154
            } else {
4155
                if (!api_chmod_R($fullpath, $filemode)) {
4156
                    return false;
4157
                }
4158
            }
4159
        }
4160
    }
4161
4162
    closedir($handler);
4163
4164
    return chmod($path, $filemode);
4165
}
4166
4167
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
4168
/**
4169
 * Parse info file format. (e.g: file.info).
4170
 *
4171
 * Files should use an ini-like format to specify values.
4172
 * White-space generally doesn't matter, except inside values.
4173
 * e.g.
4174
 *
4175
 * @verbatim
4176
 *   key = value
4177
 *   key = "value"
4178
 *   key = 'value'
4179
 *   key = "multi-line
4180
 *
4181
 *   value"
4182
 *   key = 'multi-line
4183
 *
4184
 *   value'
4185
 *   key
4186
 *   =
4187
 *   'value'
4188
 * @endverbatim
4189
 *
4190
 * Arrays are created using a GET-like syntax:
4191
 *
4192
 * @verbatim
4193
 *   key[] = "numeric array"
4194
 *   key[index] = "associative array"
4195
 *   key[index][] = "nested numeric array"
4196
 *   key[index][index] = "nested associative array"
4197
 * @endverbatim
4198
 *
4199
 * PHP constants are substituted in, but only when used as the entire value:
4200
 *
4201
 * Comments should start with a semi-colon at the beginning of a line.
4202
 *
4203
 * This function is NOT for placing arbitrary module-specific settings. Use
4204
 * variable_get() and variable_set() for that.
4205
 *
4206
 * Information stored in the module.info file:
4207
 * - name: The real name of the module for display purposes.
4208
 * - description: A brief description of the module.
4209
 * - dependencies: An array of shortnames of other modules this module depends on.
4210
 * - package: The name of the package of modules this module belongs to.
4211
 *
4212
 * Example of .info file:
4213
 * <code>
4214
 * @verbatim
4215
 *   name = Forum
4216
 *   description = Enables threaded discussions about general topics.
4217
 *   dependencies[] = taxonomy
4218
 *   dependencies[] = comment
4219
 *   package = Core - optional
4220
 *   version = VERSION
4221
 * @endverbatim
4222
 * </code>
4223
 *
4224
 * @param string $filename
4225
 *                         The file we are parsing. Accepts file with relative or absolute path.
4226
 *
4227
 * @return
4228
 *   The info array
4229
 */
4230
function api_parse_info_file($filename)
4231
{
4232
    $info = [];
4233
4234
    if (!file_exists($filename)) {
4235
        return $info;
4236
    }
4237
4238
    $data = file_get_contents($filename);
4239
    if (preg_match_all('
4240
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
4241
        ((?:
4242
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
4243
          \[[^\[\]]*\]                  # unless they are balanced and not nested
4244
        )+?)
4245
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
4246
        (?:
4247
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
4248
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
4249
          ([^\r\n]*?)                   # Non-quoted string
4250
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
4251
        @msx', $data, $matches, PREG_SET_ORDER)) {
4252
        $key = $value1 = $value2 = $value3 = '';
4253
        foreach ($matches as $match) {
4254
            // Fetch the key and value string.
4255
            $i = 0;
4256
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
4257
                $$var = isset($match[++$i]) ? $match[$i] : '';
4258
            }
4259
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
4260
4261
            // Parse array syntax.
4262
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
4263
            $last = array_pop($keys);
4264
            $parent = &$info;
4265
4266
            // Create nested arrays.
4267
            foreach ($keys as $key) {
4268
                if ('' == $key) {
4269
                    $key = count($parent);
4270
                }
4271
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
4272
                    $parent[$key] = [];
4273
                }
4274
                $parent = &$parent[$key];
4275
            }
4276
4277
            // Handle PHP constants.
4278
            if (defined($value)) {
4279
                $value = constant($value);
4280
            }
4281
4282
            // Insert actual value.
4283
            if ('' == $last) {
4284
                $last = count($parent);
4285
            }
4286
            $parent[$last] = $value;
4287
        }
4288
    }
4289
4290
    return $info;
4291
}
4292
4293
/**
4294
 * Gets Chamilo version from the configuration files.
4295
 *
4296
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
4297
 */
4298
function api_get_version()
4299
{
4300
    return (string) api_get_configuration_value('system_version');
4301
}
4302
4303
/**
4304
 * Gets the software name (the name/brand of the Chamilo-based customized system).
4305
 *
4306
 * @return string
4307
 */
4308
function api_get_software_name()
4309
{
4310
    $name = api_get_configuration_value('software_name');
4311
    if (!empty($name)) {
4312
        return $name;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $name also could return the type boolean which is incompatible with the documented return type string.
Loading history...
4313
    } else {
4314
        return 'Chamilo';
4315
    }
4316
}
4317
4318
function api_get_status_list()
4319
{
4320
    $list = [];
4321
    // Table of status
4322
    $list[COURSEMANAGER] = 'teacher'; // 1
4323
    $list[SESSIONADMIN] = 'session_admin'; // 3
4324
    $list[DRH] = 'drh'; // 4
4325
    $list[STUDENT] = 'user'; // 5
4326
    $list[ANONYMOUS] = 'anonymous'; // 6
4327
    $list[INVITEE] = 'invited'; // 20
4328
4329
    return $list;
4330
}
4331
4332
/**
4333
 * Checks whether status given in parameter exists in the platform.
4334
 *
4335
 * @param mixed the status (can be either int either string)
4336
 *
4337
 * @return bool if the status exists, else returns false
4338
 */
4339
function api_status_exists($status_asked)
4340
{
4341
    $list = api_get_status_list();
4342
4343
    return in_array($status_asked, $list) ? true : isset($list[$status_asked]);
4344
}
4345
4346
/**
4347
 * Checks whether status given in parameter exists in the platform. The function
4348
 * returns the status ID or false if it does not exist, but given the fact there
4349
 * is no "0" status, the return value can be checked against
4350
 * if(api_status_key()) to know if it exists.
4351
 *
4352
 * @param   mixed   The status (can be either int or string)
4353
 *
4354
 * @return mixed Status ID if exists, false otherwise
4355
 */
4356
function api_status_key($status)
4357
{
4358
    $list = api_get_status_list();
4359
4360
    return isset($list[$status]) ? $status : array_search($status, $list);
4361
}
4362
4363
/**
4364
 * Gets the status langvars list.
4365
 *
4366
 * @return string[] the list of status with their translations
4367
 */
4368
function api_get_status_langvars()
4369
{
4370
    return [
4371
        COURSEMANAGER => get_lang('Teacher'),
4372
        SESSIONADMIN => get_lang('Sessions administrator'),
4373
        DRH => get_lang('Human Resources Manager'),
4374
        STUDENT => get_lang('Learner'),
4375
        ANONYMOUS => get_lang('Anonymous'),
4376
        STUDENT_BOSS => get_lang('Student boss'),
4377
        INVITEE => get_lang('Invited'),
4378
    ];
4379
}
4380
4381
/**
4382
 * The function that retrieves all the possible settings for a certain config setting.
4383
 *
4384
 * @author Patrick Cool <[email protected]>, Ghent University
4385
 */
4386
function api_get_settings_options($var)
4387
{
4388
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4389
    $var = Database::escape_string($var);
4390
    $sql = "SELECT * FROM $table_settings_options
4391
            WHERE variable = '$var'
4392
            ORDER BY id";
4393
    $result = Database::query($sql);
4394
    $settings_options_array = [];
4395
    while ($row = Database::fetch_assoc($result)) {
4396
        $settings_options_array[] = $row;
4397
    }
4398
4399
    return $settings_options_array;
4400
}
4401
4402
/**
4403
 * @param array $params
4404
 */
4405
function api_set_setting_option($params)
4406
{
4407
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4408
    if (empty($params['id'])) {
4409
        Database::insert($table, $params);
4410
    } else {
4411
        Database::update($table, $params, ['id = ? ' => $params['id']]);
4412
    }
4413
}
4414
4415
/**
4416
 * @param array $params
4417
 */
4418
function api_set_setting_simple($params)
4419
{
4420
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4421
    $url_id = api_get_current_access_url_id();
4422
4423
    if (empty($params['id'])) {
4424
        $params['access_url'] = $url_id;
4425
        Database::insert($table, $params);
4426
    } else {
4427
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
4428
    }
4429
}
4430
4431
/**
4432
 * @param int $id
4433
 */
4434
function api_delete_setting_option($id)
4435
{
4436
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4437
    if (!empty($id)) {
4438
        Database::delete($table, ['id = ? ' => $id]);
4439
    }
4440
}
4441
4442
/**
4443
 * Sets a platform configuration setting to a given value.
4444
 *
4445
 * @param string    The variable we want to update
4446
 * @param string    The value we want to record
4447
 * @param string    The sub-variable if any (in most cases, this will remain null)
4448
 * @param string    The category if any (in most cases, this will remain null)
4449
 * @param int       The access_url for which this parameter is valid
4450
 * @param string $cat
4451
 *
4452
 * @return bool|null
4453
 */
4454
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
4455
{
4456
    if (empty($var)) {
4457
        return false;
4458
    }
4459
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
4460
    $var = Database::escape_string($var);
4461
    $value = Database::escape_string($value);
4462
    $access_url = (int) $access_url;
4463
    if (empty($access_url)) {
4464
        $access_url = 1;
4465
    }
4466
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
4467
    if (!empty($subvar)) {
4468
        $subvar = Database::escape_string($subvar);
4469
        $select .= " AND subkey = '$subvar'";
4470
    }
4471
    if (!empty($cat)) {
4472
        $cat = Database::escape_string($cat);
4473
        $select .= " AND category = '$cat'";
4474
    }
4475
    if ($access_url > 1) {
4476
        $select .= " AND access_url = $access_url";
4477
    } else {
4478
        $select .= " AND access_url = 1 ";
4479
    }
4480
4481
    $res = Database::query($select);
4482
    if (Database::num_rows($res) > 0) {
4483
        // Found item for this access_url.
4484
        $row = Database::fetch_array($res);
4485
        $sql = "UPDATE $t_settings SET selected_value = '$value'
4486
                WHERE id = ".$row['id'];
4487
        Database::query($sql);
4488
    } else {
4489
        // Item not found for this access_url, we have to check if it exist with access_url = 1
4490
        $select = "SELECT * FROM $t_settings
4491
                   WHERE variable = '$var' AND access_url = 1 ";
4492
        // Just in case
4493
        if (1 == $access_url) {
4494
            if (!empty($subvar)) {
4495
                $select .= " AND subkey = '$subvar'";
4496
            }
4497
            if (!empty($cat)) {
4498
                $select .= " AND category = '$cat'";
4499
            }
4500
            $res = Database::query($select);
4501
            if (Database::num_rows($res) > 0) {
4502
                // We have a setting for access_url 1, but none for the current one, so create one.
4503
                $row = Database::fetch_array($res);
4504
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
4505
                        VALUES
4506
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4507
                    "'".$row['type']."','".$row['category']."',".
4508
                    "'$value','".$row['title']."',".
4509
                    "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4510
                    "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
4511
                Database::query($insert);
4512
            } else {
4513
                // Such a setting does not exist.
4514
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
4515
            }
4516
        } else {
4517
            // Other access url.
4518
            if (!empty($subvar)) {
4519
                $select .= " AND subkey = '$subvar'";
4520
            }
4521
            if (!empty($cat)) {
4522
                $select .= " AND category = '$cat'";
4523
            }
4524
            $res = Database::query($select);
4525
4526
            if (Database::num_rows($res) > 0) {
4527
                // We have a setting for access_url 1, but none for the current one, so create one.
4528
                $row = Database::fetch_array($res);
4529
                if (1 == $row['access_url_changeable']) {
4530
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
4531
                            ('".$row['variable']."',".
4532
                        (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4533
                        "'".$row['type']."','".$row['category']."',".
4534
                        "'$value','".$row['title']."',".
4535
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
4536
                        (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4537
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
4538
                    Database::query($insert);
4539
                }
4540
            } else { // Such a setting does not exist.
4541
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
4542
            }
4543
        }
4544
    }
4545
}
4546
4547
/**
4548
 * Sets a whole category of settings to one specific value.
4549
 *
4550
 * @param string    Category
4551
 * @param string    Value
4552
 * @param int       Access URL. Optional. Defaults to 1
4553
 * @param array     Optional array of filters on field type
4554
 * @param string $category
4555
 * @param string $value
4556
 *
4557
 * @return bool
4558
 */
4559
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
4560
{
4561
    if (empty($category)) {
4562
        return false;
4563
    }
4564
    $category = Database::escape_string($category);
4565
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS);
4566
    $access_url = (int) $access_url;
4567
    if (empty($access_url)) {
4568
        $access_url = 1;
4569
    }
4570
    if (isset($value)) {
4571
        $value = Database::escape_string($value);
4572
        $sql = "UPDATE $t_s SET selected_value = '$value'
4573
                WHERE category = '$category' AND access_url = $access_url";
4574
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4575
            $sql .= " AND ( ";
4576
            $i = 0;
4577
            foreach ($fieldtype as $type) {
4578
                if ($i > 0) {
4579
                    $sql .= ' OR ';
4580
                }
4581
                $type = Database::escape_string($type);
4582
                $sql .= " type='".$type."' ";
4583
                $i++;
4584
            }
4585
            $sql .= ")";
4586
        }
4587
        $res = Database::query($sql);
4588
4589
        return false !== $res;
4590
    } else {
4591
        $sql = "UPDATE $t_s SET selected_value = NULL
4592
                WHERE category = '$category' AND access_url = $access_url";
4593
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4594
            $sql .= " AND ( ";
4595
            $i = 0;
4596
            foreach ($fieldtype as $type) {
4597
                if ($i > 0) {
4598
                    $sql .= ' OR ';
4599
                }
4600
                $type = Database::escape_string($type);
4601
                $sql .= " type='".$type."' ";
4602
                $i++;
4603
            }
4604
            $sql .= ")";
4605
        }
4606
        $res = Database::query($sql);
4607
4608
        return false !== $res;
4609
    }
4610
}
4611
4612
/**
4613
 * Gets all available access urls in an array (as in the database).
4614
 *
4615
 * @return array An array of database records
4616
 */
4617
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
4618
{
4619
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4620
    $from = (int) $from;
4621
    $to = (int) $to;
4622
    $order = Database::escape_string($order);
4623
    $direction = Database::escape_string($direction);
4624
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
4625
    $sql = "SELECT id, url, description, active, created_by, tms
4626
            FROM $table
4627
            ORDER BY `$order` $direction
4628
            LIMIT $to OFFSET $from";
4629
    $res = Database::query($sql);
4630
4631
    return Database::store_result($res);
4632
}
4633
4634
/**
4635
 * Gets the access url info in an array.
4636
 *
4637
 * @param int  $id            Id of the access url
4638
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
4639
 *
4640
 * @return array All the info (url, description, active, created_by, tms)
4641
 *               from the access_url table
4642
 *
4643
 * @author Julio Montoya
4644
 */
4645
function api_get_access_url($id, $returnDefault = true)
4646
{
4647
    static $staticResult;
4648
    $id = (int) $id;
4649
4650
    if (isset($staticResult[$id])) {
4651
        $result = $staticResult[$id];
4652
    } else {
4653
        // Calling the Database:: library dont work this is handmade.
4654
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4655
        $sql = "SELECT url, description, active, created_by, tms
4656
                FROM $table_access_url WHERE id = '$id' ";
4657
        $res = Database::query($sql);
4658
        $result = @Database::fetch_array($res);
4659
        $staticResult[$id] = $result;
4660
    }
4661
4662
    // If the result url is 'http://localhost/' (the default) and the root_web
4663
    // (=current url) is different, and the $id is = 1 (which might mean
4664
    // api_get_current_access_url_id() returned 1 by default), then return the
4665
    // root_web setting instead of the current URL
4666
    // This is provided as an option to avoid breaking the storage of URL-specific
4667
    // homepages in home/localhost/
4668
    if (1 === $id && false === $returnDefault) {
4669
        $currentUrl = api_get_current_access_url_id();
4670
        // only do this if we are on the main URL (=1), otherwise we could get
4671
        // information on another URL instead of the one asked as parameter
4672
        if (1 === $currentUrl) {
4673
            $rootWeb = api_get_path(WEB_PATH);
4674
            $default = AccessUrl::DEFAULT_ACCESS_URL;
4675
            if ($result['url'] === $default && $rootWeb != $default) {
4676
                $result['url'] = $rootWeb;
4677
            }
4678
        }
4679
    }
4680
4681
    return $result;
4682
}
4683
4684
/**
4685
 * Gets all the current settings for a specific access url.
4686
 *
4687
 * @param string    The category, if any, that we want to get
4688
 * @param string    Whether we want a simple list (display a category) or
4689
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
4690
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
4691
 *
4692
 * @return array Array of database results for the current settings of the current access URL
4693
 */
4694
function &api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
4695
{
4696
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4697
    $access_url = (int) $access_url;
4698
    $where_condition = '';
4699
    if (1 == $url_changeable) {
4700
        $where_condition = " AND access_url_changeable= '1' ";
4701
    }
4702
    if (empty($access_url) || -1 == $access_url) {
4703
        $access_url = 1;
4704
    }
4705
    $sql = "SELECT * FROM $table
4706
            WHERE access_url = $access_url  $where_condition ";
4707
4708
    if (!empty($cat)) {
4709
        $cat = Database::escape_string($cat);
4710
        $sql .= " AND category='$cat' ";
4711
    }
4712
    if ('group' == $ordering) {
4713
        $sql .= " ORDER BY id ASC";
4714
    } else {
4715
        $sql .= " ORDER BY 1,2 ASC";
4716
    }
4717
    $result = Database::query($sql);
4718
    if (null === $result) {
4719
        $result = [];
4720
        return $result;
4721
    }
4722
    $result = Database::store_result($result, 'ASSOC');
4723
4724
    return $result;
4725
}
4726
4727
/**
4728
 * @param string $value       The value we want to record
4729
 * @param string $variable    The variable name we want to insert
4730
 * @param string $subKey      The subkey for the variable we want to insert
4731
 * @param string $type        The type for the variable we want to insert
4732
 * @param string $category    The category for the variable we want to insert
4733
 * @param string $title       The title
4734
 * @param string $comment     The comment
4735
 * @param string $scope       The scope
4736
 * @param string $subKeyText  The subkey text
4737
 * @param int    $accessUrlId The access_url for which this parameter is valid
4738
 * @param int    $visibility  The changeability of this setting for non-master urls
4739
 *
4740
 * @return int The setting ID
4741
 */
4742
function api_add_setting(
4743
    $value,
4744
    $variable,
4745
    $subKey = '',
4746
    $type = 'textfield',
4747
    $category = '',
4748
    $title = '',
4749
    $comment = '',
4750
    $scope = '',
4751
    $subKeyText = '',
4752
    $accessUrlId = 1,
4753
    $visibility = 0
4754
) {
4755
    $em = Database::getManager();
4756
4757
    $settingRepo = $em->getRepository(SettingsCurrent::class);
4758
    $accessUrlId = (int) $accessUrlId ?: 1;
4759
4760
    if (is_array($value)) {
4761
        $value = serialize($value);
4762
    } else {
4763
        $value = trim($value);
4764
    }
4765
4766
    $criteria = ['variable' => $variable, 'url' => $accessUrlId];
4767
4768
    if (!empty($subKey)) {
4769
        $criteria['subkey'] = $subKey;
4770
    }
4771
4772
    // Check if this variable doesn't exist already
4773
    /** @var SettingsCurrent $setting */
4774
    $setting = $settingRepo->findOneBy($criteria);
4775
4776
    if ($setting) {
0 ignored issues
show
introduced by
$setting is of type Chamilo\CoreBundle\Entity\SettingsCurrent, thus it always evaluated to true.
Loading history...
4777
        $setting->setSelectedValue($value);
4778
4779
        $em->persist($setting);
4780
        $em->flush();
4781
4782
        return $setting->getId();
4783
    }
4784
4785
    // Item not found for this access_url, we have to check if the whole thing is missing
4786
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
4787
    $setting = new SettingsCurrent();
4788
    $url = api_get_url_entity();
4789
4790
    $setting
4791
        ->setVariable($variable)
4792
        ->setSelectedValue($value)
4793
        ->setType($type)
4794
        ->setCategory($category)
4795
        ->setSubkey($subKey)
4796
        ->setTitle($title)
4797
        ->setComment($comment)
4798
        ->setScope($scope)
4799
        ->setSubkeytext($subKeyText)
4800
        ->setUrl(api_get_url_entity())
4801
        ->setAccessUrlChangeable($visibility);
4802
4803
    $em->persist($setting);
4804
    $em->flush();
4805
4806
    return $setting->getId();
4807
}
4808
4809
/**
4810
 * Checks wether a user can or can't view the contents of a course.
4811
 *
4812
 * @deprecated use CourseManager::is_user_subscribed_in_course
4813
 *
4814
 * @param int $userid User id or NULL to get it from $_SESSION
4815
 * @param int $cid    course id to check whether the user is allowed
4816
 *
4817
 * @return bool
4818
 */
4819
function api_is_course_visible_for_user($userid = null, $cid = null)
4820
{
4821
    if (null === $userid) {
4822
        $userid = api_get_user_id();
4823
    }
4824
    if (empty($userid) || strval(intval($userid)) != $userid) {
4825
        if (api_is_anonymous()) {
4826
            $userid = api_get_anonymous_id();
4827
        } else {
4828
            return false;
4829
        }
4830
    }
4831
    $cid = Database::escape_string($cid);
4832
4833
    $courseInfo = api_get_course_info($cid);
4834
    $courseId = $courseInfo['real_id'];
4835
    $is_platformAdmin = api_is_platform_admin();
4836
4837
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
4838
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
4839
4840
    $sql = "SELECT
4841
                $course_cat_table.code AS category_code,
4842
                $course_table.visibility,
4843
                $course_table.code,
4844
                $course_cat_table.code
4845
            FROM $course_table
4846
            LEFT JOIN $course_cat_table
4847
                ON $course_table.category_id = $course_cat_table.id
4848
            WHERE
4849
                $course_table.code = '$cid'
4850
            LIMIT 1";
4851
4852
    $result = Database::query($sql);
4853
4854
    if (Database::num_rows($result) > 0) {
4855
        $visibility = Database::fetch_array($result);
4856
        $visibility = $visibility['visibility'];
4857
    } else {
4858
        $visibility = 0;
4859
    }
4860
    // Shortcut permissions in case the visibility is "open to the world".
4861
    if (COURSE_VISIBILITY_OPEN_WORLD === $visibility) {
4862
        return true;
4863
    }
4864
4865
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4866
4867
    $sql = "SELECT
4868
                is_tutor, status
4869
            FROM $tbl_course_user
4870
            WHERE
4871
                user_id  = '$userid' AND
4872
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
4873
                c_id = $courseId
4874
            LIMIT 1";
4875
4876
    $result = Database::query($sql);
4877
4878
    if (Database::num_rows($result) > 0) {
4879
        // This user has got a recorded state for this course.
4880
        $cuData = Database::fetch_array($result);
4881
        $is_courseMember = true;
4882
        $is_courseAdmin = (1 == $cuData['status']);
4883
    }
4884
4885
    if (!$is_courseAdmin) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_courseAdmin does not seem to be defined for all execution paths leading up to this point.
Loading history...
4886
        // This user has no status related to this course.
4887
        // Is it the session coach or the session admin?
4888
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4889
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4890
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4891
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4892
4893
        $sql = "SELECT sru_2.user_id AS session_admin_id, sru_1.user_id AS session_coach_id
4894
                FROM $tbl_session AS s
4895
                INNER JOIN $tblSessionRelUser sru_1
4896
                ON (sru_1.session_id = s.id AND sru_1.relation_type = ".SessionEntity::GENERAL_COACH.")
4897
                INNER JOIN $tblSessionRelUser sru_2
4898
                ON (sru_2.session_id = s.id AND sru_2.relation_type = ".SessionEntity::SESSION_ADMIN.")
4899
                INNER JOIN $tbl_session_course src
4900
                ON (src.session_id = s.id AND src.c_id = $courseId)";
4901
4902
        $result = Database::query($sql);
4903
        $row = Database::store_result($result);
4904
        $sessionAdminsId = array_column($row, 'session_admin_id');
4905
        $sessionCoachesId = array_column($row, 'session_coach_id');
4906
4907
        if (in_array($userid, $sessionCoachesId)) {
4908
            $is_courseMember = true;
4909
            $is_courseAdmin = false;
4910
        } elseif (in_array($userid, $sessionAdminsId)) {
4911
            $is_courseMember = false;
4912
            $is_courseAdmin = false;
4913
        } else {
4914
            // Check if the current user is the course coach.
4915
            $sql = "SELECT 1
4916
                    FROM $tbl_session_course
4917
                    WHERE session_rel_course.c_id = '$courseId'
4918
                    AND session_rel_course.id_coach = '$userid'
4919
                    LIMIT 1";
4920
4921
            $result = Database::query($sql);
4922
4923
            //if ($row = Database::fetch_array($result)) {
4924
            if (Database::num_rows($result) > 0) {
4925
                $is_courseMember = true;
4926
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4927
4928
                $sql = "SELECT status FROM $tbl_user
4929
                        WHERE id = $userid
4930
                        LIMIT 1";
4931
4932
                $result = Database::query($sql);
4933
4934
                if (1 == Database::result($result, 0, 0)) {
4935
                    $is_courseAdmin = true;
4936
                } else {
4937
                    $is_courseAdmin = false;
4938
                }
4939
            } else {
4940
                // Check if the user is a student is this session.
4941
                $sql = "SELECT  id
4942
                        FROM $tbl_session_course_user
4943
                        WHERE
4944
                            user_id  = '$userid' AND
4945
                            c_id = '$courseId'
4946
                        LIMIT 1";
4947
4948
                if (Database::num_rows($result) > 0) {
4949
                    // This user haa got a recorded state for this course.
4950
                    while ($row = Database::fetch_array($result)) {
4951
                        $is_courseMember = true;
4952
                        $is_courseAdmin = false;
4953
                    }
4954
                }
4955
            }
4956
        }
4957
    }
4958
4959
    switch ($visibility) {
4960
        case Course::OPEN_WORLD:
4961
            return true;
4962
        case Course::OPEN_PLATFORM:
4963
            return isset($userid);
4964
        case Course::REGISTERED:
4965
        case Course::CLOSED:
4966
            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...
4967
        case Course::HIDDEN:
4968
            return $is_platformAdmin;
4969
    }
4970
4971
    return false;
4972
}
4973
4974
/**
4975
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
4976
 *
4977
 * @param string the tool of the element
4978
 * @param int the element id in database
4979
 * @param int the session_id to compare with element session id
4980
 *
4981
 * @return bool true if the element is in the session, false else
4982
 */
4983
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
4984
{
4985
    if (is_null($session_id)) {
4986
        $session_id = api_get_session_id();
4987
    }
4988
4989
    $element_id = (int) $element_id;
4990
4991
    if (empty($element_id)) {
4992
        return false;
4993
    }
4994
4995
    // Get information to build query depending of the tool.
4996
    switch ($tool) {
4997
        case TOOL_SURVEY:
4998
            $table_tool = Database::get_course_table(TABLE_SURVEY);
4999
            $key_field = 'survey_id';
5000
            break;
5001
        case TOOL_ANNOUNCEMENT:
5002
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
5003
            $key_field = 'id';
5004
            break;
5005
        case TOOL_AGENDA:
5006
            $table_tool = Database::get_course_table(TABLE_AGENDA);
5007
            $key_field = 'id';
5008
            break;
5009
        case TOOL_GROUP:
5010
            $table_tool = Database::get_course_table(TABLE_GROUP);
5011
            $key_field = 'id';
5012
            break;
5013
        default:
5014
            return false;
5015
    }
5016
    $course_id = api_get_course_int_id();
5017
5018
    $sql = "SELECT session_id FROM $table_tool
5019
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
5020
    $rs = Database::query($sql);
5021
    if ($element_session_id = Database::result($rs, 0, 0)) {
5022
        if ($element_session_id == intval($session_id)) {
5023
            // The element belongs to the session.
5024
            return true;
5025
        }
5026
    }
5027
5028
    return false;
5029
}
5030
5031
/**
5032
 * Replaces "forbidden" characters in a filename string.
5033
 *
5034
 * @param string $filename
5035
 * @param bool   $treat_spaces_as_hyphens
5036
 *
5037
 * @return string
5038
 */
5039
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
5040
{
5041
    // Some non-properly encoded file names can cause the whole file to be
5042
    // skipped when uploaded. Avoid this by detecting the encoding and
5043
    // converting to UTF-8, setting the source as ASCII (a reasonably
5044
    // limited characters set) if nothing could be found (BT#
5045
    $encoding = api_detect_encoding($filename);
5046
    if (empty($encoding)) {
5047
        $encoding = 'ASCII';
5048
        if (!api_is_valid_ascii($filename)) {
5049
            // try iconv and try non standard ASCII a.k.a CP437
5050
            // see BT#15022
5051
            if (function_exists('iconv')) {
5052
                $result = iconv('CP437', 'UTF-8', $filename);
5053
                if (api_is_valid_utf8($result)) {
5054
                    $filename = $result;
5055
                    $encoding = 'UTF-8';
5056
                }
5057
            }
5058
        }
5059
    }
5060
5061
    $filename = api_to_system_encoding($filename, $encoding);
5062
5063
    $url = URLify::filter(
5064
        $filename,
5065
        250,
5066
        '',
5067
        true,
5068
        false,
5069
        false
5070
    );
5071
5072
    // Replace multiple dots at the end.
5073
    $regex = "/\.+$/";
5074
5075
    return preg_replace($regex, '', $url);
5076
}
5077
5078
/**
5079
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
5080
 *
5081
 * @author Ivan Tcholakov, 28-JUN-2006.
5082
 */
5083
function api_request_uri()
5084
{
5085
    if (!empty($_SERVER['REQUEST_URI'])) {
5086
        return $_SERVER['REQUEST_URI'];
5087
    }
5088
    $uri = $_SERVER['SCRIPT_NAME'];
5089
    if (!empty($_SERVER['QUERY_STRING'])) {
5090
        $uri .= '?'.$_SERVER['QUERY_STRING'];
5091
    }
5092
    $_SERVER['REQUEST_URI'] = $uri;
5093
5094
    return $uri;
5095
}
5096
5097
/**
5098
 * Gets the current access_url id of the Chamilo Platform.
5099
 *
5100
 * @return int access_url_id of the current Chamilo Installation
5101
 *
5102
 * @author Julio Montoya <[email protected]>
5103
 * @throws Exception
5104
 */
5105
function api_get_current_access_url_id(): int
5106
{
5107
    if (false === api_get_multiple_access_url()) {
5108
        return 1;
5109
    }
5110
5111
    static $id;
5112
    if (!empty($id)) {
5113
        return $id;
5114
    }
5115
5116
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5117
    $path = Database::escape_string(api_get_path(WEB_PATH));
5118
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
5119
    $result = Database::query($sql);
5120
    if (Database::num_rows($result) > 0) {
5121
        $id = Database::result($result, 0, 0);
5122
        if (false === $id) {
5123
            return -1;
5124
        }
5125
5126
        return (int) $id;
5127
    }
5128
5129
    $id = 1;
5130
5131
    //if the url in WEB_PATH was not found, it can only mean that there is
5132
    // either a configuration problem or the first URL has not been defined yet
5133
    // (by default it is http://localhost/). Thus the more sensible thing we can
5134
    // do is return 1 (the main URL) as the user cannot hack this value anyway
5135
    return 1;
5136
}
5137
5138
/**
5139
 * Gets the registered urls from a given user id.
5140
 *
5141
 * @author Julio Montoya <[email protected]>
5142
 *
5143
 * @param int $user_id
5144
 *
5145
 * @return array
5146
 */
5147
function api_get_access_url_from_user($user_id)
5148
{
5149
    $user_id = (int) $user_id;
5150
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5151
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5152
    $sql = "SELECT access_url_id
5153
            FROM $table_url_rel_user url_rel_user
5154
            INNER JOIN $table_url u
5155
            ON (url_rel_user.access_url_id = u.id)
5156
            WHERE user_id = ".$user_id;
5157
    $result = Database::query($sql);
5158
    $list = [];
5159
    while ($row = Database::fetch_assoc($result)) {
5160
        $list[] = $row['access_url_id'];
5161
    }
5162
5163
    return $list;
5164
}
5165
5166
/**
5167
 * Checks whether the curent user is in a group or not.
5168
 *
5169
 * @param string        The group id - optional (takes it from session if not given)
5170
 * @param string        The course code - optional (no additional check by course if course code is not given)
5171
 *
5172
 * @return bool
5173
 *
5174
 * @author Ivan Tcholakov
5175
 */
5176
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
5177
{
5178
    if (!empty($courseCodeParam)) {
5179
        $courseCode = api_get_course_id();
5180
        if (!empty($courseCode)) {
5181
            if ($courseCodeParam != $courseCode) {
5182
                return false;
5183
            }
5184
        } else {
5185
            return false;
5186
        }
5187
    }
5188
5189
    $groupId = api_get_group_id();
5190
5191
    if (isset($groupId) && '' != $groupId) {
5192
        if (!empty($groupIdParam)) {
5193
            return $groupIdParam == $groupId;
5194
        } else {
5195
            return true;
5196
        }
5197
    }
5198
5199
    return false;
5200
}
5201
5202
/**
5203
 * Checks whether a secret key is valid.
5204
 *
5205
 * @param string $original_key_secret - secret key from (webservice) client
5206
 * @param string $security_key        - security key from Chamilo
5207
 *
5208
 * @return bool - true if secret key is valid, false otherwise
5209
 */
5210
function api_is_valid_secret_key($original_key_secret, $security_key)
5211
{
5212
    if (empty($original_key_secret) || empty($security_key)) {
5213
        return false;
5214
    }
5215
5216
    return (string) $original_key_secret === sha1($security_key);
5217
}
5218
5219
/**
5220
 * Checks whether the server's operating system is Windows (TM).
5221
 *
5222
 * @return bool - true if the operating system is Windows, false otherwise
5223
 */
5224
function api_is_windows_os()
5225
{
5226
    if (function_exists('php_uname')) {
5227
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
5228
        // We expect that this function will always work for Chamilo 1.8.x.
5229
        $os = php_uname();
5230
    }
5231
    // The following methods are not needed, but let them stay, just in case.
5232
    elseif (isset($_ENV['OS'])) {
5233
        // Sometimes $_ENV['OS'] may not be present (bugs?)
5234
        $os = $_ENV['OS'];
5235
    } elseif (defined('PHP_OS')) {
5236
        // PHP_OS means on which OS PHP was compiled, this is why
5237
        // using PHP_OS is the last choice for detection.
5238
        $os = PHP_OS;
5239
    } else {
5240
        return false;
5241
    }
5242
5243
    return 'win' == strtolower(substr((string) $os, 0, 3));
5244
}
5245
5246
/**
5247
 * This function informs whether the sent request is XMLHttpRequest.
5248
 */
5249
function api_is_xml_http_request()
5250
{
5251
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH']);
5252
}
5253
5254
/**
5255
 * Returns a list of Chamilo's tools or
5256
 * checks whether a given identificator is a valid Chamilo's tool.
5257
 *
5258
 * @author Isaac flores paz
5259
 *
5260
 * @param string The tool name to filter
5261
 *
5262
 * @return mixed Filtered string or array
5263
 */
5264
function api_get_tools_lists($my_tool = null)
5265
{
5266
    $tools_list = [
5267
        TOOL_DOCUMENT,
5268
        TOOL_THUMBNAIL,
5269
        TOOL_HOTPOTATOES,
5270
        TOOL_CALENDAR_EVENT,
5271
        TOOL_LINK,
5272
        TOOL_COURSE_DESCRIPTION,
5273
        TOOL_SEARCH,
5274
        TOOL_LEARNPATH,
5275
        TOOL_ANNOUNCEMENT,
5276
        TOOL_FORUM,
5277
        TOOL_THREAD,
5278
        TOOL_POST,
5279
        TOOL_DROPBOX,
5280
        TOOL_QUIZ,
5281
        TOOL_USER,
5282
        TOOL_GROUP,
5283
        TOOL_BLOGS,
5284
        TOOL_CHAT,
5285
        TOOL_STUDENTPUBLICATION,
5286
        TOOL_TRACKING,
5287
        TOOL_HOMEPAGE_LINK,
5288
        TOOL_COURSE_SETTING,
5289
        TOOL_BACKUP,
5290
        TOOL_COPY_COURSE_CONTENT,
5291
        TOOL_RECYCLE_COURSE,
5292
        TOOL_COURSE_HOMEPAGE,
5293
        TOOL_COURSE_RIGHTS_OVERVIEW,
5294
        TOOL_UPLOAD,
5295
        TOOL_COURSE_MAINTENANCE,
5296
        TOOL_SURVEY,
5297
        //TOOL_WIKI,
5298
        TOOL_GLOSSARY,
5299
        TOOL_GRADEBOOK,
5300
        TOOL_NOTEBOOK,
5301
        TOOL_ATTENDANCE,
5302
        TOOL_COURSE_PROGRESS,
5303
    ];
5304
    if (empty($my_tool)) {
5305
        return $tools_list;
5306
    }
5307
5308
    return in_array($my_tool, $tools_list) ? $my_tool : '';
5309
}
5310
5311
/**
5312
 * Checks whether we already approved the last version term and condition.
5313
 *
5314
 * @param int user id
5315
 *
5316
 * @return bool true if we pass false otherwise
5317
 */
5318
function api_check_term_condition($userId)
5319
{
5320
    if ('true' === api_get_setting('allow_terms_conditions')) {
5321
        // Check if exists terms and conditions
5322
        if (0 == LegalManager::count()) {
5323
            return true;
5324
        }
5325
5326
        $extraFieldValue = new ExtraFieldValue('user');
5327
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
5328
            $userId,
5329
            'legal_accept'
5330
        );
5331
5332
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
5333
            $result = $data['value'];
5334
            $user_conditions = explode(':', $result);
5335
            $version = $user_conditions[0];
5336
            $langId = $user_conditions[1];
5337
            $realVersion = LegalManager::get_last_version($langId);
5338
5339
            return $version >= $realVersion;
5340
        }
5341
5342
        return false;
5343
    }
5344
5345
    return false;
5346
}
5347
5348
/**
5349
 * Gets all information of a tool into course.
5350
 *
5351
 * @param int The tool id
5352
 *
5353
 * @return array
5354
 */
5355
function api_get_tool_information_by_name($name)
5356
{
5357
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
5358
    $course_id = api_get_course_int_id();
5359
5360
    $sql = "SELECT id FROM tool
5361
            WHERE title = '".Database::escape_string($name)."' ";
5362
    $rs = Database::query($sql);
5363
    $data = Database::fetch_array($rs);
5364
    if ($data) {
5365
        $tool = $data['id'];
5366
        $sql = "SELECT * FROM $t_tool
5367
                WHERE c_id = $course_id  AND tool_id = '".$tool."' ";
5368
        $rs = Database::query($sql);
5369
5370
        return Database::fetch_assoc($rs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::fetch_assoc($rs) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
5371
    }
5372
5373
    return [];
5374
}
5375
5376
/**
5377
 * Function used to protect a "global" admin script.
5378
 * The function blocks access when the user has no global platform admin rights.
5379
 * Global admins are the admins that are registered in the main.admin table
5380
 * AND the users who have access to the "principal" portal.
5381
 * That means that there is a record in the main.access_url_rel_user table
5382
 * with his user id and the access_url_id=1.
5383
 *
5384
 * @author Julio Montoya
5385
 *
5386
 * @param int $user_id
5387
 *
5388
 * @return bool
5389
 */
5390
function api_is_global_platform_admin($user_id = null)
5391
{
5392
    $user_id = (int) $user_id;
5393
    if (empty($user_id)) {
5394
        $user_id = api_get_user_id();
5395
    }
5396
    if (api_is_platform_admin_by_id($user_id)) {
5397
        $urlList = api_get_access_url_from_user($user_id);
5398
        // The admin is registered in the first "main" site with access_url_id = 1
5399
        if (in_array(1, $urlList)) {
5400
            return true;
5401
        }
5402
    }
5403
5404
    return false;
5405
}
5406
5407
/**
5408
 * @param int  $admin_id_to_check
5409
 * @param int  $userId
5410
 * @param bool $allow_session_admin
5411
 *
5412
 * @return bool
5413
 */
5414
function api_global_admin_can_edit_admin(
5415
    $admin_id_to_check,
5416
    $userId = 0,
5417
    $allow_session_admin = false
5418
) {
5419
    if (empty($userId)) {
5420
        $userId = api_get_user_id();
5421
    }
5422
5423
    $iam_a_global_admin = api_is_global_platform_admin($userId);
5424
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
5425
5426
    if ($iam_a_global_admin) {
5427
        // Global admin can edit everything
5428
        return true;
5429
    }
5430
5431
    // If i'm a simple admin
5432
    $is_platform_admin = api_is_platform_admin_by_id($userId);
5433
5434
    if ($allow_session_admin && !$is_platform_admin) {
5435
        $user = api_get_user_entity($userId);
5436
        $is_platform_admin = $user->hasRole('ROLE_SESSION_MANAGER');
5437
    }
5438
5439
    if ($is_platform_admin) {
5440
        if ($user_is_global_admin) {
5441
            return false;
5442
        } else {
5443
            return true;
5444
        }
5445
    }
5446
5447
    return false;
5448
}
5449
5450
/**
5451
 * @param int  $admin_id_to_check
5452
 * @param int  $userId
5453
 * @param bool $allow_session_admin
5454
 *
5455
 * @return bool|null
5456
 */
5457
function api_protect_super_admin($admin_id_to_check, $userId = null, $allow_session_admin = false)
5458
{
5459
    if (api_global_admin_can_edit_admin($admin_id_to_check, $userId, $allow_session_admin)) {
5460
        return true;
5461
    } else {
5462
        api_not_allowed();
5463
    }
5464
}
5465
5466
/**
5467
 * Function used to protect a global admin script.
5468
 * The function blocks access when the user has no global platform admin rights.
5469
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
5470
 *
5471
 * @author Julio Montoya
5472
 */
5473
function api_protect_global_admin_script()
5474
{
5475
    if (!api_is_global_platform_admin()) {
5476
        api_not_allowed();
5477
5478
        return false;
5479
    }
5480
5481
    return true;
5482
}
5483
5484
/**
5485
 * Check browser support for specific file types or features
5486
 * This function checks if the user's browser supports a file format or given
5487
 * feature, or returns the current browser and major version when
5488
 * $format=check_browser. Only a limited number of formats and features are
5489
 * checked by this method. Make sure you check its definition first.
5490
 *
5491
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
5492
 *
5493
 * @deprecated
5494
 *
5495
 * @return bool or return text array if $format=check_browser
5496
 *
5497
 * @author Juan Carlos Raña Trabado
5498
 */
5499
function api_browser_support($format = '')
5500
{
5501
    return true;
5502
5503
    $browser = new Browser();
0 ignored issues
show
Unused Code introduced by
$browser = new Browser() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5504
    $current_browser = $browser->getBrowser();
5505
    $a_versiontemp = explode('.', $browser->getVersion());
5506
    $current_majorver = $a_versiontemp[0];
5507
5508
    static $result;
5509
5510
    if (isset($result[$format])) {
5511
        return $result[$format];
5512
    }
5513
5514
    // Native svg support
5515
    if ('svg' == $format) {
5516
        if (('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5517
            ('Firefox' == $current_browser && $current_majorver > 1) ||
5518
            ('Safari' == $current_browser && $current_majorver >= 4) ||
5519
            ('Chrome' == $current_browser && $current_majorver >= 1) ||
5520
            ('Opera' == $current_browser && $current_majorver >= 9)
5521
        ) {
5522
            $result[$format] = true;
5523
5524
            return true;
5525
        } else {
5526
            $result[$format] = false;
5527
5528
            return false;
5529
        }
5530
    } elseif ('pdf' == $format) {
5531
        // native pdf support
5532
        if ('Chrome' == $current_browser && $current_majorver >= 6) {
5533
            $result[$format] = true;
5534
5535
            return true;
5536
        } else {
5537
            $result[$format] = false;
5538
5539
            return false;
5540
        }
5541
    } elseif ('tif' == $format || 'tiff' == $format) {
5542
        //native tif support
5543
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5544
            $result[$format] = true;
5545
5546
            return true;
5547
        } else {
5548
            $result[$format] = false;
5549
5550
            return false;
5551
        }
5552
    } elseif ('ogg' == $format || 'ogx' == $format || 'ogv' == $format || 'oga' == $format) {
5553
        //native ogg, ogv,oga support
5554
        if (('Firefox' == $current_browser && $current_majorver >= 3) ||
5555
            ('Chrome' == $current_browser && $current_majorver >= 3) ||
5556
            ('Opera' == $current_browser && $current_majorver >= 9)) {
5557
            $result[$format] = true;
5558
5559
            return true;
5560
        } else {
5561
            $result[$format] = false;
5562
5563
            return false;
5564
        }
5565
    } elseif ('mpg' == $format || 'mpeg' == $format) {
5566
        //native mpg support
5567
        if (('Safari' == $current_browser && $current_majorver >= 5)) {
5568
            $result[$format] = true;
5569
5570
            return true;
5571
        } else {
5572
            $result[$format] = false;
5573
5574
            return false;
5575
        }
5576
    } elseif ('mp4' == $format) {
5577
        //native mp4 support (TODO: Android, iPhone)
5578
        if ('Android' == $current_browser || 'iPhone' == $current_browser) {
5579
            $result[$format] = true;
5580
5581
            return true;
5582
        } else {
5583
            $result[$format] = false;
5584
5585
            return false;
5586
        }
5587
    } elseif ('mov' == $format) {
5588
        //native mov support( TODO:check iPhone)
5589
        if ('Safari' == $current_browser && $current_majorver >= 5 || 'iPhone' == $current_browser) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ('Safari' == $current_br...ne' == $current_browser, Probably Intended Meaning: 'Safari' == $current_bro...e' == $current_browser)
Loading history...
5590
            $result[$format] = true;
5591
5592
            return true;
5593
        } else {
5594
            $result[$format] = false;
5595
5596
            return false;
5597
        }
5598
    } elseif ('avi' == $format) {
5599
        //native avi support
5600
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5601
            $result[$format] = true;
5602
5603
            return true;
5604
        } else {
5605
            $result[$format] = false;
5606
5607
            return false;
5608
        }
5609
    } elseif ('wmv' == $format) {
5610
        //native wmv support
5611
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5612
            $result[$format] = true;
5613
5614
            return true;
5615
        } else {
5616
            $result[$format] = false;
5617
5618
            return false;
5619
        }
5620
    } elseif ('webm' == $format) {
5621
        //native webm support (TODO:check IE9, Chrome9, Android)
5622
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5623
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5624
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5625
            ('Chrome' == $current_browser && $current_majorver >= 9) ||
5626
            'Android' == $current_browser
5627
        ) {
5628
            $result[$format] = true;
5629
5630
            return true;
5631
        } else {
5632
            $result[$format] = false;
5633
5634
            return false;
5635
        }
5636
    } elseif ('wav' == $format) {
5637
        //native wav support (only some codecs !)
5638
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5639
            ('Safari' == $current_browser && $current_majorver >= 5) ||
5640
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5641
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5642
            ('Chrome' == $current_browser && $current_majorver > 9) ||
5643
            'Android' == $current_browser ||
5644
            'iPhone' == $current_browser
5645
        ) {
5646
            $result[$format] = true;
5647
5648
            return true;
5649
        } else {
5650
            $result[$format] = false;
5651
5652
            return false;
5653
        }
5654
    } elseif ('mid' == $format || 'kar' == $format) {
5655
        //native midi support (TODO:check Android)
5656
        if ('Opera' == $current_browser && $current_majorver >= 9 || 'Android' == $current_browser) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ('Opera' == $current_bro...id' == $current_browser, Probably Intended Meaning: 'Opera' == $current_brow...d' == $current_browser)
Loading history...
5657
            $result[$format] = true;
5658
5659
            return true;
5660
        } else {
5661
            $result[$format] = false;
5662
5663
            return false;
5664
        }
5665
    } elseif ('wma' == $format) {
5666
        //native wma support
5667
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5668
            $result[$format] = true;
5669
5670
            return true;
5671
        } else {
5672
            $result[$format] = false;
5673
5674
            return false;
5675
        }
5676
    } elseif ('au' == $format) {
5677
        //native au support
5678
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5679
            $result[$format] = true;
5680
5681
            return true;
5682
        } else {
5683
            $result[$format] = false;
5684
5685
            return false;
5686
        }
5687
    } elseif ('mp3' == $format) {
5688
        //native mp3 support (TODO:check Android, iPhone)
5689
        if (('Safari' == $current_browser && $current_majorver >= 5) ||
5690
            ('Chrome' == $current_browser && $current_majorver >= 6) ||
5691
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5692
            'Android' == $current_browser ||
5693
            'iPhone' == $current_browser ||
5694
            'Firefox' == $current_browser
5695
        ) {
5696
            $result[$format] = true;
5697
5698
            return true;
5699
        } else {
5700
            $result[$format] = false;
5701
5702
            return false;
5703
        }
5704
    } elseif ('autocapitalize' == $format) {
5705
        // Help avoiding showing the autocapitalize option if the browser doesn't
5706
        // support it: this attribute is against the HTML5 standard
5707
        if ('Safari' == $current_browser || 'iPhone' == $current_browser) {
5708
            return true;
5709
        } else {
5710
            return false;
5711
        }
5712
    } elseif ("check_browser" == $format) {
5713
        $array_check_browser = [$current_browser, $current_majorver];
5714
5715
        return $array_check_browser;
5716
    } else {
5717
        $result[$format] = false;
5718
5719
        return false;
5720
    }
5721
}
5722
5723
/**
5724
 * This function checks if exist path and file browscap.ini
5725
 * In order for this to work, your browscap configuration setting in php.ini
5726
 * must point to the correct location of the browscap.ini file on your system
5727
 * http://php.net/manual/en/function.get-browser.php.
5728
 *
5729
 * @return bool
5730
 *
5731
 * @author Juan Carlos Raña Trabado
5732
 */
5733
function api_check_browscap()
5734
{
5735
    $setting = ini_get('browscap');
5736
    if ($setting) {
5737
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
5738
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
5739
            return true;
5740
        }
5741
    }
5742
5743
    return false;
5744
}
5745
5746
/**
5747
 * Returns the <script> HTML tag.
5748
 */
5749
function api_get_js($file)
5750
{
5751
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
5752
}
5753
5754
function api_get_build_js($file)
5755
{
5756
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'"></script>'."\n";
5757
}
5758
5759
function api_get_build_css($file, $media = 'screen')
5760
{
5761
    return '<link
5762
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5763
}
5764
5765
/**
5766
 * Returns the <script> HTML tag.
5767
 *
5768
 * @return string
5769
 */
5770
function api_get_asset($file)
5771
{
5772
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"></script>'."\n";
5773
}
5774
5775
/**
5776
 * Returns the <script> HTML tag.
5777
 *
5778
 * @param string $file
5779
 * @param string $media
5780
 *
5781
 * @return string
5782
 */
5783
function api_get_css_asset($file, $media = 'screen')
5784
{
5785
    return '<link
5786
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"
5787
        rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5788
}
5789
5790
/**
5791
 * Returns the <link> HTML tag.
5792
 *
5793
 * @param string $file
5794
 * @param string $media
5795
 */
5796
function api_get_css($file, $media = 'screen')
5797
{
5798
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5799
}
5800
5801
function api_get_bootstrap_and_font_awesome($returnOnlyPath = false, $returnFileLocation = false)
5802
{
5803
    $url = api_get_path(WEB_PUBLIC_PATH).'build/css/bootstrap.css';
5804
5805
    if ($returnOnlyPath) {
5806
        if ($returnFileLocation) {
5807
            return api_get_path(SYS_PUBLIC_PATH).'build/css/bootstrap.css';
5808
        }
5809
5810
        return $url;
5811
    }
5812
5813
    return '<link href="'.$url.'" rel="stylesheet" type="text/css" />'."\n";
5814
}
5815
5816
/**
5817
 * Returns the js header to include the jquery library.
5818
 */
5819
function api_get_jquery_js()
5820
{
5821
    return api_get_asset('jquery/jquery.min.js');
5822
}
5823
5824
/**
5825
 * Returns the jquery path.
5826
 *
5827
 * @return string
5828
 */
5829
function api_get_jquery_web_path()
5830
{
5831
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/jquery.min.js';
5832
}
5833
5834
/**
5835
 * @return string
5836
 */
5837
function api_get_jquery_ui_js_web_path()
5838
{
5839
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
5840
}
5841
5842
/**
5843
 * @return string
5844
 */
5845
function api_get_jquery_ui_css_web_path()
5846
{
5847
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
5848
}
5849
5850
/**
5851
 * Returns the jquery-ui library js headers.
5852
 *
5853
 * @return string html tags
5854
 */
5855
function api_get_jquery_ui_js()
5856
{
5857
    $libraries = [];
5858
5859
    return api_get_jquery_libraries_js($libraries);
5860
}
5861
5862
function api_get_jqgrid_js()
5863
{
5864
    return api_get_build_css('legacy_free-jqgrid.css').PHP_EOL
5865
        .api_get_build_js('legacy_free-jqgrid.js');
5866
}
5867
5868
/**
5869
 * Returns the jquery library js and css headers.
5870
 *
5871
 * @param   array   list of jquery libraries supported jquery-ui
5872
 * @param   bool    add the jquery library
5873
 *
5874
 * @return string html tags
5875
 */
5876
function api_get_jquery_libraries_js($libraries)
5877
{
5878
    $js = '';
5879
5880
    //Document multiple upload funcionality
5881
    if (in_array('jquery-uploadzs', $libraries)) {
5882
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
5883
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
5884
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
5885
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
5886
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
5887
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
5888
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
5889
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
5890
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
5891
5892
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
5893
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
5894
    }
5895
5896
    // jquery datepicker
5897
    if (in_array('datepicker', $libraries)) {
5898
        $languaje = 'en-GB';
5899
        $platform_isocode = strtolower(api_get_language_isocode());
5900
5901
        $datapicker_langs = [
5902
            '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',
5903
        ];
5904
        if (in_array($platform_isocode, $datapicker_langs)) {
5905
            $languaje = $platform_isocode;
5906
        }
5907
5908
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
5909
        $script = '<script>
5910
        $(function(){
5911
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
5912
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
5913
        });
5914
        </script>
5915
        ';
5916
        $js .= $script;
5917
    }
5918
5919
    return $js;
5920
}
5921
5922
/**
5923
 * Returns the URL to the course or session, removing the complexity of the URL
5924
 * building piece by piece.
5925
 *
5926
 * This function relies on api_get_course_info()
5927
 *
5928
 * @param int $courseId  The course code - optional (takes it from context if not given)
5929
 * @param int $sessionId The session ID  - optional (takes it from context if not given)
5930
 * @param int $groupId   The group ID - optional (takes it from context if not given)
5931
 *
5932
 * @return string The URL to a course, a session, or empty string if nothing works
5933
 *                e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
5934
 *
5935
 * @author  Julio Montoya
5936
 */
5937
function api_get_course_url($courseId = null, $sessionId = null, $groupId = null)
5938
{
5939
    $url = '';
5940
    // If courseCode not set, get context or []
5941
    if (empty($courseId)) {
5942
        $courseId = api_get_course_int_id();
5943
    }
5944
5945
    // If sessionId not set, get context or 0
5946
    if (empty($sessionId)) {
5947
        $sessionId = api_get_session_id();
5948
    }
5949
5950
    // If groupId not set, get context or 0
5951
    if (empty($groupId)) {
5952
        $groupId = api_get_group_id();
5953
    }
5954
5955
    // Build the URL
5956
    if (!empty($courseId)) {
5957
        $webCourseHome = '/course/'.$courseId.'/home';
5958
        // directory not empty, so we do have a course
5959
        $url = $webCourseHome.'?sid='.$sessionId.'&gid='.$groupId;
5960
    } else {
5961
        if (!empty($sessionId) && 'true' !== api_get_setting('session.remove_session_url')) {
5962
            // if the course was unset and the session was set, send directly to the session
5963
            $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
5964
        }
5965
    }
5966
5967
    // if not valid combination was found, return an empty string
5968
    return $url;
5969
}
5970
5971
/**
5972
 * Check if there is more than the default URL defined in the access_url table.
5973
 */
5974
function api_get_multiple_access_url(): bool
5975
{
5976
    return Container::$container->get(AccessUrlHelper::class)->isMultiple();
5977
}
5978
5979
/**
5980
 * @deprecated Use AccessUrlHelper::isMultiple
5981
 */
5982
function api_is_multiple_url_enabled(): bool
5983
{
5984
    return api_get_multiple_access_url();
5985
}
5986
5987
/**
5988
 * Returns a md5 unique id.
5989
 *
5990
 * @todo add more parameters
5991
 */
5992
function api_get_unique_id()
5993
{
5994
    return md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
5995
}
5996
5997
/**
5998
 * @param int Course id
5999
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
6000
 * @param int the item id (tool id, exercise id, lp id)
6001
 *
6002
 * @return bool
6003
 */
6004
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
6005
{
6006
    if (api_is_platform_admin()) {
6007
        return false;
6008
    }
6009
    if ('true' === api_get_setting('gradebook_locking_enabled')) {
6010
        if (empty($course_code)) {
6011
            $course_code = api_get_course_id();
6012
        }
6013
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
6014
        $item_id = (int) $item_id;
6015
        $link_type = (int) $link_type;
6016
        $course_code = Database::escape_string($course_code);
6017
        $sql = "SELECT locked FROM $table
6018
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
6019
        $result = Database::query($sql);
6020
        if (Database::num_rows($result)) {
6021
            return true;
6022
        }
6023
    }
6024
6025
    return false;
6026
}
6027
6028
/**
6029
 * Blocks a page if the item was added in a gradebook.
6030
 *
6031
 * @param int       exercise id, work id, thread id,
6032
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
6033
 * see gradebook/lib/be/linkfactory
6034
 * @param string    course code
6035
 *
6036
 * @return false|null
6037
 */
6038
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
6039
{
6040
    if (api_is_platform_admin()) {
6041
        return false;
6042
    }
6043
6044
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
6045
        $message = Display::return_message(
6046
            get_lang(
6047
                'This option is not available because this activity is contained by an assessment, which is currently locked. To unlock the assessment, ask your platform administrator.'
6048
            ),
6049
            'warning'
6050
        );
6051
        api_not_allowed(true, $message);
6052
    }
6053
}
6054
6055
/**
6056
 * Checks the PHP version installed is enough to run Chamilo.
6057
 *
6058
 * @param string Include path (used to load the error page)
6059
 */
6060
function api_check_php_version()
6061
{
6062
    if (!function_exists('version_compare') ||
6063
        version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')
6064
    ) {
6065
        throw new Exception('Wrong PHP version');
6066
    }
6067
}
6068
6069
/**
6070
 * Checks whether the Archive directory is present and writeable. If not,
6071
 * prints a warning message.
6072
 */
6073
function api_check_archive_dir()
6074
{
6075
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
6076
        $message = Display::return_message(
6077
            get_lang(
6078
                'The var/cache/ directory, used by this tool, is not writeable. Please contact your platform administrator.'
6079
            ),
6080
            'warning'
6081
        );
6082
        api_not_allowed(true, $message);
6083
    }
6084
}
6085
6086
/**
6087
 * Returns an array of global configuration settings which should be ignored
6088
 * when printing the configuration settings screens.
6089
 *
6090
 * @return array Array of strings, each identifying one of the excluded settings
6091
 */
6092
function api_get_locked_settings()
6093
{
6094
    return [
6095
        'permanently_remove_deleted_files',
6096
        'account_valid_duration',
6097
        'service_ppt2lp',
6098
        'wcag_anysurfer_public_pages',
6099
        'upload_extensions_list_type',
6100
        'upload_extensions_blacklist',
6101
        'upload_extensions_whitelist',
6102
        'upload_extensions_skip',
6103
        'upload_extensions_replace_by',
6104
        'hide_dltt_markup',
6105
        'split_users_upload_directory',
6106
        'permissions_for_new_directories',
6107
        'permissions_for_new_files',
6108
        'platform_charset',
6109
        'ldap_description',
6110
        'cas_activate',
6111
        'cas_server',
6112
        'cas_server_uri',
6113
        'cas_port',
6114
        'cas_protocol',
6115
        'cas_add_user_activate',
6116
        'update_user_info_cas_with_ldap',
6117
        'languagePriority1',
6118
        'languagePriority2',
6119
        'languagePriority3',
6120
        'languagePriority4',
6121
        'login_is_email',
6122
        'chamilo_database_version',
6123
    ];
6124
}
6125
6126
/**
6127
 * Guess the real ip for register in the database, even in reverse proxy cases.
6128
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
6129
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
6130
 * Note: the result of this function is not SQL-safe. Please escape it before
6131
 * inserting in a database.
6132
 *
6133
 * @return string the user's real ip (unsafe - escape it before inserting to db)
6134
 *
6135
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
6136
 *
6137
 * @version CEV CHANGE 24APR2012
6138
 * @throws RuntimeException
6139
 */
6140
function api_get_real_ip(): string
6141
{
6142
    if ('cli' === PHP_SAPI) {
6143
        $ip = '127.0.0.1';
6144
    } else {
6145
        $ip = trim($_SERVER['REMOTE_ADDR']);
6146
        if (empty($ip)) {
6147
            throw new RuntimeException("Unable to retrieve remote IP address.");
6148
        }
6149
    }
6150
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
6151
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
6152
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
6153
        } else {
6154
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
6155
        }
6156
        $ip = trim($ip1);
6157
    }
6158
6159
    return $ip;
6160
}
6161
6162
/**
6163
 * Checks whether an IP is included inside an IP range.
6164
 *
6165
 * @param string IP address
6166
 * @param string IP range
6167
 * @param string $ip
6168
 *
6169
 * @return bool True if IP is in the range, false otherwise
6170
 *
6171
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
6172
 * @author Yannick Warnier for improvements and managment of multiple ranges
6173
 *
6174
 * @todo check for IPv6 support
6175
 */
6176
function api_check_ip_in_range($ip, $range)
6177
{
6178
    if (empty($ip) or empty($range)) {
6179
        return false;
6180
    }
6181
    $ip_ip = ip2long($ip);
6182
    // divide range param into array of elements
6183
    if (false !== strpos($range, ',')) {
6184
        $ranges = explode(',', $range);
6185
    } else {
6186
        $ranges = [$range];
6187
    }
6188
    foreach ($ranges as $range) {
0 ignored issues
show
introduced by
$range is overwriting one of the parameters of this function.
Loading history...
6189
        $range = trim($range);
6190
        if (empty($range)) {
6191
            continue;
6192
        }
6193
        if (false === strpos($range, '/')) {
6194
            if (0 === strcmp($ip, $range)) {
6195
                return true; // there is a direct IP match, return OK
6196
            }
6197
            continue; //otherwise, get to the next range
6198
        }
6199
        // the range contains a "/", so analyse completely
6200
        [$net, $mask] = explode("/", $range);
6201
6202
        $ip_net = ip2long($net);
6203
        // mask binary magic
6204
        $ip_mask = ~((1 << (32 - $mask)) - 1);
6205
6206
        $ip_ip_net = $ip_ip & $ip_mask;
6207
        if ($ip_ip_net == $ip_net) {
6208
            return true;
6209
        }
6210
    }
6211
6212
    return false;
6213
}
6214
6215
function api_check_user_access_to_legal($courseInfo)
6216
{
6217
    if (empty($courseInfo)) {
6218
        return false;
6219
    }
6220
6221
    $visibility = (int) $courseInfo['visibility'];
6222
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
6223
6224
    return
6225
        in_array($visibility, $visibilityList) ||
6226
        api_is_drh() ||
6227
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
6228
}
6229
6230
/**
6231
 * Checks if the global chat is enabled or not.
6232
 *
6233
 * @return bool
6234
 */
6235
function api_is_global_chat_enabled()
6236
{
6237
    return
6238
        !api_is_anonymous() &&
6239
        'true' === api_get_setting('allow_global_chat') &&
6240
        'true' === api_get_setting('allow_social_tool');
6241
}
6242
6243
/**
6244
 * @param int   $item_id
6245
 * @param int   $tool_id
6246
 * @param int   $group_id   id
6247
 * @param array $courseInfo
6248
 * @param int   $sessionId
6249
 * @param int   $userId
6250
 *
6251
 * @deprecated
6252
 */
6253
function api_set_default_visibility(
6254
    $item_id,
6255
    $tool_id,
6256
    $group_id = 0,
6257
    $courseInfo = [],
6258
    $sessionId = 0,
6259
    $userId = 0
6260
) {
6261
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
6262
    $courseId = $courseInfo['real_id'];
6263
    $courseCode = $courseInfo['code'];
6264
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6265
    $userId = empty($userId) ? api_get_user_id() : $userId;
6266
6267
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
6268
    if (is_null($group_id)) {
6269
        $group_id = 0;
6270
    } else {
6271
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
6272
    }
6273
6274
    $groupInfo = [];
6275
    if (!empty($group_id)) {
6276
        $groupInfo = GroupManager::get_group_properties($group_id);
6277
    }
6278
    $original_tool_id = $tool_id;
6279
6280
    switch ($tool_id) {
6281
        case TOOL_LINK:
6282
        case TOOL_LINK_CATEGORY:
6283
            $tool_id = 'links';
6284
            break;
6285
        case TOOL_DOCUMENT:
6286
            $tool_id = 'documents';
6287
            break;
6288
        case TOOL_LEARNPATH:
6289
            $tool_id = 'learning';
6290
            break;
6291
        case TOOL_ANNOUNCEMENT:
6292
            $tool_id = 'announcements';
6293
            break;
6294
        case TOOL_FORUM:
6295
        case TOOL_FORUM_CATEGORY:
6296
        case TOOL_FORUM_THREAD:
6297
            $tool_id = 'forums';
6298
            break;
6299
        case TOOL_QUIZ:
6300
            $tool_id = 'quiz';
6301
            break;
6302
    }
6303
    $setting = api_get_setting('tool_visible_by_default_at_creation');
6304
6305
    if (isset($setting[$tool_id])) {
6306
        $visibility = 'invisible';
6307
        if ('true' === $setting[$tool_id]) {
6308
            $visibility = 'visible';
6309
        }
6310
6311
        // Read the portal and course default visibility
6312
        if ('documents' === $tool_id) {
6313
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
6314
        }
6315
6316
        // Fixes default visibility for tests
6317
        switch ($original_tool_id) {
6318
            case TOOL_QUIZ:
6319
                if (empty($sessionId)) {
6320
                    $objExerciseTmp = new Exercise($courseId);
6321
                    $objExerciseTmp->read($item_id);
6322
                    if ('visible' === $visibility) {
6323
                        $objExerciseTmp->enable();
6324
                        $objExerciseTmp->save();
6325
                    } else {
6326
                        $objExerciseTmp->disable();
6327
                        $objExerciseTmp->save();
6328
                    }
6329
                }
6330
                break;
6331
        }
6332
    }
6333
}
6334
6335
function api_get_roles()
6336
{
6337
    $hierarchy = Container::$container->getParameter('security.role_hierarchy.roles');
6338
    $roles = [];
6339
    array_walk_recursive($hierarchy, function ($role) use (&$roles) {
6340
        $roles[$role] = $role;
6341
    });
6342
6343
    return $roles;
6344
}
6345
6346
function api_get_user_roles(): array
6347
{
6348
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
6349
6350
    $roles = $permissionService->getUserRoles();
6351
6352
    return array_combine($roles, $roles);
6353
}
6354
6355
/**
6356
 * @param string $file
6357
 *
6358
 * @return string
6359
 */
6360
function api_get_js_simple($file)
6361
{
6362
    return '<script type="text/javascript" src="'.$file.'"></script>'."\n";
6363
}
6364
6365
/**
6366
 * Modify default memory_limit and max_execution_time limits
6367
 * Needed when processing long tasks.
6368
 */
6369
function api_set_more_memory_and_time_limits()
6370
{
6371
    if (function_exists('ini_set')) {
6372
        api_set_memory_limit('256M');
6373
        ini_set('max_execution_time', 1800);
6374
    }
6375
}
6376
6377
/**
6378
 * Tries to set memory limit, if authorized and new limit is higher than current.
6379
 *
6380
 * @param string $mem New memory limit
6381
 *
6382
 * @return bool True on success, false on failure or current is higher than suggested
6383
 * @assert (null) === false
6384
 * @assert (-1) === false
6385
 * @assert (0) === true
6386
 * @assert ('1G') === true
6387
 */
6388
function api_set_memory_limit($mem)
6389
{
6390
    //if ini_set() not available, this function is useless
6391
    if (!function_exists('ini_set') || is_null($mem) || -1 == $mem) {
6392
        return false;
6393
    }
6394
6395
    $memory_limit = ini_get('memory_limit');
6396
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
6397
        ini_set('memory_limit', $mem);
6398
6399
        return true;
6400
    }
6401
6402
    return false;
6403
}
6404
6405
/**
6406
 * Gets memory limit in bytes.
6407
 *
6408
 * @param string The memory size (128M, 1G, 1000K, etc)
6409
 *
6410
 * @return int
6411
 * @assert (null) === false
6412
 * @assert ('1t')  === 1099511627776
6413
 * @assert ('1g')  === 1073741824
6414
 * @assert ('1m')  === 1048576
6415
 * @assert ('100k') === 102400
6416
 */
6417
function api_get_bytes_memory_limit($mem)
6418
{
6419
    $size = strtolower(substr($mem, -1));
6420
6421
    switch ($size) {
6422
        case 't':
6423
            $mem = (int) substr($mem, -1) * 1024 * 1024 * 1024 * 1024;
6424
            break;
6425
        case 'g':
6426
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024 * 1024;
6427
            break;
6428
        case 'm':
6429
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024;
6430
            break;
6431
        case 'k':
6432
            $mem = (int) substr($mem, 0, -1) * 1024;
6433
            break;
6434
        default:
6435
            // we assume it's integer only
6436
            $mem = (int) $mem;
6437
            break;
6438
    }
6439
6440
    return $mem;
6441
}
6442
6443
/**
6444
 * Finds all the information about a user from username instead of user id.
6445
 *
6446
 * @param string $officialCode
6447
 *
6448
 * @return array $user_info user_id, lastname, firstname, username, email, ...
6449
 *
6450
 * @author Yannick Warnier <[email protected]>
6451
 */
6452
function api_get_user_info_from_official_code($officialCode)
6453
{
6454
    if (empty($officialCode)) {
6455
        return false;
6456
    }
6457
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
6458
            WHERE official_code ='".Database::escape_string($officialCode)."'";
6459
    $result = Database::query($sql);
6460
    if (Database::num_rows($result) > 0) {
6461
        $result_array = Database::fetch_array($result);
6462
6463
        return _api_format_user($result_array);
6464
    }
6465
6466
    return false;
6467
}
6468
6469
/**
6470
 * @param string $usernameInputId
6471
 * @param string $passwordInputId
6472
 *
6473
 * @return string|null
6474
 */
6475
function api_get_password_checker_js($usernameInputId, $passwordInputId)
6476
{
6477
    $checkPass = api_get_setting('allow_strength_pass_checker');
6478
    $useStrengthPassChecker = 'true' === $checkPass;
6479
6480
    if (false === $useStrengthPassChecker) {
6481
        return null;
6482
    }
6483
6484
    $minRequirements = Security::getPasswordRequirements()['min'];
6485
6486
    $options = [
6487
        'rules' => [],
6488
    ];
6489
6490
    if ($minRequirements['length'] > 0) {
6491
        $options['rules'][] = [
6492
            'minChar' => $minRequirements['length'],
6493
            'pattern' => '.',
6494
            'helpText' => sprintf(
6495
                get_lang('Minimum %s characters in total'),
6496
                $minRequirements['length']
6497
            ),
6498
        ];
6499
    }
6500
6501
    if ($minRequirements['lowercase'] > 0) {
6502
        $options['rules'][] = [
6503
            'minChar' => $minRequirements['lowercase'],
6504
            'pattern' => '[a-z]',
6505
            'helpText' => sprintf(
6506
                get_lang('Minimum %s lowercase characters'),
6507
                $minRequirements['lowercase']
6508
            ),
6509
        ];
6510
    }
6511
6512
    if ($minRequirements['uppercase'] > 0) {
6513
        $options['rules'][] = [
6514
            'minChar' => $minRequirements['uppercase'],
6515
            'pattern' => '[A-Z]',
6516
            'helpText' => sprintf(
6517
                get_lang('Minimum %s uppercase characters'),
6518
                $minRequirements['uppercase']
6519
            ),
6520
        ];
6521
    }
6522
6523
    if ($minRequirements['numeric'] > 0) {
6524
        $options['rules'][] = [
6525
            'minChar' => $minRequirements['numeric'],
6526
            'pattern' => '[0-9]',
6527
            'helpText' => sprintf(
6528
                get_lang('Minimum %s numerical (0-9) characters'),
6529
                $minRequirements['numeric']
6530
            ),
6531
        ];
6532
    }
6533
6534
    if ($minRequirements['specials'] > 0) {
6535
        $options['rules'][] = [
6536
            'minChar' => $minRequirements['specials'],
6537
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
6538
            'helpText' => sprintf(
6539
                get_lang('Minimum %s special characters'),
6540
                $minRequirements['specials']
6541
            ),
6542
        ];
6543
    }
6544
6545
    $js = api_get_js('password-checker/password-checker.js');
6546
    $js .= "<script>
6547
    $(function() {
6548
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
6549
    });
6550
    </script>";
6551
6552
    return $js;
6553
}
6554
6555
/**
6556
 * create an user extra field called 'captcha_blocked_until_date'.
6557
 *
6558
 * @param string $username
6559
 *
6560
 * @return bool
6561
 */
6562
function api_block_account_captcha($username)
6563
{
6564
    $userInfo = api_get_user_info_from_username($username);
6565
    if (empty($userInfo)) {
6566
        return false;
6567
    }
6568
    $minutesToBlock = api_get_setting('captcha_time_to_block');
6569
    $time = time() + $minutesToBlock * 60;
6570
    UserManager::update_extra_field_value(
6571
        $userInfo['user_id'],
6572
        'captcha_blocked_until_date',
6573
        api_get_utc_datetime($time)
6574
    );
6575
6576
    return true;
6577
}
6578
6579
/**
6580
 * @param string $username
6581
 *
6582
 * @return bool
6583
 */
6584
function api_clean_account_captcha($username)
6585
{
6586
    $userInfo = api_get_user_info_from_username($username);
6587
    if (empty($userInfo)) {
6588
        return false;
6589
    }
6590
    Session::erase('loginFailedCount');
6591
    UserManager::update_extra_field_value(
6592
        $userInfo['user_id'],
6593
        'captcha_blocked_until_date',
6594
        null
6595
    );
6596
6597
    return true;
6598
}
6599
6600
/**
6601
 * @param string $username
6602
 *
6603
 * @return bool
6604
 */
6605
function api_get_user_blocked_by_captcha($username)
6606
{
6607
    $userInfo = api_get_user_info_from_username($username);
6608
    if (empty($userInfo)) {
6609
        return false;
6610
    }
6611
    $data = UserManager::get_extra_user_data_by_field(
6612
        $userInfo['user_id'],
6613
        'captcha_blocked_until_date'
6614
    );
6615
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
6616
        return $data['captcha_blocked_until_date'];
6617
    }
6618
6619
    return false;
6620
}
6621
6622
/**
6623
 * If true, the drh can access all content (courses, users) inside a session.
6624
 *
6625
 * @return bool
6626
 */
6627
function api_drh_can_access_all_session_content()
6628
{
6629
    return 'true' === api_get_setting('drh_can_access_all_session_content');
6630
}
6631
6632
/**
6633
 * Checks if user can login as another user.
6634
 *
6635
 * @param int $loginAsUserId the user id to log in
6636
 * @param int $userId        my user id
6637
 *
6638
 * @return bool
6639
 */
6640
function api_can_login_as($loginAsUserId, $userId = null)
6641
{
6642
    $loginAsUserId = (int) $loginAsUserId;
6643
6644
    if (empty($loginAsUserId)) {
6645
        return false;
6646
    }
6647
6648
    if (empty($userId)) {
6649
        $userId = api_get_user_id();
6650
    }
6651
6652
    if ($loginAsUserId == $userId) {
6653
        return false;
6654
    }
6655
6656
    // Check if the user to login is an admin
6657
    if (api_is_platform_admin_by_id($loginAsUserId)) {
6658
        // Only super admins can login to admin accounts
6659
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
6660
            return false;
6661
        }
6662
    }
6663
6664
    $userInfo = api_get_user_info($loginAsUserId);
6665
6666
    $isDrh = function () use ($loginAsUserId) {
6667
        if (api_is_drh()) {
6668
            if (api_drh_can_access_all_session_content()) {
6669
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
6670
                    'drh_all',
6671
                    api_get_user_id()
6672
                );
6673
                $userList = [];
6674
                if (is_array($users)) {
6675
                    foreach ($users as $user) {
6676
                        $userList[] = $user['id'];
6677
                    }
6678
                }
6679
                if (in_array($loginAsUserId, $userList)) {
6680
                    return true;
6681
                }
6682
            } else {
6683
                if (api_is_drh() &&
6684
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
6685
                ) {
6686
                    return true;
6687
                }
6688
            }
6689
        }
6690
6691
        return false;
6692
    };
6693
6694
    $loginAsStatusForSessionAdmins = [STUDENT];
6695
6696
    if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
6697
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
6698
    }
6699
6700
    return api_is_platform_admin() ||
6701
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
6702
        $isDrh();
6703
}
6704
6705
/**
6706
 * Return true on https install.
6707
 *
6708
 * @return bool
6709
 */
6710
function api_is_https()
6711
{
6712
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($_SERVER['HTTP_...ttps_forwarded_proto')), Probably Intended Meaning: ! empty($_SERVER['HTTP_X...tps_forwarded_proto')))
Loading history...
6713
        'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] || !empty(api_get_configuration_value('force_https_forwarded_proto'))
6714
    ) {
6715
        $isSecured = true;
6716
    } else {
6717
        if (!empty($_SERVER['HTTPS']) && 'off' != $_SERVER['HTTPS']) {
6718
            $isSecured = true;
6719
        } else {
6720
            $isSecured = false;
6721
            // last chance
6722
            if (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) {
6723
                $isSecured = true;
6724
            }
6725
        }
6726
    }
6727
6728
    return $isSecured;
6729
}
6730
6731
/**
6732
 * Return protocol (http or https).
6733
 *
6734
 * @return string
6735
 */
6736
function api_get_protocol()
6737
{
6738
    return api_is_https() ? 'https' : 'http';
6739
}
6740
6741
/**
6742
 * Get origin.
6743
 *
6744
 * @param string
6745
 *
6746
 * @return string
6747
 */
6748
function api_get_origin()
6749
{
6750
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
6751
}
6752
6753
/**
6754
 * Warns an user that the portal reach certain limit.
6755
 *
6756
 * @param string $limitName
6757
 */
6758
function api_warn_hosting_contact($limitName)
6759
{
6760
    $hostingParams = api_get_configuration_value(1);
6761
    $email = null;
6762
6763
    if (!empty($hostingParams)) {
6764
        if (isset($hostingParams['hosting_contact_mail'])) {
6765
            $email = $hostingParams['hosting_contact_mail'];
6766
        }
6767
    }
6768
6769
    if (!empty($email)) {
6770
        $subject = get_lang('Hosting warning reached');
6771
        $body = get_lang('Portal name').': '.api_get_path(WEB_PATH)." \n ";
6772
        $body .= get_lang('Portal\'s limit type').': '.$limitName." \n ";
6773
        if (isset($hostingParams[$limitName])) {
6774
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
6775
        }
6776
        api_mail_html(null, $email, $subject, $body);
6777
    }
6778
}
6779
6780
/**
6781
 * Gets value of a variable from config/configuration.php
6782
 * Variables that are not set in the configuration.php file but set elsewhere:
6783
 * - virtual_css_theme_folder (vchamilo plugin)
6784
 * - access_url (global.inc.php)
6785
 * - apc/apc_prefix (global.inc.php).
6786
 *
6787
 * @param string $variable
6788
 *
6789
 * @return bool|mixed
6790
 */
6791
function api_get_configuration_value($variable)
6792
{
6793
    global $_configuration;
6794
    // Check the current url id, id = 1 by default
6795
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
6796
6797
    $variable = trim($variable);
6798
6799
    // Check if variable exists
6800
    if (isset($_configuration[$variable])) {
6801
        if (is_array($_configuration[$variable])) {
6802
            // Check if it exists for the sub portal
6803
            if (array_key_exists($urlId, $_configuration[$variable])) {
6804
                return $_configuration[$variable][$urlId];
6805
            } else {
6806
                // Try to found element with id = 1 (master portal)
6807
                if (array_key_exists(1, $_configuration[$variable])) {
6808
                    return $_configuration[$variable][1];
6809
                }
6810
            }
6811
        }
6812
6813
        return $_configuration[$variable];
6814
    }
6815
6816
    return false;
6817
}
6818
6819
/**
6820
 * Retreives and returns a value in a hierarchical configuration array
6821
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
6822
 *
6823
 * @param string $path      the successive array keys, separated by the separator
6824
 * @param mixed  $default   value to be returned if not found, null by default
6825
 * @param string $separator '/' by default
6826
 * @param array  $array     the active configuration array by default
6827
 *
6828
 * @return mixed the found value or $default
6829
 */
6830
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
6831
{
6832
    $pos = strpos($path, $separator);
6833
    if (false === $pos) {
6834
        if (is_null($array)) {
6835
            return api_get_configuration_value($path);
6836
        }
6837
        if (is_array($array) && array_key_exists($path, $array)) {
6838
            return $array[$path];
6839
        }
6840
6841
        return $default;
6842
    }
6843
    $key = substr($path, 0, $pos);
6844
    if (is_null($array)) {
6845
        $newArray = api_get_configuration_value($key);
6846
    } elseif (is_array($array) && array_key_exists($key, $array)) {
6847
        $newArray = $array[$key];
6848
    } else {
6849
        return $default;
6850
    }
6851
    if (is_array($newArray)) {
6852
        $newPath = substr($path, $pos + 1);
6853
6854
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
6855
    }
6856
6857
    return $default;
6858
}
6859
6860
/**
6861
 * Retrieves and returns a value in a hierarchical configuration array
6862
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
6863
 *
6864
 * @param array  $array     the recursive array that contains the value to be returned (or not)
6865
 * @param string $path      the successive array keys, separated by the separator
6866
 * @param mixed  $default   the value to be returned if not found
6867
 * @param string $separator the separator substring
6868
 *
6869
 * @return mixed the found value or $default
6870
 */
6871
function api_array_sub_value($array, $path, $default = null, $separator = '/')
6872
{
6873
    $pos = strpos($path, $separator);
6874
    if (false === $pos) {
6875
        if (is_array($array) && array_key_exists($path, $array)) {
6876
            return $array[$path];
6877
        }
6878
6879
        return $default;
6880
    }
6881
    $key = substr($path, 0, $pos);
6882
    if (is_array($array) && array_key_exists($key, $array)) {
6883
        $newArray = $array[$key];
6884
    } else {
6885
        return $default;
6886
    }
6887
    if (is_array($newArray)) {
6888
        $newPath = substr($path, $pos + 1);
6889
6890
        return api_array_sub_value($newArray, $newPath, $default);
6891
    }
6892
6893
    return $default;
6894
}
6895
6896
/**
6897
 * Returns supported image extensions in the portal.
6898
 *
6899
 * @param bool $supportVectors Whether vector images should also be accepted or not
6900
 *
6901
 * @return array Supported image extensions in the portal
6902
 */
6903
function api_get_supported_image_extensions($supportVectors = true)
6904
{
6905
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
6906
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
6907
    if ($supportVectors) {
6908
        array_push($supportedImageExtensions, 'svg');
6909
    }
6910
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
6911
        array_push($supportedImageExtensions, 'webp');
6912
    }
6913
6914
    return $supportedImageExtensions;
6915
}
6916
6917
/**
6918
 * This setting changes the registration status for the campus.
6919
 *
6920
 * @author Patrick Cool <[email protected]>, Ghent University
6921
 *
6922
 * @version August 2006
6923
 *
6924
 * @param bool $listCampus Whether we authorize
6925
 *
6926
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
6927
 */
6928
function api_register_campus($listCampus = true)
6929
{
6930
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
6931
6932
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
6933
    Database::query($sql);
6934
6935
    if (!$listCampus) {
6936
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
6937
        Database::query($sql);
6938
    }
6939
}
6940
6941
/**
6942
 * Check whether the user type should be exclude.
6943
 * Such as invited or anonymous users.
6944
 *
6945
 * @param bool $checkDB Optional. Whether check the user status
6946
 * @param int  $userId  Options. The user id
6947
 *
6948
 * @return bool
6949
 */
6950
function api_is_excluded_user_type($checkDB = false, $userId = 0)
6951
{
6952
    if ($checkDB) {
6953
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
6954
6955
        if (0 == $userId) {
6956
            return true;
6957
        }
6958
6959
        $userInfo = api_get_user_info($userId);
6960
6961
        switch ($userInfo['status']) {
6962
            case INVITEE:
6963
            case ANONYMOUS:
6964
                return true;
6965
            default:
6966
                return false;
6967
        }
6968
    }
6969
6970
    $isInvited = api_is_invitee();
6971
    $isAnonymous = api_is_anonymous();
6972
6973
    if ($isInvited || $isAnonymous) {
6974
        return true;
6975
    }
6976
6977
    return false;
6978
}
6979
6980
/**
6981
 * Get the user status to ignore in reports.
6982
 *
6983
 * @param string $format Optional. The result type (array or string)
6984
 *
6985
 * @return array|string
6986
 */
6987
function api_get_users_status_ignored_in_reports($format = 'array')
6988
{
6989
    $excludedTypes = [
6990
        INVITEE,
6991
        ANONYMOUS,
6992
    ];
6993
6994
    if ('string' == $format) {
6995
        return implode(', ', $excludedTypes);
6996
    }
6997
6998
    return $excludedTypes;
6999
}
7000
7001
/**
7002
 * Set the Site Use Cookie Warning for 1 year.
7003
 */
7004
function api_set_site_use_cookie_warning_cookie()
7005
{
7006
    setcookie('ChamiloUsesCookies', 'ok', time() + 31556926);
7007
}
7008
7009
/**
7010
 * Return true if the Site Use Cookie Warning Cookie warning exists.
7011
 *
7012
 * @return bool
7013
 */
7014
function api_site_use_cookie_warning_cookie_exist()
7015
{
7016
    return isset($_COOKIE['ChamiloUsesCookies']);
7017
}
7018
7019
/**
7020
 * Given a number of seconds, format the time to show hours, minutes and seconds.
7021
 *
7022
 * @param int    $time         The time in seconds
7023
 * @param string $originFormat Optional. PHP o JS
7024
 *
7025
 * @return string (00h00'00")
7026
 */
7027
function api_format_time($time, $originFormat = 'php')
7028
{
7029
    $h = get_lang('h');
7030
    $hours = $time / 3600;
7031
    $mins = ($time % 3600) / 60;
7032
    $secs = ($time % 60);
7033
7034
    if ($time < 0) {
7035
        $hours = 0;
7036
        $mins = 0;
7037
        $secs = 0;
7038
    }
7039
7040
    if ('js' === $originFormat) {
7041
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
7042
    } else {
7043
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
7044
    }
7045
7046
    return $formattedTime;
7047
}
7048
7049
function api_set_noreply_and_from_address_to_mailer(
7050
    TemplatedEmail $email,
7051
    array $sender,
7052
    array $replyToAddress = []
7053
): void {
7054
    $validator = Container::getLegacyHelper()->getValidator();
7055
    $emailConstraint = new Assert\Email();
7056
7057
    $noReplyAddress = api_get_setting('noreply_email_address');
7058
    $avoidReplyToAddress = false;
7059
7060
    if (!empty($noReplyAddress)) {
7061
        // $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
7062
    }
7063
7064
    // Default values
7065
    $notification = new Notification();
7066
    $defaultSenderName = $notification->getDefaultPlatformSenderName();
7067
    $defaultSenderEmail = $notification->getDefaultPlatformSenderEmail();
7068
7069
    // If the parameter is set don't use the admin.
7070
    $senderName = !empty($sender['name']) ? $sender['name'] : $defaultSenderName;
7071
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $defaultSenderEmail;
7072
7073
    // Send errors to the platform admin
7074
    $adminEmail = api_get_setting('admin.administrator_email');
7075
7076
    $adminEmailValidation = $validator->validate($adminEmail, $emailConstraint);
7077
7078
    if (!empty($adminEmail) && 0 === $adminEmailValidation->count()) {
7079
        $email
7080
            ->getHeaders()
7081
            ->addIdHeader('Errors-To', $adminEmail)
7082
        ;
7083
    }
7084
7085
    if (!$avoidReplyToAddress && !empty($replyToAddress)) {
7086
        $replyToEmailValidation = $validator->validate($replyToAddress['mail'], $emailConstraint);
7087
7088
        if (0 === $replyToEmailValidation->count()) {
7089
            $email->addReplyTo(new Address($replyToAddress['mail'], $replyToAddress['name']));
7090
        }
7091
    }
7092
7093
    if ('true' === api_get_setting('mail.smtp_unique_sender')) {
7094
        $senderName = $defaultSenderName;
7095
        $senderEmail = $defaultSenderEmail;
7096
7097
        $email->sender(new Address($senderEmail, $senderName));
7098
    }
7099
7100
    if ($senderEmail) {
7101
        $email->from(new Address($senderEmail, $senderName));
7102
    }
7103
}
7104
7105
/**
7106
 * Sends an email
7107
 * Sender name and email can be specified, if not specified
7108
 * name and email of the platform admin are used.
7109
 *
7110
 * @param string    name of recipient
7111
 * @param string    email of recipient
7112
 * @param string    email subject
7113
 * @param string    email body
7114
 * @param string    sender name
7115
 * @param string    sender e-mail
7116
 * @param array     extra headers in form $headers = array($name => $value) to allow parsing
7117
 * @param array     data file (path and filename)
7118
 * @param bool      True for attaching a embedded file inside content html (optional)
7119
 * @param array     Additional parameters
7120
 *
7121
 * @return bool true if mail was sent
7122
 */
7123
function api_mail_html(
7124
    $recipientName,
7125
    $recipientEmail,
7126
    $subject,
7127
    $body,
7128
    $senderName = '',
7129
    $senderEmail = '',
7130
    $extra_headers = [],
7131
    $data_file = [],
7132
    $embeddedImage = false,
7133
    $additionalParameters = [],
7134
    string $sendErrorTo = null
7135
) {
7136
    $mailHelper = Container::$container->get(MailHelper::class);
7137
7138
    return $mailHelper->send(
7139
        $recipientName,
7140
        $recipientEmail,
7141
        $subject,
7142
        $body,
7143
        $senderName,
7144
        $senderEmail,
7145
        $extra_headers,
7146
        $data_file,
7147
        $embeddedImage,
7148
        $additionalParameters,
7149
        $sendErrorTo
7150
    );
7151
}
7152
7153
/**
7154
 * @param int  $tool       Possible values: GroupManager::GROUP_TOOL_*
7155
 * @param bool $showHeader
7156
 */
7157
function api_protect_course_group($tool, $showHeader = true)
7158
{
7159
    $groupId = api_get_group_id();
7160
    if (!empty($groupId)) {
7161
        if (api_is_platform_admin()) {
7162
            return true;
7163
        }
7164
7165
        if (api_is_allowed_to_edit(false, true, true)) {
7166
            return true;
7167
        }
7168
7169
        $userId = api_get_user_id();
7170
        $sessionId = api_get_session_id();
7171
        if (!empty($sessionId)) {
7172
            if (api_is_coach($sessionId, api_get_course_int_id())) {
7173
                return true;
7174
            }
7175
7176
            if (api_is_drh()) {
7177
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
7178
                    return true;
7179
                }
7180
            }
7181
        }
7182
7183
        $group = api_get_group_entity($groupId);
7184
7185
        // Group doesn't exists
7186
        if (null === $group) {
7187
            api_not_allowed($showHeader);
7188
        }
7189
7190
        // Check group access
7191
        $allow = GroupManager::userHasAccess(
7192
            $userId,
7193
            $group,
7194
            $tool
7195
        );
7196
7197
        if (!$allow) {
7198
            api_not_allowed($showHeader);
7199
        }
7200
    }
7201
7202
    return false;
7203
}
7204
7205
/**
7206
 * Check if a date is in a date range.
7207
 *
7208
 * @param datetime $startDate
7209
 * @param datetime $endDate
7210
 * @param datetime $currentDate
7211
 *
7212
 * @return bool true if date is in rage, false otherwise
7213
 */
7214
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
7215
{
7216
    $startDate = strtotime(api_get_local_time($startDate));
7217
    $endDate = strtotime(api_get_local_time($endDate));
7218
    $currentDate = strtotime(api_get_local_time($currentDate));
7219
7220
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
7221
        return true;
7222
    }
7223
7224
    return false;
7225
}
7226
7227
/**
7228
 * Eliminate the duplicates of a multidimensional array by sending the key.
7229
 *
7230
 * @param array $array multidimensional array
7231
 * @param int   $key   key to find to compare
7232
 *
7233
 * @return array
7234
 */
7235
function api_unique_multidim_array($array, $key)
7236
{
7237
    $temp_array = [];
7238
    $i = 0;
7239
    $key_array = [];
7240
7241
    foreach ($array as $val) {
7242
        if (!in_array($val[$key], $key_array)) {
7243
            $key_array[$i] = $val[$key];
7244
            $temp_array[$i] = $val;
7245
        }
7246
        $i++;
7247
    }
7248
7249
    return $temp_array;
7250
}
7251
7252
/**
7253
 * Limit the access to Session Admins when the limit_session_admin_role
7254
 * configuration variable is set to true.
7255
 */
7256
function api_protect_limit_for_session_admin()
7257
{
7258
    $limitAdmin = api_get_setting('limit_session_admin_role');
7259
    if (api_is_session_admin() && 'true' === $limitAdmin) {
7260
        api_not_allowed(true);
7261
    }
7262
}
7263
7264
/**
7265
 * Limits that a session admin has access to list users.
7266
 * When limit_session_admin_list_users configuration variable is set to true.
7267
 */
7268
function api_protect_session_admin_list_users()
7269
{
7270
    $limitAdmin = ('true' === api_get_setting('session.limit_session_admin_list_users'));
7271
7272
    if (api_is_session_admin() && true === $limitAdmin) {
7273
        api_not_allowed(true);
7274
    }
7275
}
7276
7277
/**
7278
 * @return bool
7279
 */
7280
function api_is_student_view_active(): bool
7281
{
7282
    $studentView = Session::read('studentview');
7283
7284
    return 'studentview' === $studentView;
7285
}
7286
7287
/**
7288
 * Converts string value to float value.
7289
 *
7290
 * 3.141516 => 3.141516
7291
 * 3,141516 => 3.141516
7292
 *
7293
 * @todo WIP
7294
 *
7295
 * @param string $number
7296
 *
7297
 * @return float
7298
 */
7299
function api_float_val($number)
7300
{
7301
    return (float) str_replace(',', '.', trim($number));
7302
}
7303
7304
/**
7305
 * Converts float values
7306
 * Example if $decimals = 2.
7307
 *
7308
 * 3.141516 => 3.14
7309
 * 3,141516 => 3,14
7310
 *
7311
 * @param string $number            number in iso code
7312
 * @param int    $decimals
7313
 * @param string $decimalSeparator
7314
 * @param string $thousandSeparator
7315
 *
7316
 * @return bool|string
7317
 */
7318
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
7319
{
7320
    $number = api_float_val($number);
7321
7322
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
7323
}
7324
7325
/**
7326
 * Set location url with a exit break by default.
7327
 *
7328
 * @param string $url
7329
 * @param bool   $exit
7330
 */
7331
function api_location($url, $exit = true)
7332
{
7333
    header('Location: '.$url);
7334
7335
    if ($exit) {
7336
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
7337
    }
7338
}
7339
7340
/**
7341
 * @param string $from
7342
 * @param string $to
7343
 *
7344
 * @return string
7345
 */
7346
function api_get_relative_path($from, $to)
7347
{
7348
    // some compatibility fixes for Windows paths
7349
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
7350
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
7351
    $from = str_replace('\\', '/', $from);
7352
    $to = str_replace('\\', '/', $to);
7353
7354
    $from = explode('/', $from);
7355
    $to = explode('/', $to);
7356
    $relPath = $to;
7357
7358
    foreach ($from as $depth => $dir) {
7359
        // find first non-matching dir
7360
        if ($dir === $to[$depth]) {
7361
            // ignore this directory
7362
            array_shift($relPath);
7363
        } else {
7364
            // get number of remaining dirs to $from
7365
            $remaining = count($from) - $depth;
7366
            if ($remaining > 1) {
7367
                // add traversals up to first matching dir
7368
                $padLength = (count($relPath) + $remaining - 1) * -1;
7369
                $relPath = array_pad($relPath, $padLength, '..');
7370
                break;
7371
            } else {
7372
                $relPath[0] = './'.$relPath[0];
7373
            }
7374
        }
7375
    }
7376
7377
    return implode('/', $relPath);
7378
}
7379
7380
/**
7381
 * @param string $template
7382
 *
7383
 * @return string
7384
 */
7385
function api_find_template($template)
7386
{
7387
    return Template::findTemplateFilePath($template);
7388
}
7389
7390
/**
7391
 * @return array
7392
 */
7393
function api_get_language_list_for_flag()
7394
{
7395
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
7396
    $sql = "SELECT english_name, isocode FROM $table
7397
            ORDER BY original_name ASC";
7398
    static $languages = [];
7399
    if (empty($languages)) {
7400
        $result = Database::query($sql);
7401
        while ($row = Database::fetch_array($result)) {
7402
            $languages[$row['english_name']] = $row['isocode'];
7403
        }
7404
        $languages['english'] = 'gb';
7405
    }
7406
7407
    return $languages;
7408
}
7409
7410
function api_create_zip(string $name): ZipStream
7411
{
7412
    $zipStreamOptions = new Archive();
7413
    $zipStreamOptions->setSendHttpHeaders(true);
7414
    $zipStreamOptions->setContentDisposition('attachment');
7415
    $zipStreamOptions->setContentType('application/x-zip');
7416
7417
    return new ZipStream($name, $zipStreamOptions);
7418
}
7419
7420
function api_get_language_translate_html(): string
7421
{
7422
    $translate = 'true' === api_get_setting('editor.translate_html');
7423
7424
    if (!$translate) {
7425
        return '';
7426
    }
7427
7428
    /*$languageList = api_get_languages();
7429
    $hideAll = '';
7430
    foreach ($languageList as $isocode => $name) {
7431
        $hideAll .= '
7432
        $(".mce-translatehtml").hide();
7433
        $("span:lang('.$isocode.')").filter(
7434
            function(e, val) {
7435
                // Only find the spans if they have set the lang
7436
                if ($(this).attr("lang") == null) {
7437
                    return false;
7438
                }
7439
                // Ignore ckeditor classes
7440
                return !this.className.match(/cke(.*)/);
7441
        }).hide();'."\n";
7442
    }*/
7443
7444
    $userInfo = api_get_user_info();
7445
    if (!empty($userInfo['language'])) {
7446
        $isoCode = $userInfo['language'];
7447
7448
        return '
7449
            $(function() {
7450
                $(".mce-translatehtml").hide();
7451
                var defaultLanguageFromUser = "'.$isoCode.'";
7452
                $("span:lang('.$isoCode.')").show();
7453
            });
7454
        ';
7455
    }
7456
7457
    return '';
7458
}
7459
7460
function api_protect_webservices()
7461
{
7462
    if (api_get_configuration_value('disable_webservices')) {
7463
        echo "Webservices are disabled. \n";
7464
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
7465
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
7466
    }
7467
}
7468
7469
/**
7470
 * Checks if a set of roles have a specific permission.
7471
 *
7472
 * @param string $permissionSlug The slug of the permission to check.
7473
 * @param array  $roles          An array of role codes to check against.
7474
 * @return bool True if any of the roles have the permission, false otherwise.
7475
 */
7476
function api_get_permission(string $permissionSlug, array $roles): bool
7477
{
7478
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
7479
7480
    return $permissionService->hasPermission($permissionSlug, $roles);
7481
}
7482