api_set_setting()   F
last analyzed

Complexity

Conditions 23
Paths 2257

Size

Total Lines 87
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 63
nc 2257
nop 5
dl 0
loc 87
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

Loading history...
1014
1015
    if ($pluginHelper->isPluginEnabled('Positioning')) {
1016
        $plugin = $pluginHelper->loadLegacyPlugin('Positioning');
1017
1018
        if ($plugin && $plugin->get('block_course_if_initial_exercise_not_attempted') === 'true') {
1019
            $currentPath = $_SERVER['REQUEST_URI'];
1020
1021
            $allowedPatterns = [
1022
                '#^/course/\d+/home#',
1023
                '#^/plugin/Positioning/#',
1024
                '#^/main/course_home/#',
1025
                '#^/main/exercise/#',
1026
                '#^/main/inc/ajax/exercise.ajax.php#',
1027
            ];
1028
1029
            $isWhitelisted = false;
1030
            foreach ($allowedPatterns as $pattern) {
1031
                if (preg_match($pattern, $currentPath)) {
1032
                    $isWhitelisted = true;
1033
                    break;
1034
                }
1035
            }
1036
1037
            if (!$isWhitelisted) {
1038
                $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1039
1040
                if (!empty($initialData['exercise_id'])) {
1041
                    $results = Event::getExerciseResultsByUser(
0 ignored issues
show
Bug introduced by
The method getExerciseResultsByUser() does not exist on Event. ( Ignorable by Annotation )

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

1041
                    /** @scrutinizer ignore-call */ 
1042
                    $results = Event::getExerciseResultsByUser(

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

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

Loading history...
1042
                        api_get_user_id(),
1043
                        (int) $initialData['exercise_id'],
1044
                        $course_info['real_id'],
1045
                        $session_id
1046
                    );
1047
1048
                    if (empty($results)) {
1049
                        api_not_allowed($print_headers);
1050
                        return false;
1051
                    }
1052
                }
1053
            }
1054
        }
1055
    }
1056
1057
    api_block_inactive_user();
1058
1059
    return true;
1060
}
1061
1062
/**
1063
 * Function used to protect an admin script.
1064
 *
1065
 * The function blocks access when the user has no platform admin rights
1066
 * with an error message printed on default output
1067
 *
1068
 * @param bool Whether to allow session admins as well
1069
 * @param bool Whether to allow HR directors as well
1070
 * @param string An optional message (already passed through get_lang)
1071
 *
1072
 * @return bool True if user is allowed, false otherwise.
1073
 *              The function also outputs an error message in case not allowed
1074
 *
1075
 * @author Roan Embrechts (original author)
1076
 */
1077
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1078
{
1079
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1080
        api_not_allowed(true, $message);
1081
1082
        return false;
1083
    }
1084
    api_block_inactive_user();
1085
1086
    return true;
1087
}
1088
1089
/**
1090
 * Blocks inactive users with a currently active session from accessing more pages "live".
1091
 *
1092
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1093
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1094
 */
1095
function api_block_inactive_user()
1096
{
1097
    $data = true;
1098
    if ('true' !== api_get_setting('security.security_block_inactive_users_immediately')) {
1099
        return $data;
1100
    }
1101
1102
    $userId = api_get_user_id();
1103
    $homeUrl = api_get_path(WEB_PATH);
1104
    if (0 == $userId) {
1105
        return $data;
1106
    }
1107
1108
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1109
            WHERE id = $userId";
1110
1111
    $result = Database::query($sql);
1112
    if (Database::num_rows($result) > 0) {
1113
        $result_array = Database::fetch_array($result);
1114
        $data = (bool) $result_array['active'];
1115
    }
1116
    if (false == $data) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1117
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1118
        $tpl->assign('hide_login_link', 1);
1119
1120
        //api_not_allowed(true, get_lang('Account inactive'));
1121
        // we were not in a course, return to home page
1122
        $msg = Display::return_message(
1123
            get_lang('Account inactive'),
1124
            'error',
1125
            false
1126
        );
1127
1128
        $msg .= '<p class="text-center">
1129
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1130
1131
        $tpl->assign('content', $msg);
1132
        $tpl->display_one_col_template();
1133
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1134
    }
1135
1136
    return $data;
1137
}
1138
1139
/**
1140
 * Function used to protect a teacher script.
1141
 * The function blocks access when the user has no teacher rights.
1142
 *
1143
 * @return bool True if the current user can access the script, false otherwise
1144
 *
1145
 * @author Yoselyn Castillo
1146
 */
1147
function api_protect_teacher_script()
1148
{
1149
    if (!api_is_allowed_to_edit()) {
1150
        api_not_allowed(true);
1151
1152
        return false;
1153
    }
1154
1155
    return true;
1156
}
1157
1158
/**
1159
 * Function used to prevent anonymous users from accessing a script.
1160
 *
1161
 * @param bool $printHeaders
1162
 *
1163
 * @return bool
1164
 */
1165
function api_block_anonymous_users($printHeaders = true)
1166
{
1167
    $isAuth = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
1168
1169
    if (false === $isAuth) {
1170
        api_not_allowed($printHeaders);
1171
1172
        return false;
1173
    }
1174
1175
    api_block_inactive_user();
1176
1177
    return true;
1178
}
1179
1180
/**
1181
 * Returns a rough evaluation of the browser's name and version based on very
1182
 * simple regexp.
1183
 *
1184
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1185
 */
1186
function api_get_navigator()
1187
{
1188
    $navigator = 'Unknown';
1189
    $version = 0;
1190
1191
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1192
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1193
    }
1194
1195
    if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
1196
        $navigator = 'Opera';
1197
        [, $version] = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1198
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
1199
        $navigator = 'Edge';
1200
        [, $version] = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1201
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
1202
        $navigator = 'Internet Explorer';
1203
        [, $version] = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1204
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
1205
        $navigator = 'Chrome';
