Passed
Pull Request — master (#6206)
by
unknown
08:33
created

api_normalize_role_code()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

1002
    /** @scrutinizer ignore-call */ 
1003
    $pluginHelper = Container::$container->get(PluginHelper::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...
1003
1004
    if ($pluginHelper->isPluginEnabled('Positioning')) {
1005
        $plugin = $pluginHelper->loadLegacyPlugin('Positioning');
1006
1007
        if ($plugin && $plugin->get('block_course_if_initial_exercise_not_attempted') === 'true') {
1008
            $currentPath = $_SERVER['REQUEST_URI'];
1009
1010
            $allowedPatterns = [
1011
                '#^/course/\d+/home#',
1012
                '#^/plugin/Positioning/#',
1013
                '#^/main/course_home/#',
1014
                '#^/main/exercise/#',
1015
                '#^/main/inc/ajax/exercise.ajax.php#',
1016
            ];
1017
1018
            $isWhitelisted = false;
1019
            foreach ($allowedPatterns as $pattern) {
1020
                if (preg_match($pattern, $currentPath)) {
1021
                    $isWhitelisted = true;
1022
                    break;
1023
                }
1024
            }
1025
1026
            if (!$isWhitelisted) {
1027
                $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1028
1029
                if (!empty($initialData['exercise_id'])) {
1030
                    $results = Event::getExerciseResultsByUser(
0 ignored issues
show
Bug introduced by
The method getExerciseResultsByUser() does not exist on Event. ( Ignorable by Annotation )

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

1030
                    /** @scrutinizer ignore-call */ 
1031
                    $results = Event::getExerciseResultsByUser(

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...
1031
                        api_get_user_id(),
1032
                        (int) $initialData['exercise_id'],
1033
                        $course_info['real_id'],
1034
                        $session_id
1035
                    );
1036
1037
                    if (empty($results)) {
1038
                        api_not_allowed($print_headers);
1039
                        return false;
1040
                    }
1041
                }
1042
            }
1043
        }
1044
    }
1045
1046
    api_block_inactive_user();
1047
1048
    return true;
1049
}
1050
1051
/**
1052
 * Function used to protect an admin script.
1053
 *
1054
 * The function blocks access when the user has no platform admin rights
1055
 * with an error message printed on default output
1056
 *
1057
 * @param bool Whether to allow session admins as well
1058
 * @param bool Whether to allow HR directors as well
1059
 * @param string An optional message (already passed through get_lang)
1060
 *
1061
 * @return bool True if user is allowed, false otherwise.
1062
 *              The function also outputs an error message in case not allowed
1063
 *
1064
 * @author Roan Embrechts (original author)
1065
 */
1066
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1067
{
1068
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1069
        api_not_allowed(true, $message);
1070
1071
        return false;
1072
    }
1073
    api_block_inactive_user();
1074
1075
    return true;
1076
}
1077
1078
/**
1079
 * Blocks inactive users with a currently active session from accessing more pages "live".
1080
 *
1081
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1082
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1083
 */
1084
function api_block_inactive_user()
1085
{
1086
    $data = true;
1087
    if ('true' !== api_get_setting('security.security_block_inactive_users_immediately')) {
1088
        return $data;
1089
    }
1090
1091
    $userId = api_get_user_id();
1092
    $homeUrl = api_get_path(WEB_PATH);
1093
    if (0 == $userId) {
1094
        return $data;
1095
    }
1096
1097
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1098
            WHERE id = $userId";
1099
1100
    $result = Database::query($sql);
1101
    if (Database::num_rows($result) > 0) {
1102
        $result_array = Database::fetch_array($result);
1103
        $data = (bool) $result_array['active'];
1104
    }
1105
    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...
1106
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1107
        $tpl->assign('hide_login_link', 1);
1108
1109
        //api_not_allowed(true, get_lang('Account inactive'));
1110
        // we were not in a course, return to home page
1111
        $msg = Display::return_message(
1112
            get_lang('Account inactive'),
1113
            'error',
1114
            false
1115
        );
1116
1117
        $msg .= '<p class="text-center">
1118
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1119
1120
        $tpl->assign('content', $msg);
1121
        $tpl->display_one_col_template();
1122
        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...
1123
    }
1124
1125
    return $data;
1126
}
1127
1128
/**
1129
 * Function used to protect a teacher script.
1130
 * The function blocks access when the user has no teacher rights.
1131
 *
1132
 * @return bool True if the current user can access the script, false otherwise
1133
 *
1134
 * @author Yoselyn Castillo
1135
 */
1136
function api_protect_teacher_script()
1137
{
1138
    if (!api_is_allowed_to_edit()) {
1139
        api_not_allowed(true);
1140
1141
        return false;
1142
    }
1143
1144
    return true;
1145
}
1146
1147
/**
1148
 * Function used to prevent anonymous users from accessing a script.
1149
 *
1150
 * @param bool $printHeaders
1151
 *
1152
 * @return bool
1153
 */
1154
function api_block_anonymous_users($printHeaders = true)
1155
{
1156
    $isAuth = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
1157
1158
    if (false === $isAuth) {
1159
        api_not_allowed($printHeaders);
1160
1161
        return false;
1162
    }
1163
1164
    api_block_inactive_user();
1165
1166
    return true;
1167
}
1168
1169
/**
1170
 * Returns a rough evaluation of the browser's name and version based on very
1171
 * simple regexp.
1172
 *
1173
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1174
 */
1175
function api_get_navigator()
1176
{
1177
    $navigator = 'Unknown';
1178
    $version = 0;
1179
1180
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1181
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1182
    }
1183
1184
    if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
1185
        $navigator = 'Opera';
1186
        [, $version] = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1187
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
1188
        $navigator = 'Edge';
1189
        [, $version] = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1190
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
1191
        $navigator = 'Internet Explorer';
1192
        [, $version] = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1193
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
1194
        $navigator = 'Chrome';
1195
        [, $version] = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1196
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
1197
        $navigator = 'Safari';
1198
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Version/')) {
1199
            // If this Safari does have the "Version/" string in its user agent
1200
            // then use that as a version indicator rather than what's after
1201
            // "Safari/" which is rather a "build number" or something
1202
            [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1203
        } else {
1204
            [, $version] = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1205
        }
1206
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
1207
        $navigator = 'Firefox';
1208
        [, $version] = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1209
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape')) {
1210
        $navigator = 'Netscape';
1211
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/')) {
1212
            [, $version] = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1213
        } else {
1214
            [, $version] = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1215
        }
1216
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror')) {
1217
        $navigator = 'Konqueror';
1218
        [, $version] = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1219
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit')) {
1220
        $navigator = 'AppleWebKit';
1221
        [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1222
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko')) {
1223
        $navigator = 'Mozilla';
1224
        [, $version] = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1225
    }
1226
1227
    // Now cut extra stuff around (mostly *after*) the version number
1228
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1229
1230
    if (false === strpos($version, '.')) {
1231
        $version = number_format(doubleval($version), 1);
1232
    }
1233
1234
    return ['name' => $navigator, 'version' => $version];
1235
}
1236
1237
/**
1238
 * This function returns the id of the user which is stored in the $_user array.
1239
 *
1240
 * example: The function can be used to check if a user is logged in
1241
 *          if (api_get_user_id())
1242
 *
1243
 * @return int the id of the current user, 0 if is empty
1244
 */
1245
function api_get_user_id()
1246
{
1247
    $userInfo = Session::read('_user');
1248
    if ($userInfo && isset($userInfo['user_id'])) {
1249
        return (int) $userInfo['user_id'];
1250
    }
1251
1252
    return 0;
1253
}
1254
1255
/**
1256
 * Formats user information into a standard array
1257
 * This function should be only used inside api_get_user_info().
1258
 *
1259
 * @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...
1260
 * @param bool $add_password
1261
 * @param bool $loadAvatars  turn off to improve performance
1262
 *
1263
 * @return array Standard user array
1264
 */
