Passed
Pull Request — master (#6206)
by Yannick
11:11
created

api_detect_feature_context()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 28
rs 9.6333
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\MailHelper;
15
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
16
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
17
use Chamilo\CourseBundle\Entity\CGroup;
18
use Chamilo\CourseBundle\Entity\CLp;
19
use ChamiloSession as Session;
20
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Mime\Address;
23
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
use Symfony\Component\Validator\Constraints as Assert;
26
use ZipStream\Option\Archive;
27
use ZipStream\ZipStream;
28
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
29
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
30
31
/**
32
 * This is a code library for Chamilo.
33
 * It is included by default in every Chamilo file (through including the global.inc.php)
34
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
35
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
36
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
37
 */
38
39
// PHP version requirement.
40
define('REQUIRED_PHP_VERSION', '8.2');
41
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
42
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
43
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
44
45
// USER STATUS CONSTANTS
46
/** global status of a user: student */
47
define('STUDENT', 5);
48
/** global status of a user: course manager */
49
define('COURSEMANAGER', 1);
50
/** global status of a user: session admin */
51
define('SESSIONADMIN', 3);
52
/** global status of a user: human resources manager */
53
define('DRH', 4);
54
/** global status of a user: anonymous visitor */
55
define('ANONYMOUS', 6);
56
/** global status of a user: low security, necessary for inserting data from
57
 * the teacher through HTMLPurifier */
58
define('COURSEMANAGERLOWSECURITY', 10);
59
// Soft user status
60
define('PLATFORM_ADMIN', 11);
61
define('SESSION_COURSE_COACH', 12);
62
define('SESSION_GENERAL_COACH', 13);
63
define('COURSE_STUDENT', 14); //student subscribed in a course
64
define('SESSION_STUDENT', 15); //student subscribed in a session course
65
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
66
define('STUDENT_BOSS', 17); // student is boss
67
define('INVITEE', 20);
68
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
69
70
// COURSE VISIBILITY CONSTANTS
71
/** only visible for course admin */
72
define('COURSE_VISIBILITY_CLOSED', 0);
73
/** only visible for users registered in the course */
74
define('COURSE_VISIBILITY_REGISTERED', 1);
75
/** Open for all registered users on the platform */
76
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
77
/** Open for the whole world */
78
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
79
/** Invisible to all except admin */
80
define('COURSE_VISIBILITY_HIDDEN', 4);
81
82
define('COURSE_REQUEST_PENDING', 0);
83
define('COURSE_REQUEST_ACCEPTED', 1);
84
define('COURSE_REQUEST_REJECTED', 2);
85
define('DELETE_ACTION_ENABLED', false);
86
87
// EMAIL SENDING RECIPIENT CONSTANTS
88
define('SEND_EMAIL_EVERYONE', 1);
89
define('SEND_EMAIL_STUDENTS', 2);
90
define('SEND_EMAIL_TEACHERS', 3);
91
92
// SESSION VISIBILITY CONSTANTS
93
define('SESSION_VISIBLE_READ_ONLY', 1);
94
define('SESSION_VISIBLE', 2);
95
define('SESSION_INVISIBLE', 3); // not available
96
define('SESSION_AVAILABLE', 4);
97
98
define('SESSION_LINK_TARGET', '_self');
99
100
define('SUBSCRIBE_ALLOWED', 1);
101
define('SUBSCRIBE_NOT_ALLOWED', 0);
102
define('UNSUBSCRIBE_ALLOWED', 1);
103
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
104
105
// SURVEY VISIBILITY CONSTANTS
106
define('SURVEY_VISIBLE_TUTOR', 0);
107
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
108
define('SURVEY_VISIBLE_PUBLIC', 2);
109
110
// CONSTANTS defining all tools, using the english version
111
/* When you add a new tool you must add it into function api_get_tools_lists() too */
112
define('TOOL_DOCUMENT', 'document');
113
define('TOOL_LP_FINAL_ITEM', 'final_item');
114
define('TOOL_READOUT_TEXT', 'readout_text');
115
define('TOOL_THUMBNAIL', 'thumbnail');
116
define('TOOL_HOTPOTATOES', 'hotpotatoes');
117
define('TOOL_CALENDAR_EVENT', 'calendar_event');
118
define('TOOL_LINK', 'link');
119
define('TOOL_LINK_CATEGORY', 'link_category');
120
define('TOOL_COURSE_DESCRIPTION', 'course_description');
121
define('TOOL_SEARCH', 'search');
122
define('TOOL_LEARNPATH', 'learnpath');
123
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
124
define('TOOL_AGENDA', 'agenda');
125
define('TOOL_ANNOUNCEMENT', 'announcement');
126
define('TOOL_FORUM', 'forum');
127
define('TOOL_FORUM_CATEGORY', 'forum_category');
128
define('TOOL_FORUM_THREAD', 'forum_thread');
129
define('TOOL_FORUM_POST', 'forum_post');
130
define('TOOL_FORUM_ATTACH', 'forum_attachment');
131
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
132
define('TOOL_THREAD', 'thread');
133
define('TOOL_POST', 'post');
134
define('TOOL_DROPBOX', 'dropbox');
135
define('TOOL_QUIZ', 'quiz');
136
define('TOOL_TEST_CATEGORY', 'test_category');
137
define('TOOL_USER', 'user');
138
define('TOOL_GROUP', 'group');
139
define('TOOL_BLOGS', 'blog_management');
140
define('TOOL_CHAT', 'chat');
141
define('TOOL_STUDENTPUBLICATION', 'student_publication');
142
define('TOOL_TRACKING', 'tracking');
143
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
144
define('TOOL_COURSE_SETTING', 'course_setting');
145
define('TOOL_BACKUP', 'backup');
146
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
147
define('TOOL_RECYCLE_COURSE', 'recycle_course');
148
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
149
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
150
define('TOOL_UPLOAD', 'file_upload');
151
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
152
define('TOOL_SURVEY', 'survey');
153
//define('TOOL_WIKI', 'wiki');
154
define('TOOL_GLOSSARY', 'glossary');
155
define('TOOL_GRADEBOOK', 'gradebook');
156
define('TOOL_NOTEBOOK', 'notebook');
157
define('TOOL_ATTENDANCE', 'attendance');
158
define('TOOL_COURSE_PROGRESS', 'course_progress');
159
define('TOOL_PORTFOLIO', 'portfolio');
160
define('TOOL_PLAGIARISM', 'compilatio');
161
define('TOOL_XAPI', 'xapi');
162
163
// CONSTANTS defining Chamilo interface sections
164
define('SECTION_CAMPUS', 'mycampus');
165
define('SECTION_COURSES', 'mycourses');
166
define('SECTION_CATALOG', 'catalog');
167
define('SECTION_MYPROFILE', 'myprofile');
168
define('SECTION_MYAGENDA', 'myagenda');
169
define('SECTION_COURSE_ADMIN', 'course_admin');
170
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
171
define('SECTION_MYGRADEBOOK', 'mygradebook');
172
define('SECTION_TRACKING', 'session_my_space');
173
define('SECTION_SOCIAL', 'social-network');
174
define('SECTION_DASHBOARD', 'dashboard');
175
define('SECTION_REPORTS', 'reports');
176
define('SECTION_GLOBAL', 'global');
177
define('SECTION_INCLUDE', 'include');
178
define('SECTION_CUSTOMPAGE', 'custompage');
179
180
// event logs types
181
define('LOG_COURSE_DELETE', 'course_deleted');
182
define('LOG_COURSE_CREATE', 'course_created');
183
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
184
185
// @todo replace 'soc_gr' with social_group
186
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
187
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
188
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
189
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
190
191
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
192
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
193
194
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
195
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
196
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
197
198
define('LOG_MESSAGE_DATA', 'message_data');
199
define('LOG_MESSAGE_DELETE', 'msg_deleted');
200
201
const LOG_RESOURCE_LINK_DELETE = 'resource_link_deleted';
202
const LOG_RESOURCE_LINK_SOFT_DELETE = 'resource_link_soft_deleted';
203
const LOG_RESOURCE_NODE = 'resource_node_id';
204
const LOG_RESOURCE_LINK = 'resource_link_id';
205
const LOG_RESOURCE_NODE_AND_RESOURCE_LINK = 'resource_node_id_and_resource_link_id';
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_sources',
1312
        'username',
1313
        'theme',
1314
        'language',
1315
        'locale',
1316
        'creator_id',
1317
        'created_at',
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['auth_sources'] = api_get_user_entity($result_array['id'])->getAuthSourcesAuthentications();
1521
        $result_array['user_is_online_in_chat'] = 0;
1522
        if ($checkIfUserOnline) {
1523
            $use_status_in_platform = user_is_online($user_id);
1524
            $result_array['user_is_online'] = $use_status_in_platform;
1525
            $user_online_in_chat = 0;
1526
            if ($use_status_in_platform) {
1527
                $user_status = UserManager::get_extra_user_data_by_field(
1528
                    $user_id,
1529
                    'user_chat_status',
1530
                    false,
1531
                    true
1532
                );
1533
                if (1 == (int) $user_status['user_chat_status']) {
1534
                    $user_online_in_chat = 1;
1535
                }
1536
            }
1537
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1538
        }
1539
1540
        if ($loadExtraData) {
1541
            $fieldValue = new ExtraFieldValue('user');
1542
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1543
                $user_id,
1544
                $loadOnlyVisibleExtraData
1545
            );
1546
        }
1547
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1548
    }
1549
1550
    return $user;
1551
}
1552
1553
function api_get_user_info_from_entity(
1554
    User $user,
1555
    $checkIfUserOnline = false,
1556
    $showPassword = false,
1557
    $loadExtraData = false,
1558
    $loadOnlyVisibleExtraData = false,
1559
    $loadAvatars = true,
1560
    $loadCertificate = false
1561
) {
1562
    if (!$user instanceof UserInterface) {
1563
        return false;
1564
    }
1565
1566
    // Make sure user_id is safe
1567
    $user_id = (int) $user->getId();
1568
1569
    if (empty($user_id)) {
1570
        $userFromSession = Session::read('_user');
1571
1572
        if (isset($userFromSession) && !empty($userFromSession)) {
1573
            return $userFromSession;
1574
        }
1575
1576
        return false;
1577
    }
1578
1579
    $result = [];
1580
    $result['user_is_online_in_chat'] = 0;
1581
    if ($checkIfUserOnline) {
1582
        $use_status_in_platform = user_is_online($user_id);
1583
        $result['user_is_online'] = $use_status_in_platform;
1584
        $user_online_in_chat = 0;
1585
        if ($use_status_in_platform) {
1586
            $user_status = UserManager::get_extra_user_data_by_field(
1587
                $user_id,
1588
                'user_chat_status',
1589
                false,
1590
                true
1591
            );
1592
            if (1 == (int) $user_status['user_chat_status']) {
1593
                $user_online_in_chat = 1;
1594
            }
1595
        }
1596
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1597
    }
1598
1599
    if ($loadExtraData) {
1600
        $fieldValue = new ExtraFieldValue('user');
1601
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1602
            $user_id,
1603
            $loadOnlyVisibleExtraData
1604
        );
1605
    }
1606
1607
    $result['username'] = $user->getUsername();
1608
    $result['status'] = $user->getStatus();
1609
    $result['firstname'] = $user->getFirstname();
1610
    $result['lastname'] = $user->getLastname();
1611
    $result['email'] = $result['mail'] = $user->getEmail();
1612
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1613
    $result['complete_name_with_username'] = $result['complete_name'];
1614
1615
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1616
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1617
    }
1618
1619
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1620
    if (!empty($result['email'])) {
1621
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1622
        if ($showEmail) {
1623
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1624
        }
1625
    } else {
1626
        $result['complete_name_with_email'] = $result['complete_name'];
1627
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1628
    }
1629
1630
    // Kept for historical reasons
1631
    $result['firstName'] = $result['firstname'];
1632
    $result['lastName'] = $result['lastname'];
1633
1634
    $attributes = [
1635
        'picture_uri',
1636
        'last_login',
1637
        'user_is_online',
1638
    ];
1639
1640
    $result['phone'] = $user->getPhone();
1641
    $result['address'] = $user->getAddress();
1642
    $result['official_code'] = $user->getOfficialCode();
1643
    $result['active'] = $user->isActive();
1644
    $result['auth_sources'] = $user->getAuthSourcesAuthentications();
1645
    $result['language'] = $user->getLocale();
1646
    $result['creator_id'] = $user->getCreatorId();
1647
    $result['created_at'] = $user->getCreatedAt()->format('Y-m-d H:i:s');
1648
    $result['hr_dept_id'] = $user->getHrDeptId();
1649
    $result['expiration_date'] = '';
1650
    if ($user->getExpirationDate()) {
1651
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1652
    }
1653
1654
    $result['last_login'] = null;
1655
    if ($user->getLastLogin()) {
1656
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1657
    }
1658
1659
    $result['competences'] = $user->getCompetences();
1660
    $result['diplomas'] = $user->getDiplomas();
1661
    $result['teach'] = $user->getTeach();
1662
    $result['openarea'] = $user->getOpenarea();
1663
    $user_id = (int) $user->getId();
1664
1665
    // Maintain the user_id index for backwards compatibility
1666
    $result['user_id'] = $result['id'] = $user_id;
1667
1668
    if ($loadCertificate) {
1669
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1670
        $result['has_certificates'] = 0;
1671
        if (!empty($hasCertificates)) {
1672
            $result['has_certificates'] = 1;
1673
        }
1674
    }
1675
1676
    $result['icon_status'] = '';
1677
    $result['icon_status_medium'] = '';
1678
    $result['is_admin'] = UserManager::is_admin($user_id);
1679
1680
    // Getting user avatar.
1681
    if ($loadAvatars) {
1682
        $result['avatar'] = '';
1683
        $result['avatar_no_query'] = '';
1684
        $result['avatar_small'] = '';
1685
        $result['avatar_medium'] = '';
1686
        $urlImg = '/';
1687
        $iconStatus = '';
1688
        $iconStatusMedium = '';
1689
1690
        switch ($user->getStatus()) {
1691
            case STUDENT:
1692
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1693
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1694
                } else {
1695
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1696
                }
1697
                break;
1698
            case COURSEMANAGER:
1699
                if ($result['is_admin']) {
1700
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1701
                } else {
1702
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1703
                }
1704
                break;
1705
            case STUDENT_BOSS:
1706
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1707
                break;
1708
        }
1709
1710
        if (!empty($iconStatus)) {
1711
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1712
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1713
        }
1714
1715
        $result['icon_status'] = $iconStatus;
1716
        $result['icon_status_medium'] = $iconStatusMedium;
1717
    }
1718
1719
    if (isset($result['user_is_online'])) {
1720
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1721
    }
1722
    if (isset($result['user_is_online_in_chat'])) {
1723
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1724
    }
1725
1726
    $result['password'] = '';
1727
    if ($showPassword) {
1728
        $result['password'] = $user->getPassword();
1729
    }
1730
1731
    if (isset($result['profile_completed'])) {
1732
        $result['profile_completed'] = $result['profile_completed'];
1733
    }
1734
1735
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1736
1737
    // Send message link
1738
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1739
    $result['complete_name_with_message_link'] = Display::url(
1740
        $result['complete_name_with_username'],
1741
        $sendMessage,
1742
        ['class' => 'ajax']
1743
    );
1744
1745
    if (isset($result['extra'])) {
1746
        $result['extra'] = $result['extra'];
1747
    }
1748
1749
    return $result;
1750
}
1751
1752
function api_get_lp_entity(int $id): ?CLp
1753
{
1754
    return Database::getManager()->getRepository(CLp::class)->find($id);
1755
}
1756
1757
function api_get_user_entity(int $userId = 0): ?User
1758
{
1759
    $userId = $userId ?: api_get_user_id();
1760
    $repo = Container::getUserRepository();
1761
1762
    return $repo->find($userId);
1763
}
1764
1765
function api_get_current_user(): ?User
1766
{
1767
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1768
    if (false === $isLoggedIn) {
1769
        return null;
1770
    }
1771
1772
    $token = Container::getTokenStorage()->getToken();
1773
1774
    if (null !== $token) {
1775
        return $token->getUser();
1776
    }
1777
1778
    return null;
1779
}
1780
1781
/**
1782
 * Finds all the information about a user from username instead of user id.
1783
 *
1784
 * @param string $username
1785
 *
1786
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1787
 *
1788
 * @author Yannick Warnier <[email protected]>
1789
 */
1790
function api_get_user_info_from_username($username)
1791
{
1792
    if (empty($username)) {
1793
        return false;
1794
    }
1795
    $username = trim($username);
1796
1797
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1798
            WHERE username='".Database::escape_string($username)."'";
1799
    $result = Database::query($sql);
1800
    if (Database::num_rows($result) > 0) {
1801
        $resultArray = Database::fetch_array($result);
1802
1803
        return _api_format_user($resultArray);
1804
    }
1805
1806
    return false;
1807
}
1808
1809
/**
1810
 * Get first user with an email.
1811
 *
1812
 * @param string $email
1813
 *
1814
 * @return array|bool
1815
 */
1816
function api_get_user_info_from_email($email = '')
1817
{
1818
    if (empty($email)) {
1819
        return false;
1820
    }
1821
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1822
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1823
    $result = Database::query($sql);
1824
    if (Database::num_rows($result) > 0) {
1825
        $resultArray = Database::fetch_array($result);
1826
1827
        return _api_format_user($resultArray);
1828
    }
1829
1830
    return false;
1831
}
1832
1833
/**
1834
 * @return string
1835
 */
1836
function api_get_course_id()
1837
{
1838
    return Session::read('_cid', null);
1839
}
1840
1841
/**
1842
 * Returns the current course id (integer).
1843
 *
1844
 * @param ?string $code Optional course code
1845
 *
1846
 * @return int
1847
 */
1848
function api_get_course_int_id(?string $code = null): int
1849
{
1850
    if (!empty($code)) {
1851
        $code = Database::escape_string($code);
1852
        $row = Database::select(
1853
            'id',
1854
            Database::get_main_table(TABLE_MAIN_COURSE),
1855
            ['where' => ['code = ?' => [$code]]],
1856
            'first'
1857
        );
1858
1859
        if (is_array($row) && isset($row['id'])) {
1860
            return $row['id'];
1861
        } else {
1862
            return 0;
1863
        }
1864
    }
1865
1866
    $cid = Session::read('_real_cid', 0);
1867
    if (empty($cid) && isset($_REQUEST['cid'])) {
1868
        $cid = (int) $_REQUEST['cid'];
1869
    }
1870
1871
    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...
1872
}
1873
1874
/**
1875
 * Gets a course setting from the current course_setting table. Try always using integer values.
1876
 *
1877
 * @param string       $settingName The name of the setting we want from the table
1878
 * @param Course|array $courseInfo
1879
 * @param bool         $force       force checking the value in the database
1880
 *
1881
 * @return mixed The value of that setting in that table. Return -1 if not found.
1882
 */
1883
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1884
{
1885
    if (empty($courseInfo)) {
1886
        $courseInfo = api_get_course_info();
1887
    }
1888
1889
    if (empty($courseInfo) || empty($settingName)) {
1890
        return -1;
1891
    }
1892
1893
    if ($courseInfo instanceof Course) {
1894
        $courseId = $courseInfo->getId();
1895
    } else {
1896
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1897
    }
1898
1899
    if (empty($courseId)) {
1900
        return -1;
1901
    }
1902
1903
    static $courseSettingInfo = [];
1904
1905
    if ($force) {
1906
        $courseSettingInfo = [];
1907
    }
1908
1909
    if (!isset($courseSettingInfo[$courseId])) {
1910
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1911
        $settingName = Database::escape_string($settingName);
1912
1913
        $sql = "SELECT variable, value FROM $table
1914
                WHERE c_id = $courseId ";
1915
        $res = Database::query($sql);
1916
        if (Database::num_rows($res) > 0) {
1917
            $result = Database::store_result($res, 'ASSOC');
1918
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1919
1920
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1921
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1922
                if (!is_null($value)) {
1923
                    $result = explode(',', $value);
1924
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1925
                }
1926
            }
1927
        }
1928
    }
1929
1930
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1931
        return $courseSettingInfo[$courseId][$settingName];
1932
    }
1933
1934
    return -1;
1935
}
1936
1937
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1938
{
1939
    $value = api_get_course_setting($settingName, $courseInfo, true);
1940
1941
    if (-1 === $value) {
1942
        // Check global settings
1943
        $value = api_get_plugin_setting($plugin, $settingName);
1944
        if ('true' === $value) {
1945
            return 1;
1946
        }
1947
        if ('false' === $value) {
1948
            return 0;
1949
        }
1950
        if (null === $value) {
1951
            return -1;
1952
        }
1953
    }
1954
1955
    return $value;
1956
}
1957
1958
/**
1959
 * Gets an anonymous user ID.
1960
 *
1961
 * For some tools that need tracking, like the learnpath tool, it is necessary
1962
 * to have a usable user-id to enable some kind of tracking, even if not
1963
 * perfect. An anonymous ID is taken from the users table by looking for a
1964
 * status of "6" (anonymous).
1965
 *
1966
 * @return int User ID of the anonymous user, or O if no anonymous user found
1967
 */
1968
function api_get_anonymous_id()
1969
{
1970
    // Find if another anon is connected now
1971
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1972
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1973
    $ip = Database::escape_string(api_get_real_ip());
1974
    $max = (int) api_get_setting('admin.max_anonymous_users');
1975
    if ($max >= 2) {
1976
        $sql = "SELECT * FROM $table as TEL
1977
                JOIN $tableU as U
1978
                ON U.id = TEL.login_user_id
1979
                WHERE TEL.user_ip = '$ip'
1980
                    AND U.status = ".ANONYMOUS."
1981
                    AND U.id != 2 ";
1982
1983
        $result = Database::query($sql);
1984
        if (empty(Database::num_rows($result))) {
1985
            $login = uniqid('anon_');
1986
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['created_at ASC']);
1987
            if (count($anonList) >= $max) {
1988
                foreach ($anonList as $userToDelete) {
1989
                    UserManager::delete_user($userToDelete['user_id']);
1990
                    break;
1991
                }
1992
            }
1993
1994
            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...
1995
                $login,
1996
                'anon',
1997
                ANONYMOUS,
1998
                ' anonymous@localhost',
1999
                $login,
2000
                $login
2001
            );
2002
        } else {
2003
            $row = Database::fetch_assoc($result);
2004
2005
            return $row['id'];
2006
        }
2007
    }
2008
2009
    $table = Database::get_main_table(TABLE_MAIN_USER);
2010
    $sql = "SELECT id
2011
            FROM $table
2012
            WHERE status = ".ANONYMOUS." ";
2013
    $res = Database::query($sql);
2014
    if (Database::num_rows($res) > 0) {
2015
        $row = Database::fetch_assoc($res);
2016
2017
        return $row['id'];
2018
    }
2019
2020
    // No anonymous user was found.
2021
    return 0;
2022
}
2023
2024
/**
2025
 * @param int $courseId
2026
 * @param int $sessionId
2027
 * @param int $groupId
2028
 *
2029
 * @return string
2030
 */