1206
        [, $version] = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1207
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
1208
        $navigator = 'Safari';
1209
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Version/')) {
1210
            // If this Safari does have the "Version/" string in its user agent
1211
            // then use that as a version indicator rather than what's after
1212
            // "Safari/" which is rather a "build number" or something
1213
            [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1214
        } else {
1215
            [, $version] = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1216
        }
1217
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
1218
        $navigator = 'Firefox';
1219
        [, $version] = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1220
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape')) {
1221
        $navigator = 'Netscape';
1222
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/')) {
1223
            [, $version] = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1224
        } else {
1225
            [, $version] = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1226
        }
1227
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror')) {
1228
        $navigator = 'Konqueror';
1229
        [, $version] = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1230
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit')) {
1231
        $navigator = 'AppleWebKit';
1232
        [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1233
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko')) {
1234
        $navigator = 'Mozilla';
1235
        [, $version] = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1236
    }
1237
1238
    // Now cut extra stuff around (mostly *after*) the version number
1239
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1240
1241
    if (false === strpos($version, '.')) {
1242
        $version = number_format(doubleval($version), 1);
1243
    }
1244
1245
    return ['name' => $navigator, 'version' => $version];
1246
}
1247
1248
/**
1249
 * This function returns the id of the user which is stored in the $_user array.
1250
 *
1251
 * example: The function can be used to check if a user is logged in
1252
 *          if (api_get_user_id())
1253
 *
1254
 * @return int the id of the current user, 0 if is empty
1255
 */
1256
function api_get_user_id()
1257
{
1258
    $userInfo = Session::read('_user');
1259
    if ($userInfo && isset($userInfo['user_id'])) {
1260
        return (int) $userInfo['user_id'];
1261
    }
1262
1263
    return 0;
1264
}
1265
1266
/**
1267
 * Formats user information into a standard array
1268
 * This function should be only used inside api_get_user_info().
1269
 *
1270
 * @param array Non-standard user array
0 ignored issues
show
Documentation Bug introduced by
The doc comment Non-standard at position 0 could not be parsed: Unknown type name 'Non-standard' at position 0 in Non-standard.
Loading history...
1271
 * @param bool $add_password
1272
 * @param bool $loadAvatars  turn off to improve performance
1273
 *
1274
 * @return array Standard user array
1275
 */