1265
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1266
{
1267
    $result = [];
1268
1269
    if (!isset($user['id'])) {
1270
        return [];
1271
    }
1272
1273
    $result['firstname'] = null;
1274
    $result['lastname'] = null;
1275
1276
    if (isset($user['firstname']) && isset($user['lastname'])) {
1277
        // with only lowercase
1278
        $result['firstname'] = $user['firstname'];
1279
        $result['lastname'] = $user['lastname'];
1280
    } elseif (isset($user['firstName']) && isset($user['lastName'])) {
1281
        // with uppercase letters
1282
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1283
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1284
    }
1285
1286
    if (isset($user['email'])) {
1287
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1288
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1289
    } else {
1290
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1291
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1292
    }
1293
1294
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1295
    $result['complete_name_with_username'] = $result['complete_name'];
1296
1297
    if (!empty($user['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1298
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1299
    }
1300
1301
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1302
    if (!empty($user['email'])) {
1303
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1304
        if ($showEmail) {
1305
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1306
        }
1307
    } else {
1308
        $result['complete_name_with_email'] = $result['complete_name'];
1309
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1310
    }
1311
1312
    // Kept for historical reasons
1313
    $result['firstName'] = $result['firstname'];
1314
    $result['lastName'] = $result['lastname'];
1315
1316
    $attributes = [
1317
        'phone',
1318
        'address',
1319
        'picture_uri',
1320
        'official_code',
1321
        'status',
1322
        'active',
1323
        'auth_sources',
1324
        'username',
1325
        'theme',
1326
        'language',
1327
        'locale',
1328
        'creator_id',
1329
        'created_at',
1330
        'hr_dept_id',
1331
        'expiration_date',
1332
        'last_login',
1333
        'user_is_online',
1334
        'profile_completed',
1335
    ];
1336
1337
    if ('true' === api_get_setting('extended_profile')) {
1338
        $attributes[] = 'competences';
1339
        $attributes[] = 'diplomas';
1340
        $attributes[] = 'teach';
1341
        $attributes[] = 'openarea';
1342
    }
1343
1344
    foreach ($attributes as $attribute) {
1345
        $result[$attribute] = $user[$attribute] ?? null;
1346
    }
1347
1348
    $user_id = (int) $user['id'];
1349
    // Maintain the user_id index for backwards compatibility
1350
    $result['user_id'] = $result['id'] = $user_id;
1351
1352
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1353
    $result['has_certificates'] = 0;
1354
    if (!empty($hasCertificates)) {
1355
        $result['has_certificates'] = 1;
1356
    }
1357
1358
    $result['icon_status'] = '';
1359
    $result['icon_status_medium'] = '';
1360
    $result['is_admin'] = UserManager::is_admin($user_id);
1361
1362
    // Getting user avatar.
1363
    if ($loadAvatars) {
1364
        $result['avatar'] = '';
1365
        $result['avatar_no_query'] = '';
1366
        $result['avatar_small'] = '';
1367
        $result['avatar_medium'] = '';
1368
1369
        if (empty($user['avatar'])) {
1370
            $originalFile = UserManager::getUserPicture(
1371
                $user_id,
1372
                USER_IMAGE_SIZE_ORIGINAL,
1373
                null,
1374
                $result
1375
            );
1376
            $result['avatar'] = $originalFile;
1377
            $avatarString = explode('?', $result['avatar']);
1378
            $result['avatar_no_query'] = reset($avatarString);
1379
        } else {
1380
            $result['avatar'] = $user['avatar'];
1381
            $avatarString = explode('?', $user['avatar']);
1382
            $result['avatar_no_query'] = reset($avatarString);
1383
        }
1384
1385
        if (!isset($user['avatar_small'])) {
1386
            $smallFile = UserManager::getUserPicture(
1387
                $user_id,
1388
                USER_IMAGE_SIZE_SMALL,
1389
                null,
1390
                $result
1391
            );
1392
            $result['avatar_small'] = $smallFile;
1393
        } else {
1394
            $result['avatar_small'] = $user['avatar_small'];
1395
        }
1396
1397
        if (!isset($user['avatar_medium'])) {
1398
            $mediumFile = UserManager::getUserPicture(
1399
                $user_id,
1400
                USER_IMAGE_SIZE_MEDIUM,
1401
                null,
1402
                $result
1403
            );
1404
            $result['avatar_medium'] = $mediumFile;
1405
        } else {
1406
            $result['avatar_medium'] = $user['avatar_medium'];
1407
        }
1408
1409
        $urlImg = api_get_path(WEB_IMG_PATH);
1410
        $iconStatus = '';
1411
        $iconStatusMedium = '';
1412
        $label = '';
1413
1414
        switch ($result['status']) {
1415
            case STUDENT:
1416
                if ($result['has_certificates']) {
1417
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1418
                    $label = get_lang('Graduated');
1419
                } else {
1420
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1421
                    $label = get_lang('Learner');
1422
                }
1423
                break;
1424
            case COURSEMANAGER:
1425
                if ($result['is_admin']) {
1426
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1427
                    $label = get_lang('Admin');
1428
                } else {
1429
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1430
                    $label = get_lang('Teacher');
1431
                }
1432
                break;
1433
            case STUDENT_BOSS:
1434
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1435
                $label = get_lang('Student boss');
1436
                break;
1437
        }
1438
1439
        if (!empty($iconStatus)) {
1440
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1441
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1442
        }
1443
1444
        $result['icon_status'] = $iconStatus;
1445
        $result['icon_status_label'] = $label;
1446
        $result['icon_status_medium'] = $iconStatusMedium;
1447
    }
1448
1449
    if (isset($user['user_is_online'])) {
1450
        $result['user_is_online'] = true == $user['user_is_online'] ? 1 : 0;
1451
    }
1452
    if (isset($user['user_is_online_in_chat'])) {
1453
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1454
    }
1455
1456
    if ($add_password) {
1457
        $result['password'] = $user['password'];
1458
    }
1459
1460
    if (isset($result['profile_completed'])) {
1461
        $result['profile_completed'] = $user['profile_completed'];
1462
    }
1463
1464
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1465
1466
    // Send message link
1467
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1468
    $result['complete_name_with_message_link'] = Display::url(
1469
        $result['complete_name_with_username'],
1470
        $sendMessage,
1471
        ['class' => 'ajax']
1472
    );
1473
1474
    if (isset($user['extra'])) {
1475
        $result['extra'] = $user['extra'];
1476
    }
1477
1478
    return $result;
1479
}
1480
1481
/**
1482
 * Finds all the information about a user.
1483
 * If no parameter is passed you find all the information about the current user.
1484
 *
1485
 * @param int  $user_id
1486
 * @param bool $checkIfUserOnline
1487
 * @param bool $showPassword
1488
 * @param bool $loadExtraData
1489
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1490
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1491
 * @param bool $updateCache              update apc cache if exists
1492
 *
1493
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1494
 *
1495
 * @author Patrick Cool <[email protected]>
1496
 * @author Julio Montoya
1497
 *
1498
 * @version 21 September 2004
1499
 */
1500
function api_get_user_info(
1501
    $user_id = 0,
1502
    $checkIfUserOnline = false,
1503
    $showPassword = false,
1504
    $loadExtraData = false,
1505
    $loadOnlyVisibleExtraData = false,
1506
    $loadAvatars = true,
1507
    $updateCache = false
1508
) {
1509
    // Make sure user_id is safe
1510
    $user_id = (int) $user_id;
1511
    $user = false;
1512
    if (empty($user_id)) {
1513
        $userFromSession = Session::read('_user');
1514
        if (isset($userFromSession) && !empty($userFromSession)) {
1515
            return $userFromSession;
1516
            /*
1517
            return _api_format_user(
1518
                $userFromSession,
1519
                $showPassword,
1520
                $loadAvatars
1521
            );*/
1522
        }
1523
1524
        return false;
1525
    }
1526
1527
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1528
            WHERE id = $user_id";
1529
    $result = Database::query($sql);
1530
    if (Database::num_rows($result) > 0) {
1531
        $result_array = Database::fetch_array($result);
1532
        $result_array['auth_sources'] = api_get_user_entity($result_array['id'])->getAuthSourcesAuthentications();
1533
        $result_array['user_is_online_in_chat'] = 0;
1534
        if ($checkIfUserOnline) {
1535
            $use_status_in_platform = user_is_online($user_id);
1536
            $result_array['user_is_online'] = $use_status_in_platform;
1537
            $user_online_in_chat = 0;
1538
            if ($use_status_in_platform) {
1539
                $user_status = UserManager::get_extra_user_data_by_field(
1540
                    $user_id,
1541
                    'user_chat_status',
1542
                    false,
1543
                    true
1544
                );
1545
                if (1 == (int) $user_status['user_chat_status']) {
1546
                    $user_online_in_chat = 1;
1547
                }
1548
            }
1549
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1550
        }
1551
1552
        if ($loadExtraData) {
1553
            $fieldValue = new ExtraFieldValue('user');
1554
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1555
                $user_id,
1556
                $loadOnlyVisibleExtraData
1557
            );
1558
        }
1559
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1560
    }
1561
1562
    return $user;
1563
}
1564
1565
function api_get_user_info_from_entity(
1566
    User $user,
1567
    $checkIfUserOnline = false,
1568
    $showPassword = false,
1569
    $loadExtraData = false,
1570
    $loadOnlyVisibleExtraData = false,
1571
    $loadAvatars = true,
1572
    $loadCertificate = false
1573
) {
1574
    if (!$user instanceof UserInterface) {
1575
        return false;
1576
    }
1577
1578
    // Make sure user_id is safe
1579
    $user_id = (int) $user->getId();
1580
1581
    if (empty($user_id)) {
1582
        $userFromSession = Session::read('_user');
1583
1584
        if (isset($userFromSession) && !empty($userFromSession)) {
1585
            return $userFromSession;
1586
        }
1587
1588
        return false;
1589
    }
1590
1591
    $result = [];
1592
    $result['user_is_online_in_chat'] = 0;
1593
    if ($checkIfUserOnline) {
1594
        $use_status_in_platform = user_is_online($user_id);
1595
        $result['user_is_online'] = $use_status_in_platform;
1596
        $user_online_in_chat = 0;
1597
        if ($use_status_in_platform) {
1598
            $user_status = UserManager::get_extra_user_data_by_field(
1599
                $user_id,
1600
                'user_chat_status',
1601
                false,
1602
                true
1603
            );
1604
            if (1 == (int) $user_status['user_chat_status']) {
1605
                $user_online_in_chat = 1;
1606
            }
1607
        }
1608
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1609
    }
1610
1611
    if ($loadExtraData) {
1612
        $fieldValue = new ExtraFieldValue('user');
1613
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1614
            $user_id,
1615
            $loadOnlyVisibleExtraData
1616
        );
1617
    }
1618
1619
    $result['username'] = $user->getUsername();
1620
    $result['status'] = $user->getStatus();
1621
    $result['firstname'] = $user->getFirstname();
1622
    $result['lastname'] = $user->getLastname();
1623
    $result['email'] = $result['mail'] = $user->getEmail();
1624
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1625
    $result['complete_name_with_username'] = $result['complete_name'];
1626
1627
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1628
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1629
    }