2031
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2032
{
2033
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2034
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2035
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2036
2037
    $url = 'cid='.$courseId;
2038
    $url .= '&sid='.$sessionId;
2039
    $url .= '&gid='.$groupId;
2040
2041
    return $url;
2042
}
2043
2044
/**
2045
 * Returns the current course url part including session, group, and gradebook params.
2046
 *
2047
 * @param bool   $addSessionId
2048
 * @param bool   $addGroupId
2049
 * @param string $origin
2050
 *
2051
 * @return string Course & session references to add to a URL
2052
 */
2053
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2054
{
2055
    $courseId = api_get_course_int_id();
2056
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2057
        $courseId = (int) $_REQUEST['cid'];
2058
    }
2059
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2060
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2061
2062
    if ($addSessionId) {
2063
        if (!empty($url)) {
2064
            $sessionId = api_get_session_id();
2065
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2066
                $sessionId = (int) $_REQUEST['sid'];
2067
            }
2068
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2069
        }
2070
    }
2071
2072
    if ($addGroupId) {
2073
        if (!empty($url)) {
2074
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2075
        }
2076
    }
2077
2078
    if (!empty($url)) {
2079
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2080
        if (false !== $origin) {
2081
            $url .= '&origin=' . $origin;
2082
        }
2083
    }
2084
2085
    return $url;
2086
}
2087
2088
/**
2089
 * Get if we visited a gradebook page.
2090
 *
2091
 * @return bool
2092
 */
2093
function api_is_in_gradebook()
2094
{
2095
    return Session::read('in_gradebook', false);
2096
}
2097
2098
/**
2099
 * Set that we are in a page inside a gradebook.
2100
 */
2101
function api_set_in_gradebook()
2102
{
2103
    Session::write('in_gradebook', true);
2104
}
2105
2106
/**
2107
 * Remove gradebook session.
2108
 */
2109
function api_remove_in_gradebook()
2110
{
2111
    Session::erase('in_gradebook');
2112
}
2113
2114
/**
2115
 * Returns the current course info array see api_format_course_array()
2116
 * If the course_code is given, the returned array gives info about that
2117
 * particular course, if none given it gets the course info from the session.
2118
 *
2119
 * @param string $courseCode
2120
 *
2121
 * @return array
2122
 */
2123
function api_get_course_info($courseCode = null)
2124
{
2125
    if (!empty($courseCode)) {
2126
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2127
2128
        return api_format_course_array($course);
2129
    }
2130
2131
    $course = Session::read('_course');
2132
    if ('-1' == $course) {
2133
        $course = [];
2134
    }
2135
2136
    if (empty($course) && isset($_REQUEST['cid'])) {
2137
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2138
    }
2139
2140
    return $course;
2141
}
2142
2143
/**
2144
 * @param int $courseId
2145
 */
2146
function api_get_course_entity($courseId = 0): ?Course
2147
{
2148
    if (empty($courseId)) {
2149
        $courseId = api_get_course_int_id();
2150
    }
2151
2152
    if (empty($courseId)) {
2153
        return null;
2154
    }
2155
2156
    return Container::getCourseRepository()->find($courseId);
2157
}
2158
2159
/**
2160
 * @param int $id
2161
 */
2162
function api_get_session_entity($id = 0): ?SessionEntity
2163
{
2164
    if (empty($id)) {
2165
        $id = api_get_session_id();
2166
    }
2167
2168
    if (empty($id)) {
2169
        return null;
2170
    }
2171
2172
    return Container::getSessionRepository()->find($id);
2173
}
2174
2175
/**
2176
 * @param int $id
2177
 */
2178
function api_get_group_entity($id = 0): ?CGroup
2179
{
2180
    if (empty($id)) {
2181
        $id = api_get_group_id();
2182
    }
2183
2184
    return Container::getGroupRepository()->find($id);
2185
}
2186
2187
/**
2188
 * @param int $id
2189
 */
2190
function api_get_url_entity($id = 0): ?AccessUrl
2191
{
2192
    if (empty($id)) {
2193
        $id = api_get_current_access_url_id();
2194
    }
2195
2196
    return Container::getAccessUrlRepository()->find($id);
2197
}
2198
2199
/**
2200
 * Returns the current course info array.
2201
2202
 * Now if the course_code is given, the returned array gives info about that
2203
 * particular course, not specially the current one.
2204
 *
2205
 * @param int $id Numeric ID of the course
2206
 *
2207
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2208
 */
2209
function api_get_course_info_by_id(?int $id = 0)
2210
{
2211
    if (empty($id)) {
2212
        $course = Session::read('_course', []);
2213
2214
        return $course;
2215
    }
2216
2217
    $course = Container::getCourseRepository()->find($id);
2218
    if (empty($course)) {
2219
        return [];
2220
    }
2221
2222
    return api_format_course_array($course);
2223
}
2224
2225
/**
2226
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2227
 * to switch from 'code' to 'id' in the array.
2228
 *
2229
 * @return array
2230
 *
2231
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2232
 */
2233
function api_format_course_array(Course $course = null)
2234
{
2235
    if (empty($course)) {
2236
        return [];
2237
    }
2238
2239
    $courseData = [];
2240
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2241
2242
    // Added
2243
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2244
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2245
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2246
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2247
    $courseData['titular'] = $course->getTutorName();
2248
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2249
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2250
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2251
2252
    $courseData['visibility'] = $course->getVisibility();
2253
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2254
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2255
    $courseData['activate_legal'] = $course->getActivateLegal();
2256
    $courseData['legal'] = $course->getLegal();
2257
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2258
    $courseData['video_url'] = $course->getVideoUrl();
2259
    $courseData['sticky'] = (int) $course->isSticky();
2260
2261
    $coursePath = '/course/';
2262
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2263
2264
    // Course password
2265
    $courseData['registration_code'] = $course->getRegistrationCode();
2266
    $courseData['disk_quota'] = $course->getDiskQuota();
2267
    $courseData['course_public_url'] = $webCourseHome;
2268
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2269
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2270
2271
    $image = Display::getMdiIcon(
2272
        ObjectIcon::COURSE,
2273
        'ch-tool-icon',
2274
        null,
2275
        ICON_SIZE_BIG
2276
    );
2277
2278
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2279
    if (!empty($illustration)) {
2280
        $image = $illustration;
2281
    }
2282
2283
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2284
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2285
2286
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2287
        $courseData['duration'] = $course->getDuration();
2288
    }
2289
2290
    return $courseData;
2291
}
2292
2293
/**
2294
 * Returns a difficult to guess password.
2295
 */
2296
function api_generate_password(int $length = 8, $useRequirements = true): string
2297
{
2298
    if ($length < 2) {
2299
        $length = 2;
2300
    }
2301
2302
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2303
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2304
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2305
    $minNumbers = 2;
2306
    $length = $length - $minNumbers;
2307
    $minLowerCase = round($length / 2);
2308
    $minUpperCase = $length - $minLowerCase;
2309
    $minSpecials = 1; // Default minimum special characters
2310
2311
    $password = '';
2312
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2313
2314
    $factory = new RandomLib\Factory();
2315
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2316
2317
    if (!empty($passwordRequirements)) {
2318
        $length = $passwordRequirements['min']['length'];
2319
        $minNumbers = $passwordRequirements['min']['numeric'];
2320
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2321
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2322
        $minSpecials = $passwordRequirements['min']['specials'];
2323
2324
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2325
        // Add the rest to fill the length requirement
2326
        if ($rest > 0) {
2327
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2328
        }
2329
    }
2330
2331
    // Min digits default 2
2332
    for ($i = 0; $i < $minNumbers; $i++) {
2333
        $password .= $generator->generateInt(2, 9);
2334
    }
2335
2336
    // Min lowercase
2337
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2338
2339
    // Min uppercase
2340
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2341
2342
    // Min special characters
2343
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2344
2345
    // Shuffle the password to ensure randomness
2346
    $password = str_shuffle($password);
2347
2348
    return $password;
2349
}
2350
2351
/**
2352
 * Checks a password to see wether it is OK to use.
2353
 *
2354
 * @param string $password
2355
 *
2356
 * @return bool if the password is acceptable, false otherwise
2357
 *              Notes about what a password "OK to use" is:
2358
 *              1. The password should be at least 5 characters long.
2359
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2360
 *              3. The password should contain at least 3 letters.
2361
 *              4. It should contain at least 2 digits.
2362
 *              Settings will change if the configuration value is set: password_requirements
2363
 */
2364
function api_check_password($password)
2365
{
2366
    $passwordRequirements = Security::getPasswordRequirements();
2367
2368
    $minLength = $passwordRequirements['min']['length'];
2369
    $minNumbers = $passwordRequirements['min']['numeric'];
2370
    // Optional
2371
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2372
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2373
2374
    $minLetters = $minLowerCase + $minUpperCase;
2375
    $passwordLength = api_strlen($password);
2376
2377
    $conditions = [
2378
        'min_length' => $passwordLength >= $minLength,
2379
    ];
2380
2381
    $digits = 0;
2382
    $lowerCase = 0;
2383
    $upperCase = 0;
2384
2385
    for ($i = 0; $i < $passwordLength; $i++) {
2386
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2387
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2388
            $upperCase++;
2389
        }
2390
2391
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2392
            $lowerCase++;
2393
        }
2394
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2395
            $digits++;
2396
        }
2397
    }
2398
2399
    // Min number of digits
2400
    $conditions['min_numeric'] = $digits >= $minNumbers;
2401
2402
    if (!empty($minUpperCase)) {
2403
        // Uppercase
2404
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2405
    }
2406
2407
    if (!empty($minLowerCase)) {
2408
        // Lowercase
2409
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2410
    }
2411
2412
    // Min letters
2413
    $letters = $upperCase + $lowerCase;
2414
    $conditions['min_letters'] = $letters >= $minLetters;
2415
2416
    $isPasswordOk = true;
2417
    foreach ($conditions as $condition) {
2418
        if (false === $condition) {
2419
            $isPasswordOk = false;
2420
            break;
2421
        }
2422
    }
2423
2424
    if (false === $isPasswordOk) {
2425
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2426
        $output .= Security::getPasswordRequirementsToString($conditions);
2427
2428
        Display::addFlash(Display::return_message($output, 'warning', false));
2429
    }
2430
2431
    return $isPasswordOk;
2432
}
2433
2434
/**
2435
 * Gets the current Chamilo (not PHP/cookie) session ID.
2436
 *
2437
 * @return int O if no active session, the session ID otherwise
2438
 */
2439
function api_get_session_id()
2440
{
2441
    return (int) Session::read('sid', 0);
2442
}
2443
2444
/**
2445
 * Gets the current Chamilo (not social network) group ID.
2446
 *
2447
 * @return int O if no active session, the session ID otherwise
2448
 */
2449
function api_get_group_id()
2450
{
2451
    return Session::read('gid', 0);
2452
}
2453
2454
/**
2455
 * Gets the current or given session name.
2456
 *
2457
 * @param   int     Session ID (optional)
2458
 *
2459
 * @return string The session name, or null if not found
2460
 */
2461
function api_get_session_name($session_id = 0)
2462
{
2463
    if (empty($session_id)) {
2464
        $session_id = api_get_session_id();
2465
        if (empty($session_id)) {
2466
            return null;
2467
        }
2468
    }
2469
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2470
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2471
    $r = Database::query($s);
2472
    $c = Database::num_rows($r);
2473
    if ($c > 0) {
2474
        //technically, there can be only one, but anyway we take the first
2475
        $rec = Database::fetch_array($r);
2476
2477
        return $rec['title'];
2478
    }
2479
2480
    return null;
2481
}
2482
2483
/**
2484
 * Gets the session info by id.
2485
 *
2486
 * @param int $id Session ID
2487
 *
2488
 * @return array information of the session
2489
 */
2490
function api_get_session_info($id)
2491
{
2492
    return SessionManager::fetch($id);
2493
}
2494
2495
/**
2496
 * Gets the session visibility by session id.
2497
 *
2498
 * @param int  $session_id
2499
 * @param int  $courseId
2500
 * @param bool $ignore_visibility_for_admins
2501
 *
2502
 * @return int
2503
 *             0 = session still available,
2504
 *             SESSION_VISIBLE_READ_ONLY = 1,
2505
 *             SESSION_VISIBLE = 2,
2506
 *             SESSION_INVISIBLE = 3
2507
 */
2508
function api_get_session_visibility(
2509
    $session_id,
2510
    $courseId = null,
2511
    $ignore_visibility_for_admins = true,
2512
    $userId = 0
2513
) {
2514
    if (api_is_platform_admin()) {
2515
        if ($ignore_visibility_for_admins) {
2516
            return SESSION_AVAILABLE;
2517
        }
2518
    }
2519
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2520
2521
    $now = time();
2522
    if (empty($session_id)) {
2523
        return 0; // Means that the session is still available.
2524
    }
2525
2526
    $session_id = (int) $session_id;
2527
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2528
2529
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2530
2531
    if (Database::num_rows($result) <= 0) {
2532
        return SESSION_INVISIBLE;
2533
    }
2534
2535
    $row = Database::fetch_assoc($result);
2536
    $visibility = $row['visibility'];
2537
2538
    // I don't care the session visibility.
2539
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2540
        // Session duration per student.
2541
        if (isset($row['duration']) && !empty($row['duration'])) {
2542
            $duration = $row['duration'] * 24 * 60 * 60;
2543
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2544
2545
            // If there is a session duration but there is no previous
2546
            // access by the user, then the session is still available
2547
            if (0 == count($courseAccess)) {
2548
                return SESSION_AVAILABLE;
2549
            }
2550
2551
            $currentTime = time();
2552
            $firstAccess = isset($courseAccess['login_course_date'])
2553
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2554
                : 0;
2555
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2556
            $userDuration = isset($userDurationData['duration'])
2557
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2558
                : 0;
2559
2560
            $totalDuration = $firstAccess + $duration + $userDuration;
2561
2562
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2563
        }
2564
2565
        return SESSION_AVAILABLE;
2566
    }
2567
2568
    // If start date was set.
2569
    if (!empty($row['access_start_date'])) {
2570
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2571
    } else {
2572
        // If there's no start date, assume it's available until the end date
2573
        $visibility = SESSION_AVAILABLE;
2574
    }
2575
2576
    // If the end date was set.
2577
    if (!empty($row['access_end_date'])) {
2578
        // Only if date_start said that it was ok
2579
        if (SESSION_AVAILABLE === $visibility) {
2580
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2581
                ? SESSION_AVAILABLE // Date still available
2582
                : $row['visibility']; // Session ends
2583
        }
2584
    }
2585
2586
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2587
    $isCoach = api_is_coach($session_id, $courseId);
2588
2589
    if ($isCoach) {
2590
        // Test start date.
2591
        if (!empty($row['coach_access_start_date'])) {
2592
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2593
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2594
        }
2595
2596
        // Test end date.
2597
        if (!empty($row['coach_access_end_date'])) {
2598
            if (SESSION_AVAILABLE === $visibility) {
2599
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2600
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2601
            }
2602
        }
2603
    }
2604
2605
    return $visibility;
2606
}
2607
2608
/**
2609
 * This function returns a (star) session icon if the session is not null and
2610
 * the user is not a student.
2611
 *
2612
 * @param int $sessionId
2613
 * @param int $statusId  User status id - if 5 (student), will return empty
2614
 *
2615
 * @return string Session icon
2616
 */
2617
function api_get_session_image($sessionId, User $user)
2618
{
2619
    $sessionId = (int) $sessionId;
2620
    $image = '';
2621
    if (!$user->hasRole('ROLE_STUDENT')) {
2622
        // Check whether is not a student
2623
        if ($sessionId > 0) {
2624
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2625
                ObjectIcon::STAR,
2626
                'ch-tool-icon',
2627
                'align:absmiddle;',
2628
                ICON_SIZE_SMALL,
2629
                get_lang('Session-specific resource')
2630
            );
2631
        }
2632
    }
2633
2634
    return $image;
2635
}
2636
2637
/**
2638
 * This function add an additional condition according to the session of the course.
2639
 *
2640
 * @param int    $session_id        session id
2641
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2642
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2643
 *                                  false for strict session condition
2644
 * @param string $session_field
2645
 *
2646
 * @return string condition of the session
2647
 */
2648
function api_get_session_condition(
2649
    $session_id,
2650
    $and = true,
2651
    $with_base_content = false,
2652
    $session_field = 'session_id'
2653
) {
2654
    $session_id = (int) $session_id;
2655
2656
    if (empty($session_field)) {
2657
        $session_field = 'session_id';
2658
    }
2659
    // Condition to show resources by session
2660
    $condition_add = $and ? ' AND ' : ' WHERE ';
2661
2662
    if ($with_base_content) {
2663
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2664
    } else {
2665
        if (empty($session_id)) {
2666
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2667
        } else {
2668
            $condition_session = $condition_add." $session_field = $session_id ";
2669
        }
2670
    }
2671
2672
    return $condition_session;
2673
}
2674
2675
/**
2676
 * Returns the value of a setting from the web-adjustable admin config settings.
2677
 *
2678
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2679
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2680
 * instead of
2681
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2682
 *
2683
 * @param string $variable The variable name
2684
 *
2685
 * @return string|array
2686
 */
2687
function api_get_setting($variable, $isArray = false, $key = null)
2688
{
2689
    $settingsManager = Container::getSettingsManager();
2690
    if (empty($settingsManager)) {
2691
        return '';
2692
    }
2693
    $variable = trim($variable);
2694
2695
    switch ($variable) {
2696
        case 'server_type':
2697
            $test = ['dev', 'test'];
2698
            $environment = Container::getEnvironment();
2699
            if (in_array($environment, $test)) {
2700
                return 'test';
2701
            }
2702
2703
            return 'prod';
2704
        // deprecated settings
2705
        // no break
2706
        case 'openid_authentication':
2707
        case 'service_ppt2lp':
2708
        case 'formLogin_hide_unhide_label':
2709
            return false;
2710
            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...
2711
        case 'tool_visible_by_default_at_creation':
2712
            $values = $settingsManager->getSetting($variable);
2713
            $newResult = [];
2714
            foreach ($values as $parameter) {
2715
                $newResult[$parameter] = 'true';
2716
            }
2717
2718
            return $newResult;
2719
            break;
2720
        default:
2721
            $settingValue = $settingsManager->getSetting($variable, true);
2722
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2723
                // Check if the value is a valid JSON string
2724
                $decodedValue = json_decode($settingValue, true);
2725
2726
                // If it's a valid JSON string and the result is an array, return it
2727
                if (is_array($decodedValue)) {
2728
                    return $decodedValue;
2729
                }
2730
2731
                // If it's not an array, continue with the normal flow
2732
                // Optional: If you need to evaluate the value using eval
2733
                $strArrayValue = rtrim($settingValue, ';');
2734
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2735
                if (is_array($value)) {
2736
                    return $value;
2737
                }
2738
            }
2739
2740
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2741
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2742
                return $settingValue[$variable][$key];
2743
            }
2744
2745
            return $settingValue;
2746
            break;
2747
    }
2748
}
2749
2750
/**
2751
 * @param string $variable
2752
 * @param string $option
2753
 *
2754
 * @return bool
2755
 */
2756
function api_get_setting_in_list($variable, $option)
2757
{
2758
    $value = api_get_setting($variable);
2759
2760
    return in_array($option, $value);
2761
}
2762
2763
/**
2764
 * @param string $plugin
2765
 * @param string $variable
2766
 *
2767
 * @return string
2768
 */
2769
function api_get_plugin_setting($plugin, $variable)
2770
{
2771
    $variableName = $plugin.'_'.$variable;
2772
    //$result = api_get_setting($variableName);
2773
    $params = [
2774
        'category = ? AND subkey = ? AND variable = ?' => [
2775
            'Plugins',
2776
            $plugin,
2777
            $variableName,
2778
        ],
2779
    ];
2780
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2781
    $result = Database::select(
2782
        'selected_value',
2783
        $table,
2784
        ['where' => $params],
2785
        'one'
2786
    );
2787
    if ($result) {
2788
        $value = $result['selected_value'];
2789
        $serializedValue = @unserialize($result['selected_value'], []);
2790
        if (false !== $serializedValue) {
2791
            $value = $serializedValue;
2792
        }
2793
2794
        return $value;
2795
    }
2796
2797
    return null;
2798
    /// Old code
2799
2800
    $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...
2801
    $result = api_get_setting($variableName);
2802
2803
    if (isset($result[$plugin])) {
2804
        $value = $result[$plugin];
2805
2806
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2807
2808
        if (false !== $unserialized) {
2809
            $value = $unserialized;
2810
        }
2811
2812
        return $value;
2813
    }
2814
2815
    return null;
2816
}
2817
2818
/**
2819
 * Returns the value of a setting from the web-adjustable admin config settings.
2820
 */
2821
function api_get_settings_params($params)
2822
{
2823
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2824
2825
    return Database::select('*', $table, ['where' => $params]);
2826
}
2827
2828
/**
2829
 * @param array $params example: [id = ? => '1']
2830
 *
2831
 * @return array
2832
 */
2833
function api_get_settings_params_simple($params)
2834
{
2835
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2836
2837
    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...
2838
}
2839
2840
/**
2841
 * Returns the value of a setting from the web-adjustable admin config settings.
2842
 */
2843
function api_delete_settings_params($params)
2844
{
2845
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2846
2847
    return Database::delete($table, $params);
2848
}
2849
2850
/**
2851
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2852
 *
2853
 * @return string Escaped version of $_SERVER['PHP_SELF']
2854
 */
2855
function api_get_self()
2856
{
2857
    return htmlentities($_SERVER['PHP_SELF']);
2858
}
2859
2860
/**
2861
 * Checks whether current user is a platform administrator.
2862
 *
2863
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2864
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2865
 *
2866
 * @return bool true if the user has platform admin rights,
2867
 *              false otherwise
2868
 *
2869
 * @see usermanager::is_admin(user_id) for a user-id specific function
2870
 */
2871
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2872
{
2873
    $currentUser = api_get_current_user();
2874
2875
    if (null === $currentUser) {
2876
        return false;
2877
    }
2878
2879
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2880
2881
    if ($isAdmin) {
2882
        return true;
2883
    }
2884
2885
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2886
        return true;
2887
    }
2888
2889
    if ($allowDrh && $currentUser->hasRole('ROLE_HR')) {
2890
        return true;
2891
    }
2892
2893
    return false;
2894
}
2895
2896
/**
2897
 * Checks whether the user given as user id is in the admin table.
2898
 *
2899
 * @param int $user_id If none provided, will use current user
2900
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2901
 *
2902
 * @return bool True if the user is admin, false otherwise
2903
 */
2904
function api_is_platform_admin_by_id($user_id = null, $url = null)
2905
{
2906
    $user_id = (int) $user_id;
2907
    if (empty($user_id)) {
2908
        $user_id = api_get_user_id();
2909
    }
2910
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2911
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2912
    $res = Database::query($sql);
2913
    $is_admin = 1 === Database::num_rows($res);
2914
    if (!$is_admin || !isset($url)) {
2915
        return $is_admin;
2916
    }
2917
    // We get here only if $url is set
2918
    $url = (int) $url;
2919
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2920
    $sql = "SELECT * FROM $url_user_table
2921
            WHERE access_url_id = $url AND user_id = $user_id";
2922
    $res = Database::query($sql);
2923
2924
    return 1 === Database::num_rows($res);
2925
}
2926
2927
/**
2928
 * Checks whether current user is allowed to create courses.
2929
 *
2930
 * @return bool true if the user has course creation rights,
2931
 *              false otherwise
2932
 */
