Passed
Push — master ( 9fff91...5586e0 )
by Yannick
08:49
created

api_status_from_roles()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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