1630
1631
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1632
    if (!empty($result['email'])) {
1633
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1634
        if ($showEmail) {
1635
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1636
        }
1637
    } else {
1638
        $result['complete_name_with_email'] = $result['complete_name'];
1639
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1640
    }
1641
1642
    // Kept for historical reasons
1643
    $result['firstName'] = $result['firstname'];
1644
    $result['lastName'] = $result['lastname'];
1645
1646
    $attributes = [
1647
        'picture_uri',
1648
        'last_login',
1649
        'user_is_online',
1650
    ];
1651
1652
    $result['phone'] = $user->getPhone();
1653
    $result['address'] = $user->getAddress();
1654
    $result['official_code'] = $user->getOfficialCode();
1655
    $result['active'] = $user->isActive();
1656
    $result['auth_sources'] = $user->getAuthSourcesAuthentications();
1657
    $result['language'] = $user->getLocale();
1658
    $result['creator_id'] = $user->getCreatorId();
1659
    $result['created_at'] = $user->getCreatedAt()->format('Y-m-d H:i:s');
1660
    $result['hr_dept_id'] = $user->getHrDeptId();
1661
    $result['expiration_date'] = '';
1662
    if ($user->getExpirationDate()) {
1663
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1664
    }
1665
1666
    $result['last_login'] = null;
1667
    if ($user->getLastLogin()) {
1668
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1669
    }
1670
1671
    $result['competences'] = $user->getCompetences();
1672
    $result['diplomas'] = $user->getDiplomas();
1673
    $result['teach'] = $user->getTeach();
1674
    $result['openarea'] = $user->getOpenarea();
1675
    $user_id = (int) $user->getId();
1676
1677
    // Maintain the user_id index for backwards compatibility
1678
    $result['user_id'] = $result['id'] = $user_id;
1679
1680
    if ($loadCertificate) {
1681
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1682
        $result['has_certificates'] = 0;
1683
        if (!empty($hasCertificates)) {
1684
            $result['has_certificates'] = 1;
1685
        }
1686
    }
1687
1688
    $result['icon_status'] = '';
1689
    $result['icon_status_medium'] = '';
1690
    $result['is_admin'] = UserManager::is_admin($user_id);
1691
1692
    // Getting user avatar.
1693
    if ($loadAvatars) {
1694
        $result['avatar'] = '';
1695
        $result['avatar_no_query'] = '';
1696
        $result['avatar_small'] = '';
1697
        $result['avatar_medium'] = '';
1698
        $urlImg = '/';
1699
        $iconStatus = '';
1700
        $iconStatusMedium = '';
1701
1702
        switch ($user->getStatus()) {
1703
            case STUDENT:
1704
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1705
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1706
                } else {
1707
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1708
                }
1709
                break;
1710
            case COURSEMANAGER:
1711
                if ($result['is_admin']) {
1712
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1713
                } else {
1714
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1715
                }
1716
                break;
1717
            case STUDENT_BOSS:
1718
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1719
                break;
1720
        }
1721
1722
        if (!empty($iconStatus)) {
1723
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1724
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1725
        }
1726
1727
        $result['icon_status'] = $iconStatus;
1728
        $result['icon_status_medium'] = $iconStatusMedium;
1729
    }
1730
1731
    if (isset($result['user_is_online'])) {
1732
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1733
    }
1734
    if (isset($result['user_is_online_in_chat'])) {
1735
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1736
    }
1737
1738
    $result['password'] = '';
1739
    if ($showPassword) {
1740
        $result['password'] = $user->getPassword();
1741
    }
1742
1743
    if (isset($result['profile_completed'])) {
1744
        $result['profile_completed'] = $result['profile_completed'];
1745
    }
1746
1747
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1748
1749
    // Send message link
1750
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1751
    $result['complete_name_with_message_link'] = Display::url(
1752
        $result['complete_name_with_username'],
1753
        $sendMessage,
1754
        ['class' => 'ajax']
1755
    );
1756
1757
    if (isset($result['extra'])) {
1758
        $result['extra'] = $result['extra'];
1759
    }
1760
1761
    return $result;
1762
}
1763
1764
function api_get_lp_entity(int $id): ?CLp
1765
{
1766
    return Database::getManager()->getRepository(CLp::class)->find($id);
1767
}
1768
1769
function api_get_user_entity(int $userId = 0): ?User
1770
{
1771
    $userId = $userId ?: api_get_user_id();
1772
    $repo = Container::getUserRepository();
1773
1774
    return $repo->find($userId);
1775
}
1776
1777
function api_get_current_user(): ?User
1778
{
1779
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1780
    if (false === $isLoggedIn) {
1781
        return null;
1782
    }
1783
1784
    $token = Container::getTokenStorage()->getToken();
1785
1786
    if (null !== $token) {
1787
        return $token->getUser();
1788
    }
1789
1790
    return null;
1791
}
1792
1793
/**
1794
 * Finds all the information about a user from username instead of user id.
1795
 *
1796
 * @param string $username
1797
 *
1798
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1799
 *
1800
 * @author Yannick Warnier <[email protected]>
1801
 */
1802
function api_get_user_info_from_username($username)
1803
{
1804
    if (empty($username)) {
1805
        return false;
1806
    }
1807
    $username = trim($username);
1808
1809
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1810
            WHERE username='".Database::escape_string($username)."'";
1811
    $result = Database::query($sql);
1812
    if (Database::num_rows($result) > 0) {
1813
        $resultArray = Database::fetch_array($result);
1814
1815
        return _api_format_user($resultArray);
1816
    }
1817
1818
    return false;
1819
}
1820
1821
/**
1822
 * Get first user with an email.
1823
 *
1824
 * @param string $email
1825
 *
1826
 * @return array|bool
1827
 */
1828
function api_get_user_info_from_email($email = '')
1829
{
1830
    if (empty($email)) {
1831
        return false;
1832
    }
1833
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1834
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1835
    $result = Database::query($sql);
1836
    if (Database::num_rows($result) > 0) {
1837
        $resultArray = Database::fetch_array($result);
1838
1839
        return _api_format_user($resultArray);
1840
    }
1841
1842
    return false;
1843
}
1844
1845
/**
1846
 * @return string
1847
 */
1848
function api_get_course_id()
1849
{
1850
    return Session::read('_cid', null);
1851
}
1852
1853
/**
1854
 * Returns the current course id (integer).
1855
 *
1856
 * @param ?string $code Optional course code
1857
 *
1858
 * @return int
1859
 */
1860
function api_get_course_int_id(?string $code = null): int
1861
{
1862
    if (!empty($code)) {
1863
        $code = Database::escape_string($code);
1864
        $row = Database::select(
1865
            'id',
1866
            Database::get_main_table(TABLE_MAIN_COURSE),
1867
            ['where' => ['code = ?' => [$code]]],
1868
            'first'
1869
        );
1870
1871
        if (is_array($row) && isset($row['id'])) {
1872
            return $row['id'];
1873
        } else {
1874
            return 0;
1875
        }
1876
    }
1877
1878
    $cid = Session::read('_real_cid', 0);
1879
    if (empty($cid) && isset($_REQUEST['cid'])) {
1880
        $cid = (int) $_REQUEST['cid'];
1881
    }
1882
1883
    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...
1884
}
1885
1886
/**
1887
 * Gets a course setting from the current course_setting table. Try always using integer values.
1888
 *
1889
 * @param string       $settingName The name of the setting we want from the table
1890
 * @param Course|array $courseInfo
1891
 * @param bool         $force       force checking the value in the database
1892
 *
1893
 * @return mixed The value of that setting in that table. Return -1 if not found.
1894
 */
1895
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1896
{
1897
    if (empty($courseInfo)) {
1898
        $courseInfo = api_get_course_info();
1899
    }
1900
1901
    if (empty($courseInfo) || empty($settingName)) {
1902
        return -1;
1903
    }
1904
1905
    if ($courseInfo instanceof Course) {
1906
        $courseId = $courseInfo->getId();
1907
    } else {
1908
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1909
    }
1910
1911
    if (empty($courseId)) {
1912
        return -1;
1913
    }
1914
1915
    static $courseSettingInfo = [];
1916
1917
    if ($force) {
1918
        $courseSettingInfo = [];
1919
    }
1920
1921
    if (!isset($courseSettingInfo[$courseId])) {
1922
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1923
        $settingName = Database::escape_string($settingName);
1924
1925
        $sql = "SELECT variable, value FROM $table
1926
                WHERE c_id = $courseId ";
1927
        $res = Database::query($sql);
1928
        if (Database::num_rows($res) > 0) {
1929
            $result = Database::store_result($res, 'ASSOC');
1930
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1931
1932
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1933
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1934
                if (!is_null($value)) {
1935
                    $result = explode(',', $value);
1936
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1937
                }
1938
            }
1939
        }
1940
    }