2933
function api_is_allowed_to_create_course()
2934
{
2935
    if (api_is_platform_admin()) {
2936
        return true;
2937
    }
2938
2939
    // Teachers can only create courses
2940
    if (api_is_teacher()) {
2941
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2942
            return true;
2943
        } else {
2944
            return false;
2945
        }
2946
    }
2947
2948
    return Session::read('is_allowedCreateCourse');
2949
}
2950
2951
/**
2952
 * Checks whether the current user is a course administrator.
2953
 *
2954
 * @return bool True if current user is a course administrator
2955
 */
2956
function api_is_course_admin()
2957
{
2958
    if (api_is_platform_admin()) {
2959
        return true;
2960
    }
2961
2962
    $user = api_get_current_user();
2963
    if ($user) {
2964
        if (
2965
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2966
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2967
        ) {
2968
            return true;
2969
        }
2970
    }
2971
2972
    return false;
2973
}
2974
2975
/**
2976
 * Checks whether the current user is a course coach
2977
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2978
 *
2979
 * @return bool True if current user is a course coach
2980
 */
2981
function api_is_session_general_coach()
2982
{
2983
    return Session::read('is_session_general_coach');
2984
}
2985
2986
/**
2987
 * Checks whether the current user is a course tutor
2988
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2989
 *
2990
 * @return bool True if current user is a course tutor
2991
 */
2992
function api_is_course_tutor()
2993
{
2994
    return Session::read('is_courseTutor');
2995
}
2996
2997
/**
2998
 * @param int $user_id
2999
 * @param int $courseId
3000
 * @param int $session_id
3001
 *
3002
 * @return bool
3003
 */
3004
function api_is_course_session_coach($user_id, $courseId, $session_id)
3005
{
3006
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3007
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3008
3009
    $user_id = (int) $user_id;
3010
    $session_id = (int) $session_id;
3011
    $courseId = (int) $courseId;
3012
3013
    $sql = "SELECT DISTINCT session.id
3014
            FROM $session_table
3015
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3016
            ON session.id = session_rc_ru.session_id
3017
            WHERE
3018
                session_rc_ru.user_id = '".$user_id."'  AND
3019
                session_rc_ru.c_id = '$courseId' AND
3020
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3021
                session_rc_ru.session_id = '$session_id'";
3022
    $result = Database::query($sql);
3023
3024
    return Database::num_rows($result) > 0;
3025
}
3026
3027
/**
3028
 * Checks whether the current user is a course or session coach.
3029
 */
3030
function api_is_coach(int $session_id = 0, ?int $courseId = null, bool $check_student_view = true, int $userId = 0): bool
3031
{
3032
    $userId = empty($userId) ? api_get_user_id() :  $userId;
3033
3034
    if (empty($session_id)) {
3035
        $session_id = api_get_session_id();
3036
    }
3037
3038
    // The student preview was on
3039
    if ($check_student_view && api_is_student_view_active()) {
3040
        return false;
3041
    }
3042
3043
    if (empty($courseId)) {
3044
        $courseId = api_get_course_int_id();
3045
    }
3046
3047
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3048
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3049
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3050
    $sessionIsCoach = [];
3051
3052
    if (!empty($courseId)) {
3053
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3054
                FROM $session_table s
3055
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3056
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3057
                WHERE
3058
                    session_rc_ru.c_id = '$courseId' AND
3059
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3060
                    session_rc_ru.session_id = '$session_id'";
3061
        $result = Database::query($sql);
3062
        $sessionIsCoach = Database::store_result($result);
3063
    }
3064
3065
    if (!empty($session_id)) {
3066
        $sql = "SELECT DISTINCT s.id
3067
                FROM $session_table AS s
3068
                INNER JOIN $tblSessionRelUser sru
3069
                ON s.id = sru.session_id
3070
                WHERE
3071
                    sru.user_id = $userId AND
3072
                    s.id = $session_id AND
3073
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3074
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3075
        $result = Database::query($sql);
3076
        if (!empty($sessionIsCoach)) {
3077
            $sessionIsCoach = array_merge(
3078
                $sessionIsCoach,
3079
                Database::store_result($result)
3080
            );
3081
        } else {
3082
            $sessionIsCoach = Database::store_result($result);
3083
        }
3084
    }
3085
3086
    return count($sessionIsCoach) > 0;
3087
}
3088
3089
function api_user_has_role(string $role, ?User $user = null): bool
3090
{
3091
    if (null === $user) {
3092
        $user = api_get_current_user();
3093
    }
3094
3095
    if (null === $user) {
3096
        return false;
3097
    }
3098
3099
    return $user->hasRole($role);
3100
}
3101
3102
function api_is_allowed_in_course(): bool
3103
{
3104
    if (api_is_platform_admin()) {
3105
        return true;
3106
    }
3107
3108
    $user = api_get_current_user();
3109
    if ($user instanceof User) {
3110
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3111
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3112
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3113
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3114
        ) {
3115
            return true;
3116
        }
3117
    }
3118
3119
    return false;
3120
}
3121
3122
/**
3123
 * Checks whether current user is a student boss.
3124
 */
3125
function api_is_student_boss(?User $user = null): bool
3126
{
3127
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3128
}
3129
3130
/**
3131
 * Checks whether the current user is a session administrator.
3132
 *
3133
 * @return bool True if current user is a course administrator
3134
 */
3135
function api_is_session_admin(?User $user = null)
3136
{
3137
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3138
}
3139
3140
/**
3141
 * Checks whether the current user is a human resources manager.
3142
 *
3143
 * @return bool True if current user is a human resources manager
3144
 */
3145
function api_is_drh()
3146
{
3147
    return api_user_has_role('ROLE_HR');
3148
}
3149
3150
/**
3151
 * Checks whether the current user is a student.
3152
 *
3153
 * @return bool True if current user is a human resources manager
3154
 */
3155
function api_is_student()
3156
{
3157
    return api_user_has_role('ROLE_STUDENT');
3158
}
3159
3160
/**
3161
 * Checks whether the current user has the status 'teacher'.
3162
 *
3163
 * @return bool True if current user is a human resources manager
3164
 */
3165
function api_is_teacher()
3166
{
3167
    return api_user_has_role('ROLE_TEACHER');
3168
}
3169
3170
/**
3171
 * Checks whether the current user is a invited user.
3172
 *
3173
 * @return bool
3174
 */
3175
function api_is_invitee()
3176
{
3177
    return api_user_has_role('ROLE_INVITEE');
3178
}
3179
3180
/**
3181
 * This function checks whether a session is assigned into a category.
3182
 *
3183
 * @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...
3184
 * @param string    - category name
3185
 *
3186
 * @return bool - true if is found, otherwise false
3187
 */
3188
function api_is_session_in_category($session_id, $category_name)
3189
{
3190
    $session_id = (int) $session_id;
3191
    $category_name = Database::escape_string($category_name);
3192
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3193
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3194
3195
    $sql = "SELECT 1
3196
            FROM $tbl_session
3197
            WHERE $session_id IN (
3198
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3199
                WHERE
3200
                  s.session_category_id = sc.id AND
3201
                  sc.name LIKE '%$category_name'
3202
            )";
3203
    $rs = Database::query($sql);
3204
3205
    if (Database::num_rows($rs) > 0) {
3206
        return true;
3207
    }
3208
3209
    return false;
3210
}
3211
3212
/**
3213
 * Displays options for switching between student view and course manager view.
3214
 *
3215
 * Changes in version 1.2 (Patrick Cool)
3216
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3217
 * is changed explicitly
3218
 *
3219
 * Changes in version 1.1 (Patrick Cool)
3220
 * student view now works correctly in subfolders of the document tool
3221
 * student view works correctly in the new links tool
3222
 *
3223
 * Example code for using this in your tools:
3224
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3225
 * //   display_tool_view_option($isStudentView);
3226
 * //}
3227
 * //and in later sections, use api_is_allowed_to_edit()
3228
 *
3229
 * @author Roan Embrechts
3230
 * @author Patrick Cool
3231
 * @author Julio Montoya, changes added in Chamilo
3232
 *
3233
 * @version 1.2
3234
 *
3235
 * @todo rewrite code so it is easier to understand
3236
 */
3237
function api_display_tool_view_option()
3238
{
3239
    if ('true' != api_get_setting('student_view_enabled')) {
3240
        return '';
3241
    }
3242
3243
    $sourceurl = '';
3244
    $is_framed = false;
3245
    // Exceptions apply for all multi-frames pages
3246
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3247
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3248
        return '';
3249
    }
3250
3251
    // Uncomment to remove student view link from document view page
3252
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3253
        if (empty($_GET['lp_id'])) {
3254
            return '';
3255
        }
3256
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3257
        $sourceurl = str_replace(
3258
            'lp/lp_header.php',
3259
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3260
            $sourceurl
3261
        );
3262
        //showinframes doesn't handle student view anyway...
3263
        //return '';
3264
        $is_framed = true;
3265
    }
3266
3267
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3268
    if (!$is_framed) {
3269
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3270
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3271
        } else {
3272
            $sourceurl = $_SERVER['REQUEST_URI'];
3273
        }
3274
    }
3275
3276
    $output_string = '';
3277
    if (!empty($_SESSION['studentview'])) {
3278
        if ('studentview' == $_SESSION['studentview']) {
3279
            // We have to remove the isStudentView=true from the $sourceurl
3280
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3281
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3282
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3283
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3284
        } elseif ('teacherview' == $_SESSION['studentview']) {
3285
            // Switching to teacherview
3286
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3287
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3288
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3289
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3290
        }
3291
    } else {
3292
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3293
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3294
    }
3295
    $output_string = Security::remove_XSS($output_string);
3296
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3297
3298
    return $html;
3299
}
3300
3301
/**
3302
 * Determines whether the current user is allowed to edit the current context.
3303
 *
3304
 * This includes checks for platform admin, course admin, tutor, coach,
3305
 * session coach, and optionally verifies if the user is in student view mode.
3306
 * If not in a course context, it falls back to a role-based permission system.
3307
 *
3308
 * @param bool $tutor             Allow if the user is a tutor.
3309
 * @param bool $coach             Allow if the user is a coach and setting allows it.
3310
 * @param bool $session_coach     Allow if the user is a session coach.
3311
 * @param bool $check_student_view Check if student view mode is active.
3312
 *
3313
 * @return bool True if the user is allowed to edit, false otherwise.
3314
 */
3315
function api_is_allowed_to_edit(
3316
    bool $tutor = false,
3317
    bool $coach = false,
3318
    bool $session_coach = false,
3319
    bool $check_student_view = true
3320
): bool {
3321
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3322
    $sessionId = api_get_session_id();
3323
    $studentView = api_is_student_view_active();
3324
    $isAllowed = false;
3325
3326
    // If platform admin, allow unless student view is active
3327
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3328
        if ($check_student_view && $studentView) {
3329
            $isAllowed = false;
3330
        } else {
3331
            return true;
3332
        }
3333
    }
3334
3335
    // Respect session course read-only mode from extra field
3336
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3337
        $efv = new ExtraFieldValue('course');
3338
        $lock = $efv->get_values_by_handler_and_field_variable(
3339
            api_get_course_int_id(),
3340
            'session_courses_read_only_mode'
3341
        );
3342
        if (!empty($lock['value'])) {
3343
            return false;
3344
        }
3345
    }
3346
3347
    $isCourseAdmin = api_is_course_admin();
3348
    $isCoach = api_is_coach(0, null, $check_student_view);
3349
3350
    if (!$isCourseAdmin && $tutor) {
3351
        $isCourseAdmin = api_is_course_tutor();
3352
    }
3353
3354
    $sessionVisibility = api_get_session_visibility($sessionId);
3355
3356
    if (!$isCourseAdmin && $coach) {
3357
        if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
3358
            $isCoach = false;
3359
        }
3360
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3361
            $isCourseAdmin = $isCoach;
3362
        }
3363
    }
3364
3365
    if (!$isCourseAdmin && $session_coach) {
3366
        $isCourseAdmin = $isCoach;
3367
    }
3368
3369
    // Handle student view mode
3370
    if ('true' === api_get_setting('student_view_enabled')) {
3371
        if (!empty($sessionId)) {
3372
            if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
3373
                $isCoach = false;
3374
            }
3375
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3376
                $isAllowed = $isCoach;
3377
            }
3378
3379
            if ($check_student_view) {
3380
                $isAllowed = $isAllowed && !$studentView;
3381
            }
3382
        } else {
3383
            $isAllowed = $isCourseAdmin;
3384
            if ($check_student_view) {
3385
                $isAllowed = $isCourseAdmin && !$studentView;
3386
            }
3387
        }
3388
3389
        if ($isAllowed) {
3390
            return true;
3391
        }
3392
    } else {
3393
        if ($isCourseAdmin) {
3394
            return true;
3395
        }
3396
    }
3397
3398
    // Final fallback: permission-based system (only if nothing before returned true)
3399
    $courseId = api_get_course_id();
3400
    $inCourse = !empty($courseId) && $courseId != -1;
3401
3402
    if (!$inCourse) {
3403
        $userRoles = api_get_user_roles();
3404
        $feature = api_detect_feature_context();
3405
        $permission = $feature.':edit';
3406
3407
        return api_get_permission($permission, $userRoles);
3408
    }
3409
3410
    return $isAllowed;
3411
}
3412
3413
/**
3414
 * Returns the current main feature (module) based on the current script path.
3415
 * Used to determine permissions for non-course tools.
3416
 */
3417
function api_detect_feature_context(): string
3418
{
3419
    $script = $_SERVER['SCRIPT_NAME'] ?? '';
3420
    $script = basename($script);
3421
3422
    $map = [
3423
        'user_list.php' => 'user',
3424
        'user_add.php' => 'user',
3425
        'user_edit.php' => 'user',
3426
        'session_list.php' => 'session',
3427
        'session_add.php' => 'session',
3428
        'session_edit.php' => 'session',
3429
        'skill_list.php' => 'skill',
3430
        'skill_edit.php' => 'skill',
3431
        'badge_list.php' => 'badge',
3432
        'settings.php' => 'settings',
3433
        'course_list.php' => 'course',
3434
    ];
3435
3436
    if (isset($map[$script])) {
3437
        return $map[$script];
3438
    }
3439
3440
    if (preg_match('#/main/([a-z_]+)/#i', $_SERVER['SCRIPT_NAME'], $matches)) {
3441
        return $matches[1];
3442
    }
3443
3444
    return 'unknown';
3445
}
3446
3447
/**
3448
 * Returns true if user is a course coach of at least one course in session.
3449
 *
3450
 * @param int $sessionId
3451
 *
3452
 * @return bool
3453
 */
3454
function api_is_coach_of_course_in_session($sessionId)
3455
{
3456
    if (api_is_platform_admin()) {
3457
        return true;
3458
    }
3459
3460
    $userId = api_get_user_id();
3461
    $courseList = UserManager::get_courses_list_by_session(
3462
        $userId,
3463
        $sessionId
3464
    );
3465
3466
    // Session visibility.
3467
    $visibility = api_get_session_visibility(
3468
        $sessionId,
3469
        null,
3470
        false
3471
    );
3472
3473
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3474
        // Course Coach session visibility.
3475
        $blockedCourseCount = 0;
3476
        $closedVisibilityList = [
3477
            COURSE_VISIBILITY_CLOSED,
3478
            COURSE_VISIBILITY_HIDDEN,
3479
        ];
3480
3481
        foreach ($courseList as $course) {
3482
            // Checking session visibility
3483
            $sessionCourseVisibility = api_get_session_visibility(
3484
                $sessionId,
3485
                $course['real_id']
3486
            );
3487
3488
            $courseIsVisible = !in_array(
3489
                $course['visibility'],
3490
                $closedVisibilityList
3491
            );
3492
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3493
                $blockedCourseCount++;
3494
            }
3495
        }
3496
3497
        // If all courses are blocked then no show in the list.
3498
        if ($blockedCourseCount === count($courseList)) {
3499
            $visibility = SESSION_INVISIBLE;
3500
        } else {
3501
            $visibility = SESSION_VISIBLE;
3502
        }
3503
    }
3504
3505
    switch ($visibility) {
3506
        case SESSION_VISIBLE_READ_ONLY:
3507
        case SESSION_VISIBLE:
3508
        case SESSION_AVAILABLE:
3509
            return true;
3510
            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...
3511
        case SESSION_INVISIBLE:
3512
            return false;
3513
    }
3514
3515
    return false;
3516
}
3517
3518
/**
3519
 * Checks if a student can edit contents in a session depending
3520
 * on the session visibility.
3521
 *
3522
 * @param bool $tutor Whether to check if the user has the tutor role
3523
 * @param bool $coach Whether to check if the user has the coach role
3524
 *
3525
 * @return bool true: the user has the rights to edit, false: he does not
3526
 */
3527
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3528
{
3529
    if (api_is_allowed_to_edit($tutor, $coach)) {
3530
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3531
        return true;
3532
    } else {
3533
        $sessionId = api_get_session_id();
3534
3535
        if (0 == $sessionId) {
3536
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3537
            return true;
3538
        } else {
3539
            // I'm in a session and I'm a student
3540
            // Get the session visibility
3541
            $session_visibility = api_get_session_visibility($sessionId);
3542
            // if 5 the session is still available
3543
            switch ($session_visibility) {
3544
                case SESSION_VISIBLE_READ_ONLY: // 1
3545
                    return false;
3546
                case SESSION_VISIBLE:           // 2
3547
                    return true;
3548
                case SESSION_INVISIBLE:         // 3
3549
                    return false;
3550
                case SESSION_AVAILABLE:         //5
3551
                    return true;
3552
            }
3553
        }
3554
    }
3555
3556
    return false;
3557
}
3558
3559
/**
3560
 * Current user is anon?
3561
 *
3562
 * @return bool true if this user is anonymous, false otherwise
3563
 */
3564
function api_is_anonymous()
3565
{
3566
    return !Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
3567
}
3568
3569
/**
3570
 * Displays message "You are not allowed here..." and exits the entire script.
3571
 *
3572
 * @param bool $print_headers Whether to print headers (default = false -> does not print them)
3573
 * @param string $message
3574
 * @param int $responseCode
3575
 *
3576
 * @throws Exception
3577
 */
3578
function api_not_allowed(
3579
    $print_headers = false,
3580
    $message = null,
3581
    $responseCode = 0
3582
): never {
3583
    throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode);
3584
}
3585
3586
/**
3587
 * @param string $languageIsoCode
3588
 *
3589
 * @return string
3590
 */
3591
function languageToCountryIsoCode($languageIsoCode)
3592
{
3593
    $allow = ('true' === api_get_setting('language.language_flags_by_country'));
3594
3595
    // @todo save in DB
3596
    switch ($languageIsoCode) {
3597
        case 'ar':
3598
            $country = 'ae';
3599
            break;
3600
        case 'bs':
3601
            $country = 'ba';
3602
            break;
3603
        case 'ca':
3604
            $country = 'es';
3605
            if ($allow) {
3606
                $country = 'catalan';
3607
            }
3608
            break;
3609
        case 'cs':
3610
            $country = 'cz';
3611
            break;
3612
        case 'da':
3613
            $country = 'dk';
3614
            break;
3615
        case 'el':
3616
            $country = 'ae';
3617
            break;
3618
        case 'en':
3619
            $country = 'gb';
3620
            break;
3621
        case 'eu': // Euskera
3622
            $country = 'es';
3623
            if ($allow) {
3624
                $country = 'basque';
3625
            }
3626
            break;
3627
        case 'gl': // galego
3628
            $country = 'es';
3629
            if ($allow) {
3630
                $country = 'galician';
3631
            }
3632
            break;
3633
        case 'he':
3634
            $country = 'il';
3635
            break;
3636
        case 'ja':
3637
            $country = 'jp';
3638
            break;
3639
        case 'ka':
3640
            $country = 'ge';
3641
            break;
3642
        case 'ko':
3643
            $country = 'kr';
3644
            break;
3645
        case 'ms':
3646
            $country = 'my';
3647
            break;
3648
        case 'pt-BR':
3649
            $country = 'br';
3650
            break;
3651
        case 'qu':
3652
            $country = 'pe';
3653
            break;
3654
        case 'sl':
3655
            $country = 'si';
3656
            break;
3657
        case 'sv':
3658
            $country = 'se';
3659
            break;
3660
        case 'uk': // Ukraine
3661
            $country = 'ua';
3662
            break;
3663
        case 'zh-TW':
3664
        case 'zh':
3665
            $country = 'cn';
3666
            break;
3667
        default:
3668
            $country = $languageIsoCode;
3669
            break;
3670
    }
3671
    $country = strtolower($country);
3672
3673
    return $country;
3674
}
3675
3676
/**
3677
 * Returns a list of all the languages that are made available by the admin.
3678
 *
3679
 * @return array An array with all languages. Structure of the array is
3680
 *               array['name'] = An array with the name of every language
3681
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
3682
 */
3683
function api_get_languages()
3684
{
3685
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3686
    $sql = "SELECT * FROM $table WHERE available='1'
3687
            ORDER BY original_name ASC";
3688
    $result = Database::query($sql);
3689
    $languages = [];
3690
    while ($row = Database::fetch_assoc($result)) {
3691
        $languages[$row['isocode']] = $row['original_name'];
3692
    }
3693
3694
    return $languages;
3695
}
3696
3697
/**
3698
 * Returns the id (the database id) of a language.
3699
 *
3700
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
3701
 *
3702
 * @return int id of the language
3703
 */
3704
function api_get_language_id($language)
3705
{
3706
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3707
    if (empty($language)) {
3708
        return null;
3709
    }
3710
3711
    // We check the language by iscocode
3712
    $langInfo = api_get_language_from_iso($language);
3713
    if (null !== $langInfo && !empty($langInfo->getId())) {
3714
        return $langInfo->getId();
3715
    }
3716
3717
    $language = Database::escape_string($language);
3718
    $sql = "SELECT id FROM $tbl_language
3719
            WHERE english_name = '$language' LIMIT 1";
3720
    $result = Database::query($sql);
3721
    $row = Database::fetch_array($result);
3722
3723
    return $row['id'];
3724
}
3725
3726
/**
3727
 * Get the language information by its id.
3728
 *
3729
 * @param int $languageId
3730
 *
3731
 * @throws Exception
3732
 *
3733
 * @return array
3734
 */
3735
function api_get_language_info($languageId)
3736
{
3737
    if (empty($languageId)) {
3738
        return [];
3739
    }
3740
3741
    $language = Database::getManager()->find(Language::class, $languageId);
3742
3743
    if (!$language) {
3744
        return [];
3745
    }
3746
3747
    return [
3748
        'id' => $language->getId(),
3749
        'original_name' => $language->getOriginalName(),
3750
        'english_name' => $language->getEnglishName(),
3751
        'isocode' => $language->getIsocode(),
3752
        'available' => $language->getAvailable(),
3753
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
3754
    ];
3755
}
3756
3757
/**
3758
 * @param string $code
3759
 *
3760
 * @return Language
3761
 */