1276
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1277
{
1278
    $result = [];
1279
1280
    if (!isset($user['id'])) {
1281
        return [];
1282
    }
1283
1284
    $result['firstname'] = null;
1285
    $result['lastname'] = null;
1286
1287
    if (isset($user['firstname']) && isset($user['lastname'])) {
1288
        // with only lowercase
1289
        $result['firstname'] = $user['firstname'];
1290
        $result['lastname'] = $user['lastname'];
1291
    } elseif (isset($user['firstName']) && isset($user['lastName'])) {
1292
        // with uppercase letters
1293
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1294
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1295
    }
1296
1297
    if (isset($user['email'])) {
1298
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1299
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1300
    } else {
1301
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1302
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1303
    }
1304
1305
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1306
    $result['complete_name_with_username'] = $result['complete_name'];
1307
1308
    if (!empty($user['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1309
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1310
    }
1311
1312
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1313
    if (!empty($user['email'])) {
1314
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1315
        if ($showEmail) {
1316
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1317
        }
1318
    } else {
1319
        $result['complete_name_with_email'] = $result['complete_name'];
1320
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1321
    }
1322
1323
    // Kept for historical reasons
1324
    $result['firstName'] = $result['firstname'];
1325
    $result['lastName'] = $result['lastname'];
1326
1327
    $attributes = [
1328
        'phone',
1329
        'address',
1330
        'picture_uri',
1331
        'official_code',
1332
        'status',
1333
        'active',
1334
        'auth_sources',
1335
        'username',
1336
        'theme',
1337
        'language',
1338
        'locale',
1339
        'creator_id',
1340
        'created_at',
1341
        'hr_dept_id',
1342
        'expiration_date',
1343
        'last_login',
1344
        'user_is_online',
1345
        'profile_completed',
1346
    ];
1347
1348
    if ('true' === api_get_setting('extended_profile')) {
1349
        $attributes[] = 'competences';
1350
        $attributes[] = 'diplomas';
1351
        $attributes[] = 'teach';
1352
        $attributes[] = 'openarea';
1353
    }
1354
1355
    foreach ($attributes as $attribute) {
1356
        $result[$attribute] = $user[$attribute] ?? null;
1357
    }
1358
1359
    $user_id = (int) $user['id'];
1360
    // Maintain the user_id index for backwards compatibility
1361
    $result['user_id'] = $result['id'] = $user_id;
1362
1363
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1364
    $result['has_certificates'] = 0;
1365
    if (!empty($hasCertificates)) {
1366
        $result['has_certificates'] = 1;
1367
    }
1368
1369
    $result['icon_status'] = '';
1370
    $result['icon_status_medium'] = '';
1371
    $result['is_admin'] = UserManager::is_admin($user_id);
1372
1373
    // Getting user avatar.
1374
    if ($loadAvatars) {
1375
        $result['avatar'] = '';
1376
        $result['avatar_no_query'] = '';
1377
        $result['avatar_small'] = '';
1378
        $result['avatar_medium'] = '';
1379
1380
        if (empty($user['avatar'])) {
1381
            $originalFile = UserManager::getUserPicture(
1382
                $user_id,
1383
                USER_IMAGE_SIZE_ORIGINAL,
1384
                null,
1385
                $result
1386
            );
1387
            $result['avatar'] = $originalFile;
1388
            $avatarString = explode('?', $result['avatar']);
1389
            $result['avatar_no_query'] = reset($avatarString);
1390
        } else {
1391
            $result['avatar'] = $user['avatar'];
1392
            $avatarString = explode('?', $user['avatar']);
1393
            $result['avatar_no_query'] = reset($avatarString);
1394
        }
1395
1396
        if (!isset($user['avatar_small'])) {
1397
            $smallFile = UserManager::getUserPicture(
1398
                $user_id,
1399
                USER_IMAGE_SIZE_SMALL,
1400
                null,
1401
                $result
1402
            );
1403
            $result['avatar_small'] = $smallFile;
1404
        } else {
1405
            $result['avatar_small'] = $user['avatar_small'];
1406
        }
1407
1408
        if (!isset($user['avatar_medium'])) {
1409
            $mediumFile = UserManager::getUserPicture(
1410
                $user_id,
1411
                USER_IMAGE_SIZE_MEDIUM,
1412
                null,
1413
                $result
1414
            );
1415
            $result['avatar_medium'] = $mediumFile;
1416
        } else {
1417
            $result['avatar_medium'] = $user['avatar_medium'];
1418
        }
1419
1420
        $urlImg = api_get_path(WEB_IMG_PATH);
1421
        $iconStatus = '';
1422
        $iconStatusMedium = '';
1423
        $label = '';
1424
1425
        switch ($result['status']) {
1426
            case STUDENT:
1427
                if ($result['has_certificates']) {
1428
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1429
                    $label = get_lang('Graduated');
1430
                } else {
1431
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1432
                    $label = get_lang('Learner');
1433
                }
1434
                break;
1435
            case COURSEMANAGER:
1436
                if ($result['is_admin']) {
1437
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1438
                    $label = get_lang('Admin');
1439
                } else {
1440
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1441
                    $label = get_lang('Teacher');
1442
                }
1443
                break;
1444
            case STUDENT_BOSS:
1445
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1446
                $label = get_lang('Student boss');
1447
                break;
1448
        }
1449
1450
        if (!empty($iconStatus)) {
1451
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1452
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1453
        }
1454
1455
        $result['icon_status'] = $iconStatus;
1456
        $result['icon_status_label'] = $label;
1457
        $result['icon_status_medium'] = $iconStatusMedium;
1458
    }
1459
1460
    if (isset($user['user_is_online'])) {
1461
        $result['user_is_online'] = true == $user['user_is_online'] ? 1 : 0;
1462
    }
1463
    if (isset($user['user_is_online_in_chat'])) {
1464
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1465
    }
1466
1467
    if ($add_password) {
1468
        $result['password'] = $user['password'];
1469
    }
1470
1471
    if (isset($result['profile_completed'])) {
1472
        $result['profile_completed'] = $user['profile_completed'];
1473
    }
1474
1475
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1476
1477
    // Send message link
1478
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1479
    $result['complete_name_with_message_link'] = Display::url(
1480
        $result['complete_name_with_username'],
1481
        $sendMessage,
1482
        ['class' => 'ajax']
1483
    );
1484
1485
    if (isset($user['extra'])) {
1486
        $result['extra'] = $user['extra'];
1487
    }
1488
1489
    return $result;
1490
}
1491
1492
/**
1493
 * Finds all the information about a user.
1494
 * If no parameter is passed you find all the information about the current user.
1495
 *
1496
 * @param int  $user_id
1497
 * @param bool $checkIfUserOnline
1498
 * @param bool $showPassword
1499
 * @param bool $loadExtraData
1500
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1501
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1502
 * @param bool $updateCache              update apc cache if exists
1503
 *
1504
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1505
 *
1506
 * @author Patrick Cool <[email protected]>
1507
 * @author Julio Montoya
1508
 *
1509
 * @version 21 September 2004
1510
 */
1511
function api_get_user_info(
1512
    $user_id = 0,
1513
    $checkIfUserOnline = false,
1514
    $showPassword = false,
1515
    $loadExtraData = false,
1516
    $loadOnlyVisibleExtraData = false,
1517
    $loadAvatars = true,
1518
    $updateCache = false
1519
) {
1520
    // Make sure user_id is safe
1521
    $user_id = (int) $user_id;
1522
    $user = false;
1523
    if (empty($user_id)) {
1524
        $userFromSession = Session::read('_user');
1525
        if (isset($userFromSession) && !empty($userFromSession)) {
1526
            return $userFromSession;
1527
            /*
1528
            return _api_format_user(
1529
                $userFromSession,
1530
                $showPassword,
1531
                $loadAvatars
1532
            );*/
1533
        }
1534
1535
        return false;
1536
    }
1537
1538
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1539
            WHERE id = $user_id";
1540
    $result = Database::query($sql);
1541
    if (Database::num_rows($result) > 0) {
1542
        $result_array = Database::fetch_array($result);
1543
        $result_array['auth_sources'] = api_get_user_entity($result_array['id'])->getAuthSourcesAuthentications();
1544
        $result_array['user_is_online_in_chat'] = 0;
1545
        if ($checkIfUserOnline) {
1546
            $use_status_in_platform = user_is_online($user_id);
1547
            $result_array['user_is_online'] = $use_status_in_platform;
1548
            $user_online_in_chat = 0;
1549
            if ($use_status_in_platform) {
1550
                $user_status = UserManager::get_extra_user_data_by_field(
1551
                    $user_id,
1552
                    'user_chat_status',
1553
                    false,
1554
                    true
1555
                );
1556
                if (1 == (int) $user_status['user_chat_status']) {
1557
                    $user_online_in_chat = 1;
1558
                }
1559
            }
1560
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1561
        }
1562
1563
        if ($loadExtraData) {
1564
            $fieldValue = new ExtraFieldValue('user');
1565
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1566
                $user_id,
1567
                $loadOnlyVisibleExtraData
1568
            );
1569
        }
1570
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1571
    }
1572
1573
    return $user;
1574
}
1575
1576
function api_get_user_info_from_entity(
1577
    User $user,
1578
    $checkIfUserOnline = false,
1579
    $showPassword = false,
1580
    $loadExtraData = false,
1581
    $loadOnlyVisibleExtraData = false,
1582
    $loadAvatars = true,
1583
    $loadCertificate = false
1584
) {
1585
    if (!$user instanceof UserInterface) {
1586
        return false;
1587
    }
1588
1589
    // Make sure user_id is safe
1590
    $user_id = (int) $user->getId();
1591
1592
    if (empty($user_id)) {
1593
        $userFromSession = Session::read('_user');
1594
1595
        if (isset($userFromSession) && !empty($userFromSession)) {
1596
            return $userFromSession;
1597
        }
1598
1599
        return false;
1600
    }
1601
1602
    $result = [];
1603
    $result['user_is_online_in_chat'] = 0;
1604
    if ($checkIfUserOnline) {
1605
        $use_status_in_platform = user_is_online($user_id);
1606
        $result['user_is_online'] = $use_status_in_platform;
1607
        $user_online_in_chat = 0;
1608
        if ($use_status_in_platform) {
1609
            $user_status = UserManager::get_extra_user_data_by_field(
1610
                $user_id,
1611
                'user_chat_status',
1612
                false,
1613
                true
1614
            );
1615
            if (1 == (int) $user_status['user_chat_status']) {
1616
                $user_online_in_chat = 1;
1617
            }
1618
        }
1619
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1620
    }
1621
1622
    if ($loadExtraData) {
1623
        $fieldValue = new ExtraFieldValue('user');
1624
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1625
            $user_id,
1626
            $loadOnlyVisibleExtraData
1627
        );
1628
    }
1629
1630
    $result['username'] = $user->getUsername();
1631
    $result['status'] = $user->getStatus();
1632
    $result['firstname'] = $user->getFirstname();
1633
    $result['lastname'] = $user->getLastname();
1634
    $result['email'] = $result['mail'] = $user->getEmail();
1635
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1636
    $result['complete_name_with_username'] = $result['complete_name'];
1637
1638
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1639
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1640
    }