1941
1942
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1943
        return $courseSettingInfo[$courseId][$settingName];
1944
    }
1945
1946
    return -1;
1947
}
1948
1949
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1950
{
1951
    $value = api_get_course_setting($settingName, $courseInfo, true);
1952
1953
    if (-1 === $value) {
1954
        // Check global settings
1955
        $value = api_get_plugin_setting($plugin, $settingName);
1956
        if ('true' === $value) {
1957
            return 1;
1958
        }
1959
        if ('false' === $value) {
1960
            return 0;
1961
        }
1962
        if (null === $value) {
1963
            return -1;
1964
        }
1965
    }
1966
1967
    return $value;
1968
}
1969
1970
/**
1971
 * Gets an anonymous user ID.
1972
 *
1973
 * For some tools that need tracking, like the learnpath tool, it is necessary
1974
 * to have a usable user-id to enable some kind of tracking, even if not
1975
 * perfect. An anonymous ID is taken from the users table by looking for a
1976
 * status of "6" (anonymous).
1977
 *
1978
 * @return int User ID of the anonymous user, or O if no anonymous user found
1979
 */
1980
function api_get_anonymous_id()
1981
{
1982
    // Find if another anon is connected now
1983
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1984
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1985
    $ip = Database::escape_string(api_get_real_ip());
1986
    $max = (int) api_get_setting('admin.max_anonymous_users');
1987
    if ($max >= 2) {
1988
        $sql = "SELECT * FROM $table as TEL
1989
                JOIN $tableU as U
1990
                ON U.id = TEL.login_user_id
1991
                WHERE TEL.user_ip = '$ip'
1992
                    AND U.status = ".ANONYMOUS."
1993
                    AND U.id != 2 ";
1994
1995
        $result = Database::query($sql);
1996
        if (empty(Database::num_rows($result))) {
1997
            $login = uniqid('anon_');
1998
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['created_at ASC']);
1999
            if (count($anonList) >= $max) {
2000
                foreach ($anonList as $userToDelete) {
2001
                    UserManager::delete_user($userToDelete['user_id']);
2002
                    break;
2003
                }
2004
            }
2005
2006
            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...
2007
                $login,
2008
                'anon',
2009
                ANONYMOUS,
2010
                ' anonymous@localhost',
2011
                $login,
2012
                $login
2013
            );
2014
        } else {
2015
            $row = Database::fetch_assoc($result);
2016
2017
            return $row['id'];
2018
        }
2019
    }
2020
2021
    $table = Database::get_main_table(TABLE_MAIN_USER);
2022
    $sql = "SELECT id
2023
            FROM $table
2024
            WHERE status = ".ANONYMOUS." ";
2025
    $res = Database::query($sql);
2026
    if (Database::num_rows($res) > 0) {
2027
        $row = Database::fetch_assoc($res);
2028
2029
        return $row['id'];
2030
    }
2031
2032
    // No anonymous user was found.
2033
    return 0;
2034
}
2035
2036
/**
2037
 * @param int $courseId
2038
 * @param int $sessionId
2039
 * @param int $groupId
2040
 *
2041
 * @return string
2042
 */
2043
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2044
{
2045
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2046
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2047
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2048
2049
    $url = 'cid='.$courseId;
2050
    $url .= '&sid='.$sessionId;
2051
    $url .= '&gid='.$groupId;
2052
2053
    return $url;
2054
}
2055
2056
/**
2057
 * Returns the current course url part including session, group, and gradebook params.
2058
 *
2059
 * @param bool   $addSessionId
2060
 * @param bool   $addGroupId
2061
 * @param string $origin
2062
 *
2063
 * @return string Course & session references to add to a URL
2064
 */
2065
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2066
{
2067
    $courseId = api_get_course_int_id();
2068
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2069
        $courseId = (int) $_REQUEST['cid'];
2070
    }
2071
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2072
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2073
2074
    if ($addSessionId) {
2075
        if (!empty($url)) {
2076
            $sessionId = api_get_session_id();
2077
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2078
                $sessionId = (int) $_REQUEST['sid'];
2079
            }
2080
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2081
        }
2082
    }
2083
2084
    if ($addGroupId) {
2085
        if (!empty($url)) {
2086
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2087
        }
2088
    }
2089
2090
    if (!empty($url)) {
2091
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2092
        if (false !== $origin) {
2093
            $url .= '&origin=' . $origin;
2094
        }
2095
    }
2096
2097
    return $url;
2098
}
2099
2100
/**
2101
 * Get if we visited a gradebook page.
2102
 *
2103
 * @return bool
2104
 */
2105
function api_is_in_gradebook()
2106
{
2107
    return Session::read('in_gradebook', false);
2108
}
2109
2110
/**
2111
 * Set that we are in a page inside a gradebook.
2112
 */
2113
function api_set_in_gradebook()
2114
{
2115
    Session::write('in_gradebook', true);
2116
}
2117
2118
/**
2119
 * Remove gradebook session.
2120
 */
2121
function api_remove_in_gradebook()
2122
{
2123
    Session::erase('in_gradebook');
2124
}
2125
2126
/**
2127
 * Returns the current course info array see api_format_course_array()
2128
 * If the course_code is given, the returned array gives info about that
2129
 * particular course, if none given it gets the course info from the session.
2130
 *
2131
 * @param string $courseCode
2132
 *
2133
 * @return array
2134
 */
2135
function api_get_course_info($courseCode = null)
2136
{
2137
    if (!empty($courseCode)) {
2138
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2139
2140
        return api_format_course_array($course);
2141
    }
2142
2143
    $course = Session::read('_course');
2144
    if ('-1' == $course) {
2145
        $course = [];
2146
    }
2147
2148
    if (empty($course) && isset($_REQUEST['cid'])) {
2149
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2150
    }
2151
2152
    return $course;
2153
}
2154
2155
/**
2156
 * @param int $courseId
2157
 */
2158
function api_get_course_entity($courseId = 0): ?Course
2159
{
2160
    if (empty($courseId)) {
2161
        $courseId = api_get_course_int_id();
2162
    }
2163
2164
    if (empty($courseId)) {
2165
        return null;
2166
    }
2167
2168
    return Container::getCourseRepository()->find($courseId);
2169
}
2170
2171
/**
2172
 * @param int $id
2173
 */
2174
function api_get_session_entity($id = 0): ?SessionEntity
2175
{
2176
    if (empty($id)) {
2177
        $id = api_get_session_id();
2178
    }
2179
2180
    if (empty($id)) {
2181
        return null;
2182
    }
2183
2184
    return Container::getSessionRepository()->find($id);
2185
}
2186
2187
/**
2188
 * @param int $id
2189
 */
2190
function api_get_group_entity($id = 0): ?CGroup
2191
{
2192
    if (empty($id)) {
2193
        $id = api_get_group_id();
2194
    }
2195
2196
    return Container::getGroupRepository()->find($id);
2197
}
2198
2199
/**
2200
 * @param int $id
2201
 */
2202
function api_get_url_entity($id = 0): ?AccessUrl
2203
{
2204
    if (empty($id)) {
2205
        $id = api_get_current_access_url_id();
2206
    }
2207
2208
    return Container::getAccessUrlRepository()->find($id);
2209
}
2210
2211
/**
2212
 * Returns the current course info array.
2213
2214
 * Now if the course_code is given, the returned array gives info about that
2215
 * particular course, not specially the current one.
2216
 *
2217
 * @param int $id Numeric ID of the course
2218
 *
2219
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2220
 */
2221
function api_get_course_info_by_id(?int $id = 0)
2222
{
2223
    if (empty($id)) {
2224
        $course = Session::read('_course', []);
2225
2226
        return $course;
2227
    }
2228
2229
    $course = Container::getCourseRepository()->find($id);
2230
    if (empty($course)) {
2231
        return [];
2232
    }
2233
2234
    return api_format_course_array($course);
2235
}
2236
2237
/**
2238
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2239
 * to switch from 'code' to 'id' in the array.
2240
 *
2241
 * @return array
2242
 *
2243
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2244
 */