3762
function api_get_language_from_iso($code)
3763
{
3764
    $em = Database::getManager();
3765
3766
    return $em->getRepository(Language::class)->findOneBy(['isocode' => $code]);
3767
}
3768
3769
/**
3770
 * Shortcut to ThemeHelper::getVisualTheme()
3771
 */
3772
function api_get_visual_theme(): string
3773
{
3774
    $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

3774
    /** @scrutinizer ignore-call */ 
3775
    $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...
3775
3776
    return $themeHelper->getVisualTheme();
3777
}
3778
3779
/**
3780
 * Returns a list of CSS themes currently available in the CSS folder
3781
 * The folder must have a default.css file.
3782
 *
3783
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
3784
 *
3785
 * @return array list of themes directories from the css folder
3786
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
3787
 */
3788
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
3789
{
3790
    // This configuration value is set by the vchamilo plugin
3791
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
3792
3793
    $readCssFolder = function ($dir) use ($virtualTheme) {
3794
        $finder = new Finder();
3795
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
3796
        $list = [];
3797
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
3798
        foreach ($themes as $theme) {
3799
            $folder = $theme->getFilename();
3800
            // A theme folder is consider if there's a default.css file
3801
            if (!file_exists($theme->getPathname().'/default.css')) {
3802
                continue;
3803
            }
3804
            $name = ucwords(str_replace('_', ' ', $folder));
3805
            if ($folder == $virtualTheme) {
3806
                continue;
3807
            }
3808
            $list[$folder] = $name;
3809
        }
3810
3811
        return $list;
3812
    };
3813
3814
    $dir = Container::getProjectDir().'var/themes/';
3815
    $list = $readCssFolder($dir);
3816
3817
    if (!empty($virtualTheme)) {
3818
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
3819
        if ($getOnlyThemeFromVirtualInstance) {
3820
            return $newList;
3821
        }
3822
        $list = $list + $newList;
3823
        asort($list);
3824
    }
3825
3826
    return $list;
3827
}
3828
3829
/**
3830
 * Find the largest sort value in a given user_course_category
3831
 * This function is used when we are moving a course to a different category
3832
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
3833
 *
3834
 * @param int $courseCategoryId the id of the user_course_category
3835
 * @param int $userId
3836
 *
3837
 * @return int the value of the highest sort of the user_course_category
3838
 */
3839
function api_max_sort_value($courseCategoryId, $userId)
3840
{
3841
    $user = api_get_user_entity($userId);
3842
    $userCourseCategory = Database::getManager()->getRepository(UserCourseCategory::class)->find($courseCategoryId);
3843
3844
    return null === $user ? 0 : $user->getMaxSortValue($userCourseCategory);
3845
}
3846
3847
/**
3848
 * Transforms a number of seconds in hh:mm:ss format.
3849
 *
3850
 * @author Julian Prud'homme
3851
 *
3852
 * @param int    $seconds      number of seconds
3853
 * @param string $space
3854
 * @param bool   $showSeconds
3855
 * @param bool   $roundMinutes
3856
 *
3857
 * @return string the formatted time
3858
 */
3859
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
3860
{
3861
    // $seconds = -1 means that we have wrong data in the db.
3862
    if (-1 == $seconds) {
3863
        return
3864
            get_lang('Unknown').
3865
            Display::getMdiIcon(
3866
                ActionIcon::INFORMATION,
3867
                'ch-tool-icon',
3868
                'align: absmiddle; hspace: 3px',
3869
                ICON_SIZE_SMALL,
3870
                get_lang('The datas about this user were registered when the calculation of time spent on the platform wasn\'t possible.')
3871
            );
3872
    }
3873
3874
    // How many hours ?
3875
    $hours = floor($seconds / 3600);
3876
3877
    // How many minutes ?
3878
    $min = floor(($seconds - ($hours * 3600)) / 60);
3879
3880
    if ($roundMinutes) {
3881
        if ($min >= 45) {
3882
            $min = 45;
3883
        }
3884
3885
        if ($min >= 30 && $min <= 44) {
3886
            $min = 30;
3887
        }
3888
3889
        if ($min >= 15 && $min <= 29) {
3890
            $min = 15;
3891
        }
3892
3893
        if ($min >= 0 && $min <= 14) {
3894
            $min = 0;
3895
        }
3896
    }
3897
3898
    // How many seconds
3899
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
3900
3901
    if ($hours < 10) {
3902
        $hours = "0$hours";
3903
    }
3904
3905
    if ($sec < 10) {
3906
        $sec = "0$sec";
3907
    }
3908
3909
    if ($min < 10) {
3910
        $min = "0$min";
3911
    }
3912
3913
    $seconds = '';
3914
    if ($showSeconds) {
3915
        $seconds = $space.$sec;
3916
    }
3917
3918
    return $hours.$space.$min.$seconds;
3919
}
3920
3921
/**
3922
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3923
 * The return value is based on the platform administrator's setting
3924
 * "Administration > Configuration settings > Security > Permissions for new directories".
3925
 *
3926
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
3927
 */
3928
function api_get_permissions_for_new_directories()
3929
{
3930
    static $permissions;
3931
    if (!isset($permissions)) {
3932
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
3933
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
3934
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
3935
    }
3936
3937
    return $permissions;
3938
}
3939
3940
/**
3941
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3942
 * The return value is based on the platform administrator's setting
3943
 * "Administration > Configuration settings > Security > Permissions for new files".
3944
 *
3945
 * @return int returns the permissions in the format
3946
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
3947
 */
3948
function api_get_permissions_for_new_files()
3949
{
3950
    static $permissions;
3951
    if (!isset($permissions)) {
3952
        $permissions = trim(api_get_setting('permissions_for_new_files'));
3953
        // The default value 0666 is according to that in the platform
3954
        // administration panel after fresh system installation.
3955
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
3956
    }
3957
3958
    return $permissions;
3959
}
3960
3961
/**
3962
 * Deletes a file, or a folder and its contents.
3963
 *
3964
 * @author      Aidan Lister <[email protected]>
3965
 *
3966
 * @version     1.0.3
3967
 *
3968
 * @param string $dirname Directory to delete
3969
 * @param       bool     Deletes only the content or not
3970
 * @param bool $strict if one folder/file fails stop the loop
3971
 *
3972
 * @return bool Returns TRUE on success, FALSE on failure
3973
 *
3974
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
3975
 *
3976
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
3977
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
3978
 */
3979
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
3980
{
3981
    $res = true;
3982
    // A sanity check.
3983
    if (!file_exists($dirname)) {
3984
        return false;
3985
    }
3986
    $php_errormsg = '';
3987
    // Simple delete for a file.
3988
    if (is_file($dirname) || is_link($dirname)) {
3989
        $res = unlink($dirname);
3990
        if (false === $res) {
3991
            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);
3992
        }
3993
3994
        return $res;
3995
    }
3996
3997
    // Loop through the folder.
3998
    $dir = dir($dirname);
3999
    // A sanity check.
4000
    $is_object_dir = is_object($dir);
4001
    if ($is_object_dir) {
4002
        while (false !== $entry = $dir->read()) {
4003
            // Skip pointers.
4004
            if ('.' == $entry || '..' == $entry) {
4005
                continue;
4006
            }
4007
4008
            // Recurse.
4009
            if ($strict) {
4010
                $result = rmdirr("$dirname/$entry");
4011
                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...
4012
                    $res = false;
4013
                    break;
4014
                }
4015
            } else {
4016
                rmdirr("$dirname/$entry");
4017
            }
4018
        }
4019
    }
4020
4021
    // Clean up.
4022
    if ($is_object_dir) {
4023
        $dir->close();
4024
    }
4025
4026
    if (false == $delete_only_content_in_folder) {
4027
        $res = rmdir($dirname);
4028
        if (false === $res) {
4029
            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);
4030
        }
4031
    }
4032
4033
    return $res;
4034
}
4035
4036
// TODO: This function is to be simplified. File access modes to be implemented.
4037
/**
4038
 * function adapted from a php.net comment
4039
 * copy recursively a folder.
4040
 *
4041
 * @param the source folder
4042
 * @param the dest folder
4043
 * @param an array of excluded file_name (without extension)
4044
 * @param copied_files the returned array of copied files
4045
 * @param string $source
4046
 * @param string $dest
4047
 */
4048
function copyr($source, $dest, $exclude = [], $copied_files = [])
4049
{
4050
    if (empty($dest)) {
4051
        return false;
4052
    }
4053
    // Simple copy for a file
4054
    if (is_file($source)) {
4055
        $path_info = pathinfo($source);
4056
        if (!in_array($path_info['filename'], $exclude)) {
4057
            copy($source, $dest);
4058
        }
4059
4060
        return true;
4061
    } elseif (!is_dir($source)) {
4062
        //then source is not a dir nor a file, return
4063
        return false;
4064
    }
4065
4066
    // Make destination directory.
4067
    if (!is_dir($dest)) {
4068
        mkdir($dest, api_get_permissions_for_new_directories());
4069
    }
4070
4071
    // Loop through the folder.
4072
    $dir = dir($source);
4073
    while (false !== $entry = $dir->read()) {
4074
        // Skip pointers
4075
        if ('.' == $entry || '..' == $entry) {
4076
            continue;
4077
        }
4078
4079
        // Deep copy directories.
4080
        if ($dest !== "$source/$entry") {
4081
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
4082
        }
4083
    }
4084
    // Clean up.
4085
    $dir->close();
4086
4087
    return true;
4088
}
4089
4090
/**
4091
 * @todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
4092
 * Documentation header to be added here.
4093
 *
4094
 * @param string $pathname
4095
 * @param string $base_path_document
4096
 * @param int    $session_id
4097
 *
4098
 * @return mixed True if directory already exists, false if a file already exists at
4099
 *               the destination and null if everything goes according to plan
4100
 */
4101
function copy_folder_course_session(
4102
    $pathname,
4103
    $base_path_document,
4104
    $session_id,
4105
    $course_info,
4106
    $document,
4107
    $source_course_id,
4108
    array $originalFolderNameList = [],
4109
    string $originalBaseName = ''
4110
) {
4111
    $table = Database::get_course_table(TABLE_DOCUMENT);
4112
    $session_id = intval($session_id);
4113
    $source_course_id = intval($source_course_id);
4114
4115
    // Check whether directory already exists.
4116
    if (empty($pathname) || is_dir($pathname)) {
4117
        return true;
4118
    }
4119
4120
    // Ensure that a file with the same name does not already exist.
4121
    if (is_file($pathname)) {
4122
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
4123
4124
        return false;
4125
    }
4126
4127
    $baseNoDocument = str_replace('document', '', $originalBaseName);
4128
    $folderTitles = explode('/', $baseNoDocument);
4129
    $folderTitles = array_filter($folderTitles);
4130
4131
    $table = Database::get_course_table(TABLE_DOCUMENT);
4132
    $session_id = (int) $session_id;
4133
    $source_course_id = (int) $source_course_id;
4134
4135
    $course_id = $course_info['real_id'];
4136
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
4137
    $new_pathname = $base_path_document;
4138
    $path = '';
4139
4140
    foreach ($folders as $index => $folder) {
4141
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
4142
        $path .= DIRECTORY_SEPARATOR.$folder;
4143
4144
        if (!file_exists($new_pathname)) {
4145
            $path = Database::escape_string($path);
4146
4147
            $sql = "SELECT * FROM $table
4148
                    WHERE
4149
                        c_id = $source_course_id AND
4150
                        path = '$path' AND
4151
                        filetype = 'folder' AND
4152
                        session_id = '$session_id'";
4153
            $rs1 = Database::query($sql);
4154
            $num_rows = Database::num_rows($rs1);
4155
4156
            if (0 == $num_rows) {
4157
                mkdir($new_pathname, api_get_permissions_for_new_directories());
4158
                $title = basename($new_pathname);
4159
4160
                if (isset($folderTitles[$index + 1])) {
4161
                    $checkPath = $folderTitles[$index +1];
4162
4163
                    if (isset($originalFolderNameList[$checkPath])) {
4164
                        $title = $originalFolderNameList[$checkPath];
4165
                    }
4166
                }
4167
4168
                // Insert new folder with destination session_id.
4169
                $params = [
4170
                    'c_id' => $course_id,
4171
                    'path' => $path,
4172
                    'comment' => $document->comment,
4173
                    'title' => $title,
4174
                    'filetype' => 'folder',
4175
                    'size' => '0',
4176
                    'session_id' => $session_id,
4177
                ];
4178
                Database::insert($table, $params);
4179
            }
4180
        }
4181
    } // en foreach
4182
}
4183
4184
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
4185
/**
4186
 * @param string $path
4187
 */
4188
function api_chmod_R($path, $filemode)
4189
{
4190
    if (!is_dir($path)) {
4191
        return chmod($path, $filemode);
4192
    }
4193
4194
    $handler = opendir($path);
4195
    while ($file = readdir($handler)) {
4196
        if ('.' != $file && '..' != $file) {
4197
            $fullpath = "$path/$file";
4198
            if (!is_dir($fullpath)) {
4199
                if (!chmod($fullpath, $filemode)) {
4200
                    return false;
4201
                }
4202
            } else {
4203
                if (!api_chmod_R($fullpath, $filemode)) {
4204
                    return false;
4205
                }
4206
            }
4207
        }
4208
    }
4209
4210
    closedir($handler);
4211
4212
    return chmod($path, $filemode);
4213
}
4214
4215
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
4216
/**
4217
 * Parse info file format. (e.g: file.info).
4218
 *
4219
 * Files should use an ini-like format to specify values.
4220
 * White-space generally doesn't matter, except inside values.
4221
 * e.g.
4222
 *
4223
 * @verbatim
4224
 *   key = value
4225
 *   key = "value"
4226
 *   key = 'value'
4227
 *   key = "multi-line
4228
 *
4229
 *   value"
4230
 *   key = 'multi-line
4231
 *
4232
 *   value'
4233
 *   key
4234
 *   =
4235
 *   'value'
4236
 * @endverbatim
4237
 *
4238
 * Arrays are created using a GET-like syntax:
4239
 *
4240
 * @verbatim
4241
 *   key[] = "numeric array"
4242
 *   key[index] = "associative array"
4243
 *   key[index][] = "nested numeric array"
4244
 *   key[index][index] = "nested associative array"
4245
 * @endverbatim
4246
 *
4247
 * PHP constants are substituted in, but only when used as the entire value:
4248
 *
4249
 * Comments should start with a semi-colon at the beginning of a line.
4250
 *
4251
 * This function is NOT for placing arbitrary module-specific settings. Use
4252
 * variable_get() and variable_set() for that.
4253
 *
4254
 * Information stored in the module.info file:
4255
 * - name: The real name of the module for display purposes.
4256
 * - description: A brief description of the module.
4257
 * - dependencies: An array of shortnames of other modules this module depends on.
4258
 * - package: The name of the package of modules this module belongs to.
4259
 *
4260
 * Example of .info file:
4261
 * <code>
4262
 * @verbatim
4263
 *   name = Forum
4264
 *   description = Enables threaded discussions about general topics.
4265
 *   dependencies[] = taxonomy
4266
 *   dependencies[] = comment
4267
 *   package = Core - optional
4268
 *   version = VERSION
4269
 * @endverbatim
4270
 * </code>
4271
 *
4272
 * @param string $filename
4273
 *                         The file we are parsing. Accepts file with relative or absolute path.
4274
 *
4275
 * @return
4276
 *   The info array
4277
 */
4278
function api_parse_info_file($filename)
4279
{
4280
    $info = [];
4281
4282
    if (!file_exists($filename)) {
4283
        return $info;
4284
    }
4285
4286
    $data = file_get_contents($filename);
4287
    if (preg_match_all('
4288
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
4289
        ((?:
4290
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
4291
          \[[^\[\]]*\]                  # unless they are balanced and not nested
4292
        )+?)
4293
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
4294
        (?:
4295
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
4296
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
4297
          ([^\r\n]*?)                   # Non-quoted string
4298
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
4299
        @msx', $data, $matches, PREG_SET_ORDER)) {
4300
        $key = $value1 = $value2 = $value3 = '';
4301
        foreach ($matches as $match) {
4302
            // Fetch the key and value string.
4303
            $i = 0;
4304
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
4305
                $$var = isset($match[++$i]) ? $match[$i] : '';
4306
            }
4307
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
4308
4309
            // Parse array syntax.
4310
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
4311
            $last = array_pop($keys);
4312
            $parent = &$info;
4313
4314
            // Create nested arrays.
4315
            foreach ($keys as $key) {
4316
                if ('' == $key) {
4317
                    $key = count($parent);
4318
                }
4319
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
4320
                    $parent[$key] = [];
4321
                }
4322
                $parent = &$parent[$key];
4323
            }
4324
4325
            // Handle PHP constants.
4326
            if (defined($value)) {
4327
                $value = constant($value);
4328
            }
4329
4330
            // Insert actual value.
4331
            if ('' == $last) {
4332
                $last = count($parent);
4333
            }
4334
            $parent[$last] = $value;
4335
        }
4336
    }
4337
4338
    return $info;
4339
}
4340
4341
/**
4342
 * Gets Chamilo version from the configuration files.
4343
 *
4344
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
4345
 */
4346
function api_get_version()
4347
{
4348
    return (string) api_get_configuration_value('system_version');
4349
}
4350
4351
/**
4352
 * Gets the software name (the name/brand of the Chamilo-based customized system).
4353
 *
4354
 * @return string
4355
 */
4356
function api_get_software_name()
4357
{
4358
    $name = api_get_env_variable('SOFTWARE_NAME', 'Chamilo');
4359
    return $name;
4360
}
4361
4362
function api_get_status_list()
4363
{
4364
    $list = [];
4365
    // Table of status
4366
    $list[COURSEMANAGER] = 'teacher'; // 1
4367
    $list[SESSIONADMIN] = 'session_admin'; // 3
4368
    $list[DRH] = 'drh'; // 4
4369
    $list[STUDENT] = 'user'; // 5
4370
    $list[ANONYMOUS] = 'anonymous'; // 6
4371
    $list[INVITEE] = 'invited'; // 20
4372
4373
    return $list;
4374
}
4375
4376
/**
4377
 * Checks whether status given in parameter exists in the platform.
4378
 *
4379
 * @param mixed the status (can be either int either string)
4380
 *
4381
 * @return bool if the status exists, else returns false
4382
 */
4383
function api_status_exists($status_asked)
4384
{
4385
    $list = api_get_status_list();
4386
4387
    return in_array($status_asked, $list) ? true : isset($list[$status_asked]);
4388
}
4389
4390
/**
4391
 * Checks whether status given in parameter exists in the platform. The function
4392
 * returns the status ID or false if it does not exist, but given the fact there
4393
 * is no "0" status, the return value can be checked against
4394
 * if(api_status_key()) to know if it exists.
4395
 *
4396
 * @param   mixed   The status (can be either int or string)
4397
 *
4398
 * @return mixed Status ID if exists, false otherwise
4399
 */
4400
function api_status_key($status)
4401
{
4402
    $list = api_get_status_list();
4403
4404
    return isset($list[$status]) ? $status : array_search($status, $list);
4405
}
4406
4407
/**
4408
 * Gets the status langvars list.
4409
 *
4410
 * @return string[] the list of status with their translations
4411
 */
4412
function api_get_status_langvars()
4413
{
4414
    return [
4415
        COURSEMANAGER => get_lang('Teacher'),
4416
        SESSIONADMIN => get_lang('Sessions administrator'),
4417
        DRH => get_lang('Human Resources Manager'),
4418
        STUDENT => get_lang('Learner'),
4419
        ANONYMOUS => get_lang('Anonymous'),
4420
        STUDENT_BOSS => get_lang('Student boss'),
4421
        INVITEE => get_lang('Invited'),
4422
    ];
4423
}
4424
4425
/**
4426
 * The function that retrieves all the possible settings for a certain config setting.
4427
 *
4428
 * @author Patrick Cool <[email protected]>, Ghent University
4429
 */
4430
function api_get_settings_options($var)
4431
{
4432
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4433
    $var = Database::escape_string($var);
4434
    $sql = "SELECT * FROM $table_settings_options
4435
            WHERE variable = '$var'
4436
            ORDER BY id";
4437
    $result = Database::query($sql);
4438
    $settings_options_array = [];
4439
    while ($row = Database::fetch_assoc($result)) {
4440
        $settings_options_array[] = $row;
4441
    }
4442
4443
    return $settings_options_array;
4444
}
4445
4446
/**
4447
 * @param array $params
4448
 */
4449
function api_set_setting_option($params)
4450
{
4451
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4452
    if (empty($params['id'])) {
4453
        Database::insert($table, $params);
4454
    } else {
4455
        Database::update($table, $params, ['id = ? ' => $params['id']]);
4456
    }
4457
}
4458
4459
/**
4460
 * @param array $params
4461
 */
4462
function api_set_setting_simple($params)
4463
{
4464
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4465
    $url_id = api_get_current_access_url_id();
4466
4467
    if (empty($params['id'])) {
4468
        $params['access_url'] = $url_id;
4469
        Database::insert($table, $params);
4470
    } else {
4471
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
4472
    }
4473
}
4474
4475
/**
4476
 * @param int $id
4477
 */
4478
function api_delete_setting_option($id)
4479
{
4480
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4481
    if (!empty($id)) {
4482
        Database::delete($table, ['id = ? ' => $id]);
4483
    }
4484
}
4485
4486
/**
4487
 * Sets a platform configuration setting to a given value.
4488
 *
4489
 * @param string    The variable we want to update
4490
 * @param string    The value we want to record
4491
 * @param string    The sub-variable if any (in most cases, this will remain null)
4492
 * @param string    The category if any (in most cases, this will remain null)
4493
 * @param int       The access_url for which this parameter is valid
4494
 * @param string $cat
4495
 *
4496
 * @return bool|null
4497
 */