1641
1642
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1643
    if (!empty($result['email'])) {
1644
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1645
        if ($showEmail) {
1646
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1647
        }
1648
    } else {
1649
        $result['complete_name_with_email'] = $result['complete_name'];
1650
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1651
    }
1652
1653
    // Kept for historical reasons
1654
    $result['firstName'] = $result['firstname'];
1655
    $result['lastName'] = $result['lastname'];
1656
1657
    $attributes = [
1658
        'picture_uri',
1659
        'last_login',
1660
        'user_is_online',
1661
    ];
1662
1663
    $result['phone'] = $user->getPhone();
1664
    $result['address'] = $user->getAddress();
1665
    $result['official_code'] = $user->getOfficialCode();
1666
    $result['active'] = $user->isActive();
1667
    $result['auth_sources'] = $user->getAuthSourcesAuthentications();
1668
    $result['language'] = $user->getLocale();
1669
    $result['creator_id'] = $user->getCreatorId();
1670
    $result['created_at'] = $user->getCreatedAt()->format('Y-m-d H:i:s');
1671
    $result['hr_dept_id'] = $user->getHrDeptId();
1672
    $result['expiration_date'] = '';
1673
    if ($user->getExpirationDate()) {
1674
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1675
    }
1676
1677
    $result['last_login'] = null;
1678
    if ($user->getLastLogin()) {
1679
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1680
    }
1681
1682
    $result['competences'] = $user->getCompetences();
1683
    $result['diplomas'] = $user->getDiplomas();
1684
    $result['teach'] = $user->getTeach();
1685
    $result['openarea'] = $user->getOpenarea();
1686
    $user_id = (int) $user->getId();
1687
1688
    // Maintain the user_id index for backwards compatibility
1689
    $result['user_id'] = $result['id'] = $user_id;
1690
1691
    if ($loadCertificate) {
1692
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1693
        $result['has_certificates'] = 0;
1694
        if (!empty($hasCertificates)) {
1695
            $result['has_certificates'] = 1;
1696
        }
1697
    }
1698
1699
    $result['icon_status'] = '';
1700
    $result['icon_status_medium'] = '';
1701
    $result['is_admin'] = UserManager::is_admin($user_id);
1702
1703
    // Getting user avatar.
1704
    if ($loadAvatars) {
1705
        $result['avatar'] = '';
1706
        $result['avatar_no_query'] = '';
1707
        $result['avatar_small'] = '';
1708
        $result['avatar_medium'] = '';
1709
        $urlImg = '/';
1710
        $iconStatus = '';
1711
        $iconStatusMedium = '';
1712
1713
        switch ($user->getStatus()) {
1714
            case STUDENT:
1715
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1716
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1717
                } else {
1718
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1719
                }
1720
                break;
1721
            case COURSEMANAGER:
1722
                if ($result['is_admin']) {
1723
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1724
                } else {
1725
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1726
                }
1727
                break;
1728
            case STUDENT_BOSS:
1729
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1730
                break;
1731
        }
1732
1733
        if (!empty($iconStatus)) {
1734
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1735
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1736
        }
1737
1738
        $result['icon_status'] = $iconStatus;
1739
        $result['icon_status_medium'] = $iconStatusMedium;
1740
    }
1741
1742
    if (isset($result['user_is_online'])) {
1743
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1744
    }
1745
    if (isset($result['user_is_online_in_chat'])) {
1746
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1747
    }
1748
1749
    $result['password'] = '';
1750
    if ($showPassword) {
1751
        $result['password'] = $user->getPassword();
1752
    }
1753
1754
    if (isset($result['profile_completed'])) {
1755
        $result['profile_completed'] = $result['profile_completed'];
1756
    }
1757
1758
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1759
1760
    // Send message link
1761
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1762
    $result['complete_name_with_message_link'] = Display::url(
1763
        $result['complete_name_with_username'],
1764
        $sendMessage,
1765
        ['class' => 'ajax']
1766
    );
