Passed
Pull Request — master (#6376)
by
unknown
08:39
created

api_get_setting()   D

Complexity

Conditions 18
Paths 16

Size

Total Lines 66
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 42
c 0
b 0
f 0
nc 16
nop 3
dl 0
loc 66
rs 4.8666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\AccessUrl;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\Language;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\SettingsCurrent;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\UserCourseCategory;
12
use Chamilo\CoreBundle\Exception\NotAllowedException;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
15
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
16
use Chamilo\CoreBundle\ServiceHelper\PluginServiceHelper;
17
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
18
use Chamilo\CourseBundle\Entity\CGroup;
19
use Chamilo\CourseBundle\Entity\CLp;
20
use ChamiloSession as Session;
21
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
22
use Symfony\Component\Finder\Finder;
23
use Symfony\Component\Mime\Address;
24
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Validator\Constraints as Assert;
27
use ZipStream\Option\Archive;
28
use ZipStream\ZipStream;
29
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
30
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
31
32
/**
33
 * This is a code library for Chamilo.
34
 * It is included by default in every Chamilo file (through including the global.inc.php)
35
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
36
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
37
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
38
 */
39
40
// PHP version requirement.
41
define('REQUIRED_PHP_VERSION', '8.2');
42
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
43
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
44
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
45
46
// USER STATUS CONSTANTS
47
/** global status of a user: student */
48
define('STUDENT', 5);
49
/** global status of a user: course manager */
50
define('COURSEMANAGER', 1);
51
/** global status of a user: session admin */
52
define('SESSIONADMIN', 3);
53
/** global status of a user: human resources manager */
54
define('DRH', 4);
55
/** global status of a user: anonymous visitor */
56
define('ANONYMOUS', 6);
57
/** global status of a user: low security, necessary for inserting data from
58
 * the teacher through HTMLPurifier */
59
define('COURSEMANAGERLOWSECURITY', 10);
60
// Soft user status
61
define('PLATFORM_ADMIN', 11);
62
define('SESSION_COURSE_COACH', 12);
63
define('SESSION_GENERAL_COACH', 13);
64
define('COURSE_STUDENT', 14); //student subscribed in a course
65
define('SESSION_STUDENT', 15); //student subscribed in a session course
66
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
67
define('STUDENT_BOSS', 17); // student is boss
68
define('INVITEE', 20);
69
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
70
71
// COURSE VISIBILITY CONSTANTS
72
/** only visible for course admin */
73
define('COURSE_VISIBILITY_CLOSED', 0);
74
/** only visible for users registered in the course */
75
define('COURSE_VISIBILITY_REGISTERED', 1);
76
/** Open for all registered users on the platform */
77
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
78
/** Open for the whole world */
79
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
80
/** Invisible to all except admin */
81
define('COURSE_VISIBILITY_HIDDEN', 4);
82
83
define('COURSE_REQUEST_PENDING', 0);
84
define('COURSE_REQUEST_ACCEPTED', 1);
85
define('COURSE_REQUEST_REJECTED', 2);
86
define('DELETE_ACTION_ENABLED', false);
87
88
// EMAIL SENDING RECIPIENT CONSTANTS
89
define('SEND_EMAIL_EVERYONE', 1);
90
define('SEND_EMAIL_STUDENTS', 2);
91
define('SEND_EMAIL_TEACHERS', 3);
92
93
// SESSION VISIBILITY CONSTANTS
94
define('SESSION_VISIBLE_READ_ONLY', 1);
95
define('SESSION_VISIBLE', 2);
96
/**
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
498
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
499
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
500
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
501
502
// Question selection type
503
define('EX_Q_SELECTION_ORDERED', 1);
504
define('EX_Q_SELECTION_RANDOM', 2);
505
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
506
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
507
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
508
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
509
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
510
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
511
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
512
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
513
514
// Used to save the skill_rel_item table
515
define('ITEM_TYPE_EXERCISE', 1);
516
define('ITEM_TYPE_HOTPOTATOES', 2);
517
define('ITEM_TYPE_LINK', 3);
518
define('ITEM_TYPE_LEARNPATH', 4);
519
define('ITEM_TYPE_GRADEBOOK', 5);
520
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
521
//define('ITEM_TYPE_FORUM', 7);
522
define('ITEM_TYPE_ATTENDANCE', 8);
523
define('ITEM_TYPE_SURVEY', 9);
524
define('ITEM_TYPE_FORUM_THREAD', 10);
525
define('ITEM_TYPE_PORTFOLIO', 11);
526
527
// Course description blocks.
528
define('ADD_BLOCK', 8);
529
530
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
531
define(
532
    'QUESTION_TYPES',
533
    UNIQUE_ANSWER.':'.
534
    MULTIPLE_ANSWER.':'.
535
    FILL_IN_BLANKS.':'.
536
    MATCHING.':'.
537
    FREE_ANSWER.':'.
538
    HOT_SPOT.':'.
539
    HOT_SPOT_ORDER.':'.
540
    HOT_SPOT_DELINEATION.':'.
541
    MULTIPLE_ANSWER_COMBINATION.':'.
542
    UNIQUE_ANSWER_NO_OPTION.':'.
543
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
544
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
545
    ORAL_EXPRESSION.':'.
546
    GLOBAL_MULTIPLE_ANSWER.':'.
547
    MEDIA_QUESTION.':'.
548
    CALCULATED_ANSWER.':'.
549
    UNIQUE_ANSWER_IMAGE.':'.
550
    DRAGGABLE.':'.
551
    MATCHING_DRAGGABLE.':'.
552
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
553
    ANNOTATION
554
);
555
556
//Some alias used in the QTI exports
557
define('MCUA', 1);
558
define('TF', 1);
559
define('MCMA', 2);
560
define('FIB', 3);
561
562
// Message
563
define('MESSAGE_STATUS_INVITATION_PENDING', 5);
564
define('MESSAGE_STATUS_INVITATION_ACCEPTED', 6);
565
define('MESSAGE_STATUS_INVITATION_DENIED', 7);
566
define('MESSAGE_STATUS_WALL', 8);
567
568
define('MESSAGE_STATUS_WALL_DELETE', 9);
569
define('MESSAGE_STATUS_WALL_POST', 10);
570
571
define('MESSAGE_STATUS_FORUM', 12);
572
define('MESSAGE_STATUS_PROMOTED', 13);
573
574
// Images
575
define('IMAGE_WALL_SMALL_SIZE', 200);
576
define('IMAGE_WALL_MEDIUM_SIZE', 500);
577
define('IMAGE_WALL_BIG_SIZE', 2000);
578
define('IMAGE_WALL_SMALL', 'small');
579
define('IMAGE_WALL_MEDIUM', 'medium');
580
define('IMAGE_WALL_BIG', 'big');
581
582
// Social PLUGIN PLACES
583
define('SOCIAL_LEFT_PLUGIN', 1);
584
define('SOCIAL_CENTER_PLUGIN', 2);
585
define('SOCIAL_RIGHT_PLUGIN', 3);
586
define('CUT_GROUP_NAME', 50);
587
588
/**
589
 * FormValidator Filter.
590
 */
591
define('NO_HTML', 1);
592
define('STUDENT_HTML', 2);
593
define('TEACHER_HTML', 3);
594
define('STUDENT_HTML_FULLPAGE', 4);
595
define('TEACHER_HTML_FULLPAGE', 5);
596
597
// Timeline
598
define('TIMELINE_STATUS_ACTIVE', '1');
599
define('TIMELINE_STATUS_INACTIVE', '2');
600
601
// Event email template class
602
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
603
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
604
605
// Course home
606
define('SHORTCUTS_HORIZONTAL', 0);
607
define('SHORTCUTS_VERTICAL', 1);
608
609
// Course copy
610
define('FILE_SKIP', 1);
611
define('FILE_RENAME', 2);
612
define('FILE_OVERWRITE', 3);
613
define('UTF8_CONVERT', false); //false by default
614
615
define('DOCUMENT', 'file');
616
define('FOLDER', 'folder');
617
618
define('RESOURCE_ASSET', 'asset');
619
define('RESOURCE_DOCUMENT', 'document');
620
define('RESOURCE_GLOSSARY', 'glossary');
621
define('RESOURCE_EVENT', 'calendar_event');
622
define('RESOURCE_LINK', 'link');
623
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
624
define('RESOURCE_LEARNPATH', 'learnpath');
625
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
626
define('RESOURCE_ANNOUNCEMENT', 'announcement');
627
define('RESOURCE_FORUM', 'forum');
628
define('RESOURCE_FORUMTOPIC', 'thread');
629
define('RESOURCE_FORUMPOST', 'post');
630
define('RESOURCE_QUIZ', 'quiz');
631
define('RESOURCE_TEST_CATEGORY', 'test_category');
632
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
633
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
634
define('RESOURCE_LINKCATEGORY', 'Link_Category');
635
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
636
define('RESOURCE_SCORM', 'Scorm');
637
define('RESOURCE_SURVEY', 'survey');
638
define('RESOURCE_SURVEYQUESTION', 'survey_question');
639
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
640
define('RESOURCE_WIKI', 'wiki');
641
define('RESOURCE_THEMATIC', 'thematic');
642
define('RESOURCE_ATTENDANCE', 'attendance');
643
define('RESOURCE_WORK', 'work');
644
define('RESOURCE_SESSION_COURSE', 'session_course');
645
define('RESOURCE_GRADEBOOK', 'gradebook');
646
define('ADD_THEMATIC_PLAN', 6);
647
648
// Max online users to show per page (whoisonline)
649
define('MAX_ONLINE_USERS', 12);
650
651
define('TOOL_AUTHORING', 'toolauthoring');
652
define('TOOL_INTERACTION', 'toolinteraction');
653
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
654
define('TOOL_ADMIN', 'tooladmin');
655
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
656
define('TOOL_DRH', 'tool_drh');
657
define('TOOL_STUDENT_VIEW', 'toolstudentview');
658
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
659
660
// Search settings (from main/inc/lib/search/IndexableChunk.class.php )
661
// some constants to avoid serialize string keys on serialized data array
662
define('SE_COURSE_ID', 0);
663
define('SE_TOOL_ID', 1);
664
define('SE_DATA', 2);
665
define('SE_USER', 3);
666
667
// in some cases we need top differenciate xapian documents of the same tool
668
define('SE_DOCTYPE_EXERCISE_EXERCISE', 0);
669
define('SE_DOCTYPE_EXERCISE_QUESTION', 1);
670
671
// xapian prefixes
672
define('XAPIAN_PREFIX_COURSEID', 'C');
673
define('XAPIAN_PREFIX_TOOLID', 'O');
674
675
// User active field constants
676
define('USER_ACTIVE', 1);
677
define('USER_INACTIVE', 0);
678
define('USER_INACTIVE_AUTOMATIC', -1);
679
define('USER_SOFT_DELETED', -2);
680
681
/**
682
 * Returns a path to a certain resource within Chamilo.
683
 *
684
 * @param string $path A path which type is to be converted. Also, it may be a defined constant for a path.
685
 *
686
 * @return string the requested path or the converted path
687
 *
688
 * Notes about the current behaviour model:
689
 * 1. Windows back-slashes are converted to slashes in the result.
690
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
691
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
692
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
693
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
694
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
695
 * It has not been identified as needed yet.
696
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
697
 *
698
 * Vchamilo changes : allow using an alternate configuration
699
 * to get vchamilo  instance paths
700
 */
701
function api_get_path($path = '', $configuration = [])
702
{
703
    global $paths;
704
705
    // get proper configuration data if exists
706
    global $_configuration;
707
708
    $emptyConfigurationParam = false;
709
    if (empty($configuration)) {
710
        $configuration = (array) $_configuration;
711
        $emptyConfigurationParam = true;
712
    }
713
714
    $root_sys = Container::getProjectDir();
715
    $root_web = '';
716
    if (isset(Container::$container)) {
717
        $root_web = Container::$container->get('router')->generate(
718
            'index',
719
            [],
720
            UrlGeneratorInterface::ABSOLUTE_URL
721
        );
722
    }
723
724
    /*if (api_get_multiple_access_url()) {
725
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the main_api.lib.php
726
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
727
            // We look into the DB the function api_get_access_url
728
            $urlInfo = api_get_access_url($configuration['access_url']);
729
            // Avoid default value
730
            $defaultValues = ['http://localhost/', 'https://localhost/'];
731
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
732
                $root_web = 1 == $urlInfo['active'] ? $urlInfo['url'] : $configuration['root_web'];
733
            }
734
        }
735
    }*/
736
737
    $paths = [
738
        WEB_PATH => $root_web,
739
        SYMFONY_SYS_PATH => $root_sys,
740
        SYS_PATH => $root_sys.'public/',
741
        REL_PATH => '',
742
        CONFIGURATION_PATH => 'app/config/',
743
        LIBRARY_PATH => $root_sys.'public/main/inc/lib/',
744
745
        REL_COURSE_PATH => '',
746
        REL_CODE_PATH => '/main/',
747
748
        SYS_CODE_PATH => $root_sys.'public/main/',
749
        SYS_CSS_PATH => $root_sys.'public/build/css/',
750
        SYS_PLUGIN_PATH => $root_sys.'public/plugin/',
751
        SYS_ARCHIVE_PATH => $root_sys.'var/cache/',
752
        SYS_TEST_PATH => $root_sys.'tests/',
753
        SYS_TEMPLATE_PATH => $root_sys.'public/main/template/',
754
        SYS_PUBLIC_PATH => $root_sys.'public/',
755
        SYS_FONTS_PATH => $root_sys.'public/fonts/',
756
757
        WEB_CODE_PATH => $root_web.'main/',
758
        WEB_PLUGIN_ASSET_PATH => $root_web.'plugins/',
759
        WEB_COURSE_PATH => $root_web.'course/',
760
        WEB_IMG_PATH => $root_web.'img/',
761
        WEB_CSS_PATH => $root_web.'build/css/',
762
        WEB_AJAX_PATH => $root_web.'main/inc/ajax/',
763
        WEB_LIBRARY_PATH => $root_web.'main/inc/lib/',
764
        WEB_LIBRARY_JS_PATH => $root_web.'main/inc/lib/javascript/',
765
        WEB_PLUGIN_PATH => $root_web.'plugin/',
766
        WEB_PUBLIC_PATH => $root_web,
767
    ];
768
769
    $root_rel = '';
770
771
    global $virtualChamilo;
772
    if (!empty($virtualChamilo)) {
773
        $paths[SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
774
        //$paths[SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
775
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
776
        $paths[WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
777
        //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
778
779
        // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
780
781
        // RewriteEngine On
782
        // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
783
784
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
785
        //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
786
        //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
787
    }
788
789
    $path = trim($path);
790
791
    // Retrieving a common-purpose path.
792
    if (isset($paths[$path])) {
793
        return $paths[$path];
794
    }
795
796
    return false;
797
}
798
799
/**
800
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
801
 *
802
 * @param string $path the input path
803
 *
804
 * @return string returns the modified path
805
 */
806
function api_add_trailing_slash($path)
807
{
808
    return '/' === substr($path, -1) ? $path : $path.'/';
809
}
810
811
/**
812
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
813
 *
814
 * @param string $path the input path
815
 *
816
 * @return string returns the modified path
817
 */
818
function api_remove_trailing_slash($path)
819
{
820
    return '/' === substr($path, -1) ? substr($path, 0, -1) : $path;
821
}
822
823
/**
824
 * Checks the RFC 3986 syntax of a given URL.
825
 *
826
 * @param string $url      the URL to be checked
827
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
828
 *
829
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
830
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
831
 *
832
 * @see http://drupal.org
833
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
834
 * @see http://bugs.php.net/51192
835
 */
836
function api_valid_url($url, $absolute = false)
837
{
838
    if ($absolute) {
839
        if (preg_match("
840
            /^                                                      # Start at the beginning of the text
841
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
842
            (?:                                                     # Userinfo (optional) which is typically
843
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
844
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
845
            )?
846
            (?:
847
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
848
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
849
            )
850
            (?::[0-9]+)?                                            # Server port number (optional)
851
            (?:[\/|\?]
852
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
853
            *)?
854
            $/xi", $url)) {
855
            return $url;
856
        }
857
858
        return false;
859
    } else {
860
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
861
    }
862
}
863
864
/**
865
 * Checks whether a given string looks roughly like an email address.
866
 *
867
 * @param string $address the e-mail address to be checked
868
 *
869
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
870
 */
871
function api_valid_email($address)
872
{
873
    return filter_var($address, FILTER_VALIDATE_EMAIL);
874
}
875
876
/**
877
 * Function used to protect a course script.
878
 * The function blocks access when
879
 * - there is no $_SESSION["_course"] defined; or
880
 * - $is_allowed_in_course is set to false (this depends on the course
881
 * visibility and user status).
882
 *
883
 * This is only the first proposal, test and improve!
884
 *
885
 * @param bool Option to print headers when displaying error message. Default: false
886
 * @param bool whether session admins should be allowed or not
887
 * @param string $checkTool check if tool is available for users (user, group)
888
 *
889
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
890
 *
891
 * @todo replace global variable
892
 *
893
 * @author Roan Embrechts
894
 */
895
function api_protect_course_script($print_headers = false, $allow_session_admins = false, string $checkTool = '', $cid = null): bool
896
{
897
    $course_info = api_get_course_info();
898
    if (empty($course_info) && isset($_REQUEST['cid'])) {
899
        $course_info = api_get_course_info_by_id((int) $_REQUEST['cid']);
900
    }
901
902
    if (isset($cid)) {
903
        $course_info = api_get_course_info_by_id($cid);
904
    }
905
906
    if (empty($course_info)) {
907
        api_not_allowed($print_headers);
908
909
        return false;
910
    }
911
912
    if (api_is_drh()) {
913
        return true;
914
    }
915
916
    // Session admin has access to course
917
    $sessionAccess = ('true' === api_get_setting('session.session_admins_access_all_content'));
918
    if ($sessionAccess) {
919
        $allow_session_admins = true;
920
    }
921
922
    if (api_is_platform_admin($allow_session_admins)) {
923
        return true;
924
    }
925
926
    $isAllowedInCourse = api_is_allowed_in_course();
927
    $is_visible = false;
928
    if (isset($course_info) && isset($course_info['visibility'])) {
929
        switch ($course_info['visibility']) {
930
            default:
931
            case Course::CLOSED:
932
                // Completely closed: the course is only accessible to the teachers. - 0
933
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
934
                    $is_visible = true;
935
                }
936
                break;
937
            case Course::REGISTERED:
938
                // Private - access authorized to course members only - 1
939
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
940
                    $is_visible = true;
941
                }
942
                break;
943
            case Course::OPEN_PLATFORM:
944
                // Open - access allowed for users registered on the platform - 2
945
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
946
                    $is_visible = true;
947
                }
948
                break;
949
            case Course::OPEN_WORLD:
950
                //Open - access allowed for the whole world - 3
951
                $is_visible = true;
952
                break;
953
            case Course::HIDDEN:
954
                //Completely closed: the course is only accessible to the teachers. - 0
955
                if (api_is_platform_admin()) {
956
                    $is_visible = true;
957
                }
958
                break;
959
        }
960
961
        //If password is set and user is not registered to the course then the course is not visible
962
        if (false === $isAllowedInCourse &&
963
            isset($course_info['registration_code']) &&
964
            !empty($course_info['registration_code'])
965
        ) {
966
            $is_visible = false;
967
        }
968
    }
969
970
    if (!empty($checkTool)) {
971
        if (!api_is_allowed_to_edit(true, true, true)) {
972
            $toolInfo = api_get_tool_information_by_name($checkTool);
973
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && 0 == $toolInfo['visibility']) {
974
                api_not_allowed(true);
975
976
                return false;
977
            }
978
        }
979
    }
980
981
    // Check session visibility
982
    $session_id = api_get_session_id();
983
984
    if (!empty($session_id)) {
985
        // $isAllowedInCourse was set in local.inc.php
986
        if (!$isAllowedInCourse) {
987
            $is_visible = false;
988
        }
989
        // Check if course is inside session.
990
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
991
            $is_visible = false;
992
        }
993
    }