4498
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
4499
{
4500
    if (empty($var)) {
4501
        return false;
4502
    }
4503
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
4504
    $var = Database::escape_string($var);
4505
    $value = Database::escape_string($value);
4506
    $access_url = (int) $access_url;
4507
    if (empty($access_url)) {
4508
        $access_url = 1;
4509
    }
4510
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
4511
    if (!empty($subvar)) {
4512
        $subvar = Database::escape_string($subvar);
4513
        $select .= " AND subkey = '$subvar'";
4514
    }
4515
    if (!empty($cat)) {
4516
        $cat = Database::escape_string($cat);
4517
        $select .= " AND category = '$cat'";
4518
    }
4519
    if ($access_url > 1) {
4520
        $select .= " AND access_url = $access_url";
4521
    } else {
4522
        $select .= " AND access_url = 1 ";
4523
    }
4524
4525
    $res = Database::query($select);
4526
    if (Database::num_rows($res) > 0) {
4527
        // Found item for this access_url.
4528
        $row = Database::fetch_array($res);
4529
        $sql = "UPDATE $t_settings SET selected_value = '$value'
4530
                WHERE id = ".$row['id'];
4531
        Database::query($sql);
4532
    } else {
4533
        // Item not found for this access_url, we have to check if it exist with access_url = 1
4534
        $select = "SELECT * FROM $t_settings
4535
                   WHERE variable = '$var' AND access_url = 1 ";
4536
        // Just in case
4537
        if (1 == $access_url) {
4538
            if (!empty($subvar)) {
4539
                $select .= " AND subkey = '$subvar'";
4540
            }
4541
            if (!empty($cat)) {
4542
                $select .= " AND category = '$cat'";
4543
            }
4544
            $res = Database::query($select);
4545
            if (Database::num_rows($res) > 0) {
4546
                // We have a setting for access_url 1, but none for the current one, so create one.
4547
                $row = Database::fetch_array($res);
4548
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
4549
                        VALUES
4550
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4551
                    "'".$row['type']."','".$row['category']."',".
4552
                    "'$value','".$row['title']."',".
4553
                    "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4554
                    "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
4555
                Database::query($insert);
4556
            } else {
4557
                // Such a setting does not exist.
4558
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
4559
            }
4560
        } else {
4561
            // Other access url.
4562
            if (!empty($subvar)) {
4563
                $select .= " AND subkey = '$subvar'";
4564
            }
4565
            if (!empty($cat)) {
4566
                $select .= " AND category = '$cat'";
4567
            }
4568
            $res = Database::query($select);
4569
4570
            if (Database::num_rows($res) > 0) {
4571
                // We have a setting for access_url 1, but none for the current one, so create one.
4572
                $row = Database::fetch_array($res);
4573
                if (1 == $row['access_url_changeable']) {
4574
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
4575
                            ('".$row['variable']."',".
4576
                        (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4577
                        "'".$row['type']."','".$row['category']."',".
4578
                        "'$value','".$row['title']."',".
4579
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
4580
                        (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4581
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
4582
                    Database::query($insert);
4583
                }
4584
            } else { // Such a setting does not exist.
4585
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
4586
            }
4587
        }
4588
    }
4589
}
4590
4591
/**
4592
 * Sets a whole category of settings to one specific value.
4593
 *
4594
 * @param string    Category
4595
 * @param string    Value
4596
 * @param int       Access URL. Optional. Defaults to 1
4597
 * @param array     Optional array of filters on field type
4598
 * @param string $category
4599
 * @param string $value
4600
 *
4601
 * @return bool
4602
 */
4603
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
4604
{
4605
    if (empty($category)) {
4606
        return false;
4607
    }
4608
    $category = Database::escape_string($category);
4609
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS);
4610
    $access_url = (int) $access_url;
4611
    if (empty($access_url)) {
4612
        $access_url = 1;
4613
    }
4614
    if (isset($value)) {
4615
        $value = Database::escape_string($value);
4616
        $sql = "UPDATE $t_s SET selected_value = '$value'
4617
                WHERE category = '$category' AND access_url = $access_url";
4618
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4619
            $sql .= " AND ( ";
4620
            $i = 0;
4621
            foreach ($fieldtype as $type) {
4622
                if ($i > 0) {
4623
                    $sql .= ' OR ';
4624
                }
4625
                $type = Database::escape_string($type);
4626
                $sql .= " type='".$type."' ";
4627
                $i++;
4628
            }
4629
            $sql .= ")";
4630
        }
4631
        $res = Database::query($sql);
4632
4633
        return false !== $res;
4634
    } else {
4635
        $sql = "UPDATE $t_s SET selected_value = NULL
4636
                WHERE category = '$category' AND access_url = $access_url";
4637
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4638
            $sql .= " AND ( ";
4639
            $i = 0;
4640
            foreach ($fieldtype as $type) {
4641
                if ($i > 0) {
4642
                    $sql .= ' OR ';
4643
                }
4644
                $type = Database::escape_string($type);
4645
                $sql .= " type='".$type."' ";
4646
                $i++;
4647
            }
4648
            $sql .= ")";
4649
        }
4650
        $res = Database::query($sql);
4651
4652
        return false !== $res;
4653
    }
4654
}
4655
4656
/**
4657
 * Gets all available access urls in an array (as in the database).
4658
 *
4659
 * @return array An array of database records
4660
 */
4661
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
4662
{
4663
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4664
    $from = (int) $from;
4665
    $to = (int) $to;
4666
    $order = Database::escape_string($order);
4667
    $direction = Database::escape_string($direction);
4668
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
4669
    $sql = "SELECT id, url, description, active, created_by, tms
4670
            FROM $table
4671
            ORDER BY `$order` $direction
4672
            LIMIT $to OFFSET $from";
4673
    $res = Database::query($sql);
4674
4675
    return Database::store_result($res);
4676
}
4677
4678
/**
4679
 * Gets the access url info in an array.
4680
 *
4681
 * @param int  $id            Id of the access url
4682
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
4683
 *
4684
 * @return array All the info (url, description, active, created_by, tms)
4685
 *               from the access_url table
4686
 *
4687
 * @author Julio Montoya
4688
 */
4689
function api_get_access_url($id, $returnDefault = true)
4690
{
4691
    static $staticResult;
4692
    $id = (int) $id;
4693
4694
    if (isset($staticResult[$id])) {
4695
        $result = $staticResult[$id];
4696
    } else {
4697
        // Calling the Database:: library dont work this is handmade.
4698
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4699
        $sql = "SELECT url, description, active, created_by, tms
4700
                FROM $table_access_url WHERE id = '$id' ";
4701
        $res = Database::query($sql);
4702
        $result = @Database::fetch_array($res);
4703
        $staticResult[$id] = $result;
4704
    }
4705
4706
    // If the result url is 'http://localhost/' (the default) and the root_web
4707
    // (=current url) is different, and the $id is = 1 (which might mean
4708
    // api_get_current_access_url_id() returned 1 by default), then return the
4709
    // root_web setting instead of the current URL
4710
    // This is provided as an option to avoid breaking the storage of URL-specific
4711
    // homepages in home/localhost/
4712
    if (1 === $id && false === $returnDefault) {
4713
        $currentUrl = api_get_current_access_url_id();
4714
        // only do this if we are on the main URL (=1), otherwise we could get
4715
        // information on another URL instead of the one asked as parameter
4716
        if (1 === $currentUrl) {
4717
            $rootWeb = api_get_path(WEB_PATH);
4718
            $default = AccessUrl::DEFAULT_ACCESS_URL;
4719
            if ($result['url'] === $default && $rootWeb != $default) {
4720
                $result['url'] = $rootWeb;
4721
            }
4722
        }
4723
    }
4724
4725
    return $result;
4726
}
4727
4728
/**
4729
 * Gets all the current settings for a specific access url.
4730
 *
4731
 * @param string    The category, if any, that we want to get
4732
 * @param string    Whether we want a simple list (display a category) or
4733
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
4734
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
4735
 *
4736
 * @return array Array of database results for the current settings of the current access URL
4737
 */
4738
function &api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
4739
{
4740
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4741
    $access_url = (int) $access_url;
4742
    $where_condition = '';
4743
    if (1 == $url_changeable) {
4744
        $where_condition = " AND access_url_changeable= '1' ";
4745
    }
4746
    if (empty($access_url) || -1 == $access_url) {
4747
        $access_url = 1;
4748
    }
4749
    $sql = "SELECT * FROM $table
4750
            WHERE access_url = $access_url  $where_condition ";
4751
4752
    if (!empty($cat)) {
4753
        $cat = Database::escape_string($cat);
4754
        $sql .= " AND category='$cat' ";
4755
    }
4756
    if ('group' == $ordering) {
4757
        $sql .= " ORDER BY id ASC";
4758
    } else {
4759
        $sql .= " ORDER BY 1,2 ASC";
4760
    }
4761
    $result = Database::query($sql);
4762
    if (null === $result) {
4763
        $result = [];
4764
        return $result;
4765
    }
4766
    $result = Database::store_result($result, 'ASSOC');
4767
4768
    return $result;
4769
}
4770
4771
/**
4772
 * @param string $value       The value we want to record
4773
 * @param string $variable    The variable name we want to insert
4774
 * @param string $subKey      The subkey for the variable we want to insert
4775
 * @param string $type        The type for the variable we want to insert
4776
 * @param string $category    The category for the variable we want to insert
4777
 * @param string $title       The title
4778
 * @param string $comment     The comment
4779
 * @param string $scope       The scope
4780
 * @param string $subKeyText  The subkey text
4781
 * @param int    $accessUrlId The access_url for which this parameter is valid
4782
 * @param int    $visibility  The changeability of this setting for non-master urls
4783
 *
4784
 * @return int The setting ID
4785
 */
4786
function api_add_setting(
4787
    $value,
4788
    $variable,
4789
    $subKey = '',
4790
    $type = 'textfield',
4791
    $category = '',
4792
    $title = '',
4793
    $comment = '',
4794
    $scope = '',
4795
    $subKeyText = '',
4796
    $accessUrlId = 1,
4797
    $visibility = 0
4798
) {
4799
    $em = Database::getManager();
4800
4801
    $settingRepo = $em->getRepository(SettingsCurrent::class);
4802
    $accessUrlId = (int) $accessUrlId ?: 1;
4803
4804
    if (is_array($value)) {
4805
        $value = serialize($value);
4806
    } else {
4807
        $value = trim($value);
4808
    }
4809
4810
    $criteria = ['variable' => $variable, 'url' => $accessUrlId];
4811
4812
    if (!empty($subKey)) {
4813
        $criteria['subkey'] = $subKey;
4814
    }
4815
4816
    // Check if this variable doesn't exist already
4817
    /** @var SettingsCurrent $setting */
4818
    $setting = $settingRepo->findOneBy($criteria);
4819
4820
    if ($setting) {
0 ignored issues
show
introduced by
$setting is of type Chamilo\CoreBundle\Entity\SettingsCurrent, thus it always evaluated to true.
Loading history...
4821
        $setting->setSelectedValue($value);
4822
4823
        $em->persist($setting);
4824
        $em->flush();
4825
4826
        return $setting->getId();
4827
    }
4828
4829
    // Item not found for this access_url, we have to check if the whole thing is missing
4830
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
4831
    $setting = new SettingsCurrent();
4832
    $url = api_get_url_entity();
4833
4834
    $setting
4835
        ->setVariable($variable)
4836
        ->setSelectedValue($value)
4837
        ->setType($type)
4838
        ->setCategory($category)
4839
        ->setSubkey($subKey)
4840
        ->setTitle($title)
4841
        ->setComment($comment)
4842
        ->setScope($scope)
4843
        ->setSubkeytext($subKeyText)
4844
        ->setUrl(api_get_url_entity())
4845
        ->setAccessUrlChangeable($visibility);
4846
4847
    $em->persist($setting);
4848
    $em->flush();
4849
4850
    return $setting->getId();
4851
}
4852
4853
/**
4854
 * Checks wether a user can or can't view the contents of a course.
4855
 *
4856
 * @deprecated use CourseManager::is_user_subscribed_in_course
4857
 *
4858
 * @param int $userid User id or NULL to get it from $_SESSION
4859
 * @param int $cid    course id to check whether the user is allowed
4860
 *
4861
 * @return bool
4862
 */
4863
function api_is_course_visible_for_user($userid = null, $cid = null)
4864
{
4865
    if (null === $userid) {
4866
        $userid = api_get_user_id();
4867
    }
4868
    if (empty($userid) || strval(intval($userid)) != $userid) {
4869
        if (api_is_anonymous()) {
4870
            $userid = api_get_anonymous_id();
4871
        } else {
4872
            return false;
4873
        }
4874
    }
4875
    $cid = Database::escape_string($cid);
4876
4877
    $courseInfo = api_get_course_info($cid);
4878
    $courseId = $courseInfo['real_id'];
4879
    $is_platformAdmin = api_is_platform_admin();
4880
4881
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
4882
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
4883
4884
    $sql = "SELECT
4885
                $course_cat_table.code AS category_code,
4886
                $course_table.visibility,
4887
                $course_table.code,
4888
                $course_cat_table.code
4889
            FROM $course_table
4890
            LEFT JOIN $course_cat_table
4891
                ON $course_table.category_id = $course_cat_table.id
4892
            WHERE
4893
                $course_table.code = '$cid'
4894
            LIMIT 1";
4895
4896
    $result = Database::query($sql);
4897
4898
    if (Database::num_rows($result) > 0) {
4899
        $visibility = Database::fetch_array($result);
4900
        $visibility = $visibility['visibility'];
4901
    } else {
4902
        $visibility = 0;
4903
    }
4904
    // Shortcut permissions in case the visibility is "open to the world".
4905
    if (COURSE_VISIBILITY_OPEN_WORLD === $visibility) {
4906
        return true;
4907
    }
4908
4909
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4910
4911
    $sql = "SELECT
4912
                is_tutor, status
4913
            FROM $tbl_course_user
4914
            WHERE
4915
                user_id  = '$userid' AND
4916
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
4917
                c_id = $courseId
4918
            LIMIT 1";
4919
4920
    $result = Database::query($sql);
4921
4922
    if (Database::num_rows($result) > 0) {
4923
        // This user has got a recorded state for this course.
4924
        $cuData = Database::fetch_array($result);
4925
        $is_courseMember = true;
4926
        $is_courseAdmin = (1 == $cuData['status']);
4927
    }
4928
4929
    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...
4930
        // This user has no status related to this course.
4931
        // Is it the session coach or the session admin?
4932
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4933
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4934
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4935
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4936
4937
        $sql = "SELECT sru_2.user_id AS session_admin_id, sru_1.user_id AS session_coach_id
4938
                FROM $tbl_session AS s
4939
                INNER JOIN $tblSessionRelUser sru_1
4940
                ON (sru_1.session_id = s.id AND sru_1.relation_type = ".SessionEntity::GENERAL_COACH.")
4941
                INNER JOIN $tblSessionRelUser sru_2
4942
                ON (sru_2.session_id = s.id AND sru_2.relation_type = ".SessionEntity::SESSION_ADMIN.")
4943
                INNER JOIN $tbl_session_course src
4944
                ON (src.session_id = s.id AND src.c_id = $courseId)";
4945
4946
        $result = Database::query($sql);
4947
        $row = Database::store_result($result);
4948
        $sessionAdminsId = array_column($row, 'session_admin_id');
4949
        $sessionCoachesId = array_column($row, 'session_coach_id');
4950
4951
        if (in_array($userid, $sessionCoachesId)) {
4952
            $is_courseMember = true;
4953
            $is_courseAdmin = false;
4954
        } elseif (in_array($userid, $sessionAdminsId)) {
4955
            $is_courseMember = false;
4956
            $is_courseAdmin = false;
4957
        } else {
4958
            // Check if the current user is the course coach.
4959
            $sql = "SELECT 1
4960
                    FROM $tbl_session_course
4961
                    WHERE session_rel_course.c_id = '$courseId'
4962
                    AND session_rel_course.id_coach = '$userid'
4963
                    LIMIT 1";
4964
4965
            $result = Database::query($sql);
4966
4967
            //if ($row = Database::fetch_array($result)) {
4968
            if (Database::num_rows($result) > 0) {
4969
                $is_courseMember = true;
4970
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4971
4972
                $sql = "SELECT status FROM $tbl_user
4973
                        WHERE id = $userid
4974
                        LIMIT 1";
4975
4976
                $result = Database::query($sql);
4977
4978
                if (1 == Database::result($result, 0, 0)) {
4979
                    $is_courseAdmin = true;
4980
                } else {
4981
                    $is_courseAdmin = false;
4982
                }
4983
            } else {
4984
                // Check if the user is a student is this session.
4985
                $sql = "SELECT  id
4986
                        FROM $tbl_session_course_user
4987
                        WHERE
4988
                            user_id  = '$userid' AND
4989
                            c_id = '$courseId'
4990
                        LIMIT 1";
4991
4992
                if (Database::num_rows($result) > 0) {
4993
                    // This user haa got a recorded state for this course.
4994
                    while ($row = Database::fetch_array($result)) {
4995
                        $is_courseMember = true;
4996
                        $is_courseAdmin = false;
4997
                    }
4998
                }
4999
            }
5000
        }
5001
    }
5002
5003
    switch ($visibility) {
5004
        case Course::OPEN_WORLD:
5005
            return true;
5006
        case Course::OPEN_PLATFORM:
5007
            return isset($userid);
5008
        case Course::REGISTERED:
5009
        case Course::CLOSED:
5010
            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...
5011
        case Course::HIDDEN:
5012
            return $is_platformAdmin;
5013
    }
5014
5015
    return false;
5016
}
5017
5018
/**
5019
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
5020
 *
5021
 * @param string the tool of the element
5022
 * @param int the element id in database
5023
 * @param int the session_id to compare with element session id
5024
 *
5025
 * @return bool true if the element is in the session, false else
5026
 */
5027
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
5028
{
5029
    if (is_null($session_id)) {
5030
        $session_id = api_get_session_id();
5031
    }
5032
5033
    $element_id = (int) $element_id;
5034
5035
    if (empty($element_id)) {
5036
        return false;
5037
    }
5038
5039
    // Get information to build query depending of the tool.
5040
    switch ($tool) {
5041
        case TOOL_SURVEY:
5042
            $table_tool = Database::get_course_table(TABLE_SURVEY);
5043
            $key_field = 'survey_id';
5044
            break;
5045
        case TOOL_ANNOUNCEMENT:
5046
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
5047
            $key_field = 'id';
5048
            break;
5049
        case TOOL_AGENDA:
5050
            $table_tool = Database::get_course_table(TABLE_AGENDA);
5051
            $key_field = 'id';
5052
            break;
5053
        case TOOL_GROUP:
5054
            $table_tool = Database::get_course_table(TABLE_GROUP);
5055
            $key_field = 'id';
5056
            break;
5057
        default:
5058
            return false;
5059
    }
5060
    $course_id = api_get_course_int_id();
5061
5062
    $sql = "SELECT session_id FROM $table_tool
5063
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
5064
    $rs = Database::query($sql);
5065
    if ($element_session_id = Database::result($rs, 0, 0)) {
5066
        if ($element_session_id == intval($session_id)) {
5067
            // The element belongs to the session.
5068
            return true;
5069
        }
5070
    }
5071
5072
    return false;
5073
}
5074
5075
/**
5076
 * Replaces "forbidden" characters in a filename string.
5077
 *
5078
 * @param string $filename
5079
 * @param bool   $treat_spaces_as_hyphens
5080
 *
5081
 * @return string
5082
 */
5083
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
5084
{
5085
    // Some non-properly encoded file names can cause the whole file to be
5086
    // skipped when uploaded. Avoid this by detecting the encoding and
5087
    // converting to UTF-8, setting the source as ASCII (a reasonably
5088
    // limited characters set) if nothing could be found (BT#
5089
    $encoding = api_detect_encoding($filename);
5090
    if (empty($encoding)) {
5091
        $encoding = 'ASCII';
5092
        if (!api_is_valid_ascii($filename)) {
5093
            // try iconv and try non standard ASCII a.k.a CP437
5094
            // see BT#15022
5095
            if (function_exists('iconv')) {
5096
                $result = iconv('CP437', 'UTF-8', $filename);
5097
                if (api_is_valid_utf8($result)) {
5098
                    $filename = $result;
5099
                    $encoding = 'UTF-8';
5100
                }
5101
            }
5102
        }
5103
    }
5104
5105
    $filename = api_to_system_encoding($filename, $encoding);
5106
5107
    $url = URLify::filter(
5108
        $filename,
5109
        250,
5110
        '',
5111
        true,
5112
        false,
5113
        false
5114
    );
5115
5116
    // Replace multiple dots at the end.
5117
    $regex = "/\.+$/";
5118
5119
    return preg_replace($regex, '', $url);
5120
}
5121
5122
/**
5123
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
5124
 *
5125
 * @author Ivan Tcholakov, 28-JUN-2006.
5126
 */
5127
function api_request_uri()
5128
{
5129
    if (!empty($_SERVER['REQUEST_URI'])) {
5130
        return $_SERVER['REQUEST_URI'];
5131
    }
5132
    $uri = $_SERVER['SCRIPT_NAME'];
5133
    if (!empty($_SERVER['QUERY_STRING'])) {
5134
        $uri .= '?'.$_SERVER['QUERY_STRING'];
5135
    }
5136
    $_SERVER['REQUEST_URI'] = $uri;
5137
5138
    return $uri;
5139
}
5140
5141
/**
5142
 * Gets the current access_url id of the Chamilo Platform.
5143
 *
5144
 * @return int access_url_id of the current Chamilo Installation
5145
 *
5146
 * @author Julio Montoya <[email protected]>
5147
 * @throws Exception
5148
 */
5149
function api_get_current_access_url_id(): int
5150
{
5151
    if (false === api_get_multiple_access_url()) {
5152
        return 1;
5153
    }
5154
5155
    static $id;
5156
    if (!empty($id)) {
5157
        return $id;
5158
    }
5159
5160
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5161
    $path = Database::escape_string(api_get_path(WEB_PATH));
5162
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
5163
    $result = Database::query($sql);
5164
    if (Database::num_rows($result) > 0) {
5165
        $id = Database::result($result, 0, 0);
5166
        if (false === $id) {
5167
            return -1;
5168
        }
5169
5170
        return (int) $id;
5171
    }
5172
5173
    $id = 1;
5174
5175
    //if the url in WEB_PATH was not found, it can only mean that there is
5176
    // either a configuration problem or the first URL has not been defined yet
5177
    // (by default it is http://localhost/). Thus the more sensible thing we can
5178
    // do is return 1 (the main URL) as the user cannot hack this value anyway
5179
    return 1;
5180
}
5181
5182
/**
5183
 * Gets the registered urls from a given user id.
5184
 *
5185
 * @author Julio Montoya <[email protected]>
5186
 *
5187
 * @param int $user_id
5188
 *
5189
 * @return array
5190
 */
5191
function api_get_access_url_from_user($user_id)
5192
{
5193
    $user_id = (int) $user_id;
5194
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5195
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5196
    $sql = "SELECT access_url_id
5197
            FROM $table_url_rel_user url_rel_user
5198
            INNER JOIN $table_url u
5199
            ON (url_rel_user.access_url_id = u.id)
5200
            WHERE user_id = ".$user_id;
5201
    $result = Database::query($sql);
5202
    $list = [];
5203
    while ($row = Database::fetch_assoc($result)) {
5204
        $list[] = $row['access_url_id'];
5205
    }
5206
5207
    return $list;
5208
}
5209
5210
/**
5211
 * Checks whether the curent user is in a group or not.
5212
 *
5213
 * @param string        The group id - optional (takes it from session if not given)
5214
 * @param string        The course code - optional (no additional check by course if course code is not given)
5215
 *
5216
 * @return bool
5217
 *
5218
 * @author Ivan Tcholakov
5219
 */
5220
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
5221
{
5222
    if (!empty($courseCodeParam)) {
5223
        $courseCode = api_get_course_id();
5224
        if (!empty($courseCode)) {
5225
            if ($courseCodeParam != $courseCode) {
5226
                return false;
5227
            }
5228
        } else {
5229
            return false;
5230
        }
5231
    }
5232
5233
    $groupId = api_get_group_id();
5234
5235
    if (isset($groupId) && '' != $groupId) {
5236
        if (!empty($groupIdParam)) {
5237
            return $groupIdParam == $groupId;
5238
        } else {
5239
            return true;
5240
        }
5241
    }
5242
5243
    return false;
5244
}
5245
5246
/**
5247
 * Checks whether a secret key is valid.
5248
 *
5249
 * @param string $original_key_secret - secret key from (webservice) client
5250
 * @param string $security_key        - security key from Chamilo
5251
 *
5252
 * @return bool - true if secret key is valid, false otherwise
5253
 */