1767
1768
    if (isset($result['extra'])) {
1769
        $result['extra'] = $result['extra'];
1770
    }
1771
1772
    return $result;
1773
}
1774
1775
function api_get_lp_entity(int $id): ?CLp
1776
{
1777
    return Database::getManager()->getRepository(CLp::class)->find($id);
1778
}
1779
1780
function api_get_user_entity(int $userId = 0): ?User
1781
{
1782
    $userId = $userId ?: api_get_user_id();
1783
    $repo = Container::getUserRepository();
1784
1785
    return $repo->find($userId);
1786
}
1787
1788
function api_get_current_user(): ?User
1789
{
1790
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1791
    if (false === $isLoggedIn) {
1792
        return null;
1793
    }
1794
1795
    $token = Container::getTokenStorage()->getToken();
1796
1797
    if (null !== $token) {
1798
        return $token->getUser();
1799
    }
1800
1801
    return null;
1802
}
1803
1804
/**
1805
 * Finds all the information about a user from username instead of user id.
1806
 *
1807
 * @param string $username
1808
 *
1809
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1810
 *
1811
 * @author Yannick Warnier <[email protected]>
1812
 */
1813
function api_get_user_info_from_username($username)
1814
{
1815
    if (empty($username)) {
1816
        return false;
1817
    }
1818
    $username = trim($username);
1819
1820
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1821
            WHERE username='".Database::escape_string($username)."'";
1822
    $result = Database::query($sql);
1823
    if (Database::num_rows($result) > 0) {
1824
        $resultArray = Database::fetch_array($result);
1825
1826
        return _api_format_user($resultArray);
1827
    }
1828
1829
    return false;
1830
}
1831
1832
/**
1833
 * Get first user with an email.
1834
 *
1835
 * @param string $email
1836
 *
1837
 * @return array|bool
1838
 */
1839
function api_get_user_info_from_email($email = '')
1840
{
1841
    if (empty($email)) {
1842
        return false;
1843
    }
1844
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1845
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1846
    $result = Database::query($sql);
1847
    if (Database::num_rows($result) > 0) {
1848
        $resultArray = Database::fetch_array($result);
1849
1850
        return _api_format_user($resultArray);
1851
    }
1852
1853
    return false;
1854
}
1855
1856
/**
1857
 * @return string
1858
 */
1859
function api_get_course_id()
1860
{
1861
    return Session::read('_cid', null);
1862
}
1863
1864
/**
1865
 * Returns the current course id (integer).
1866
 *
1867
 * @param ?string $code Optional course code
1868
 *
1869
 * @return int
1870
 */
1871
function api_get_course_int_id(?string $code = null): int
1872
{
1873
    if (!empty($code)) {
1874
        $code = Database::escape_string($code);
1875
        $row = Database::select(
1876
            'id',
1877
            Database::get_main_table(TABLE_MAIN_COURSE),
1878
            ['where' => ['code = ?' => [$code]]],
1879
            'first'
1880
        );
1881
1882
        if (is_array($row) && isset($row['id'])) {
1883
            return $row['id'];
1884
        } else {
1885
            return 0;
1886
        }
1887
    }
1888
1889
    $cid = Session::read('_real_cid', 0);
1890
    if (empty($cid) && isset($_REQUEST['cid'])) {
1891
        $cid = (int) $_REQUEST['cid'];
1892
    }
1893
1894
    return $cid;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cid could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
1895
}
1896
1897
/**
1898
 * Gets a course setting from the current course_setting table. Try always using integer values.
1899
 *
1900
 * @param string       $settingName The name of the setting we want from the table
1901
 * @param Course|array $courseInfo
1902
 * @param bool         $force       force checking the value in the database
1903
 *
1904
 * @return mixed The value of that setting in that table. Return -1 if not found.
1905
 */
1906
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1907
{
1908
    if (empty($courseInfo)) {
1909
        $courseInfo = api_get_course_info();
1910
    }
1911
1912
    if (empty($courseInfo) || empty($settingName)) {
1913
        return -1;
1914
    }
1915
1916
    if ($courseInfo instanceof Course) {
1917
        $courseId = $courseInfo->getId();
1918
    } else {
1919
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1920
    }
1921
1922
    if (empty($courseId)) {
1923
        return -1;
1924
    }
1925
1926
    static $courseSettingInfo = [];
1927
1928
    if ($force) {
1929
        $courseSettingInfo = [];
1930
    }
1931
1932
    if (!isset($courseSettingInfo[$courseId])) {
1933
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1934
        $settingName = Database::escape_string($settingName);
1935
1936
        $sql = "SELECT variable, value FROM $table
1937
                WHERE c_id = $courseId ";
1938
        $res = Database::query($sql);
1939
        if (Database::num_rows($res) > 0) {
1940
            $result = Database::store_result($res, 'ASSOC');
1941
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1942
1943
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1944
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1945
                if (!is_null($value)) {
1946
                    $result = explode(',', $value);
1947
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1948
                }
1949
            }
1950
        }
1951
    }
1952
1953
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1954
        return $courseSettingInfo[$courseId][$settingName];
1955
    }
1956
1957
    return -1;
1958
}
1959
1960
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1961
{
1962
    $value = api_get_course_setting($settingName, $courseInfo, true);
1963
1964
    if (-1 === $value) {
1965
        // Check global settings
1966
        $value = api_get_plugin_setting($plugin, $settingName);
1967
        if ('true' === $value) {
1968
            return 1;
1969
        }
1970
        if ('false' === $value) {
1971
            return 0;
1972
        }
1973
        if (null === $value) {
1974
            return -1;
1975
        }
1976
    }
1977
1978
    return $value;
1979
}
1980
1981
/**
1982
 * Gets an anonymous user ID.
1983
 *
1984
 * For some tools that need tracking, like the learnpath tool, it is necessary
1985
 * to have a usable user-id to enable some kind of tracking, even if not
1986
 * perfect. An anonymous ID is taken from the users table by looking for a
1987
 * status of "6" (anonymous).
1988
 *
1989
 * @return int User ID of the anonymous user, or O if no anonymous user found
1990
 */