994
995
    if (!$is_visible) {
996
        api_not_allowed($print_headers);
997
998
        return false;
999
    }
1000
1001
    $pluginHelper = Container::$container->get(PluginServiceHelper::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

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

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

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

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

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

3383
    $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...
3384
    $is_courseAdmin = api_is_course_admin();
3385
3386
    if (!$is_courseAdmin && $tutor) {
3387
        // If we also want to check if the user is a tutor...
3388
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3389
    }
3390
3391
    if (!$is_courseAdmin && $coach) {
3392
        // If we also want to check if the user is a coach...';
3393
        // Check if session visibility is read only for coaches.
3394
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3395
            $is_allowed_coach_to_edit = false;
3396
        }
3397
3398
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3399
            // Check if coach is allowed to edit a course.
3400
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3401
        }
3402
    }
3403
3404
    if (!$is_courseAdmin && $session_coach) {
3405
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3406
    }
3407
3408
    // Check if the student_view is enabled, and if so, if it is activated.
3409
    if ('true' === api_get_setting('student_view_enabled')) {
3410
        $studentView = api_is_student_view_active();
3411
        if (!empty($sessionId)) {
3412
            // Check if session visibility is read only for coaches.
3413
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3414
                $is_allowed_coach_to_edit = false;
3415
            }
3416
3417
            $is_allowed = false;
3418
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3419
                // Check if coach is allowed to edit a course.
3420
                $is_allowed = $is_allowed_coach_to_edit;
3421
            }
