Passed
Push — master ( d77a08...b61ae7 )
by
unknown
08:40 queued 17s
created

api_get_languages_with_platform_default()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

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

1013
    /** @scrutinizer ignore-call */ 
1014
    $pluginHelper = Container::$container->get(PluginHelper::class);

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

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

Loading history...
1014
1015
    if ($pluginHelper->isPluginEnabled('Positioning')) {
1016
        $plugin = $pluginHelper->loadLegacyPlugin('Positioning');
1017
1018
        if ($plugin && $plugin->get('block_course_if_initial_exercise_not_attempted') === 'true') {
1019
            $currentPath = $_SERVER['REQUEST_URI'];
1020
1021
            $allowedPatterns = [
1022
                '#^/course/\d+/home#',
1023
                '#^/plugin/Positioning/#',
1024
                '#^/main/course_home/#',
1025
                '#^/main/exercise/#',
1026
                '#^/main/inc/ajax/exercise.ajax.php#',
1027
            ];
1028
1029
            $isWhitelisted = false;
1030
            foreach ($allowedPatterns as $pattern) {
1031
                if (preg_match($pattern, $currentPath)) {
1032
                    $isWhitelisted = true;
1033
                    break;
1034
                }
1035
            }
1036
1037
            if (!$isWhitelisted) {
1038
                $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1039
1040
                if (!empty($initialData['exercise_id'])) {
1041
                    $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

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

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

2595
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2596
    } else {
2597
        // If there's no start date, assume it's available until the end date
2598
        $visibility = SESSION_AVAILABLE;
2599
    }
2600
2601
    // If the end date was set.
2602
    if (!empty($row['access_end_date'])) {
2603
        // Only if date_start said that it was ok
2604
        if (SESSION_AVAILABLE === $visibility) {
2605
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2606
                ? SESSION_AVAILABLE // Date still available
2607
                : $row['visibility']; // Session ends
2608
        }
2609
    }
2610
2611
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2612
    $isCoach = api_is_coach($session_id, $courseId);
2613
2614
    if ($isCoach) {
2615
        // Test start date.
2616
        if (!empty($row['coach_access_start_date'])) {
2617
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2618
            $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

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

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

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

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

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

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

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

3468
            $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...
3469
                $sessionId,
3470
                $course['real_id']
3471
            );
3472
3473
            $courseIsVisible = !in_array(
3474
                $course['visibility'],
3475
                $closedVisibilityList
3476
            );
3477
            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

3477
            if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
3478
                $blockedCourseCount++;
3479
            }
3480
        }
3481
3482
        // If all courses are blocked then no show in the list.
3483
        if ($blockedCourseCount === count($courseList)) {
3484
            $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

3484
            $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
3485
        } else {
3486
            $visibility = SESSION_VISIBLE;
3487
        }
3488
    }
3489
3490
    switch ($visibility) {
3491
        case SESSION_VISIBLE_READ_ONLY:
3492
        case SESSION_VISIBLE:
3493
        case SESSION_AVAILABLE:
3494
            return true;
3495
            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...
3496
        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

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

3526
            $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...
3527
            // if 5 the session is still available
3528
            switch ($session_visibility) {
3529
                case SESSION_VISIBLE_READ_ONLY: // 1
3530
                    return false;
3531
                case SESSION_VISIBLE:           // 2
3532
                    return true;
3533
                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

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