1991
function api_get_anonymous_id()
1992
{
1993
    // Find if another anon is connected now
1994
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1995
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1996
    $ip = Database::escape_string(api_get_real_ip());
1997
    $max = (int) api_get_setting('admin.max_anonymous_users');
1998
    if ($max >= 2) {
1999
        $sql = "SELECT * FROM $table as TEL
2000
                JOIN $tableU as U
2001
                ON U.id = TEL.login_user_id
2002
                WHERE TEL.user_ip = '$ip'
2003
                    AND U.status = ".ANONYMOUS."
2004
                    AND U.id != 2 ";
2005
2006
        $result = Database::query($sql);
2007
        if (empty(Database::num_rows($result))) {
2008
            $login = uniqid('anon_');
2009
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['created_at ASC']);
2010
            if (count($anonList) >= $max) {
2011
                foreach ($anonList as $userToDelete) {
2012
                    UserManager::delete_user($userToDelete['user_id']);
2013
                    break;
2014
                }
2015
            }
2016
2017
            return UserManager::create_user(
0 ignored issues
show
Bug Best Practice introduced by
The expression return UserManager::crea...lhost', $login, $login) could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2018
                $login,
2019
                'anon',
2020
                ANONYMOUS,
2021
                ' anonymous@localhost',
2022
                $login,
2023
                $login
2024
            );
2025
        } else {
2026
            $row = Database::fetch_assoc($result);
2027
2028
            return $row['id'];
2029
        }
2030
    }
2031
2032
    $table = Database::get_main_table(TABLE_MAIN_USER);
2033
    $sql = "SELECT id
2034
            FROM $table
2035
            WHERE status = ".ANONYMOUS." ";
2036
    $res = Database::query($sql);
2037
    if (Database::num_rows($res) > 0) {
2038
        $row = Database::fetch_assoc($res);
2039
2040
        return $row['id'];
2041
    }
2042
2043
    // No anonymous user was found.
2044
    return 0;
2045
}
2046
2047
/**
2048
 * @param int $courseId
2049
 * @param int $sessionId
2050
 * @param int $groupId
2051
 *
2052
 * @return string
2053
 */
2054
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2055
{
2056
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2057
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2058
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2059
2060
    $url = 'cid='.$courseId;
2061
    $url .= '&sid='.$sessionId;
2062
    $url .= '&gid='.$groupId;
2063
2064
    return $url;
2065
}
2066
2067
/**
2068
 * Returns the current course url part including session, group, and gradebook params.
2069
 *
2070
 * @param bool   $addSessionId
2071
 * @param bool   $addGroupId
2072
 * @param string $origin
2073
 *
2074
 * @return string Course & session references to add to a URL
2075
 */
2076
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2077
{
2078
    $courseId = api_get_course_int_id();
2079
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2080
        $courseId = (int) $_REQUEST['cid'];
2081
    }
2082
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2083
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2084
2085
    if ($addSessionId) {
2086
        if (!empty($url)) {
2087
            $sessionId = api_get_session_id();
2088
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2089
                $sessionId = (int) $_REQUEST['sid'];
2090
            }
2091
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2092
        }
2093
    }
2094
2095
    if ($addGroupId) {
2096
        if (!empty($url)) {
2097
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2098
        }
2099
    }
2100
2101
    if (!empty($url)) {
2102
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2103
        if (false !== $origin) {
2104
            $url .= '&origin=' . $origin;
2105
        }
2106
    }
2107
2108
    return $url;
2109
}
2110
2111
/**
2112
 * Get if we visited a gradebook page.
2113
 *
2114
 * @return bool
2115
 */
2116
function api_is_in_gradebook()
2117
{
2118
    return Session::read('in_gradebook', false);
2119
}
2120
2121
/**
2122
 * Set that we are in a page inside a gradebook.
2123
 */
2124
function api_set_in_gradebook()
2125
{
2126
    Session::write('in_gradebook', true);
2127
}
2128
2129
/**
2130
 * Remove gradebook session.
2131
 */
2132
function api_remove_in_gradebook()
2133
{
2134
    Session::erase('in_gradebook');
2135
}
2136
2137
/**
2138
 * Returns the current course info array see api_format_course_array()
2139
 * If the course_code is given, the returned array gives info about that
2140
 * particular course, if none given it gets the course info from the session.
2141
 *
2142
 * @param string $courseCode
2143
 *
2144
 * @return array
2145
 */
2146
function api_get_course_info($courseCode = null)
2147
{
2148
    if (!empty($courseCode)) {
2149
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2150
2151
        return api_format_course_array($course);
2152
    }
2153
2154
    $course = Session::read('_course');
2155
    if ('-1' == $course) {
2156
        $course = [];
2157
    }
2158
2159
    if (empty($course) && isset($_REQUEST['cid'])) {
2160
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2161
    }
2162
2163
    return $course;
2164
}
2165
2166
/**
2167
 * @param int $courseId
2168
 */
2169
function api_get_course_entity($courseId = 0): ?Course
2170
{
2171
    if (empty($courseId)) {
2172
        $courseId = api_get_course_int_id();
2173
    }
2174
2175
    if (empty($courseId)) {
2176
        return null;
2177
    }
2178
2179
    return Container::getCourseRepository()->find($courseId);
2180
}
2181
2182
/**
2183
 * @param int $id
2184
 */