3422
            if ($check_student_view) {
3423
                $is_allowed = $is_allowed && false === $studentView;
3424
            }
3425
        } else {
3426
            $is_allowed = $is_courseAdmin;
3427
            if ($check_student_view) {
3428
                $is_allowed = $is_courseAdmin && false === $studentView;
3429
            }
3430
        }
3431
3432
        return $is_allowed;
3433
    } else {
3434
        return $is_courseAdmin;
3435
    }
3436
}
3437
3438
/**
3439
 * Returns true if user is a course coach of at least one course in session.
3440
 *
3441
 * @param int $sessionId
3442
 *
3443
 * @return bool
3444
 */
3445
function api_is_coach_of_course_in_session($sessionId)
3446
{
3447
    if (api_is_platform_admin()) {
3448
        return true;
3449
    }
3450
3451
    $userId = api_get_user_id();
3452
    $courseList = UserManager::get_courses_list_by_session(
3453
        $userId,
3454
        $sessionId
3455
    );
3456
3457
    // Session visibility.
3458
    $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

3458
    $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...
3459
        $sessionId,
3460
        null,
3461
        false
3462
    );
3463
3464
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3465
        // Course Coach session visibility.
3466
        $blockedCourseCount = 0;
3467
        $closedVisibilityList = [
3468
            COURSE_VISIBILITY_CLOSED,
3469
            COURSE_VISIBILITY_HIDDEN,
3470
        ];