2245
function api_format_course_array(Course $course = null)
2246
{
2247
    if (empty($course)) {
2248
        return [];
2249
    }
2250
2251
    $courseData = [];
2252
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2253
2254
    // Added
2255
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2256
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2257
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2258
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2259
    $courseData['titular'] = $course->getTutorName();
2260
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2261
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2262
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2263
2264
    $courseData['visibility'] = $course->getVisibility();
2265
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2266
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2267
    $courseData['activate_legal'] = $course->getActivateLegal();
2268
    $courseData['legal'] = $course->getLegal();
2269
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2270
    $courseData['video_url'] = $course->getVideoUrl();
2271
    $courseData['sticky'] = (int) $course->isSticky();
2272
2273
    $coursePath = '/course/';
2274
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2275
2276
    // Course password
2277
    $courseData['registration_code'] = $course->getRegistrationCode();
2278
    $courseData['disk_quota'] = $course->getDiskQuota();
2279
    $courseData['course_public_url'] = $webCourseHome;
2280
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2281
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2282
2283
    $image = Display::getMdiIcon(
2284
        ObjectIcon::COURSE,
2285
        'ch-tool-icon',
2286
        null,
2287
        ICON_SIZE_BIG
2288
    );
2289
2290
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2291
    if (!empty($illustration)) {
2292
        $image = $illustration;
2293
    }
2294
2295
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2296
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2297
2298
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2299
        $courseData['duration'] = $course->getDuration();
2300
    }
2301
2302
    return $courseData;
2303
}
2304
2305
/**
2306
 * Returns a difficult to guess password.
2307
 */
2308
function api_generate_password(int $length = 8, $useRequirements = true): string
2309
{
2310
    if ($length < 2) {
2311
        $length = 2;
2312
    }
2313
2314
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2315
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2316
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2317
    $minNumbers = 2;
2318
    $length = $length - $minNumbers;
2319
    $minLowerCase = round($length / 2);
2320
    $minUpperCase = $length - $minLowerCase;
2321
    $minSpecials = 1; // Default minimum special characters
2322
2323
    $password = '';
2324
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2325
2326
    $factory = new RandomLib\Factory();
2327
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2328
2329
    if (!empty($passwordRequirements)) {
2330
        $length = $passwordRequirements['min']['length'];
2331
        $minNumbers = $passwordRequirements['min']['numeric'];
2332
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2333
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2334
        $minSpecials = $passwordRequirements['min']['specials'];
2335
2336
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2337
        // Add the rest to fill the length requirement
2338
        if ($rest > 0) {
2339
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2340
        }
2341
    }
2342
2343
    // Min digits default 2
2344
    for ($i = 0; $i < $minNumbers; $i++) {
2345
        $password .= $generator->generateInt(2, 9);
2346
    }
2347
2348
    // Min lowercase
2349
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2350
2351
    // Min uppercase
2352
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2353
2354
    // Min special characters
2355
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2356
2357
    // Shuffle the password to ensure randomness
2358
    $password = str_shuffle($password);
2359
2360
    return $password;
2361
}
2362
2363
/**
2364
 * Checks a password to see wether it is OK to use.
2365
 *
2366
 * @param string $password
2367
 *
2368
 * @return bool if the password is acceptable, false otherwise
2369
 *              Notes about what a password "OK to use" is:
2370
 *              1. The password should be at least 5 characters long.
2371
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2372
 *              3. The password should contain at least 3 letters.
2373
 *              4. It should contain at least 2 digits.
2374
 *              Settings will change if the configuration value is set: password_requirements
2375
 */
2376
function api_check_password($password)
2377
{
2378
    $passwordRequirements = Security::getPasswordRequirements();
2379
2380
    $minLength = $passwordRequirements['min']['length'];
2381
    $minNumbers = $passwordRequirements['min']['numeric'];
2382
    // Optional
2383
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2384
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2385
2386
    $minLetters = $minLowerCase + $minUpperCase;
2387
    $passwordLength = api_strlen($password);
2388
2389
    $conditions = [
2390
        'min_length' => $passwordLength >= $minLength,
2391
    ];
2392
2393
    $digits = 0;
2394
    $lowerCase = 0;
2395
    $upperCase = 0;
2396
2397
    for ($i = 0; $i < $passwordLength; $i++) {
2398
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2399
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2400
            $upperCase++;
2401
        }
2402
2403
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2404
            $lowerCase++;
2405
        }
2406
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2407
            $digits++;
2408
        }
2409
    }
2410
2411
    // Min number of digits
2412
    $conditions['min_numeric'] = $digits >= $minNumbers;
2413
2414
    if (!empty($minUpperCase)) {
2415
        // Uppercase
2416
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2417
    }
2418
2419
    if (!empty($minLowerCase)) {
2420
        // Lowercase
2421
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2422
    }
2423
2424
    // Min letters
2425
    $letters = $upperCase + $lowerCase;
2426
    $conditions['min_letters'] = $letters >= $minLetters;
2427
2428
    $isPasswordOk = true;
2429
    foreach ($conditions as $condition) {
2430
        if (false === $condition) {
2431
            $isPasswordOk = false;
2432
            break;
2433
        }
2434
    }
2435
2436
    if (false === $isPasswordOk) {
2437
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2438
        $output .= Security::getPasswordRequirementsToString($conditions);
2439
2440
        Display::addFlash(Display::return_message($output, 'warning', false));
2441
    }
2442
2443
    return $isPasswordOk;
2444
}
2445
2446
/**
2447
 * Gets the current Chamilo (not PHP/cookie) session ID.
2448
 *
2449
 * @return int O if no active session, the session ID otherwise
2450
 */
2451
function api_get_session_id()
2452
{
2453
    return (int) Session::read('sid', 0);
2454
}
2455
2456
/**
2457
 * Gets the current Chamilo (not social network) group ID.
2458
 *
2459
 * @return int O if no active session, the session ID otherwise
2460
 */
2461
function api_get_group_id()
2462
{
2463
    return Session::read('gid', 0);
2464
}
2465
2466
/**
2467
 * Gets the current or given session name.
2468
 *
2469
 * @param   int     Session ID (optional)
2470
 *
2471
 * @return string The session name, or null if not found
2472
 */
2473
function api_get_session_name($session_id = 0)
2474
{
2475
    if (empty($session_id)) {
2476
        $session_id = api_get_session_id();
2477
        if (empty($session_id)) {
2478
            return null;
2479
        }
2480
    }
2481
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2482
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2483
    $r = Database::query($s);
2484
    $c = Database::num_rows($r);
2485
    if ($c > 0) {
2486
        //technically, there can be only one, but anyway we take the first
2487
        $rec = Database::fetch_array($r);
2488
2489
        return $rec['title'];
2490
    }
2491
2492
    return null;
2493
}
2494
2495
/**
2496
 * Gets the session info by id.
2497
 *
2498
 * @param int $id Session ID
2499
 *
2500
 * @return array information of the session
2501
 */
2502
function api_get_session_info($id)
2503
{
2504
    return SessionManager::fetch($id);
2505
}
2506
2507
/**
2508
 * Gets the session visibility by session id.
2509
 *
2510
 * @deprecated Use Session::setAccessVisibilityByUser() instead.
2511
 *
2512
 * @param int  $session_id
2513
 * @param int  $courseId
2514
 * @param bool $ignore_visibility_for_admins
2515
 *
2516
 * @return int
2517
 *             0 = session still available,
2518
 *             SESSION_VISIBLE_READ_ONLY = 1,
2519
 *             SESSION_VISIBLE = 2,
2520
 *             SESSION_INVISIBLE = 3
2521
 */
2522
function api_get_session_visibility(
2523
    $session_id,
2524
    $courseId = null,
2525
    $ignore_visibility_for_admins = true,
2526
    $userId = 0
2527
) {
2528
    if (api_is_platform_admin()) {
2529
        if ($ignore_visibility_for_admins) {
2530
            return SESSION_AVAILABLE;
2531
        }
2532
    }
2533
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2534
2535
    $now = time();
2536
    if (empty($session_id)) {
2537
        return 0; // Means that the session is still available.
2538
    }
2539
2540
    $session_id = (int) $session_id;
2541
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2542
2543
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2544
2545
    if (Database::num_rows($result) <= 0) {
2546
        return SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2546
        return /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2547
    }
2548
2549
    $row = Database::fetch_assoc($result);
2550
    $visibility = $row['visibility'];
2551
2552
    // I don't care the session visibility.
2553
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2554
        // Session duration per student.
2555
        if (isset($row['duration']) && !empty($row['duration'])) {
2556
            $duration = $row['duration'] * 24 * 60 * 60;
2557
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2558
2559
            // If there is a session duration but there is no previous
2560
            // access by the user, then the session is still available
2561
            if (0 == count($courseAccess)) {
2562
                return SESSION_AVAILABLE;
2563
            }
2564
2565
            $currentTime = time();
2566
            $firstAccess = isset($courseAccess['login_course_date'])
2567
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2568
                : 0;
2569
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2570
            $userDuration = isset($userDurationData['duration'])
2571
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2572
                : 0;
2573
2574
            $totalDuration = $firstAccess + $duration + $userDuration;
2575
2576
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2577
        }
2578
2579
        return SESSION_AVAILABLE;
2580
    }
2581
2582
    // If start date was set.
2583
    if (!empty($row['access_start_date'])) {
2584
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2584
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2585
    } else {
2586
        // If there's no start date, assume it's available until the end date
2587
        $visibility = SESSION_AVAILABLE;
2588
    }
2589
2590
    // If the end date was set.
2591
    if (!empty($row['access_end_date'])) {
2592
        // Only if date_start said that it was ok
2593
        if (SESSION_AVAILABLE === $visibility) {
2594
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2595
                ? SESSION_AVAILABLE // Date still available
2596
                : $row['visibility']; // Session ends
2597
        }
2598
    }