5254
function api_is_valid_secret_key($original_key_secret, $security_key)
5255
{
5256
    if (empty($original_key_secret) || empty($security_key)) {
5257
        return false;
5258
    }
5259
5260
    return (string) $original_key_secret === hash('sha512', $security_key);
5261
}
5262
5263
/**
5264
 * Checks whether the server's operating system is Windows (TM).
5265
 *
5266
 * @return bool - true if the operating system is Windows, false otherwise
5267
 */
5268
function api_is_windows_os()
5269
{
5270
    if (function_exists('php_uname')) {
5271
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
5272
        // We expect that this function will always work for Chamilo 1.8.x.
5273
        $os = php_uname();
5274
    }
5275
    // The following methods are not needed, but let them stay, just in case.
5276
    elseif (isset($_ENV['OS'])) {
5277
        // Sometimes $_ENV['OS'] may not be present (bugs?)
5278
        $os = $_ENV['OS'];
5279
    } elseif (defined('PHP_OS')) {
5280
        // PHP_OS means on which OS PHP was compiled, this is why
5281
        // using PHP_OS is the last choice for detection.
5282
        $os = PHP_OS;
5283
    } else {
5284
        return false;
5285
    }
5286
5287
    return 'win' == strtolower(substr((string) $os, 0, 3));
5288
}
5289
5290
/**
5291
 * This function informs whether the sent request is XMLHttpRequest.
5292
 */
5293
function api_is_xml_http_request()
5294
{
5295
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH']);
5296
}
5297
5298
/**
5299
 * Returns a list of Chamilo's tools or
5300
 * checks whether a given identificator is a valid Chamilo's tool.
5301
 *
5302
 * @author Isaac flores paz
5303
 *
5304
 * @param string The tool name to filter
5305
 *
5306
 * @return mixed Filtered string or array
5307
 */
5308
function api_get_tools_lists($my_tool = null)
5309
{
5310
    $tools_list = [
5311
        TOOL_DOCUMENT,
5312
        TOOL_THUMBNAIL,
5313
        TOOL_HOTPOTATOES,
5314
        TOOL_CALENDAR_EVENT,
5315
        TOOL_LINK,
5316
        TOOL_COURSE_DESCRIPTION,
5317
        TOOL_SEARCH,
5318
        TOOL_LEARNPATH,
5319
        TOOL_ANNOUNCEMENT,
5320
        TOOL_FORUM,
5321
        TOOL_THREAD,
5322
        TOOL_POST,
5323
        TOOL_DROPBOX,
5324
        TOOL_QUIZ,
5325
        TOOL_USER,
5326
        TOOL_GROUP,
5327
        TOOL_BLOGS,
5328
        TOOL_CHAT,
5329
        TOOL_STUDENTPUBLICATION,
5330
        TOOL_TRACKING,
5331
        TOOL_HOMEPAGE_LINK,
5332
        TOOL_COURSE_SETTING,
5333
        TOOL_BACKUP,
5334
        TOOL_COPY_COURSE_CONTENT,
5335
        TOOL_RECYCLE_COURSE,
5336
        TOOL_COURSE_HOMEPAGE,
5337
        TOOL_COURSE_RIGHTS_OVERVIEW,
5338
        TOOL_UPLOAD,
5339
        TOOL_COURSE_MAINTENANCE,
5340
        TOOL_SURVEY,
5341
        //TOOL_WIKI,
5342
        TOOL_GLOSSARY,
5343
        TOOL_GRADEBOOK,
5344
        TOOL_NOTEBOOK,
5345
        TOOL_ATTENDANCE,
5346
        TOOL_COURSE_PROGRESS,
5347
    ];
5348
    if (empty($my_tool)) {
5349
        return $tools_list;
5350
    }
5351
5352
    return in_array($my_tool, $tools_list) ? $my_tool : '';
5353
}
5354
5355
/**
5356
 * Checks whether we already approved the last version term and condition.
5357
 *
5358
 * @param int user id
5359
 *
5360
 * @return bool true if we pass false otherwise
5361
 */
5362
function api_check_term_condition($userId)
5363
{
5364
    if ('true' === api_get_setting('allow_terms_conditions')) {
5365
        // Check if exists terms and conditions
5366
        if (0 == LegalManager::count()) {
5367
            return true;
5368
        }
5369
5370
        $extraFieldValue = new ExtraFieldValue('user');
5371
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
5372
            $userId,
5373
            'legal_accept'
5374
        );
5375
5376
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
5377
            $result = $data['value'];
5378
            $user_conditions = explode(':', $result);
5379
            $version = $user_conditions[0];
5380
            $langId = $user_conditions[1];
5381
            $realVersion = LegalManager::get_last_version($langId);
5382
5383
            return $version >= $realVersion;
5384
        }
5385
5386
        return false;
5387
    }
5388
5389
    return false;
5390
}
5391
5392
/**
5393
 * Gets all information of a tool into course.
5394
 *
5395
 * @param int The tool id
5396
 *
5397
 * @return array
5398
 */
5399
function api_get_tool_information_by_name($name)
5400
{
5401
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
5402
    $course_id = api_get_course_int_id();
5403
5404
    $sql = "SELECT id FROM tool
5405
            WHERE title = '".Database::escape_string($name)."' ";
5406
    $rs = Database::query($sql);
5407
    $data = Database::fetch_array($rs);
5408
    if ($data) {
5409
        $tool = $data['id'];
5410
        $sql = "SELECT * FROM $t_tool
5411
                WHERE c_id = $course_id  AND tool_id = '".$tool."' ";
5412
        $rs = Database::query($sql);
5413
5414
        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...
5415
    }
5416
5417
    return [];
5418
}
5419
5420
/**
5421
 * Function used to protect a "global" admin script.
5422
 * The function blocks access when the user has no global platform admin rights.
5423
 * Global admins are the admins that are registered in the main.admin table
5424
 * AND the users who have access to the "principal" portal.
5425
 * That means that there is a record in the main.access_url_rel_user table
5426
 * with his user id and the access_url_id=1.
5427
 *
5428
 * @author Julio Montoya
5429
 *
5430
 * @param int $user_id
5431
 *
5432
 * @return bool
5433
 */
5434
function api_is_global_platform_admin($user_id = null)
5435
{
5436
    $user_id = (int) $user_id;
5437
    if (empty($user_id)) {
5438
        $user_id = api_get_user_id();
5439
    }
5440
    if (api_is_platform_admin_by_id($user_id)) {
5441
        $urlList = api_get_access_url_from_user($user_id);
5442
        // The admin is registered in the first "main" site with access_url_id = 1
5443
        if (in_array(1, $urlList)) {
5444
            return true;
5445
        }
5446
    }
5447
5448
    return false;
5449
}
5450
5451
/**
5452
 * @param int  $admin_id_to_check
5453
 * @param int  $userId
5454
 * @param bool $allow_session_admin
5455
 *
5456
 * @return bool
5457
 */
5458
function api_global_admin_can_edit_admin(
5459
    $admin_id_to_check,
5460
    $userId = 0,
5461
    $allow_session_admin = false
5462
) {
5463
    if (empty($userId)) {
5464
        $userId = api_get_user_id();
5465
    }
5466
5467
    $iam_a_global_admin = api_is_global_platform_admin($userId);
5468
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
5469
5470
    if ($iam_a_global_admin) {
5471
        // Global admin can edit everything
5472
        return true;
5473
    }
5474
5475
    // If i'm a simple admin
5476
    $is_platform_admin = api_is_platform_admin_by_id($userId);
5477
5478
    if ($allow_session_admin && !$is_platform_admin) {
5479
        $user = api_get_user_entity($userId);
5480
        $is_platform_admin = $user->hasRole('ROLE_SESSION_MANAGER');
5481
    }
5482
5483
    if ($is_platform_admin) {
5484
        if ($user_is_global_admin) {
5485
            return false;
5486
        } else {
5487
            return true;
5488
        }
5489
    }
5490
5491
    return false;
5492
}
5493
5494
/**
5495
 * @param int  $admin_id_to_check
5496
 * @param int  $userId
5497
 * @param bool $allow_session_admin
5498
 *
5499
 * @return bool|null
5500
 */
5501
function api_protect_super_admin($admin_id_to_check, $userId = null, $allow_session_admin = false)
5502
{
5503
    if (api_global_admin_can_edit_admin($admin_id_to_check, $userId, $allow_session_admin)) {
5504
        return true;
5505
    } else {
5506
        api_not_allowed();
5507
    }
5508
}
5509
5510
/**
5511
 * Function used to protect a global admin script.
5512
 * The function blocks access when the user has no global platform admin rights.
5513
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
5514
 *
5515
 * @author Julio Montoya
5516
 */
5517
function api_protect_global_admin_script()
5518
{
5519
    if (!api_is_global_platform_admin()) {
5520
        api_not_allowed();
5521
5522
        return false;
5523
    }
5524
5525
    return true;
5526
}
5527
5528
/**
5529
 * Check browser support for specific file types or features
5530
 * This function checks if the user's browser supports a file format or given
5531
 * feature, or returns the current browser and major version when
5532
 * $format=check_browser. Only a limited number of formats and features are
5533
 * checked by this method. Make sure you check its definition first.
5534
 *
5535
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
5536
 *
5537
 * @deprecated
5538
 *
5539
 * @return bool or return text array if $format=check_browser
5540
 *
5541
 * @author Juan Carlos Raña Trabado
5542
 */
5543
function api_browser_support($format = '')
5544
{
5545
    return true;
5546
5547
    $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...
5548
    $current_browser = $browser->getBrowser();
5549
    $a_versiontemp = explode('.', $browser->getVersion());
5550
    $current_majorver = $a_versiontemp[0];
5551
5552
    static $result;
5553
5554
    if (isset($result[$format])) {
5555
        return $result[$format];
5556
    }
5557
5558
    // Native svg support
5559
    if ('svg' == $format) {
5560
        if (('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5561
            ('Firefox' == $current_browser && $current_majorver > 1) ||
5562
            ('Safari' == $current_browser && $current_majorver >= 4) ||
5563
            ('Chrome' == $current_browser && $current_majorver >= 1) ||
5564
            ('Opera' == $current_browser && $current_majorver >= 9)
5565
        ) {
5566
            $result[$format] = true;
5567
5568
            return true;
5569
        } else {
5570
            $result[$format] = false;
5571
5572
            return false;
5573
        }
5574
    } elseif ('pdf' == $format) {
5575
        // native pdf support
5576
        if ('Chrome' == $current_browser && $current_majorver >= 6) {
5577
            $result[$format] = true;
5578
5579
            return true;
5580
        } else {
5581
            $result[$format] = false;
5582
5583
            return false;
5584
        }
5585
    } elseif ('tif' == $format || 'tiff' == $format) {
5586
        //native tif support
5587
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5588
            $result[$format] = true;
5589
5590
            return true;
5591
        } else {
5592
            $result[$format] = false;
5593
5594
            return false;
5595
        }
5596
    } elseif ('ogg' == $format || 'ogx' == $format || 'ogv' == $format || 'oga' == $format) {
5597
        //native ogg, ogv,oga support
5598
        if (('Firefox' == $current_browser && $current_majorver >= 3) ||
5599
            ('Chrome' == $current_browser && $current_majorver >= 3) ||
5600
            ('Opera' == $current_browser && $current_majorver >= 9)) {
5601
            $result[$format] = true;
5602
5603
            return true;
5604
        } else {
5605
            $result[$format] = false;
5606
5607
            return false;
5608
        }
5609
    } elseif ('mpg' == $format || 'mpeg' == $format) {
5610
        //native mpg support
5611
        if (('Safari' == $current_browser && $current_majorver >= 5)) {
5612
            $result[$format] = true;
5613
5614
            return true;
5615
        } else {
5616
            $result[$format] = false;
5617
5618
            return false;
5619
        }
5620
    } elseif ('mp4' == $format) {
5621
        //native mp4 support (TODO: Android, iPhone)
5622
        if ('Android' == $current_browser || 'iPhone' == $current_browser) {
5623
            $result[$format] = true;
5624
5625
            return true;
5626
        } else {
5627
            $result[$format] = false;
5628
5629
            return false;
5630
        }
5631
    } elseif ('mov' == $format) {
5632
        //native mov support( TODO:check iPhone)
5633
        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...
5634
            $result[$format] = true;
5635
5636
            return true;
5637
        } else {
5638
            $result[$format] = false;
5639
5640
            return false;
5641
        }
5642
    } elseif ('avi' == $format) {
5643
        //native avi support
5644
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5645
            $result[$format] = true;
5646
5647
            return true;
5648
        } else {
5649
            $result[$format] = false;
5650
5651
            return false;
5652
        }
5653
    } elseif ('wmv' == $format) {
5654
        //native wmv support
5655
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5656
            $result[$format] = true;
5657
5658
            return true;
5659
        } else {
5660
            $result[$format] = false;
5661
5662
            return false;
5663
        }
5664
    } elseif ('webm' == $format) {
5665
        //native webm support (TODO:check IE9, Chrome9, Android)
5666
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5667
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5668
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5669
            ('Chrome' == $current_browser && $current_majorver >= 9) ||
5670
            'Android' == $current_browser
5671
        ) {
5672
            $result[$format] = true;
5673
5674
            return true;
5675
        } else {
5676
            $result[$format] = false;
5677
5678
            return false;
5679
        }
5680
    } elseif ('wav' == $format) {
5681
        //native wav support (only some codecs !)
5682
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5683
            ('Safari' == $current_browser && $current_majorver >= 5) ||
5684
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5685
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5686
            ('Chrome' == $current_browser && $current_majorver > 9) ||
5687
            'Android' == $current_browser ||
5688
            'iPhone' == $current_browser
5689
        ) {
5690
            $result[$format] = true;
5691
5692
            return true;
5693
        } else {
5694
            $result[$format] = false;
5695
5696
            return false;
5697
        }
5698
    } elseif ('mid' == $format || 'kar' == $format) {
5699
        //native midi support (TODO:check Android)
5700
        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...
5701
            $result[$format] = true;
5702
5703
            return true;
5704
        } else {
5705
            $result[$format] = false;
5706
5707
            return false;
5708
        }
5709
    } elseif ('wma' == $format) {
5710
        //native wma support
5711
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5712
            $result[$format] = true;
5713
5714
            return true;
5715
        } else {
5716
            $result[$format] = false;
5717
5718
            return false;
5719
        }
5720
    } elseif ('au' == $format) {
5721
        //native au support
5722
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5723
            $result[$format] = true;
5724
5725
            return true;
5726
        } else {
5727
            $result[$format] = false;
5728
5729
            return false;
5730
        }
5731
    } elseif ('mp3' == $format) {
5732
        //native mp3 support (TODO:check Android, iPhone)
5733
        if (('Safari' == $current_browser && $current_majorver >= 5) ||
5734
            ('Chrome' == $current_browser && $current_majorver >= 6) ||
5735
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5736
            'Android' == $current_browser ||
5737
            'iPhone' == $current_browser ||
5738
            'Firefox' == $current_browser
5739
        ) {
5740
            $result[$format] = true;
5741
5742
            return true;
5743
        } else {
5744
            $result[$format] = false;
5745
5746
            return false;
5747
        }
5748
    } elseif ('autocapitalize' == $format) {
5749
        // Help avoiding showing the autocapitalize option if the browser doesn't
5750
        // support it: this attribute is against the HTML5 standard
5751
        if ('Safari' == $current_browser || 'iPhone' == $current_browser) {
5752
            return true;
5753
        } else {
5754
            return false;
5755
        }
5756
    } elseif ("check_browser" == $format) {
5757
        $array_check_browser = [$current_browser, $current_majorver];
5758
5759
        return $array_check_browser;
5760
    } else {
5761
        $result[$format] = false;
5762
5763
        return false;
5764
    }
5765
}
5766
5767
/**
5768
 * This function checks if exist path and file browscap.ini
5769
 * In order for this to work, your browscap configuration setting in php.ini
5770
 * must point to the correct location of the browscap.ini file on your system
5771
 * http://php.net/manual/en/function.get-browser.php.
5772
 *
5773
 * @return bool
5774
 *
5775
 * @author Juan Carlos Raña Trabado
5776
 */
5777
function api_check_browscap()
5778
{
5779
    $setting = ini_get('browscap');
5780
    if ($setting) {
5781
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
5782
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
5783
            return true;
5784
        }
5785
    }
5786
5787
    return false;
5788
}
5789
5790
/**
5791
 * Returns the <script> HTML tag.
5792
 */
5793
function api_get_js($file)
5794
{
5795
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
5796
}
5797
5798
function api_get_build_js($file)
5799
{
5800
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'"></script>'."\n";
5801
}
5802
5803
function api_get_build_css($file, $media = 'screen')
5804
{
5805
    return '<link
5806
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5807
}
5808
5809
/**
5810
 * Returns the <script> HTML tag.
5811
 *
5812
 * @return string
5813
 */
5814
function api_get_asset($file)
5815
{
5816
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"></script>'."\n";
5817
}
5818
5819
/**
5820
 * Returns the <script> HTML tag.
5821
 *
5822
 * @param string $file
5823
 * @param string $media
5824
 *
5825
 * @return string
5826
 */
5827
function api_get_css_asset($file, $media = 'screen')
5828
{
5829
    return '<link
5830
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"
5831
        rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5832
}
5833
5834
/**
5835
 * Returns the <link> HTML tag.
5836
 *
5837
 * @param string $file
5838
 * @param string $media
5839
 */
5840
function api_get_css($file, $media = 'screen')
5841
{
5842
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5843
}
5844
5845
function api_get_bootstrap_and_font_awesome($returnOnlyPath = false, $returnFileLocation = false)
5846
{
5847
    $url = api_get_path(WEB_PUBLIC_PATH).'build/css/bootstrap.css';
5848
5849
    if ($returnOnlyPath) {
5850
        if ($returnFileLocation) {
5851
            return api_get_path(SYS_PUBLIC_PATH).'build/css/bootstrap.css';
5852
        }
5853
5854
        return $url;
5855
    }
5856
5857
    return '<link href="'.$url.'" rel="stylesheet" type="text/css" />'."\n";
5858
}
5859
5860
/**
5861
 * Returns the js header to include the jquery library.
5862
 */
5863
function api_get_jquery_js()
5864
{
5865
    return api_get_asset('jquery/jquery.min.js');
5866
}
5867
5868
/**
5869
 * Returns the jquery path.
5870
 *
5871
 * @return string
5872
 */
5873
function api_get_jquery_web_path()
5874
{
5875
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/jquery.min.js';
5876
}
5877
5878
/**
5879
 * @return string
5880
 */
5881
function api_get_jquery_ui_js_web_path()
5882
{
5883
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
5884
}
5885
5886
/**
5887
 * @return string
5888
 */
5889
function api_get_jquery_ui_css_web_path()
5890
{
5891
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
5892
}
5893
5894
/**
5895
 * Returns the jquery-ui library js headers.
5896
 *
5897
 * @return string html tags
5898
 */
5899
function api_get_jquery_ui_js()
5900
{
5901
    $libraries = [];
5902
5903
    return api_get_jquery_libraries_js($libraries);
5904
}
5905
5906
function api_get_jqgrid_js()
5907
{
5908
    return api_get_build_css('legacy_free-jqgrid.css').PHP_EOL
5909
        .api_get_build_js('legacy_free-jqgrid.js');
5910
}
5911
5912
/**
5913
 * Returns the jquery library js and css headers.
5914
 *
5915
 * @param   array   list of jquery libraries supported jquery-ui
5916
 * @param   bool    add the jquery library
5917
 *
5918
 * @return string html tags
5919
 */
5920
function api_get_jquery_libraries_js($libraries)
5921
{
5922
    $js = '';
5923
5924
    //Document multiple upload funcionality
5925
    if (in_array('jquery-uploadzs', $libraries)) {
5926
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
5927
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
5928
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
5929
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
5930
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
5931
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
5932
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
5933
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
5934
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
5935
5936
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
5937
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
5938
    }
5939
5940
    // jquery datepicker
5941
    if (in_array('datepicker', $libraries)) {
5942
        $languaje = 'en-GB';
5943
        $platform_isocode = strtolower(api_get_language_isocode());
5944
5945
        $datapicker_langs = [
5946
            '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',
5947
        ];
5948
        if (in_array($platform_isocode, $datapicker_langs)) {
5949
            $languaje = $platform_isocode;
5950
        }
5951
5952
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
5953
        $script = '<script>
5954
        $(function(){
5955
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
5956
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
5957
        });
5958
        </script>
5959
        ';
5960
        $js .= $script;
5961
    }
5962
5963
    return $js;
5964
}
5965
5966
/**
5967
 * Returns the URL to the course or session, removing the complexity of the URL
5968
 * building piece by piece.
5969
 *
5970
 * This function relies on api_get_course_info()
5971
 *
5972
 * @param int $courseId  The course code - optional (takes it from context if not given)
5973
 * @param int $sessionId The session ID  - optional (takes it from context if not given)
5974
 * @param int $groupId   The group ID - optional (takes it from context if not given)
5975
 *
5976
 * @return string The URL to a course, a session, or empty string if nothing works
5977
 *                e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
5978
 *
5979
 * @author  Julio Montoya
5980
 */
5981
function api_get_course_url($courseId = null, $sessionId = null, $groupId = null)
5982
{
5983
    $url = '';
5984
    // If courseCode not set, get context or []
5985
    if (empty($courseId)) {
5986
        $courseId = api_get_course_int_id();
5987
    }
5988
5989
    // If sessionId not set, get context or 0
5990
    if (empty($sessionId)) {
5991
        $sessionId = api_get_session_id();
5992
    }
5993
5994
    // If groupId not set, get context or 0
5995
    if (empty($groupId)) {
5996
        $groupId = api_get_group_id();
5997
    }
5998
5999
    // Build the URL
6000
    if (!empty($courseId)) {
6001
        $webCourseHome = '/course/'.$courseId.'/home';
6002
        // directory not empty, so we do have a course
6003
        $url = $webCourseHome.'?sid='.$sessionId.'&gid='.$groupId;
6004
    } else {
6005
        if (!empty($sessionId) && 'true' !== api_get_setting('session.remove_session_url')) {
6006
            // if the course was unset and the session was set, send directly to the session
6007
            $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
6008
        }
6009
    }
6010
6011
    // if not valid combination was found, return an empty string
6012
    return $url;
6013
}
6014
6015
/**
6016
 * Check if there is more than the default URL defined in the access_url table.
6017
 */
6018
function api_get_multiple_access_url(): bool
6019
{
6020
    return Container::getAccessUrlHelper()->isMultiple();
6021
}
6022
6023
/**
6024
 * @deprecated Use AccessUrlHelper::isMultiple
6025
 */
6026
function api_is_multiple_url_enabled(): bool
6027
{
6028
    return api_get_multiple_access_url();
6029
}
6030
6031
/**
6032
 * Returns a md5 unique id.
6033
 *
6034
 * @todo add more parameters
6035
 */