3471
3472
        foreach ($courseList as $course) {
3473
            // Checking session visibility
3474
            $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

3474
            $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...
3475
                $sessionId,
3476
                $course['real_id']
3477
            );
3478
3479
            $courseIsVisible = !in_array(
3480
                $course['visibility'],
3481
                $closedVisibilityList
3482
            );
3483
            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

3483
            if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
3484
                $blockedCourseCount++;
3485
            }
3486
        }
3487
3488
        // If all courses are blocked then no show in the list.
3489
        if ($blockedCourseCount === count($courseList)) {
3490
            $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

3490
            $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
3491
        } else {
3492
            $visibility = SESSION_VISIBLE;
3493
        }
3494
    }
3495
3496
    switch ($visibility) {
3497
        case SESSION_VISIBLE_READ_ONLY:
3498
        case SESSION_VISIBLE:
3499
        case SESSION_AVAILABLE:
3500
            return true;
3501
            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...
3502
        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

3502
        case /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE:
Loading history...
3503
            return false;
3504
    }
3505
3506
    return false;
3507
}
3508
3509
/**
3510
 * Checks if a student can edit contents in a session depending
3511
 * on the session visibility.
3512
 *
3513
 * @param bool $tutor Whether to check if the user has the tutor role
3514
 * @param bool $coach Whether to check if the user has the coach role
3515
 *
3516
 * @return bool true: the user has the rights to edit, false: he does not
3517
 */
3518
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3519
{
3520
    if (api_is_allowed_to_edit($tutor, $coach)) {
3521
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3522
        return true;
3523
    } else {
3524
        $sessionId = api_get_session_id();
3525
3526
        if (0 == $sessionId) {
3527
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3528
            return true;
3529
        } else {
3530
            // I'm in a session and I'm a student
3531
            // Get the session visibility
3532
            $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

3532
            $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...
3533
            // if 5 the session is still available
3534
            switch ($session_visibility) {
3535
                case SESSION_VISIBLE_READ_ONLY: // 1
3536
                    return false;
3537
                case SESSION_VISIBLE:           // 2
3538
                    return true;
3539
                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

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