2599
2600
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2601
    $isCoach = api_is_coach($session_id, $courseId);
2602
2603
    if ($isCoach) {
2604
        // Test start date.
2605
        if (!empty($row['coach_access_start_date'])) {
2606
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2607
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2607
            $visibility = $start < $now ? SESSION_AVAILABLE : /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2608
        }
2609
2610
        // Test end date.
2611
        if (!empty($row['coach_access_end_date'])) {
2612
            if (SESSION_AVAILABLE === $visibility) {
2613
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2614
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2615
            }
2616
        }
2617
    }
2618
2619
    return $visibility;
2620
}
2621
2622
/**
2623
 * This function returns a (star) session icon if the session is not null and
2624
 * the user is not a student.
2625
 *
2626
 * @param int $sessionId
2627
 * @param int $statusId  User status id - if 5 (student), will return empty
2628
 *
2629
 * @return string Session icon
2630
 */
2631
function api_get_session_image($sessionId, User $user)
2632
{
2633
    $sessionId = (int) $sessionId;
2634
    $image = '';
2635
    if (!$user->isStudent()) {
2636
        // Check whether is not a student
2637
        if ($sessionId > 0) {
2638
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2639
                ObjectIcon::STAR,
2640
                'ch-tool-icon',
2641
                'align:absmiddle;',
2642
                ICON_SIZE_SMALL,
2643
                get_lang('Session-specific resource')
2644
            );
2645
        }
2646
    }
2647
2648
    return $image;
2649
}
2650
2651
/**
2652
 * This function add an additional condition according to the session of the course.
2653
 *
2654
 * @param int    $session_id        session id
2655
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2656
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2657
 *                                  false for strict session condition
2658
 * @param string $session_field
2659
 *
2660
 * @return string condition of the session
2661
 */
2662
function api_get_session_condition(
2663
    $session_id,
2664
    $and = true,
2665
    $with_base_content = false,
2666
    $session_field = 'session_id'
2667
) {
2668
    $session_id = (int) $session_id;
2669
2670
    if (empty($session_field)) {
2671
        $session_field = 'session_id';
2672
    }
2673
    // Condition to show resources by session
2674
    $condition_add = $and ? ' AND ' : ' WHERE ';
2675
2676
    if ($with_base_content) {
2677
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2678
    } else {
2679
        if (empty($session_id)) {
2680
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2681
        } else {
2682
            $condition_session = $condition_add." $session_field = $session_id ";
2683
        }
2684
    }
2685
2686
    return $condition_session;
2687
}
2688
2689
/**
2690
 * Returns the value of a setting from the web-adjustable admin config settings.
2691
 *
2692
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2693
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2694
 * instead of
2695
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2696
 *
2697
 * @param string $variable The variable name
2698
 *
2699
 * @return string|array
2700
 */
2701
function api_get_setting($variable, $isArray = false, $key = null)
2702
{
2703
    $settingsManager = Container::getSettingsManager();
2704
    if (empty($settingsManager)) {
2705
        return '';
2706
    }
2707
    $variable = trim($variable);
2708
    // Normalize setting name: keep full name for lookup, extract short name for switch matching
2709
    $full   = $variable;
2710
    $short  = str_contains($variable, '.') ? substr($variable, strrpos($variable, '.') + 1) : $variable;
2711
2712
    switch ($short) {
2713
        case 'server_type':
2714
            $settingValue = $settingsManager->getSetting($full, true);
2715
            return $settingValue ?: 'prod';
2716
        case 'openid_authentication':
2717
        case 'service_ppt2lp':
2718
        case 'formLogin_hide_unhide_label':
2719
            return false;
2720
        case 'tool_visible_by_default_at_creation':
2721
            $values = $settingsManager->getSetting($full);
2722
            $newResult = [];
2723
            foreach ($values as $parameter) {
2724
                $newResult[$parameter] = 'true';
2725
            }
2726
2727
            return $newResult;
2728
2729
        default:
2730
            $settingValue = $settingsManager->getSetting($full, true);
2731
            if (is_string($settingValue) && $isArray && $settingValue !== '') {
2732
                // Check if the value is a valid JSON string
2733
                $decodedValue = json_decode($settingValue, true);
2734
2735
                // If it's a valid JSON string and the result is an array, return it
2736
                if (is_array($decodedValue)) {
2737
                    return $decodedValue;
2738
                }
2739
                $value = eval('return ' . rtrim($settingValue, ';') . ';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2740
                if (is_array($value)) {
2741
                    return $value;
2742
                }
2743
            }
2744
2745
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2746
            if ($key !== null && isset($settingValue[$variable][$key])) {
2747
                return $settingValue[$variable][$key];
2748
            }
2749
2750
            return $settingValue;
2751
    }
2752
}
2753
2754
/**
2755
 * @param string $variable
2756
 * @param string $option
2757
 *
2758
 * @return bool
2759
 */
2760
function api_get_setting_in_list($variable, $option)
2761
{
2762
    $value = api_get_setting($variable);
2763
2764
    return in_array($option, $value);
2765
}
2766
2767
/**
2768
 * @param string $plugin
2769
 * @param string $variable
2770
 *
2771
 * @return string
2772
 */
2773
function api_get_plugin_setting($plugin, $variable)
2774
{
2775
    $variableName = $plugin.'_'.$variable;
2776
    //$result = api_get_setting($variableName);
2777
    $params = [
2778
        'category = ? AND subkey = ? AND variable = ?' => [
2779
            'Plugins',
2780
            $plugin,
2781
            $variableName,
2782
        ],
2783
    ];
2784
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2785
    $result = Database::select(
2786
        'selected_value',
2787
        $table,
2788
        ['where' => $params],
2789
        'one'
2790
    );
2791
    if ($result) {
2792
        $value = $result['selected_value'];
2793
        $serializedValue = @unserialize($result['selected_value'], []);
2794
        if (false !== $serializedValue) {
2795
            $value = $serializedValue;
2796
        }
2797
2798
        return $value;
2799
    }
2800
2801
    return null;
2802
    /// Old code
2803
2804
    $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...
2805
    $result = api_get_setting($variableName);
2806
2807
    if (isset($result[$plugin])) {
2808
        $value = $result[$plugin];
2809
2810
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2811
2812
        if (false !== $unserialized) {
2813
            $value = $unserialized;
2814
        }
2815
2816
        return $value;
2817
    }
2818
2819
    return null;
2820
}
2821
2822
/**
2823
 * Returns the value of a setting from the web-adjustable admin config settings.
2824
 */
2825
function api_get_settings_params($params)
2826
{
2827
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2828
2829
    return Database::select('*', $table, ['where' => $params]);
2830
}
2831
2832
/**
2833
 * @param array $params example: [id = ? => '1']
2834
 *
2835
 * @return array
2836
 */
2837
function api_get_settings_params_simple($params)
2838
{
2839
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2840
2841
    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...
2842
}
2843
2844
/**
2845
 * Returns the value of a setting from the web-adjustable admin config settings.
2846
 */
2847
function api_delete_settings_params($params)
2848
{
2849
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2850
2851
    return Database::delete($table, $params);
2852
}
2853
2854
/**
2855
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2856
 *
2857
 * @return string Escaped version of $_SERVER['PHP_SELF']
2858
 */
2859
function api_get_self()
2860
{
2861
    return htmlentities($_SERVER['PHP_SELF']);
2862
}
2863
2864
/**
2865
 * Checks whether current user is a platform administrator.
2866
 *
2867
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2868
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2869
 *
2870
 * @return bool true if the user has platform admin rights,
2871
 *              false otherwise
2872
 *
2873
 * @see usermanager::is_admin(user_id) for a user-id specific function
2874
 */
2875
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2876
{
2877
    $currentUser = api_get_current_user();
2878
2879
    if (null === $currentUser) {
2880
        return false;
2881
    }
2882
2883
    $isAdmin = $currentUser->isAdmin() || $currentUser->isSuperAdmin();
2884
2885
    if ($isAdmin) {
2886
        return true;
2887
    }
2888
2889
    if ($allowSessionAdmins && $currentUser->isSessionAdmin()) {
2890
        return true;
2891
    }
2892
2893
    if ($allowDrh && $currentUser->isHRM()) {
2894
        return true;
2895
    }
2896
2897
    return false;
2898
}
2899
2900
/**
2901
 * Checks whether the user given as user id is in the admin table.
2902
 *
2903
 * @param int $user_id If none provided, will use current user
2904
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2905
 *
2906
 * @return bool True if the user is admin, false otherwise
2907
 */
2908
function api_is_platform_admin_by_id($user_id = null, $url = null)
2909
{
2910
    $user_id = (int) $user_id;
2911
    if (empty($user_id)) {
2912
        $user_id = api_get_user_id();
2913
    }
2914
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2915
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2916
    $res = Database::query($sql);
2917
    $is_admin = 1 === Database::num_rows($res);
2918
    if (!$is_admin || !isset($url)) {
2919
        return $is_admin;
2920
    }
2921
    // We get here only if $url is set
2922
    $url = (int) $url;
2923
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2924
    $sql = "SELECT * FROM $url_user_table
2925
            WHERE access_url_id = $url AND user_id = $user_id";
2926
    $res = Database::query($sql);
2927
2928
    return 1 === Database::num_rows($res);
2929
}
2930
2931
/**
2932
 * Checks whether current user is allowed to create courses.
2933
 *
2934
 * @return bool true if the user has course creation rights,
2935
 *              false otherwise
2936
 */
2937
function api_is_allowed_to_create_course()
2938
{
2939
    if (api_is_platform_admin()) {
2940
        return true;
2941
    }
2942
2943
    // Teachers can only create courses
2944
    if (api_is_teacher()) {
2945
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2946
            return true;
2947
        } else {
2948
            return false;
2949
        }
2950
    }
2951
2952
    return Session::read('is_allowedCreateCourse');
2953
}
2954
2955
/**
2956
 * Checks whether the current user is a course administrator.
2957
 *
2958
 * @return bool True if current user is a course administrator
2959
 */