6036
function api_get_unique_id()
6037
{
6038
    return md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
6039
}
6040
6041
/**
6042
 * @param int Course id
6043
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
6044
 * @param int the item id (tool id, exercise id, lp id)
6045
 *
6046
 * @return bool
6047
 */
6048
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
6049
{
6050
    if (api_is_platform_admin()) {
6051
        return false;
6052
    }
6053
    if ('true' === api_get_setting('gradebook_locking_enabled')) {
6054
        if (empty($course_code)) {
6055
            $course_code = api_get_course_id();
6056
        }
6057
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
6058
        $item_id = (int) $item_id;
6059
        $link_type = (int) $link_type;
6060
        $course_code = Database::escape_string($course_code);
6061
        $sql = "SELECT locked FROM $table
6062
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
6063
        $result = Database::query($sql);
6064
        if (Database::num_rows($result)) {
6065
            return true;
6066
        }
6067
    }
6068
6069
    return false;
6070
}
6071
6072
/**
6073
 * Blocks a page if the item was added in a gradebook.
6074
 *
6075
 * @param int       exercise id, work id, thread id,
6076
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
6077
 * see gradebook/lib/be/linkfactory
6078
 * @param string    course code
6079
 *
6080
 * @return false|null
6081
 */
6082
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
6083
{
6084
    if (api_is_platform_admin()) {
6085
        return false;
6086
    }
6087
6088
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
6089
        $message = Display::return_message(
6090
            get_lang(
6091
                '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.'
6092
            ),
6093
            'warning'
6094
        );
6095
        api_not_allowed(true, $message);
6096
    }
6097
}
6098
6099
/**
6100
 * Checks the PHP version installed is enough to run Chamilo.
6101
 *
6102
 * @param string Include path (used to load the error page)
6103
 */
6104
function api_check_php_version()
6105
{
6106
    if (!function_exists('version_compare') ||
6107
        version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')
6108
    ) {
6109
        throw new Exception('Wrong PHP version');
6110
    }
6111
}
6112
6113
/**
6114
 * Checks whether the Archive directory is present and writeable. If not,
6115
 * prints a warning message.
6116
 */
6117
function api_check_archive_dir()
6118
{
6119
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
6120
        $message = Display::return_message(
6121
            get_lang(
6122
                'The var/cache/ directory, used by this tool, is not writeable. Please contact your platform administrator.'
6123
            ),
6124
            'warning'
6125
        );
6126
        api_not_allowed(true, $message);
6127
    }
6128
}
6129
6130
/**
6131
 * Returns an array of global configuration settings which should be ignored
6132
 * when printing the configuration settings screens.
6133
 *
6134
 * @return array Array of strings, each identifying one of the excluded settings
6135
 */
6136
function api_get_locked_settings()
6137
{
6138
    return [
6139
        'permanently_remove_deleted_files',
6140
        'account_valid_duration',
6141
        'service_ppt2lp',
6142
        'wcag_anysurfer_public_pages',
6143
        'upload_extensions_list_type',
6144
        'upload_extensions_blacklist',
6145
        'upload_extensions_whitelist',
6146
        'upload_extensions_skip',
6147
        'upload_extensions_replace_by',
6148
        'hide_dltt_markup',
6149
        'split_users_upload_directory',
6150
        'permissions_for_new_directories',
6151
        'permissions_for_new_files',
6152
        'platform_charset',
6153
        'ldap_description',
6154
        'cas_activate',
6155
        'cas_server',
6156
        'cas_server_uri',
6157
        'cas_port',
6158
        'cas_protocol',
6159
        'cas_add_user_activate',
6160
        'update_user_info_cas_with_ldap',
6161
        'languagePriority1',
6162
        'languagePriority2',
6163
        'languagePriority3',
6164
        'languagePriority4',
6165
        'login_is_email',
6166
        'chamilo_database_version',
6167
    ];
6168
}
6169
6170
/**
6171
 * Guess the real ip for register in the database, even in reverse proxy cases.
6172
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
6173
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
6174
 * Note: the result of this function is not SQL-safe. Please escape it before
6175
 * inserting in a database.
6176
 *
6177
 * @return string the user's real ip (unsafe - escape it before inserting to db)
6178
 *
6179
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
6180
 *
6181
 * @version CEV CHANGE 24APR2012
6182
 * @throws RuntimeException
6183
 */
6184
function api_get_real_ip(): string
6185
{
6186
    if ('cli' === PHP_SAPI) {
6187
        $ip = '127.0.0.1';
6188
    } else {
6189
        $ip = trim($_SERVER['REMOTE_ADDR']);
6190
        if (empty($ip)) {
6191
            throw new RuntimeException("Unable to retrieve remote IP address.");
6192
        }
6193
    }
6194
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
6195
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
6196
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
6197
        } else {
6198
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
6199
        }
6200
        $ip = trim($ip1);
6201
    }
6202
6203
    return $ip;
6204
}
6205
6206
/**
6207
 * Checks whether an IP is included inside an IP range.
6208
 *
6209
 * @param string IP address
6210
 * @param string IP range
6211
 * @param string $ip
6212
 *
6213
 * @return bool True if IP is in the range, false otherwise
6214
 *
6215
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
6216
 * @author Yannick Warnier for improvements and managment of multiple ranges
6217
 *
6218
 * @todo check for IPv6 support
6219
 */
6220
function api_check_ip_in_range($ip, $range)
6221
{
6222
    if (empty($ip) or empty($range)) {
6223
        return false;
6224
    }
6225
    $ip_ip = ip2long($ip);
6226
    // divide range param into array of elements
6227
    if (false !== strpos($range, ',')) {
6228
        $ranges = explode(',', $range);
6229
    } else {
6230
        $ranges = [$range];
6231
    }
6232
    foreach ($ranges as $range) {
0 ignored issues
show
introduced by
$range is overwriting one of the parameters of this function.
Loading history...
6233
        $range = trim($range);
6234
        if (empty($range)) {
6235
            continue;
6236
        }
6237
        if (false === strpos($range, '/')) {
6238
            if (0 === strcmp($ip, $range)) {
6239
                return true; // there is a direct IP match, return OK
6240
            }
6241
            continue; //otherwise, get to the next range
6242
        }
6243
        // the range contains a "/", so analyse completely
6244
        [$net, $mask] = explode("/", $range);
6245
6246
        $ip_net = ip2long($net);
6247
        // mask binary magic
6248
        $ip_mask = ~((1 << (32 - $mask)) - 1);
6249
6250
        $ip_ip_net = $ip_ip & $ip_mask;
6251
        if ($ip_ip_net == $ip_net) {
6252
            return true;
6253
        }
6254
    }
6255
6256
    return false;
6257
}
6258
6259
function api_check_user_access_to_legal($courseInfo)
6260
{
6261
    if (empty($courseInfo)) {
6262
        return false;
6263
    }
6264
6265
    $visibility = (int) $courseInfo['visibility'];
6266
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
6267
6268
    return
6269
        in_array($visibility, $visibilityList) ||
6270
        api_is_drh() ||
6271
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
6272
}
6273
6274
/**
6275
 * Checks if the global chat is enabled or not.
6276
 *
6277
 * @return bool
6278
 */
6279
function api_is_global_chat_enabled()
6280
{
6281
    return
6282
        !api_is_anonymous() &&
6283
        'true' === api_get_setting('allow_global_chat') &&
6284
        'true' === api_get_setting('allow_social_tool');
6285
}
6286
6287
/**
6288
 * @param int   $item_id
6289
 * @param int   $tool_id
6290
 * @param int   $group_id   id
6291
 * @param array $courseInfo
6292
 * @param int   $sessionId
6293
 * @param int   $userId
6294
 *
6295
 * @deprecated
6296
 */
6297
function api_set_default_visibility(
6298
    $item_id,
6299
    $tool_id,
6300
    $group_id = 0,
6301
    $courseInfo = [],
6302
    $sessionId = 0,
6303
    $userId = 0
6304
) {
6305
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
6306
    $courseId = $courseInfo['real_id'];
6307
    $courseCode = $courseInfo['code'];
6308
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6309
    $userId = empty($userId) ? api_get_user_id() : $userId;
6310
6311
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
6312
    if (is_null($group_id)) {
6313
        $group_id = 0;
6314
    } else {
6315
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
6316
    }
6317
6318
    $groupInfo = [];
6319
    if (!empty($group_id)) {
6320
        $groupInfo = GroupManager::get_group_properties($group_id);
6321
    }
6322
    $original_tool_id = $tool_id;
6323
6324
    switch ($tool_id) {
6325
        case TOOL_LINK:
6326
        case TOOL_LINK_CATEGORY:
6327
            $tool_id = 'links';
6328
            break;
6329
        case TOOL_DOCUMENT:
6330
            $tool_id = 'documents';
6331
            break;
6332
        case TOOL_LEARNPATH:
6333
            $tool_id = 'learning';
6334
            break;
6335
        case TOOL_ANNOUNCEMENT:
6336
            $tool_id = 'announcements';
6337
            break;
6338
        case TOOL_FORUM:
6339
        case TOOL_FORUM_CATEGORY:
6340
        case TOOL_FORUM_THREAD:
6341
            $tool_id = 'forums';
6342
            break;
6343
        case TOOL_QUIZ:
6344
            $tool_id = 'quiz';
6345
            break;
6346
    }
6347
    $setting = api_get_setting('tool_visible_by_default_at_creation');
6348
6349
    if (isset($setting[$tool_id])) {
6350
        $visibility = 'invisible';
6351
        if ('true' === $setting[$tool_id]) {
6352
            $visibility = 'visible';
6353
        }
6354
6355
        // Read the portal and course default visibility
6356
        if ('documents' === $tool_id) {
6357
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
6358
        }
6359
6360
        // Fixes default visibility for tests
6361
        switch ($original_tool_id) {
6362
            case TOOL_QUIZ:
6363
                if (empty($sessionId)) {
6364
                    $objExerciseTmp = new Exercise($courseId);
6365
                    $objExerciseTmp->read($item_id);
6366
                    if ('visible' === $visibility) {
6367
                        $objExerciseTmp->enable();
6368
                        $objExerciseTmp->save();
6369
                    } else {
6370
                        $objExerciseTmp->disable();
6371
                        $objExerciseTmp->save();
6372
                    }
6373
                }
6374
                break;
6375
        }
6376
    }
6377
}
6378
6379
function api_get_roles(): array
6380
{
6381
    $roles = Container::$container->get(\Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper::class)->getUserRoles();
6382
6383
    return array_combine($roles, $roles);
6384
}
6385
6386
function api_get_user_roles(): array
6387
{
6388
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
6389
6390
    $roles = $permissionService->getUserRoles();
6391
6392
    return array_combine($roles, $roles);
6393
}
6394
6395
/**
6396
 * @param string $file
6397
 *
6398
 * @return string
6399
 */
6400
function api_get_js_simple($file)
6401
{
6402
    return '<script type="text/javascript" src="'.$file.'"></script>'."\n";
6403
}
6404
6405
/**
6406
 * Modify default memory_limit and max_execution_time limits
6407
 * Needed when processing long tasks.
6408
 */
6409
function api_set_more_memory_and_time_limits()
6410
{
6411
    if (function_exists('ini_set')) {
6412
        api_set_memory_limit('256M');
6413
        ini_set('max_execution_time', 1800);
6414
    }
6415
}
6416
6417
/**
6418
 * Tries to set memory limit, if authorized and new limit is higher than current.
6419
 *
6420
 * @param string $mem New memory limit
6421
 *
6422
 * @return bool True on success, false on failure or current is higher than suggested
6423
 * @assert (null) === false
6424
 * @assert (-1) === false
6425
 * @assert (0) === true
6426
 * @assert ('1G') === true
6427
 */
6428
function api_set_memory_limit($mem)
6429
{
6430
    //if ini_set() not available, this function is useless
6431
    if (!function_exists('ini_set') || is_null($mem) || -1 == $mem) {
6432
        return false;
6433
    }
6434
6435
    $memory_limit = ini_get('memory_limit');
6436
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
6437
        ini_set('memory_limit', $mem);
6438
6439
        return true;
6440
    }
6441
6442
    return false;
6443
}
6444
6445
/**
6446
 * Gets memory limit in bytes.
6447
 *
6448
 * @param string The memory size (128M, 1G, 1000K, etc)
6449
 *
6450
 * @return int
6451
 * @assert (null) === false
6452
 * @assert ('1t')  === 1099511627776
6453
 * @assert ('1g')  === 1073741824
6454
 * @assert ('1m')  === 1048576
6455
 * @assert ('100k') === 102400
6456
 */
6457
function api_get_bytes_memory_limit($mem)
6458
{
6459
    $size = strtolower(substr($mem, -1));
6460
6461
    switch ($size) {
6462
        case 't':
6463
            $mem = (int) substr($mem, -1) * 1024 * 1024 * 1024 * 1024;
6464
            break;
6465
        case 'g':
6466
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024 * 1024;
6467
            break;
6468
        case 'm':
6469
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024;
6470
            break;
6471
        case 'k':
6472
            $mem = (int) substr($mem, 0, -1) * 1024;
6473
            break;
6474
        default:
6475
            // we assume it's integer only
6476
            $mem = (int) $mem;
6477
            break;
6478
    }
6479
6480
    return $mem;
6481
}
6482
6483
/**
6484
 * Finds all the information about a user from username instead of user id.
6485
 *
6486
 * @param string $officialCode
6487
 *
6488
 * @return array $user_info user_id, lastname, firstname, username, email, ...
6489
 *
6490
 * @author Yannick Warnier <[email protected]>
6491
 */
6492
function api_get_user_info_from_official_code($officialCode)
6493
{
6494
    if (empty($officialCode)) {
6495
        return false;
6496
    }
6497
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
6498
            WHERE official_code ='".Database::escape_string($officialCode)."'";
6499
    $result = Database::query($sql);
6500
    if (Database::num_rows($result) > 0) {
6501
        $result_array = Database::fetch_array($result);
6502
6503
        return _api_format_user($result_array);
6504
    }
6505
6506
    return false;
6507
}
6508
6509
/**
6510
 * @param string $usernameInputId
6511
 * @param string $passwordInputId
6512
 *
6513
 * @return string|null
6514
 */
6515
function api_get_password_checker_js($usernameInputId, $passwordInputId)
6516
{
6517
    $checkPass = api_get_setting('allow_strength_pass_checker');
6518
    $useStrengthPassChecker = 'true' === $checkPass;
6519
6520
    if (false === $useStrengthPassChecker) {
6521
        return null;
6522
    }
6523
6524
    $minRequirements = Security::getPasswordRequirements()['min'];
6525
6526
    $options = [
6527
        'rules' => [],
6528
    ];
6529
6530
    if ($minRequirements['length'] > 0) {
6531
        $options['rules'][] = [
6532
            'minChar' => $minRequirements['length'],
6533
            'pattern' => '.',
6534
            'helpText' => sprintf(
6535
                get_lang('Minimum %s characters in total'),
6536
                $minRequirements['length']
6537
            ),
6538
        ];
6539
    }
6540
6541
    if ($minRequirements['lowercase'] > 0) {
6542
        $options['rules'][] = [
6543
            'minChar' => $minRequirements['lowercase'],
6544
            'pattern' => '[a-z]',
6545
            'helpText' => sprintf(
6546
                get_lang('Minimum %s lowercase characters'),
6547
                $minRequirements['lowercase']
6548
            ),
6549
        ];
6550
    }
6551
6552
    if ($minRequirements['uppercase'] > 0) {
6553
        $options['rules'][] = [
6554
            'minChar' => $minRequirements['uppercase'],
6555
            'pattern' => '[A-Z]',
6556
            'helpText' => sprintf(
6557
                get_lang('Minimum %s uppercase characters'),
6558
                $minRequirements['uppercase']
6559
            ),
6560
        ];
6561
    }
6562
6563
    if ($minRequirements['numeric'] > 0) {
6564
        $options['rules'][] = [
6565
            'minChar' => $minRequirements['numeric'],
6566
            'pattern' => '[0-9]',
6567
            'helpText' => sprintf(
6568
                get_lang('Minimum %s numerical (0-9) characters'),
6569
                $minRequirements['numeric']
6570
            ),
6571
        ];
6572
    }
6573
6574
    if ($minRequirements['specials'] > 0) {
6575
        $options['rules'][] = [
6576
            'minChar' => $minRequirements['specials'],
6577
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
6578
            'helpText' => sprintf(
6579
                get_lang('Minimum %s special characters'),
6580
                $minRequirements['specials']
6581
            ),
6582
        ];
6583
    }
6584
6585
    $js = api_get_js('password-checker/password-checker.js');
6586
    $js .= "<script>
6587
    $(function() {
6588
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
6589
    });
6590
    </script>";
6591
6592
    return $js;
6593
}
6594
6595
/**
6596
 * create an user extra field called 'captcha_blocked_until_date'.
6597
 *
6598
 * @param string $username
6599
 *
6600
 * @return bool
6601
 */
6602
function api_block_account_captcha($username)
6603
{
6604
    $userInfo = api_get_user_info_from_username($username);
6605
    if (empty($userInfo)) {
6606
        return false;
6607
    }
6608
    $minutesToBlock = api_get_setting('captcha_time_to_block');
6609
    $time = time() + $minutesToBlock * 60;
6610
    UserManager::update_extra_field_value(
6611
        $userInfo['user_id'],
6612
        'captcha_blocked_until_date',
6613
        api_get_utc_datetime($time)
6614
    );
6615
6616
    return true;
6617
}
6618
6619
/**
6620
 * @param string $username
6621
 *
6622
 * @return bool
6623
 */
6624
function api_clean_account_captcha($username)
6625
{
6626
    $userInfo = api_get_user_info_from_username($username);
6627
    if (empty($userInfo)) {
6628
        return false;
6629
    }
6630
    Session::erase('loginFailedCount');
6631
    UserManager::update_extra_field_value(
6632
        $userInfo['user_id'],
6633
        'captcha_blocked_until_date',
6634
        null
6635
    );
6636
6637
    return true;
6638
}
6639
6640
/**
6641
 * @param string $username
6642
 *
6643
 * @return bool
6644
 */
6645
function api_get_user_blocked_by_captcha($username)
6646
{
6647
    $userInfo = api_get_user_info_from_username($username);
6648
    if (empty($userInfo)) {
6649
        return false;
6650
    }
6651
    $data = UserManager::get_extra_user_data_by_field(
6652
        $userInfo['user_id'],
6653
        'captcha_blocked_until_date'
6654
    );
6655
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
6656
        return $data['captcha_blocked_until_date'];
6657
    }
6658
6659
    return false;
6660
}
6661
6662
/**
6663
 * If true, the drh can access all content (courses, users) inside a session.
6664
 *
6665
 * @return bool
6666
 */
6667
function api_drh_can_access_all_session_content()
6668
{
6669
    return 'true' === api_get_setting('drh_can_access_all_session_content');
6670
}
6671
6672
/**
6673
 * Checks if user can login as another user.
6674
 *
6675
 * @param int $loginAsUserId the user id to log in
6676
 * @param int $userId        my user id
6677
 *
6678
 * @return bool
6679
 */
6680
function api_can_login_as($loginAsUserId, $userId = null)
6681
{
6682
    $loginAsUserId = (int) $loginAsUserId;
6683
6684
    if (empty($loginAsUserId)) {
6685
        return false;
6686
    }
6687
6688
    if (empty($userId)) {
6689
        $userId = api_get_user_id();
6690
    }
6691
6692
    if ($loginAsUserId == $userId) {
6693
        return false;
6694
    }
6695
6696
    // Check if the user to login is an admin
6697
    if (api_is_platform_admin_by_id($loginAsUserId)) {
6698
        // Only super admins can login to admin accounts
6699
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
6700
            return false;
6701
        }
6702
    }
6703
6704
    $userInfo = api_get_user_info($loginAsUserId);
6705
6706
    $isDrh = function () use ($loginAsUserId) {
6707
        if (api_is_drh()) {
6708
            if (api_drh_can_access_all_session_content()) {
6709
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
6710
                    'drh_all',
6711
                    api_get_user_id()
6712
                );
6713
                $userList = [];
6714
                if (is_array($users)) {
6715
                    foreach ($users as $user) {
6716
                        $userList[] = $user['id'];
6717
                    }
6718
                }
6719
                if (in_array($loginAsUserId, $userList)) {
6720
                    return true;
6721
                }
6722
            } else {
6723
                if (api_is_drh() &&
6724
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
6725
                ) {
6726
                    return true;
6727
                }
6728
            }
6729
        }
6730
6731
        return false;
6732
    };
6733
6734
    $loginAsStatusForSessionAdmins = [STUDENT];
6735
6736
    if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
6737
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
6738
    }
6739
6740
    return api_is_platform_admin() ||
6741
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
6742
        $isDrh();
6743
}
6744
6745
/**
6746
 * Return true on https install.
6747
 *
6748
 * @return bool
6749
 */
6750
function api_is_https()
6751
{
6752
    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...
6753
        'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] || !empty(api_get_configuration_value('force_https_forwarded_proto'))
6754
    ) {
6755
        $isSecured = true;
6756
    } else {
6757
        if (!empty($_SERVER['HTTPS']) && 'off' != $_SERVER['HTTPS']) {
6758
            $isSecured = true;
6759
        } else {
6760
            $isSecured = false;
6761
            // last chance
6762
            if (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) {
6763
                $isSecured = true;
6764
            }
6765
        }
6766
    }
6767
6768
    return $isSecured;
6769
}
6770
6771
/**
6772
 * Return protocol (http or https).
6773
 *
6774
 * @return string
6775
 */
6776
function api_get_protocol()
6777
{
6778
    return api_is_https() ? 'https' : 'http';
6779
}
6780
6781
/**
6782
 * Get origin.
6783
 *
6784
 * @param string
6785
 *
6786
 * @return string
6787
 */
6788
function api_get_origin()
6789
{
6790
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
6791
}
6792
6793
/**
6794
 * Warns an user that the portal reach certain limit.
6795
 *
6796
 * @param string $limitName
6797
 */
6798
function api_warn_hosting_contact($limitName)
6799
{
6800
    $hostingParams = api_get_configuration_value(1);
6801
    $email = null;
6802
6803
    if (!empty($hostingParams)) {
6804
        if (isset($hostingParams['hosting_contact_mail'])) {
6805
            $email = $hostingParams['hosting_contact_mail'];
6806
        }
6807
    }
6808
6809
    if (!empty($email)) {
6810
        $subject = get_lang('Hosting warning reached');
6811
        $body = get_lang('Portal name').': '.api_get_path(WEB_PATH)." \n ";
6812
        $body .= get_lang('Portal\'s limit type').': '.$limitName." \n ";
6813
        if (isset($hostingParams[$limitName])) {
6814
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
6815
        }
6816
        api_mail_html(null, $email, $subject, $body);
6817
    }
6818
}
6819
6820
/**
6821
 * Gets value of a variable from config/configuration.php
6822
 * Variables that are not set in the configuration.php file but set elsewhere:
6823
 * - virtual_css_theme_folder (vchamilo plugin)
6824
 * - access_url (global.inc.php)
6825
 * - apc/apc_prefix (global.inc.php).
6826
 *
6827
 * @param string $variable
6828
 *
6829
 * @return bool|mixed
6830
 */