2185
function api_get_session_entity($id = 0): ?SessionEntity
2186
{
2187
    if (empty($id)) {
2188
        $id = api_get_session_id();
2189
    }
2190
2191
    if (empty($id)) {
2192
        return null;
2193
    }
2194
2195
    return Container::getSessionRepository()->find($id);
2196
}
2197
2198
/**
2199
 * @param int $id
2200
 */
2201
function api_get_group_entity($id = 0): ?CGroup
2202
{
2203
    if (empty($id)) {
2204
        $id = api_get_group_id();
2205
    }
2206
2207
    return Container::getGroupRepository()->find($id);
2208
}
2209
2210
/**
2211
 * @param int $id
2212
 */
2213
function api_get_url_entity($id = 0): ?AccessUrl
2214
{
2215
    if (empty($id)) {
2216
        $id = api_get_current_access_url_id();
2217
    }
2218
2219
    return Container::getAccessUrlRepository()->find($id);
2220
}
2221
2222
/**
2223
 * Returns the current course info array.
2224
2225
 * Now if the course_code is given, the returned array gives info about that
2226
 * particular course, not specially the current one.
2227
 *
2228
 * @param int $id Numeric ID of the course
2229
 *
2230
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2231
 */
2232
function api_get_course_info_by_id(?int $id = 0)
2233
{
2234
    if (empty($id)) {
2235
        $course = Session::read('_course', []);
2236
2237
        return $course;
2238
    }
2239
2240
    $course = Container::getCourseRepository()->find($id);
2241
    if (empty($course)) {
2242
        return [];
2243
    }
2244
2245
    return api_format_course_array($course);
2246
}
2247
2248
/**
2249
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2250
 * to switch from 'code' to 'id' in the array.
2251
 *
2252
 * @return array
2253
 *
2254
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2255
 */
2256
function api_format_course_array(Course $course = null)
2257
{
2258
    if (empty($course)) {
2259
        return [];
2260
    }
2261
2262
    $courseData = [];
2263
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2264
2265
    // Added
2266
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2267
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2268
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2269
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2270
    $courseData['titular'] = $course->getTutorName();
2271
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2272
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2273
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2274
2275
    $courseData['visibility'] = $course->getVisibility();
2276
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2277
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2278
    $courseData['activate_legal'] = $course->getActivateLegal();
2279
    $courseData['legal'] = $course->getLegal();
2280
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2281
    $courseData['video_url'] = $course->getVideoUrl();
2282
    $courseData['sticky'] = (int) $course->isSticky();
2283
2284
    $coursePath = '/course/';
2285
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2286
2287
    // Course password
2288
    $courseData['registration_code'] = $course->getRegistrationCode();
2289
    $courseData['disk_quota'] = $course->getDiskQuota();
2290
    $courseData['course_public_url'] = $webCourseHome;
2291
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2292
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2293
2294
    $image = Display::getMdiIcon(
2295
        ObjectIcon::COURSE,
2296
        'ch-tool-icon',
2297
        null,
2298
        ICON_SIZE_BIG
2299
    );
2300
2301
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2302
    if (!empty($illustration)) {
2303
        $image = $illustration;
2304
    }
2305
2306
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2307
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2308
2309
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2310
        $courseData['duration'] = $course->getDuration();
2311
    }
2312
2313
    return $courseData;
2314
}
2315
2316
/**
2317
 * Returns a difficult to guess password.
2318
 */
2319
function api_generate_password(int $length = 8, $useRequirements = true): string
2320
{
2321
    if ($length < 2) {
2322
        $length = 2;
2323
    }
2324
2325
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2326
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2327
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2328
    $minNumbers = 2;
2329
    $length = $length - $minNumbers;
2330
    $minLowerCase = round($length / 2);
2331
    $minUpperCase = $length - $minLowerCase;
2332
    $minSpecials = 1; // Default minimum special characters
2333
2334
    $password = '';
2335
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2336
2337
    $factory = new RandomLib\Factory();
2338
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2339
2340
    if (!empty($passwordRequirements)) {
2341
        $length = $passwordRequirements['min']['length'];
2342
        $minNumbers = $passwordRequirements['min']['numeric'];
2343
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2344
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2345
        $minSpecials = $passwordRequirements['min']['specials'];
2346
2347
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2348
        // Add the rest to fill the length requirement
2349
        if ($rest > 0) {
2350
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2351
        }
2352
    }
2353
2354
    // Min digits default 2
2355
    for ($i = 0; $i < $minNumbers; $i++) {
2356
        $password .= $generator->generateInt(2, 9);
2357
    }
2358
2359
    // Min lowercase
2360
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2361
2362
    // Min uppercase
2363
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2364
2365
    // Min special characters
2366
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2367
2368
    // Shuffle the password to ensure randomness
2369
    $password = str_shuffle($password);
2370
2371
    return $password;
2372
}
2373
2374
/**
2375
 * Checks a password to see wether it is OK to use.
2376
 *
2377
 * @param string $password
2378
 *
2379
 * @return bool if the password is acceptable, false otherwise
2380
 *              Notes about what a password "OK to use" is:
2381
 *              1. The password should be at least 5 characters long.
2382
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2383
 *              3. The password should contain at least 3 letters.
2384
 *              4. It should contain at least 2 digits.
2385
 *              Settings will change if the configuration value is set: password_requirements
2386
 */
2387
function api_check_password($password)
2388
{
2389
    $passwordRequirements = Security::getPasswordRequirements();
2390
2391
    $minLength = $passwordRequirements['min']['length'];
2392
    $minNumbers = $passwordRequirements['min']['numeric'];
2393
    // Optional
2394
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2395
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2396
2397
    $minLetters = $minLowerCase + $minUpperCase;
2398
    $passwordLength = api_strlen($password);
2399
2400
    $conditions = [
2401
        'min_length' => $passwordLength >= $minLength,
2402
    ];
2403
2404
    $digits = 0;
2405
    $lowerCase = 0;
2406
    $upperCase = 0;
2407
2408
    for ($i = 0; $i < $passwordLength; $i++) {
2409
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2410
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2411
            $upperCase++;
2412
        }
2413
2414
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2415
            $lowerCase++;
2416
        }