2960
function api_is_course_admin()
2961
{
2962
    if (api_is_platform_admin()) {
2963
        return true;
2964
    }
2965
2966
    $user = api_get_current_user();
2967
    if ($user) {
2968
        if (
2969
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2970
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2971
        ) {
2972
            return true;
2973
        }
2974
    }
2975
2976
    return false;
2977
}
2978
2979
/**
2980
 * Checks whether the current user is a course coach
2981
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2982
 *
2983
 * @return bool True if current user is a course coach
2984
 */
2985
function api_is_session_general_coach()
2986
{
2987
    return Session::read('is_session_general_coach');
2988
}
2989
2990
/**
2991
 * Checks whether the current user is a course tutor
2992
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2993
 *
2994
 * @return bool True if current user is a course tutor
2995
 */
2996
function api_is_course_tutor()
2997
{
2998
    return Session::read('is_courseTutor');
2999
}
3000
3001
/**
3002
 * @param int $user_id
3003
 * @param int $courseId
3004
 * @param int $session_id
3005
 *
3006
 * @return bool
3007
 */
3008
function api_is_course_session_coach($user_id, $courseId, $session_id)
3009
{
3010
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3011
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3012
3013
    $user_id = (int) $user_id;
3014
    $session_id = (int) $session_id;
3015
    $courseId = (int) $courseId;
3016
3017
    $sql = "SELECT DISTINCT session.id
3018
            FROM $session_table
3019
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3020
            ON session.id = session_rc_ru.session_id
3021
            WHERE
3022
                session_rc_ru.user_id = '".$user_id."'  AND
3023
                session_rc_ru.c_id = '$courseId' AND
3024
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3025
                session_rc_ru.session_id = '$session_id'";
3026
    $result = Database::query($sql);
3027
3028
    return Database::num_rows($result) > 0;
3029
}
3030
3031
/**
3032
 * Checks whether the current user is a course or session coach.
3033
 */
3034
function api_is_coach(int $session_id = 0, ?int $courseId = null, bool $check_student_view = true, int $userId = 0): bool
3035
{
3036
    $userId = empty($userId) ? api_get_user_id() :  $userId;
3037
3038
    if (empty($session_id)) {
3039
        $session_id = api_get_session_id();
3040
    }
3041
3042
    // The student preview was on
3043
    if ($check_student_view && api_is_student_view_active()) {
3044
        return false;
3045
    }
3046
3047
    if (empty($courseId)) {
3048
        $courseId = api_get_course_int_id();
3049
    }
3050
3051
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3052
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3053
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3054
    $sessionIsCoach = [];
3055
3056
    if (!empty($courseId)) {
3057
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3058
                FROM $session_table s
3059
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3060
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3061
                WHERE
3062
                    session_rc_ru.c_id = '$courseId' AND
3063
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3064
                    session_rc_ru.session_id = '$session_id'";
3065
        $result = Database::query($sql);
3066
        $sessionIsCoach = Database::store_result($result);
3067
    }
3068
3069
    if (!empty($session_id)) {
3070
        $sql = "SELECT DISTINCT s.id
3071
                FROM $session_table AS s
3072
                INNER JOIN $tblSessionRelUser sru
3073
                ON s.id = sru.session_id
3074
                WHERE
3075
                    sru.user_id = $userId AND
3076
                    s.id = $session_id AND
3077
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3078
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3079
        $result = Database::query($sql);
3080
        if (!empty($sessionIsCoach)) {
3081
            $sessionIsCoach = array_merge(
3082
                $sessionIsCoach,
3083
                Database::store_result($result)
3084
            );
3085
        } else {
3086
            $sessionIsCoach = Database::store_result($result);
3087
        }
3088
    }
3089
3090
    return count($sessionIsCoach) > 0;
3091
}
3092
3093
function api_user_has_role(string $role, ?User $user = null): bool
3094
{
3095
    if (null === $user) {
3096
        $user = api_get_current_user();
3097
    }
3098
3099
    if (null === $user) {
3100
        return false;
3101
    }
3102
3103
    return $user->hasRole($role);
3104
}
3105
3106
function api_is_allowed_in_course(): bool
3107
{
3108
    if (api_is_platform_admin()) {
3109
        return true;
3110
    }
3111
3112
    $user = api_get_current_user();
3113
    if ($user instanceof User) {
3114
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3115
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3116
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3117
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3118
        ) {
3119
            return true;
3120
        }
3121
    }
3122
3123
    return false;
3124
}
3125
3126
/**
3127
 * Checks whether current user is a student boss.
3128
 */
3129
function api_is_student_boss(?User $user = null): bool
3130
{
3131
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3132
}
3133
3134
/**
3135
 * Checks whether the current user is a session administrator.
3136
 *
3137
 * @return bool True if current user is a course administrator
3138
 */
3139
function api_is_session_admin(?User $user = null)
3140
{
3141
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3142
}
3143
3144
/**
3145
 * Checks whether the current user is a human resources manager.
3146
 *
3147
 * @return bool True if current user is a human resources manager
3148
 */
3149
function api_is_drh()
3150
{
3151
    return api_user_has_role('ROLE_HR');
3152
}
3153
3154
/**
3155
 * Checks whether the current user is a student.
3156
 *
3157
 * @return bool True if current user is a human resources manager
3158
 */
3159
function api_is_student()
3160
{
3161
    return api_user_has_role('ROLE_STUDENT');
3162
}
3163
3164
/**
3165
 * Checks whether the current user has the status 'teacher'.
3166
 *
3167
 * @return bool True if current user is a human resources manager
3168
 */
3169
function api_is_teacher()
3170
{
3171
    return api_user_has_role('ROLE_TEACHER');
3172
}
3173
3174
/**
3175
 * Checks whether the current user is a invited user.
3176
 *
3177
 * @return bool
3178
 */
3179
function api_is_invitee()
3180
{
3181
    return api_user_has_role('ROLE_INVITEE');
3182
}
3183
3184
/**
3185
 * This function checks whether a session is assigned into a category.
3186
 *
3187
 * @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...
3188
 * @param string    - category name
3189
 *
3190
 * @return bool - true if is found, otherwise false
3191
 */
3192
function api_is_session_in_category($session_id, $category_name)
3193
{
3194
    $session_id = (int) $session_id;
3195
    $category_name = Database::escape_string($category_name);
3196
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3197
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3198
3199
    $sql = "SELECT 1
3200
            FROM $tbl_session
3201
            WHERE $session_id IN (
3202
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3203
                WHERE
3204
                  s.session_category_id = sc.id AND
3205
                  sc.name LIKE '%$category_name'
3206
            )";
3207
    $rs = Database::query($sql);
3208
3209
    if (Database::num_rows($rs) > 0) {
3210
        return true;
3211
    }
3212
3213
    return false;
3214
}
3215
3216
/**
3217
 * Displays options for switching between student view and course manager view.
3218
 *
3219
 * Changes in version 1.2 (Patrick Cool)
3220
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3221
 * is changed explicitly
3222
 *
3223
 * Changes in version 1.1 (Patrick Cool)
3224
 * student view now works correctly in subfolders of the document tool
3225
 * student view works correctly in the new links tool
3226
 *
3227
 * Example code for using this in your tools:
3228
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3229
 * //   display_tool_view_option($isStudentView);
3230
 * //}
3231
 * //and in later sections, use api_is_allowed_to_edit()
3232
 *
3233
 * @author Roan Embrechts
3234
 * @author Patrick Cool
3235
 * @author Julio Montoya, changes added in Chamilo
3236
 *
3237
 * @version 1.2
3238
 *
3239
 * @todo rewrite code so it is easier to understand
3240
 */
3241
function api_display_tool_view_option()
3242
{
3243
    if ('true' != api_get_setting('student_view_enabled')) {
3244
        return '';
3245
    }
3246
3247
    $sourceurl = '';
3248
    $is_framed = false;
3249
    // Exceptions apply for all multi-frames pages
3250
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3251
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3252
        return '';
3253
    }
3254
3255
    // Uncomment to remove student view link from document view page
3256
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3257
        if (empty($_GET['lp_id'])) {
3258
            return '';
3259
        }