6831
function api_get_configuration_value($variable)
6832
{
6833
    global $_configuration;
6834
    // Check the current url id, id = 1 by default
6835
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
6836
6837
    $variable = trim($variable);
6838
6839
    // Check if variable exists
6840
    if (isset($_configuration[$variable])) {
6841
        if (is_array($_configuration[$variable])) {
6842
            // Check if it exists for the sub portal
6843
            if (array_key_exists($urlId, $_configuration[$variable])) {
6844
                return $_configuration[$variable][$urlId];
6845
            } else {
6846
                // Try to found element with id = 1 (master portal)
6847
                if (array_key_exists(1, $_configuration[$variable])) {
6848
                    return $_configuration[$variable][1];
6849
                }
6850
            }
6851
        }
6852
6853
        return $_configuration[$variable];
6854
    }
6855
6856
    return false;
6857
}
6858
6859
/**
6860
 * Gets a specific hosting limit.
6861
 *
6862
 * @param int $urlId The URL ID.
6863
 * @param string $limitName The name of the limit.
6864
 * @return mixed The value of the limit, or null if not found.
6865
 */
6866
function get_hosting_limit(int $urlId, string $limitName): mixed
6867
{
6868
    if (!Container::$container->hasParameter('settings_overrides')) {
6869
        return [];
6870
    }
6871
6872
    $settingsOverrides = Container::$container->getParameter('settings_overrides');
6873
6874
    $limits = $settingsOverrides[$urlId]['hosting_limit'] ?? $settingsOverrides['default']['hosting_limit'];
6875
6876
    foreach ($limits as $limitArray) {
6877
        if (isset($limitArray[$limitName])) {
6878
            return $limitArray[$limitName];
6879
        }
6880
    }
6881
6882
    return null;
6883
}
6884
6885
6886
/**
6887
 * Retrieves an environment variable value with validation and handles boolean conversion.
6888
 *
6889
 * @param string $variable The name of the environment variable.
6890
 * @param mixed $default The default value to return if the variable is not set.
6891
 * @return mixed The value of the environment variable, converted to boolean if necessary, or the default value.
6892
 */
6893
function api_get_env_variable(string $variable, mixed $default = null): mixed
6894
{
6895
    $variable = strtolower($variable);
6896
    if (Container::$container->hasParameter($variable)) {
6897
        return Container::$container->getParameter($variable);
6898
    }
6899
6900
    return $default;
6901
}
6902
6903
/**
6904
 * Retreives and returns a value in a hierarchical configuration array
6905
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
6906
 *
6907
 * @param string $path      the successive array keys, separated by the separator
6908
 * @param mixed  $default   value to be returned if not found, null by default
6909
 * @param string $separator '/' by default
6910
 * @param array  $array     the active configuration array by default
6911
 *
6912
 * @return mixed the found value or $default
6913
 */
6914
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
6915
{
6916
    $pos = strpos($path, $separator);
6917
    if (false === $pos) {
6918
        if (is_null($array)) {
6919
            return api_get_configuration_value($path);
6920
        }
6921
        if (is_array($array) && array_key_exists($path, $array)) {
6922
            return $array[$path];
6923
        }
6924
6925
        return $default;
6926
    }
6927
    $key = substr($path, 0, $pos);
6928
    if (is_null($array)) {
6929
        $newArray = api_get_configuration_value($key);
6930
    } elseif (is_array($array) && array_key_exists($key, $array)) {
6931
        $newArray = $array[$key];
6932
    } else {
6933
        return $default;
6934
    }
6935
    if (is_array($newArray)) {
6936
        $newPath = substr($path, $pos + 1);
6937
6938
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
6939
    }
6940
6941
    return $default;
6942
}
6943
6944
/**
6945
 * Retrieves and returns a value in a hierarchical configuration array
6946
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
6947
 *
6948
 * @param array  $array     the recursive array that contains the value to be returned (or not)
6949
 * @param string $path      the successive array keys, separated by the separator
6950
 * @param mixed  $default   the value to be returned if not found
6951
 * @param string $separator the separator substring
6952
 *
6953
 * @return mixed the found value or $default
6954
 */
6955
function api_array_sub_value($array, $path, $default = null, $separator = '/')
6956
{
6957
    $pos = strpos($path, $separator);
6958
    if (false === $pos) {
6959
        if (is_array($array) && array_key_exists($path, $array)) {
6960
            return $array[$path];
6961
        }
6962
6963
        return $default;
6964
    }
6965
    $key = substr($path, 0, $pos);
6966
    if (is_array($array) && array_key_exists($key, $array)) {
6967
        $newArray = $array[$key];
6968
    } else {
6969
        return $default;
6970
    }
6971
    if (is_array($newArray)) {
6972
        $newPath = substr($path, $pos + 1);
6973
6974
        return api_array_sub_value($newArray, $newPath, $default);
6975
    }
6976
6977
    return $default;
6978
}
6979
6980
/**
6981
 * Returns supported image extensions in the portal.
6982
 *
6983
 * @param bool $supportVectors Whether vector images should also be accepted or not
6984
 *
6985
 * @return array Supported image extensions in the portal
6986
 */
6987
function api_get_supported_image_extensions($supportVectors = true)
6988
{
6989
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
6990
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
6991
    if ($supportVectors) {
6992
        array_push($supportedImageExtensions, 'svg');
6993
    }
6994
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
6995
        array_push($supportedImageExtensions, 'webp');
6996
    }
6997
6998
    return $supportedImageExtensions;
6999
}
7000
7001
/**
7002
 * This setting changes the registration status for the campus.
7003
 *
7004
 * @author Patrick Cool <[email protected]>, Ghent University
7005
 *
7006
 * @version August 2006
7007
 *
7008
 * @param bool $listCampus Whether we authorize
7009
 *
7010
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
7011
 */
7012
function api_register_campus($listCampus = true)
7013
{
7014
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
7015
7016
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
7017
    Database::query($sql);
7018
7019
    if (!$listCampus) {
7020
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
7021
        Database::query($sql);
7022
    }
7023
}
7024
7025
/**
7026
 * Check whether the user type should be exclude.
7027
 * Such as invited or anonymous users.
7028
 *
7029
 * @param bool $checkDB Optional. Whether check the user status
7030
 * @param int  $userId  Options. The user id
7031
 *
7032
 * @return bool
7033
 */
7034
function api_is_excluded_user_type($checkDB = false, $userId = 0)
7035
{
7036
    if ($checkDB) {
7037
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
7038
7039
        if (0 == $userId) {
7040
            return true;
7041
        }
7042
7043
        $userInfo = api_get_user_info($userId);
7044
7045
        switch ($userInfo['status']) {
7046
            case INVITEE:
7047
            case ANONYMOUS:
7048
                return true;
7049
            default:
7050
                return false;
7051
        }
7052
    }
7053
7054
    $isInvited = api_is_invitee();
7055
    $isAnonymous = api_is_anonymous();
7056
7057
    if ($isInvited || $isAnonymous) {
7058
        return true;
7059
    }
7060
7061
    return false;
7062
}
7063
7064
/**
7065
 * Get the user status to ignore in reports.
7066
 *
7067
 * @param string $format Optional. The result type (array or string)
7068
 *
7069
 * @return array|string
7070
 */
7071
function api_get_users_status_ignored_in_reports($format = 'array')
7072
{
7073
    $excludedTypes = [
7074
        INVITEE,
7075
        ANONYMOUS,
7076
    ];
7077
7078
    if ('string' == $format) {
7079
        return implode(', ', $excludedTypes);
7080
    }
7081
7082
    return $excludedTypes;
7083
}
7084
7085
/**
7086
 * Set the Site Use Cookie Warning for 1 year.
7087
 */
7088
function api_set_site_use_cookie_warning_cookie()
7089
{
7090
    setcookie('ChamiloUsesCookies', 'ok', time() + 31556926);
7091
}
7092
7093
/**
7094
 * Return true if the Site Use Cookie Warning Cookie warning exists.
7095
 *
7096
 * @return bool
7097
 */
7098
function api_site_use_cookie_warning_cookie_exist()
7099
{
7100
    return isset($_COOKIE['ChamiloUsesCookies']);
7101
}
7102
7103
/**
7104
 * Given a number of seconds, format the time to show hours, minutes and seconds.
7105
 *
7106
 * @param int    $time         The time in seconds
7107
 * @param string $originFormat Optional. PHP o JS
7108
 *
7109
 * @return string (00h00'00")
7110
 */
7111
function api_format_time($time, $originFormat = 'php')
7112
{
7113
    $h = get_lang('h');
7114
    $hours = $time / 3600;
7115
    $mins = ($time % 3600) / 60;
7116
    $secs = ($time % 60);
7117
7118
    if ($time < 0) {
7119
        $hours = 0;
7120
        $mins = 0;
7121
        $secs = 0;
7122
    }
7123
7124
    if ('js' === $originFormat) {
7125
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
7126
    } else {
7127
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
7128
    }
7129
7130
    return $formattedTime;
7131
}
7132
7133
function api_set_noreply_and_from_address_to_mailer(
7134
    TemplatedEmail $email,
7135
    array $sender,
7136
    array $replyToAddress = []
7137
): void {
7138
    $validator = Container::getLegacyHelper()->getValidator();
7139
    $emailConstraint = new Assert\Email();
7140
7141
    $noReplyAddress = api_get_setting('noreply_email_address');
7142
    $avoidReplyToAddress = false;
7143
7144
    if (!empty($noReplyAddress)) {
7145
        // $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
7146
    }
7147
7148
    // Default values
7149
    $notification = new Notification();
7150
    $defaultSenderName = $notification->getDefaultPlatformSenderName();
7151
    $defaultSenderEmail = $notification->getDefaultPlatformSenderEmail();
7152
7153
    // If the parameter is set don't use the admin.
7154
    $senderName = !empty($sender['name']) ? $sender['name'] : $defaultSenderName;
7155
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $defaultSenderEmail;
7156
7157
    // Send errors to the platform admin
7158
    $adminEmail = api_get_setting('admin.administrator_email');
7159
7160
    $adminEmailValidation = $validator->validate($adminEmail, $emailConstraint);
7161
7162
    if (!empty($adminEmail) && 0 === $adminEmailValidation->count()) {
7163
        $email
7164
            ->getHeaders()
7165
            ->addIdHeader('Errors-To', $adminEmail)
7166
        ;
7167
    }
7168
7169
    if (!$avoidReplyToAddress && !empty($replyToAddress)) {
7170
        $replyToEmailValidation = $validator->validate($replyToAddress['mail'], $emailConstraint);
7171
7172
        if (0 === $replyToEmailValidation->count()) {
7173
            $email->addReplyTo(new Address($replyToAddress['mail'], $replyToAddress['name']));
7174
        }
7175
    }
7176
7177
    if ('true' === api_get_setting('mail.smtp_unique_sender')) {
7178
        $senderName = $defaultSenderName;
7179
        $senderEmail = $defaultSenderEmail;
7180
7181
        $email->sender(new Address($senderEmail, $senderName));
7182
    }
7183
7184
    if ($senderEmail) {
7185
        $email->from(new Address($senderEmail, $senderName));
7186
    }
7187
}
7188
7189
/**
7190
 * Sends an email
7191
 * Sender name and email can be specified, if not specified
7192
 * name and email of the platform admin are used.
7193
 *
7194
 * @param string    name of recipient
7195
 * @param string    email of recipient
7196
 * @param string    email subject
7197
 * @param string    email body
7198
 * @param string    sender name
7199
 * @param string    sender e-mail
7200
 * @param array     extra headers in form $headers = array($name => $value) to allow parsing
7201
 * @param array     data file (path and filename)
7202
 * @param bool      True for attaching a embedded file inside content html (optional)
7203
 * @param array     Additional parameters
7204
 *
7205
 * @return bool true if mail was sent
7206
 */
7207
function api_mail_html(
7208
    $recipientName,
7209
    $recipientEmail,
7210
    $subject,
7211
    $body,
7212
    $senderName = '',
7213
    $senderEmail = '',
7214
    $extra_headers = [],
7215
    $data_file = [],
7216
    $embeddedImage = false,
7217
    $additionalParameters = [],
7218
    string $sendErrorTo = null
7219
) {
7220
    $mailHelper = Container::$container->get(MailHelper::class);
7221
7222
    return $mailHelper->send(
7223
        $recipientName,
7224
        $recipientEmail,
7225
        $subject,
7226
        $body,
7227
        $senderName,
7228
        $senderEmail,
7229
        $extra_headers,
7230
        $data_file,
7231
        $embeddedImage,
7232
        $additionalParameters,
7233
        $sendErrorTo
7234
    );
7235
}
7236
7237
/**
7238
 * @param int  $tool       Possible values: GroupManager::GROUP_TOOL_*
7239
 * @param bool $showHeader
7240
 */
7241
function api_protect_course_group($tool, $showHeader = true)
7242
{
7243
    $groupId = api_get_group_id();
7244
    if (!empty($groupId)) {
7245
        if (api_is_platform_admin()) {
7246
            return true;
7247
        }
7248
7249
        if (api_is_allowed_to_edit(false, true, true)) {
7250
            return true;
7251
        }
7252
7253
        $userId = api_get_user_id();
7254
        $sessionId = api_get_session_id();
7255
        if (!empty($sessionId)) {
7256
            if (api_is_coach($sessionId, api_get_course_int_id())) {
7257
                return true;
7258
            }
7259
7260
            if (api_is_drh()) {
7261
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
7262
                    return true;
7263
                }
7264
            }
7265
        }
7266
7267
        $group = api_get_group_entity($groupId);
7268
7269
        // Group doesn't exists
7270
        if (null === $group) {
7271
            api_not_allowed($showHeader);
7272
        }
7273
7274
        // Check group access
7275
        $allow = GroupManager::userHasAccess(
7276
            $userId,
7277
            $group,
7278
            $tool
7279
        );
7280
7281
        if (!$allow) {
7282
            api_not_allowed($showHeader);
7283
        }
7284
    }
7285
7286
    return false;
7287
}
7288
7289
/**
7290
 * Check if a date is in a date range.
7291
 *
7292
 * @param datetime $startDate
7293
 * @param datetime $endDate
7294
 * @param datetime $currentDate
7295
 *
7296
 * @return bool true if date is in rage, false otherwise
7297
 */
7298
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
7299
{
7300
    $startDate = strtotime(api_get_local_time($startDate));
7301
    $endDate = strtotime(api_get_local_time($endDate));
7302
    $currentDate = strtotime(api_get_local_time($currentDate));
7303
7304
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
7305
        return true;
7306
    }
7307
7308
    return false;
7309
}
7310
7311
/**
7312
 * Eliminate the duplicates of a multidimensional array by sending the key.
7313
 *
7314
 * @param array $array multidimensional array
7315
 * @param int   $key   key to find to compare
7316
 *
7317
 * @return array
7318
 */
7319
function api_unique_multidim_array($array, $key)
7320
{
7321
    $temp_array = [];
7322
    $i = 0;
7323
    $key_array = [];
7324
7325
    foreach ($array as $val) {
7326
        if (!in_array($val[$key], $key_array)) {
7327
            $key_array[$i] = $val[$key];
7328
            $temp_array[$i] = $val;
7329
        }
7330
        $i++;
7331
    }
7332
7333
    return $temp_array;
7334
}
7335
7336
/**
7337
 * Limit the access to Session Admins when the limit_session_admin_role
7338
 * configuration variable is set to true.
7339
 */
7340
function api_protect_limit_for_session_admin()
7341
{
7342
    $limitAdmin = api_get_setting('limit_session_admin_role');
7343
    if (api_is_session_admin() && 'true' === $limitAdmin) {
7344
        api_not_allowed(true);
7345
    }
7346
}
7347
7348
/**
7349
 * Limits that a session admin has access to list users.
7350
 * When limit_session_admin_list_users configuration variable is set to true.
7351
 */
7352
function api_protect_session_admin_list_users()
7353
{
7354
    $limitAdmin = ('true' === api_get_setting('session.limit_session_admin_list_users'));
7355
7356
    if (api_is_session_admin() && true === $limitAdmin) {
7357
        api_not_allowed(true);
7358
    }
7359
}
7360
7361
/**
7362
 * @return bool
7363
 */
7364
function api_is_student_view_active(): bool
7365
{
7366
    $studentView = Session::read('studentview');
7367
7368
    return 'studentview' === $studentView;
7369
}
7370
7371
/**
7372
 * Converts string value to float value.
7373
 *
7374
 * 3.141516 => 3.141516
7375
 * 3,141516 => 3.141516
7376
 *
7377
 * @todo WIP
7378
 *
7379
 * @param string $number
7380
 *
7381
 * @return float
7382
 */
7383
function api_float_val($number)
7384
{
7385
    return (float) str_replace(',', '.', trim($number));
7386
}
7387
7388
/**
7389
 * Converts float values
7390
 * Example if $decimals = 2.
7391
 *
7392
 * 3.141516 => 3.14
7393
 * 3,141516 => 3,14
7394
 *
7395
 * @param string $number            number in iso code
7396
 * @param int    $decimals
7397
 * @param string $decimalSeparator
7398
 * @param string $thousandSeparator
7399
 *
7400
 * @return bool|string
7401
 */
7402
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
7403
{
7404
    $number = api_float_val($number);
7405
7406
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
7407
}
7408
7409
/**
7410
 * Set location url with a exit break by default.
7411
 *
7412
 * @param string $url
7413
 * @param bool   $exit
7414
 */
7415
function api_location($url, $exit = true)
7416
{
7417
    header('Location: '.$url);
7418
7419
    if ($exit) {
7420
        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...
7421
    }
7422
}
7423
7424
/**
7425
 * @param string $from
7426
 * @param string $to
7427
 *
7428
 * @return string
7429
 */
7430
function api_get_relative_path($from, $to)
7431
{
7432
    // some compatibility fixes for Windows paths
7433
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
7434
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
7435
    $from = str_replace('\\', '/', $from);
7436
    $to = str_replace('\\', '/', $to);
7437
7438
    $from = explode('/', $from);
7439
    $to = explode('/', $to);
7440
    $relPath = $to;
7441
7442
    foreach ($from as $depth => $dir) {
7443
        // find first non-matching dir
7444
        if ($dir === $to[$depth]) {
7445
            // ignore this directory
7446
            array_shift($relPath);
7447
        } else {
7448
            // get number of remaining dirs to $from
7449
            $remaining = count($from) - $depth;
7450
            if ($remaining > 1) {
7451
                // add traversals up to first matching dir
7452
                $padLength = (count($relPath) + $remaining - 1) * -1;
7453
                $relPath = array_pad($relPath, $padLength, '..');
7454
                break;
7455
            } else {
7456
                $relPath[0] = './'.$relPath[0];
7457
            }
7458
        }
7459
    }
7460
7461
    return implode('/', $relPath);
7462
}
7463
7464
/**
7465
 * @param string $template
7466
 *
7467
 * @return string
7468
 */
7469
function api_find_template($template)
7470
{
7471
    return Template::findTemplateFilePath($template);
7472
}
7473
7474
/**
7475
 * @return array
7476
 */
7477
function api_get_language_list_for_flag()
7478
{
7479
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
7480
    $sql = "SELECT english_name, isocode FROM $table
7481
            ORDER BY original_name ASC";
7482
    static $languages = [];
7483
    if (empty($languages)) {
7484
        $result = Database::query($sql);
7485
        while ($row = Database::fetch_array($result)) {
7486
            $languages[$row['english_name']] = $row['isocode'];
7487
        }
7488
        $languages['english'] = 'gb';
7489
    }
7490
7491
    return $languages;
7492
}
7493
7494
function api_create_zip(string $name): ZipStream
7495
{
7496
    $zipStreamOptions = new Archive();
7497
    $zipStreamOptions->setSendHttpHeaders(true);
7498
    $zipStreamOptions->setContentDisposition('attachment');
7499
    $zipStreamOptions->setContentType('application/x-zip');
7500
7501
    return new ZipStream($name, $zipStreamOptions);
7502
}
7503
7504
function api_get_language_translate_html(): string
7505
{
7506
    $translate = 'true' === api_get_setting('editor.translate_html');
7507
7508
    if (!$translate) {
7509
        return '';
7510
    }
7511
7512
    /*$languageList = api_get_languages();
7513
    $hideAll = '';
7514
    foreach ($languageList as $isocode => $name) {
7515
        $hideAll .= '
7516
        $(".mce-translatehtml").hide();
7517
        $("span:lang('.$isocode.')").filter(
7518
            function(e, val) {
7519
                // Only find the spans if they have set the lang
7520
                if ($(this).attr("lang") == null) {
7521
                    return false;
7522
                }
7523
                // Ignore ckeditor classes
7524
                return !this.className.match(/cke(.*)/);
7525
        }).hide();'."\n";
7526
    }*/
7527
7528
    $userInfo = api_get_user_info();
7529
    if (!empty($userInfo['language'])) {
7530
        $isoCode = $userInfo['language'];
7531
7532
        return '
7533
            $(function() {
7534
                $(".mce-translatehtml").hide();
7535
                var defaultLanguageFromUser = "'.$isoCode.'";
7536
                $("span:lang('.$isoCode.')").show();
7537
            });
7538
        ';
7539
    }
7540
7541
    return '';
7542
}
7543
7544
function api_protect_webservices()
7545
{
7546
    if (api_get_configuration_value('disable_webservices')) {
7547
        echo "Webservices are disabled. \n";
7548
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
7549
        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...
7550
    }
7551
}
7552
7553
function api_filename_has_blacklisted_stream_wrapper(string $filename) {
7554
    if (strpos($filename, '://') > 0) {
7555
        $wrappers = stream_get_wrappers();
7556
        $allowedWrappers = ['http', 'https', 'file'];
7557
        foreach ($wrappers as $wrapper) {
7558
            if (in_array($wrapper, $allowedWrappers)) {
7559
                continue;
7560
            }
7561
            if (stripos($filename, $wrapper . '://') === 0) {
7562
                return true;
7563
            }
7564
        }
7565
    }
7566
    return false;
7567
}
7568
7569
/**
7570
 * Checks if a set of roles have a specific permission.
7571
 *
7572
 * @param string $permissionSlug The slug of the permission to check.
7573
 * @param array  $roles          An array of role codes to check against.
7574
 * @return bool True if any of the roles have the permission, false otherwise.
7575
 */
7576
function api_get_permission(string $permissionSlug, array $roles): bool
7577
{
7578
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
7579
7580
    return $permissionService->hasPermission($permissionSlug, $roles);
7581
}
7582
7583
/**
7584
 * Calculate the percentage of change between two numbers.
7585
 *
7586
 * @param int $newValue
7587
 * @param int $oldValue
7588
 * @return string
7589
 */
7590
function api_calculate_increment_percent(int $newValue, int $oldValue): string
7591
{
7592
    if ($oldValue <= 0) {
7593
        $result = " - ";
7594
    } else {
7595
        $result = ' '.round(100 * (($newValue / $oldValue) - 1), 2).' %';
7596
    }
7597
    return $result;
7598
}
7599