2417
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2418
            $digits++;
2419
        }
2420
    }
2421
2422
    // Min number of digits
2423
    $conditions['min_numeric'] = $digits >= $minNumbers;
2424
2425
    if (!empty($minUpperCase)) {
2426
        // Uppercase
2427
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2428
    }
2429
2430
    if (!empty($minLowerCase)) {
2431
        // Lowercase
2432
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2433
    }
2434
2435
    // Min letters
2436
    $letters = $upperCase + $lowerCase;
2437
    $conditions['min_letters'] = $letters >= $minLetters;
2438
2439
    $isPasswordOk = true;
2440
    foreach ($conditions as $condition) {
2441
        if (false === $condition) {
2442
            $isPasswordOk = false;
2443
            break;
2444
        }
2445
    }
2446
2447
    if (false === $isPasswordOk) {
2448
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2449
        $output .= Security::getPasswordRequirementsToString($conditions);
2450
2451
        Display::addFlash(Display::return_message($output, 'warning', false));
2452
    }
2453
2454
    return $isPasswordOk;
2455
}
2456
2457
/**
2458
 * Gets the current Chamilo (not PHP/cookie) session ID.
2459
 *
2460
 * @return int O if no active session, the session ID otherwise
2461
 */
2462
function api_get_session_id()
2463
{
2464
    return (int) Session::read('sid', 0);
2465
}
2466
2467
/**
2468
 * Gets the current Chamilo (not social network) group ID.
2469
 *
2470
 * @return int O if no active session, the session ID otherwise
2471
 */
2472
function api_get_group_id()
2473
{
2474
    return Session::read('gid', 0);
2475
}
2476
2477
/**
2478
 * Gets the current or given session name.
2479
 *
2480
 * @param   int     Session ID (optional)
2481
 *
2482
 * @return string The session name, or null if not found
2483
 */
2484
function api_get_session_name($session_id = 0)
2485
{
2486
    if (empty($session_id)) {
2487
        $session_id = api_get_session_id();
2488
        if (empty($session_id)) {
2489
            return null;
2490
        }
2491
    }
2492
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2493
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2494
    $r = Database::query($s);
2495
    $c = Database::num_rows($r);
2496
    if ($c > 0) {
2497
        //technically, there can be only one, but anyway we take the first
2498
        $rec = Database::fetch_array($r);
2499
2500
        return $rec['title'];
2501
    }
2502
2503
    return null;
2504
}
2505
2506
/**
2507
 * Gets the session info by id.
2508
 *
2509
 * @param int $id Session ID
2510
 *
2511
 * @return array information of the session
2512
 */
2513
function api_get_session_info($id)
2514
{
2515
    return SessionManager::fetch($id);
2516
}
2517
2518
/**
2519
 * Gets the session visibility by session id.
2520
 *
2521
 * @deprecated Use Session::setAccessVisibilityByUser() instead.
2522
 *
2523
 * @param int  $session_id
2524
 * @param int  $courseId
2525
 * @param bool $ignore_visibility_for_admins
2526
 *
2527
 * @return int
2528
 *             0 = session still available,
2529
 *             SESSION_VISIBLE_READ_ONLY = 1,
2530
 *             SESSION_VISIBLE = 2,
2531
 *             SESSION_INVISIBLE = 3
2532
 */
2533
function api_get_session_visibility(
2534
    $session_id,
2535
    $courseId = null,
2536
    $ignore_visibility_for_admins = true,
2537
    $userId = 0
2538
) {
2539
    if (api_is_platform_admin()) {
2540
        if ($ignore_visibility_for_admins) {
2541
            return SESSION_AVAILABLE;
2542
        }
2543
    }
2544
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2545
2546
    $now = time();
2547
    if (empty($session_id)) {
2548
        return 0; // Means that the session is still available.
2549
    }
2550
2551
    $session_id = (int) $session_id;
2552
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2553
2554
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2555
2556
    if (Database::num_rows($result) <= 0) {
2557
        return SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
3453
        $sessionId,
3454
        null,
3455
        false
3456
    );
3457
3458
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3459
        // Course Coach session visibility.
3460
        $blockedCourseCount = 0;
3461
        $closedVisibilityList = [
3462
            COURSE_VISIBILITY_CLOSED,
3463
            COURSE_VISIBILITY_HIDDEN,
3464
        ];
3465
3466
        foreach ($courseList as $course) {
3467
            // Checking session visibility
3468
            $sessionCourseVisibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

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

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

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

Loading history...
3469
                $sessionId,
3470
                $course['real_id']
3471
            );
3472
3473
            $courseIsVisible = !in_array(
3474
                $course['visibility'],
3475
                $closedVisibilityList
3476
            );
3477
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3477
            if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
3478
                $blockedCourseCount++;
3479
            }
3480
        }
3481
3482
        // If all courses are blocked then no show in the list.
3483
        if ($blockedCourseCount === count($courseList)) {
3484
            $visibility = SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3484
            $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
3485
        } else {
3486
            $visibility = SESSION_VISIBLE;
3487
        }
3488
    }
3489
3490
    switch ($visibility) {
3491
        case SESSION_VISIBLE_READ_ONLY:
3492
        case SESSION_VISIBLE:
3493
        case SESSION_AVAILABLE:
3494
            return true;
3495
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3496
        case SESSION_INVISIBLE:
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

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

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

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

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

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

Loading history...
3527
            // if 5 the session is still available
3528
            switch ($session_visibility) {
3529
                case SESSION_VISIBLE_READ_ONLY: // 1
3530
                    return false;
3531
                case SESSION_VISIBLE:           // 2
3532
                    return true;
3533
                case SESSION_INVISIBLE:         // 3
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

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