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

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

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

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

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

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

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

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

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

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

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

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

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

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