3260
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3261
        $sourceurl = str_replace(
3262
            'lp/lp_header.php',
3263
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3264
            $sourceurl
3265
        );
3266
        //showinframes doesn't handle student view anyway...
3267
        //return '';
3268
        $is_framed = true;
3269
    }
3270
3271
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3272
    if (!$is_framed) {
3273
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3274
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3275
        } else {
3276
            $sourceurl = $_SERVER['REQUEST_URI'];
3277
        }
3278
    }
3279
3280
    $output_string = '';
3281
    if (!empty($_SESSION['studentview'])) {
3282
        if ('studentview' == $_SESSION['studentview']) {
3283
            // We have to remove the isStudentView=true from the $sourceurl
3284
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3285
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3286
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3287
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3288
        } elseif ('teacherview' == $_SESSION['studentview']) {
3289
            // Switching to teacherview
3290
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3291
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
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
    } else {
3296
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3297
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3298
    }
3299
    $output_string = Security::remove_XSS($output_string);
3300
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3301
3302
    return $html;
3303
}
3304
3305
/**
3306
 * Determines whether the current user is allowed to edit the current context.
3307
 *
3308
 * This includes checks for platform admin, course admin, tutor, coach,
3309
 * session coach, and optionally verifies if the user is in student view mode.
3310
 * If not in a course context, it falls back to a role-based permission system.
3311
 *
3312
 * @param bool $tutor             Allow if the user is a tutor.
3313
 * @param bool $coach             Allow if the user is a coach and setting allows it.
3314
 * @param bool $session_coach     Allow if the user is a session coach.
3315
 * @param bool $check_student_view Check if student view mode is active.
3316
 *
3317
 * @return bool True if the user is allowed to edit, false otherwise.
3318
 */
3319
function api_is_allowed_to_edit(
3320
    bool $tutor = false,
3321
    bool $coach = false,
3322
    bool $session_coach = false,
3323
    bool $check_student_view = true
3324
): bool {
3325
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3326
    $sessionId = api_get_session_id();
3327
    $sessionVisibility = api_get_session_visibility($sessionId);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3327
    $sessionVisibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($sessionId);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3328
    $studentView = api_is_student_view_active();
3329
    $isAllowed = false;
3330
3331
    // If platform admin, allow unless student view is active
3332
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3333
        if ($check_student_view && $studentView) {
3334
            $isAllowed = false;
3335
        } else {
3336
            return true;
3337
        }
3338
    }
3339
3340
    // Respect session course read-only mode from extra field
3341
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3342
        $efv = new ExtraFieldValue('course');
3343
        $lock = $efv->get_values_by_handler_and_field_variable(
3344
            api_get_course_int_id(),
3345
            'session_courses_read_only_mode'
3346
        );
3347
        if (!empty($lock['value'])) {
3348
            return false;
3349
        }
3350
    }
3351
3352
    $isCourseAdmin = api_is_course_admin();
3353
    $isCoach = api_is_coach(0, null, $check_student_view);
3354
3355
    if (!$isCourseAdmin && $tutor) {
3356
        $isCourseAdmin = api_is_course_tutor();
3357
    }
3358
3359
    if (!$isCourseAdmin && $coach) {
3360
        if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
3361
            $isCoach = false;
3362
        }
3363
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3364
            $isCourseAdmin = $isCoach;
3365
        }
3366
    }
3367
3368
    if (!$isCourseAdmin && $session_coach) {
3369
        $isCourseAdmin = $isCoach;
3370
    }
3371
3372
    // Handle student view mode
3373
    if ('true' === api_get_setting('student_view_enabled')) {
3374
        if (!empty($sessionId)) {
3375
            if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
3376
                $isCoach = false;
3377
            }
3378
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3379
                $isAllowed = $isCoach;
3380
            }
3381
3382
            if ($check_student_view) {
3383
                $isAllowed = $isAllowed && !$studentView;
3384
            }
3385
        } else {
3386
            $isAllowed = $isCourseAdmin;
3387
            if ($check_student_view) {
3388
                $isAllowed = $isCourseAdmin && !$studentView;
3389
            }
3390
        }
3391
3392
        if ($isAllowed) {
3393
            return true;
3394
        }
3395
    } else {
3396
        if ($isCourseAdmin) {
3397
            return true;
3398
        }
3399
    }
3400
3401
    // Final fallback: permission-based system (only if nothing before returned true)
3402
    $courseId = api_get_course_id();
3403
    $inCourse = !empty($courseId) && $courseId != -1;
3404
3405
    if (!$inCourse) {
3406
        $userRoles = api_get_user_roles();
3407
        $feature = api_detect_feature_context();
3408
        $permission = $feature.':edit';
3409
3410
        return api_get_permission($permission, $userRoles);
3411
    }
3412
3413
    return $isAllowed;
3414
}
3415
3416
/**
3417
 * Returns the current main feature (module) based on the current script path.
3418
 * Used to determine permissions for non-course tools.
3419
 */
3420
function api_detect_feature_context(): string
3421
{
3422
    $script = $_SERVER['SCRIPT_NAME'] ?? '';
3423
    $script = basename($script);
3424
3425
    $map = [
3426
        'user_list.php' => 'user',
3427
        'user_add.php' => 'user',
3428
        'user_edit.php' => 'user',
3429
        'session_list.php' => 'session',
3430
        'session_add.php' => 'session',
3431
        'session_edit.php' => 'session',
3432
        'skill_list.php' => 'skill',
3433
        'skill_edit.php' => 'skill',
3434
        'badge_list.php' => 'badge',
3435
        'settings.php' => 'settings',
3436
        'course_list.php' => 'course',
3437
    ];
3438
3439
    if (isset($map[$script])) {
3440
        return $map[$script];
3441
    }
3442
3443
    if (preg_match('#/main/([a-z_]+)/#i', $_SERVER['SCRIPT_NAME'], $matches)) {
3444
        return $matches[1];
3445
    }
3446
3447
    return 'unknown';
3448
}
3449
3450
/**
3451
 * Returns true if user is a course coach of at least one course in session.
3452
 *
3453
 * @param int $sessionId
3454
 *
3455
 * @return bool
3456
 */
3457
function api_is_coach_of_course_in_session($sessionId)
3458
{
3459
    if (api_is_platform_admin()) {
3460
        return true;
3461
    }
3462
3463
    $userId = api_get_user_id();
3464
    $courseList = UserManager::get_courses_list_by_session(
3465
        $userId,
3466
        $sessionId
3467
    );
3468
3469
    // Session visibility.
3470
    $visibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3470
    $visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3471
        $sessionId,
3472
        null,
3473
        false
3474
    );
3475
3476
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3477
        // Course Coach session visibility.
3478
        $blockedCourseCount = 0;
3479
        $closedVisibilityList = [
3480
            COURSE_VISIBILITY_CLOSED,
3481
            COURSE_VISIBILITY_HIDDEN,
3482
        ];
3483
3484
        foreach ($courseList as $course) {
3485
            // Checking session visibility
3486
            $sessionCourseVisibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3486
            $sessionCourseVisibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3487
                $sessionId,
3488
                $course['real_id']
3489
            );
3490
3491
            $courseIsVisible = !in_array(
3492
                $course['visibility'],
3493
                $closedVisibilityList
3494
            );
3495
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3495
            if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
3496
                $blockedCourseCount++;
3497
            }
3498
        }
3499
3500
        // If all courses are blocked then no show in the list.
3501
        if ($blockedCourseCount === count($courseList)) {
3502
            $visibility = SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3502
            $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
3503
        } else {
3504
            $visibility = SESSION_VISIBLE;
3505
        }
3506
    }
3507
3508
    switch ($visibility) {
3509
        case SESSION_VISIBLE_READ_ONLY:
3510
        case SESSION_VISIBLE:
3511
        case SESSION_AVAILABLE:
3512
            return true;
3513
            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...
3514
        case SESSION_INVISIBLE:
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3514
        case /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE:
Loading history...
3515
            return false;
3516
    }
3517
3518
    return false;
3519
}
3520
3521
/**
3522
 * Checks if a student can edit contents in a session depending
3523
 * on the session visibility.
3524
 *
3525
 * @param bool $tutor Whether to check if the user has the tutor role
3526
 * @param bool $coach Whether to check if the user has the coach role
3527
 *
3528
 * @return bool true: the user has the rights to edit, false: he does not
3529
 */
3530
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3531
{
3532
    if (api_is_allowed_to_edit($tutor, $coach)) {
3533
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3534
        return true;
3535
    } else {
3536
        $sessionId = api_get_session_id();
3537
3538
        if (0 == $sessionId) {
3539
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3540
            return true;
3541
        } else {
3542
            // I'm in a session and I'm a student
3543
            // Get the session visibility
3544
            $session_visibility = api_get_session_visibility($sessionId);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3544
            $session_visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($sessionId);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3545
            // if 5 the session is still available
3546
            switch ($session_visibility) {
3547
                case SESSION_VISIBLE_READ_ONLY: // 1
3548
                    return false;
3549
                case SESSION_VISIBLE:           // 2
3550
                    return true;
3551
                case SESSION_INVISIBLE:         // 3
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

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