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('workflows.allow_users_to_create_courses')) {
2928
            return true;
2929
        } else {
2930
            return false;
2931
        }
2932
    }
2933
2934
    return Session::read('is_allowedCreateCourse');
2935
}
2936
2937
/**
2938
 * Checks whether the current user is a course administrator.
2939
 *
2940
 * @return bool True if current user is a course administrator
2941
 */
2942
function api_is_course_admin()
2943
{
2944
    if (api_is_platform_admin()) {
2945
        return true;
2946
    }
2947
2948
    $user = api_get_current_user();
2949
    if ($user) {
2950
        if (
2951
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2952
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2953
        ) {
2954
            return true;
2955
        }
2956
    }
2957
2958
    return false;
2959
}
2960
2961
/**
2962
 * Checks whether the current user is a course coach
2963
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2964
 *
2965
 * @return bool True if current user is a course coach
2966
 */
2967
function api_is_session_general_coach()
2968
{
2969
    return Session::read('is_session_general_coach');
2970
}
2971
2972
/**
2973
 * Checks whether the current user is a course tutor
2974
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2975
 *
2976
 * @return bool True if current user is a course tutor
2977
 */
2978
function api_is_course_tutor()
2979
{
2980
    return Session::read('is_courseTutor');
2981
}
2982
2983
/**
2984
 * @param int $user_id
2985
 * @param int $courseId
2986
 * @param int $session_id
2987
 *
2988
 * @return bool
2989
 */
2990
function api_is_course_session_coach($user_id, $courseId, $session_id)
2991
{
2992
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
2993
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2994
2995
    $user_id = (int) $user_id;
2996
    $session_id = (int) $session_id;
2997
    $courseId = (int) $courseId;
2998
2999
    $sql = "SELECT DISTINCT session.id
3000
            FROM $session_table
3001
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3002
            ON session.id = session_rc_ru.session_id
3003
            WHERE
3004
                session_rc_ru.user_id = '".$user_id."'  AND
3005
                session_rc_ru.c_id = '$courseId' AND
3006
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3007
                session_rc_ru.session_id = '$session_id'";
3008
    $result = Database::query($sql);
3009
3010
    return Database::num_rows($result) > 0;
3011
}
3012
3013
/**
3014
 * Checks whether the current user is a course or session coach.
3015
 */
3016
function api_is_coach(int $session_id = 0, ?int $courseId = null, bool $check_student_view = true, int $userId = 0): bool
3017
{
3018
    $userId = empty($userId) ? api_get_user_id() :  $userId;
3019
3020
    if (empty($session_id)) {
3021
        $session_id = api_get_session_id();
3022
    }
3023
3024
    // The student preview was on
3025
    if ($check_student_view && api_is_student_view_active()) {
3026
        return false;
3027
    }
3028
3029
    if (empty($courseId)) {
3030
        $courseId = api_get_course_int_id();
3031
    }
3032
3033
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3034
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3035
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3036
    $sessionIsCoach = [];
3037
3038
    if (!empty($courseId)) {
3039
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3040
                FROM $session_table s
3041
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3042
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3043
                WHERE
3044
                    session_rc_ru.c_id = '$courseId' AND
3045
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3046
                    session_rc_ru.session_id = '$session_id'";
3047
        $result = Database::query($sql);
3048
        $sessionIsCoach = Database::store_result($result);
3049
    }
3050
3051
    if (!empty($session_id)) {
3052
        $sql = "SELECT DISTINCT s.id
3053
                FROM $session_table AS s
3054
                INNER JOIN $tblSessionRelUser sru
3055
                ON s.id = sru.session_id
3056
                WHERE
3057
                    sru.user_id = $userId AND
3058
                    s.id = $session_id AND
3059
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3060
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3061
        $result = Database::query($sql);
3062
        if (!empty($sessionIsCoach)) {
3063
            $sessionIsCoach = array_merge(
3064
                $sessionIsCoach,
3065
                Database::store_result($result)
3066
            );
3067
        } else {
3068
            $sessionIsCoach = Database::store_result($result);
3069
        }
3070
    }
3071
3072
    return count($sessionIsCoach) > 0;
3073
}
3074
3075
function api_user_has_role(string $role, ?User $user = null): bool
3076
{
3077
    if (null === $user) {
3078
        $user = api_get_current_user();
3079
    }
3080
3081
    if (null === $user) {
3082
        return false;
3083
    }
3084
3085
    return $user->hasRole($role);
3086
}
3087
3088
function api_is_allowed_in_course(): bool
3089
{
3090
    if (api_is_platform_admin()) {
3091
        return true;
3092
    }
3093
3094
    $user = api_get_current_user();
3095
    if ($user instanceof User) {
3096
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3097
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3098
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3099
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3100
        ) {
3101
            return true;
3102
        }
3103
    }
3104
3105
    return false;
3106
}
3107
3108
/**
3109
 * Checks whether current user is a student boss.
3110
 */
3111
function api_is_student_boss(?User $user = null): bool
3112
{
3113
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3114
}
3115
3116
/**
3117
 * Checks whether the current user is a session administrator.
3118
 *
3119
 * @return bool True if current user is a course administrator
3120
 */
3121
function api_is_session_admin(?User $user = null)
3122
{
3123
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3124
}
3125
3126
/**
3127
 * Checks whether the current user is a human resources manager.
3128
 *
3129
 * @return bool True if current user is a human resources manager
3130
 */
3131
function api_is_drh()
3132
{
3133
    return api_user_has_role('ROLE_HR');
3134
}
3135
3136
/**
3137
 * Checks whether the current user is a student.
3138
 *
3139
 * @return bool True if current user is a human resources manager
3140
 */
3141
function api_is_student()
3142
{
3143
    return api_user_has_role('ROLE_STUDENT');
3144
}
3145
3146
/**
3147
 * Checks whether the current user has the status 'teacher'.
3148
 *
3149
 * @return bool True if current user is a human resources manager
3150
 */
3151
function api_is_teacher()
3152
{
3153
    return api_user_has_role('ROLE_TEACHER');
3154
}
3155
3156
/**
3157
 * Checks whether the current user is a invited user.
3158
 *
3159
 * @return bool
3160
 */
3161
function api_is_invitee()
3162
{
3163
    return api_user_has_role('ROLE_INVITEE');
3164
}
3165
3166
/**
3167
 * This function checks whether a session is assigned into a category.
3168
 *
3169
 * @param int       - session id
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
3170
 * @param string    - category name
3171
 *
3172
 * @return bool - true if is found, otherwise false
3173
 */
3174
function api_is_session_in_category($session_id, $category_name)
3175
{
3176
    $session_id = (int) $session_id;
3177
    $category_name = Database::escape_string($category_name);
3178
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3179
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3180
3181
    $sql = "SELECT 1
3182
            FROM $tbl_session
3183
            WHERE $session_id IN (
3184
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3185
                WHERE
3186
                  s.session_category_id = sc.id AND
3187
                  sc.name LIKE '%$category_name'
3188
            )";
3189
    $rs = Database::query($sql);
3190
3191
    if (Database::num_rows($rs) > 0) {
3192
        return true;
3193
    }
3194
3195
    return false;
3196
}
3197
3198
/**
3199
 * Displays options for switching between student view and course manager view.
3200
 *
3201
 * Changes in version 1.2 (Patrick Cool)
3202
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3203
 * is changed explicitly
3204
 *
3205
 * Changes in version 1.1 (Patrick Cool)
3206
 * student view now works correctly in subfolders of the document tool
3207
 * student view works correctly in the new links tool
3208
 *
3209
 * Example code for using this in your tools:
3210
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3211
 * //   display_tool_view_option($isStudentView);
3212
 * //}
3213
 * //and in later sections, use api_is_allowed_to_edit()
3214
 *
3215
 * @author Roan Embrechts
3216
 * @author Patrick Cool
3217
 * @author Julio Montoya, changes added in Chamilo
3218
 *
3219
 * @version 1.2
3220
 *
3221
 * @todo rewrite code so it is easier to understand
3222
 */
3223
function api_display_tool_view_option()
3224
{
3225
    if ('true' != api_get_setting('student_view_enabled')) {
3226
        return '';
3227
    }
3228
3229
    $sourceurl = '';
3230
    $is_framed = false;
3231
    // Exceptions apply for all multi-frames pages
3232
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3233
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3234
        return '';
3235
    }
3236
3237
    // Uncomment to remove student view link from document view page
3238
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3239
        if (empty($_GET['lp_id'])) {
3240
            return '';
3241
        }
3242
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3243
        $sourceurl = str_replace(
3244
            'lp/lp_header.php',
3245
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3246
            $sourceurl
3247
        );
3248
        //showinframes doesn't handle student view anyway...
3249
        //return '';
3250
        $is_framed = true;
3251
    }
3252
3253
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3254
    if (!$is_framed) {
3255
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3256
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3257
        } else {
3258
            $sourceurl = $_SERVER['REQUEST_URI'];
3259
        }
3260
    }
3261
3262
    $output_string = '';
3263
    if (!empty($_SESSION['studentview'])) {
3264
        if ('studentview' == $_SESSION['studentview']) {
3265
            // We have to remove the isStudentView=true from the $sourceurl
3266
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3267
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3268
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3269
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3270
        } elseif ('teacherview' == $_SESSION['studentview']) {
3271
            // Switching to teacherview
3272
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3273
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3274
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3275
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3276
        }
3277
    } else {
3278
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3279
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3280
    }
3281
    $output_string = Security::remove_XSS($output_string);
3282
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3283
3284
    return $html;
3285
}
3286
3287
/**
3288
 * Determines whether the current user is allowed to edit the current context.
3289
 *
3290
 * This includes checks for platform admin, course admin, tutor, coach,
3291
 * session coach, and optionally verifies if the user is in student view mode.
3292
 * If not in a course context, it falls back to a role-based permission system.
3293
 *
3294
 * @param bool $tutor             Allow if the user is a tutor.
3295
 * @param bool $coach             Allow if the user is a coach and setting allows it.
3296
 * @param bool $session_coach     Allow if the user is a session coach.
3297
 * @param bool $check_student_view Check if student view mode is active.
3298
 *
3299
 * @return bool True if the user is allowed to edit, false otherwise.
3300
 */
3301
function api_is_allowed_to_edit(
3302
    $tutor = false,
3303
    $coach = false,
3304
    $session_coach = false,
3305
    $check_student_view = true
3306
) {
3307
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3308
    $sessionId = api_get_session_id();
3309
    $sessionVisibility = api_get_session_visibility($sessionId);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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