Passed
Pull Request — master (#6164)
by Angel Fernando Quiroz
17:09 queued 08:48
created

load_hosting_limits()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 9
rs 10
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\Exception\NotAllowedException;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
15
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
16
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
17
use Chamilo\CourseBundle\Entity\CGroup;
18
use Chamilo\CourseBundle\Entity\CLp;
19
use ChamiloSession as Session;
20
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Mime\Address;
23
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
use Symfony\Component\Validator\Constraints as Assert;
26
use ZipStream\Option\Archive;
27
use ZipStream\ZipStream;
28
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
29
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
30
31
/**
32
 * This is a code library for Chamilo.
33
 * It is included by default in every Chamilo file (through including the global.inc.php)
34
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
35
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
36
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
37
 */
38
39
// PHP version requirement.
40
define('REQUIRED_PHP_VERSION', '8.2');
41
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
42
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
43
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
44
45
// USER STATUS CONSTANTS
46
/** global status of a user: student */
47
define('STUDENT', 5);
48
/** global status of a user: course manager */
49
define('COURSEMANAGER', 1);
50
/** global status of a user: session admin */
51
define('SESSIONADMIN', 3);
52
/** global status of a user: human resources manager */
53
define('DRH', 4);
54
/** global status of a user: anonymous visitor */
55
define('ANONYMOUS', 6);
56
/** global status of a user: low security, necessary for inserting data from
57
 * the teacher through HTMLPurifier */
58
define('COURSEMANAGERLOWSECURITY', 10);
59
// Soft user status
60
define('PLATFORM_ADMIN', 11);
61
define('SESSION_COURSE_COACH', 12);
62
define('SESSION_GENERAL_COACH', 13);
63
define('COURSE_STUDENT', 14); //student subscribed in a course
64
define('SESSION_STUDENT', 15); //student subscribed in a session course
65
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
66
define('STUDENT_BOSS', 17); // student is boss
67
define('INVITEE', 20);
68
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
69
70
// COURSE VISIBILITY CONSTANTS
71
/** only visible for course admin */
72
define('COURSE_VISIBILITY_CLOSED', 0);
73
/** only visible for users registered in the course */
74
define('COURSE_VISIBILITY_REGISTERED', 1);
75
/** Open for all registered users on the platform */
76
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
77
/** Open for the whole world */
78
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
79
/** Invisible to all except admin */
80
define('COURSE_VISIBILITY_HIDDEN', 4);
81
82
define('COURSE_REQUEST_PENDING', 0);
83
define('COURSE_REQUEST_ACCEPTED', 1);
84
define('COURSE_REQUEST_REJECTED', 2);
85
define('DELETE_ACTION_ENABLED', false);
86
87
// EMAIL SENDING RECIPIENT CONSTANTS
88
define('SEND_EMAIL_EVERYONE', 1);
89
define('SEND_EMAIL_STUDENTS', 2);
90
define('SEND_EMAIL_TEACHERS', 3);
91
92
// SESSION VISIBILITY CONSTANTS
93
define('SESSION_VISIBLE_READ_ONLY', 1);
94
define('SESSION_VISIBLE', 2);
95
define('SESSION_INVISIBLE', 3); // not available
96
define('SESSION_AVAILABLE', 4);
97
98
define('SESSION_LINK_TARGET', '_self');
99
100
define('SUBSCRIBE_ALLOWED', 1);
101
define('SUBSCRIBE_NOT_ALLOWED', 0);
102
define('UNSUBSCRIBE_ALLOWED', 1);
103
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
104
105
// SURVEY VISIBILITY CONSTANTS
106
define('SURVEY_VISIBLE_TUTOR', 0);
107
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
108
define('SURVEY_VISIBLE_PUBLIC', 2);
109
110
// CONSTANTS defining all tools, using the english version
111
/* When you add a new tool you must add it into function api_get_tools_lists() too */
112
define('TOOL_DOCUMENT', 'document');
113
define('TOOL_LP_FINAL_ITEM', 'final_item');
114
define('TOOL_READOUT_TEXT', 'readout_text');
115
define('TOOL_THUMBNAIL', 'thumbnail');
116
define('TOOL_HOTPOTATOES', 'hotpotatoes');
117
define('TOOL_CALENDAR_EVENT', 'calendar_event');
118
define('TOOL_LINK', 'link');
119
define('TOOL_LINK_CATEGORY', 'link_category');
120
define('TOOL_COURSE_DESCRIPTION', 'course_description');
121
define('TOOL_SEARCH', 'search');
122
define('TOOL_LEARNPATH', 'learnpath');
123
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
124
define('TOOL_AGENDA', 'agenda');
125
define('TOOL_ANNOUNCEMENT', 'announcement');
126
define('TOOL_FORUM', 'forum');
127
define('TOOL_FORUM_CATEGORY', 'forum_category');
128
define('TOOL_FORUM_THREAD', 'forum_thread');
129
define('TOOL_FORUM_POST', 'forum_post');
130
define('TOOL_FORUM_ATTACH', 'forum_attachment');
131
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
132
define('TOOL_THREAD', 'thread');
133
define('TOOL_POST', 'post');
134
define('TOOL_DROPBOX', 'dropbox');
135
define('TOOL_QUIZ', 'quiz');
136
define('TOOL_TEST_CATEGORY', 'test_category');
137
define('TOOL_USER', 'user');
138
define('TOOL_GROUP', 'group');
139
define('TOOL_BLOGS', 'blog_management');
140
define('TOOL_CHAT', 'chat');
141
define('TOOL_STUDENTPUBLICATION', 'student_publication');
142
define('TOOL_TRACKING', 'tracking');
143
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
144
define('TOOL_COURSE_SETTING', 'course_setting');
145
define('TOOL_BACKUP', 'backup');
146
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
147
define('TOOL_RECYCLE_COURSE', 'recycle_course');
148
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
149
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
150
define('TOOL_UPLOAD', 'file_upload');
151
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
152
define('TOOL_SURVEY', 'survey');
153
//define('TOOL_WIKI', 'wiki');
154
define('TOOL_GLOSSARY', 'glossary');
155
define('TOOL_GRADEBOOK', 'gradebook');
156
define('TOOL_NOTEBOOK', 'notebook');
157
define('TOOL_ATTENDANCE', 'attendance');
158
define('TOOL_COURSE_PROGRESS', 'course_progress');
159
define('TOOL_PORTFOLIO', 'portfolio');
160
define('TOOL_PLAGIARISM', 'compilatio');
161
define('TOOL_XAPI', 'xapi');
162
163
// CONSTANTS defining Chamilo interface sections
164
define('SECTION_CAMPUS', 'mycampus');
165
define('SECTION_COURSES', 'mycourses');
166
define('SECTION_CATALOG', 'catalog');
167
define('SECTION_MYPROFILE', 'myprofile');
168
define('SECTION_MYAGENDA', 'myagenda');
169
define('SECTION_COURSE_ADMIN', 'course_admin');
170
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
171
define('SECTION_MYGRADEBOOK', 'mygradebook');
172
define('SECTION_TRACKING', 'session_my_space');
173
define('SECTION_SOCIAL', 'social-network');
174
define('SECTION_DASHBOARD', 'dashboard');
175
define('SECTION_REPORTS', 'reports');
176
define('SECTION_GLOBAL', 'global');
177
define('SECTION_INCLUDE', 'include');
178
define('SECTION_CUSTOMPAGE', 'custompage');
179
180
// event logs types
181
define('LOG_COURSE_DELETE', 'course_deleted');
182
define('LOG_COURSE_CREATE', 'course_created');
183
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
184
185
// @todo replace 'soc_gr' with social_group
186
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
187
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
188
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
189
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
190
191
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
192
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
193
194
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
195
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
196
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
197
198
define('LOG_MESSAGE_DATA', 'message_data');
199
define('LOG_MESSAGE_DELETE', 'msg_deleted');
200
201
const LOG_RESOURCE_LINK_DELETE = 'resource_link_deleted';
202
const LOG_RESOURCE_LINK_SOFT_DELETE = 'resource_link_soft_deleted';
203
const LOG_RESOURCE_NODE = 'resource_node_id';
204
const LOG_RESOURCE_LINK = 'resource_link_id';
205
const LOG_RESOURCE_NODE_AND_RESOURCE_LINK = 'resource_node_id_and_resource_link_id';
206
207
define('LOG_USER_DELETE', 'user_deleted');
208
define('LOG_USER_PREDELETE', 'user_predeleted');
209
define('LOG_USER_CREATE', 'user_created');
210
define('LOG_USER_UPDATE', 'user_updated');
211
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
212
define('LOG_USER_ENABLE', 'user_enable');
213
define('LOG_USER_DISABLE', 'user_disable');
214
define('LOG_USER_ANONYMIZE', 'user_anonymized');
215
define('LOG_USER_FIELD_CREATE', 'user_field_created');
216
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
217
define('LOG_SESSION_CREATE', 'session_created');
218
define('LOG_SESSION_DELETE', 'session_deleted');
219
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
220
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
221
define('LOG_SESSION_ADD_USER', 'session_add_user');
222
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
223
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
224
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
225
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
226
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
227
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
228
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
229
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
230
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
231
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
232
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
233
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
234
define('LOG_PROMOTION_CREATE', 'promotion_created');
235
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
236
define('LOG_CAREER_CREATE', 'career_created');
237
define('LOG_CAREER_DELETE', 'career_deleted');
238
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
239
//define('LOG_WIKI_ACCESS', 'wiki_page_view');
240
// All results from an exercise
241
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
242
// Logs only the one attempt
243
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
244
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
245
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
246
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
247
248
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
249
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
250
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
251
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
252
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
253
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
254
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
255
256
// Event logs data types (max 20 chars)
257
define('LOG_COURSE_CODE', 'course_code');
258
define('LOG_COURSE_ID', 'course_id');
259
define('LOG_USER_ID', 'user_id');
260
define('LOG_USER_OBJECT', 'user_object');
261
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
262
define('LOG_SESSION_ID', 'session_id');
263
264
define('LOG_QUESTION_ID', 'question_id');
265
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
266
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
267
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
268
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
269
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
270
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
271
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
272
define('LOG_CAREER_ID', 'career_id');
273
define('LOG_PROMOTION_ID', 'promotion_id');
274
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
275
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
276
define('LOG_GRADEBOOK_ID', 'gradebook_id');
277
//define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
278
define('LOG_EXERCISE_ID', 'exercise_id');
279
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
280
define('LOG_LP_ID', 'lp_id');
281
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
282
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
283
284
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
285
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
286
define('LOG_WORK_DATA', 'work_data_array');
287
288
define('LOG_MY_FOLDER_PATH', 'path');
289
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
290
291
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
292
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
293
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
294
295
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
296
297
define('LOG_QUESTION_CREATED', 'question_created');
298
define('LOG_QUESTION_UPDATED', 'question_updated');
299
define('LOG_QUESTION_DELETED', 'question_deleted');
300
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
301
302
define('LOG_SURVEY_ID', 'survey_id');
303
define('LOG_SURVEY_CREATED', 'survey_created');
304
define('LOG_SURVEY_DELETED', 'survey_deleted');
305
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
306
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
307
308
//used when login_is_email setting is true
309
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
310
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
311
312
// This constant is a result of Windows OS detection, it has a boolean value:
313
// true whether the server runs on Windows OS, false otherwise.
314
define('IS_WINDOWS_OS', api_is_windows_os());
315
316
// Patterns for processing paths. Examples.
317
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
318
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
319
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
320
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
321
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
322
// basic (leaf elements)
323
define('REL_CODE_PATH', 'REL_CODE_PATH');
324
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
325
define('REL_HOME_PATH', 'REL_HOME_PATH');
326
327
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
328
define('WEB_PATH', 'WEB_PATH');
329
define('SYS_PATH', 'SYS_PATH');
330
define('SYMFONY_SYS_PATH', 'SYMFONY_SYS_PATH');
331
332
define('REL_PATH', 'REL_PATH');
333
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
334
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
335
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
336
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
337
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
338
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
339
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
340
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
341
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
342
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
343
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
344
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
345
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
346
define('LIBRARY_PATH', 'LIBRARY_PATH');
347
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
348
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
349
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
350
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
351
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
352
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
353
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
354
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
355
356
// Relations type with Course manager
357
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
358
359
// Relations type with Human resources manager
360
define('COURSE_RELATION_TYPE_RRHH', 1);
361
362
// User image sizes
363
define('USER_IMAGE_SIZE_ORIGINAL', 1);
364
define('USER_IMAGE_SIZE_BIG', 2);
365
define('USER_IMAGE_SIZE_MEDIUM', 3);
366
define('USER_IMAGE_SIZE_SMALL', 4);
367
368
// Gradebook link constants
369
// Please do not change existing values, they are used in the database !
370
define('GRADEBOOK_ITEM_LIMIT', 1000);
371
372
define('LINK_EXERCISE', 1);
373
define('LINK_DROPBOX', 2);
374
define('LINK_STUDENTPUBLICATION', 3);
375
define('LINK_LEARNPATH', 4);
376
define('LINK_FORUM_THREAD', 5);
377
//define('LINK_WORK',6);
378
define('LINK_ATTENDANCE', 7);
379
define('LINK_SURVEY', 8);
380
define('LINK_HOTPOTATOES', 9);
381
define('LINK_PORTFOLIO', 10);
382
383
// Score display types constants
384
define('SCORE_DIV', 1); // X / Y
385
define('SCORE_PERCENT', 2); // XX %
386
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
387
define('SCORE_AVERAGE', 4); // XX %
388
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
389
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
390
define('SCORE_SIMPLE', 7); // X
391
define('SCORE_IGNORE_SPLIT', 8); //  ??
392
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
393
define('SCORE_CUSTOM', 10); // Good!
394
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
395
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
396
define('SCORE_ONLY_SCORE', 13); // X - Good!
397
define('SCORE_NUMERIC', 14);
398
399
define('SCORE_BOTH', 1);
400
define('SCORE_ONLY_DEFAULT', 2);
401
define('SCORE_ONLY_CUSTOM', 3);
402
403
// From display.lib.php
404
405
define('MAX_LENGTH_BREADCRUMB', 100);
406
define('ICON_SIZE_ATOM', 8);
407
define('ICON_SIZE_TINY', 16);
408
define('ICON_SIZE_SMALL', 22);
409
define('ICON_SIZE_MEDIUM', 32);
410
define('ICON_SIZE_LARGE', 48);
411
define('ICON_SIZE_BIG', 64);
412
define('ICON_SIZE_HUGE', 128);
413
define('SHOW_TEXT_NEAR_ICONS', false);
414
415
// Session catalog
416
define('CATALOG_COURSES', 0);
417
define('CATALOG_SESSIONS', 1);
418
define('CATALOG_COURSES_SESSIONS', 2);
419
420
// Hook type events, pre-process and post-process.
421
// All means to be executed for both hook event types
422
define('HOOK_EVENT_TYPE_PRE', 0);
423
define('HOOK_EVENT_TYPE_POST', 1);
424
define('HOOK_EVENT_TYPE_ALL', 10);
425
426
// Group permissions
427
define('GROUP_PERMISSION_OPEN', '1');
428
define('GROUP_PERMISSION_CLOSED', '2');
429
430
// Group user permissions
431
define('GROUP_USER_PERMISSION_ADMIN', 1); // the admin of a group
432
define('GROUP_USER_PERMISSION_READER', 2); // a normal user
433
define('GROUP_USER_PERMISSION_PENDING_INVITATION', 3); // When an admin/moderator invites a user
434
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', 4); // an user joins a group
435
define('GROUP_USER_PERMISSION_MODERATOR', 5); // a moderator
436
define('GROUP_USER_PERMISSION_ANONYMOUS', 6); // an anonymous user
437
define('GROUP_USER_PERMISSION_HRM', 7); // a human resources manager
438
439
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
440
define('GROUP_IMAGE_SIZE_BIG', 2);
441
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
442
define('GROUP_IMAGE_SIZE_SMALL', 4);
443
define('GROUP_TITLE_LENGTH', 50);
444
445
// Exercise
446
// @todo move into a class
447
define('ALL_ON_ONE_PAGE', 1);
448
define('ONE_PER_PAGE', 2);
449
450
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
451
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
452
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
453
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
454
455
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
456
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
457
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
458
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
459
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
460
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
461
define('RESULT_DISABLE_RANKING', 6);
462
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
463
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
464
define('RESULT_DISABLE_RADAR', 9);
465
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
466
467
define('EXERCISE_MAX_NAME_SIZE', 80);
468
469
// Question types (edit next array as well when adding values)
470
// @todo move into a class
471
define('UNIQUE_ANSWER', 1);
472
define('MULTIPLE_ANSWER', 2);
473
define('FILL_IN_BLANKS', 3);
474
define('MATCHING', 4);
475
define('FREE_ANSWER', 5);
476
define('HOT_SPOT', 6);
477
define('HOT_SPOT_ORDER', 7);
478
define('HOT_SPOT_DELINEATION', 8);
479
define('MULTIPLE_ANSWER_COMBINATION', 9);
480
define('UNIQUE_ANSWER_NO_OPTION', 10);
481
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
482
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
483
define('ORAL_EXPRESSION', 13);
484
define('GLOBAL_MULTIPLE_ANSWER', 14);
485
define('MEDIA_QUESTION', 15);
486
define('CALCULATED_ANSWER', 16);
487
define('UNIQUE_ANSWER_IMAGE', 17);
488
define('DRAGGABLE', 18);
489
define('MATCHING_DRAGGABLE', 19);
490
define('ANNOTATION', 20);
491
define('READING_COMPREHENSION', 21);
492
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
493
494
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
495
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
496
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
497
498
// Question selection type
499
define('EX_Q_SELECTION_ORDERED', 1);
500
define('EX_Q_SELECTION_RANDOM', 2);
501
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
502
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
503
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
504
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
505
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
506
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
507
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
508
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
509
510
// Used to save the skill_rel_item table
511
define('ITEM_TYPE_EXERCISE', 1);
512
define('ITEM_TYPE_HOTPOTATOES', 2);
513
define('ITEM_TYPE_LINK', 3);
514
define('ITEM_TYPE_LEARNPATH', 4);
515
define('ITEM_TYPE_GRADEBOOK', 5);
516
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
517
//define('ITEM_TYPE_FORUM', 7);
518
define('ITEM_TYPE_ATTENDANCE', 8);
519
define('ITEM_TYPE_SURVEY', 9);
520
define('ITEM_TYPE_FORUM_THREAD', 10);
521
define('ITEM_TYPE_PORTFOLIO', 11);
522
523
// Course description blocks.
524
define('ADD_BLOCK', 8);
525
526
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
527
define(
528
    'QUESTION_TYPES',
529
    UNIQUE_ANSWER.':'.
530
    MULTIPLE_ANSWER.':'.
531
    FILL_IN_BLANKS.':'.
532
    MATCHING.':'.
533
    FREE_ANSWER.':'.
534
    HOT_SPOT.':'.
535
    HOT_SPOT_ORDER.':'.
536
    HOT_SPOT_DELINEATION.':'.
537
    MULTIPLE_ANSWER_COMBINATION.':'.
538
    UNIQUE_ANSWER_NO_OPTION.':'.
539
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
540
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
541
    ORAL_EXPRESSION.':'.
542
    GLOBAL_MULTIPLE_ANSWER.':'.
543
    MEDIA_QUESTION.':'.
544
    CALCULATED_ANSWER.':'.
545
    UNIQUE_ANSWER_IMAGE.':'.
546
    DRAGGABLE.':'.
547
    MATCHING_DRAGGABLE.':'.
548
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
549
    ANNOTATION
550
);
551
552
//Some alias used in the QTI exports
553
define('MCUA', 1);
554
define('TF', 1);
555
define('MCMA', 2);
556
define('FIB', 3);
557
558
// Message
559
define('MESSAGE_STATUS_INVITATION_PENDING', 5);
560
define('MESSAGE_STATUS_INVITATION_ACCEPTED', 6);
561
define('MESSAGE_STATUS_INVITATION_DENIED', 7);
562
define('MESSAGE_STATUS_WALL', 8);
563
564
define('MESSAGE_STATUS_WALL_DELETE', 9);
565
define('MESSAGE_STATUS_WALL_POST', 10);
566
567
define('MESSAGE_STATUS_FORUM', 12);
568
define('MESSAGE_STATUS_PROMOTED', 13);
569
570
// Images
571
define('IMAGE_WALL_SMALL_SIZE', 200);
572
define('IMAGE_WALL_MEDIUM_SIZE', 500);
573
define('IMAGE_WALL_BIG_SIZE', 2000);
574
define('IMAGE_WALL_SMALL', 'small');
575
define('IMAGE_WALL_MEDIUM', 'medium');
576
define('IMAGE_WALL_BIG', 'big');
577
578
// Social PLUGIN PLACES
579
define('SOCIAL_LEFT_PLUGIN', 1);
580
define('SOCIAL_CENTER_PLUGIN', 2);
581
define('SOCIAL_RIGHT_PLUGIN', 3);
582
define('CUT_GROUP_NAME', 50);
583
584
/**
585
 * FormValidator Filter.
586
 */
587
define('NO_HTML', 1);
588
define('STUDENT_HTML', 2);
589
define('TEACHER_HTML', 3);
590
define('STUDENT_HTML_FULLPAGE', 4);
591
define('TEACHER_HTML_FULLPAGE', 5);
592
593
// Timeline
594
define('TIMELINE_STATUS_ACTIVE', '1');
595
define('TIMELINE_STATUS_INACTIVE', '2');
596
597
// Event email template class
598
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
599
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
600
601
// Course home
602
define('SHORTCUTS_HORIZONTAL', 0);
603
define('SHORTCUTS_VERTICAL', 1);
604
605
// Course copy
606
define('FILE_SKIP', 1);
607
define('FILE_RENAME', 2);
608
define('FILE_OVERWRITE', 3);
609
define('UTF8_CONVERT', false); //false by default
610
611
define('DOCUMENT', 'file');
612
define('FOLDER', 'folder');
613
614
define('RESOURCE_ASSET', 'asset');
615
define('RESOURCE_DOCUMENT', 'document');
616
define('RESOURCE_GLOSSARY', 'glossary');
617
define('RESOURCE_EVENT', 'calendar_event');
618
define('RESOURCE_LINK', 'link');
619
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
620
define('RESOURCE_LEARNPATH', 'learnpath');
621
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
622
define('RESOURCE_ANNOUNCEMENT', 'announcement');
623
define('RESOURCE_FORUM', 'forum');
624
define('RESOURCE_FORUMTOPIC', 'thread');
625
define('RESOURCE_FORUMPOST', 'post');
626
define('RESOURCE_QUIZ', 'quiz');
627
define('RESOURCE_TEST_CATEGORY', 'test_category');
628
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
629
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
630
define('RESOURCE_LINKCATEGORY', 'Link_Category');
631
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
632
define('RESOURCE_SCORM', 'Scorm');
633
define('RESOURCE_SURVEY', 'survey');
634
define('RESOURCE_SURVEYQUESTION', 'survey_question');
635
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
636
define('RESOURCE_WIKI', 'wiki');
637
define('RESOURCE_THEMATIC', 'thematic');
638
define('RESOURCE_ATTENDANCE', 'attendance');
639
define('RESOURCE_WORK', 'work');
640
define('RESOURCE_SESSION_COURSE', 'session_course');
641
define('RESOURCE_GRADEBOOK', 'gradebook');
642
define('ADD_THEMATIC_PLAN', 6);
643
644
// Max online users to show per page (whoisonline)
645
define('MAX_ONLINE_USERS', 12);
646
647
define('TOOL_AUTHORING', 'toolauthoring');
648
define('TOOL_INTERACTION', 'toolinteraction');
649
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
650
define('TOOL_ADMIN', 'tooladmin');
651
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
652
define('TOOL_DRH', 'tool_drh');
653
define('TOOL_STUDENT_VIEW', 'toolstudentview');
654
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
655
656
// Search settings (from main/inc/lib/search/IndexableChunk.class.php )
657
// some constants to avoid serialize string keys on serialized data array
658
define('SE_COURSE_ID', 0);
659
define('SE_TOOL_ID', 1);
660
define('SE_DATA', 2);
661
define('SE_USER', 3);
662
663
// in some cases we need top differenciate xapian documents of the same tool
664
define('SE_DOCTYPE_EXERCISE_EXERCISE', 0);
665
define('SE_DOCTYPE_EXERCISE_QUESTION', 1);
666
667
// xapian prefixes
668
define('XAPIAN_PREFIX_COURSEID', 'C');
669
define('XAPIAN_PREFIX_TOOLID', 'O');
670
671
// User active field constants
672
define('USER_ACTIVE', 1);
673
define('USER_INACTIVE', 0);
674
define('USER_INACTIVE_AUTOMATIC', -1);
675
define('USER_SOFT_DELETED', -2);
676
677
/**
678
 * Returns a path to a certain resource within Chamilo.
679
 *
680
 * @param string $path A path which type is to be converted. Also, it may be a defined constant for a path.
681
 *
682
 * @return string the requested path or the converted path
683
 *
684
 * Notes about the current behaviour model:
685
 * 1. Windows back-slashes are converted to slashes in the result.
686
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
687
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
688
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
689
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
690
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
691
 * It has not been identified as needed yet.
692
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
693
 *
694
 * Vchamilo changes : allow using an alternate configuration
695
 * to get vchamilo  instance paths
696
 */
697
function api_get_path($path = '', $configuration = [])
698
{
699
    global $paths;
700
701
    // get proper configuration data if exists
702
    global $_configuration;
703
704
    $emptyConfigurationParam = false;
705
    if (empty($configuration)) {
706
        $configuration = (array) $_configuration;
707
        $emptyConfigurationParam = true;
708
    }
709
710
    $root_sys = Container::getProjectDir();
711
    $root_web = '';
712
    if (isset(Container::$container)) {
713
        $root_web = Container::$container->get('router')->generate(
714
            'index',
715
            [],
716
            UrlGeneratorInterface::ABSOLUTE_URL
717
        );
718
    }
719
720
    /*if (api_get_multiple_access_url()) {
721
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the main_api.lib.php
722
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
723
            // We look into the DB the function api_get_access_url
724
            $urlInfo = api_get_access_url($configuration['access_url']);
725
            // Avoid default value
726
            $defaultValues = ['http://localhost/', 'https://localhost/'];
727
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
728
                $root_web = 1 == $urlInfo['active'] ? $urlInfo['url'] : $configuration['root_web'];
729
            }
730
        }
731
    }*/
732
733
    $paths = [
734
        WEB_PATH => $root_web,
735
        SYMFONY_SYS_PATH => $root_sys,
736
        SYS_PATH => $root_sys.'public/',
737
        REL_PATH => '',
738
        CONFIGURATION_PATH => 'app/config/',
739
        LIBRARY_PATH => $root_sys.'public/main/inc/lib/',
740
741
        REL_COURSE_PATH => '',
742
        REL_CODE_PATH => '/main/',
743
744
        SYS_CODE_PATH => $root_sys.'public/main/',
745
        SYS_CSS_PATH => $root_sys.'public/build/css/',
746
        SYS_PLUGIN_PATH => $root_sys.'public/plugin/',
747
        SYS_ARCHIVE_PATH => $root_sys.'var/cache/',
748
        SYS_TEST_PATH => $root_sys.'tests/',
749
        SYS_TEMPLATE_PATH => $root_sys.'public/main/template/',
750
        SYS_PUBLIC_PATH => $root_sys.'public/',
751
        SYS_FONTS_PATH => $root_sys.'public/fonts/',
752
753
        WEB_CODE_PATH => $root_web.'main/',
754
        WEB_PLUGIN_ASSET_PATH => $root_web.'plugins/',
755
        WEB_COURSE_PATH => $root_web.'course/',
756
        WEB_IMG_PATH => $root_web.'img/',
757
        WEB_CSS_PATH => $root_web.'build/css/',
758
        WEB_AJAX_PATH => $root_web.'main/inc/ajax/',
759
        WEB_LIBRARY_PATH => $root_web.'main/inc/lib/',
760
        WEB_LIBRARY_JS_PATH => $root_web.'main/inc/lib/javascript/',
761
        WEB_PLUGIN_PATH => $root_web.'plugin/',
762
        WEB_PUBLIC_PATH => $root_web,
763
    ];
764
765
    $root_rel = '';
766
767
    global $virtualChamilo;
768
    if (!empty($virtualChamilo)) {
769
        $paths[SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
770
        //$paths[SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
771
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
772
        $paths[WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
773
        //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
774
775
        // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
776
777
        // RewriteEngine On
778
        // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
779
780
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
781
        //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
782
        //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
783
    }
784
785
    $path = trim($path);
786
787
    // Retrieving a common-purpose path.
788
    if (isset($paths[$path])) {
789
        return $paths[$path];
790
    }
791
792
    return false;
793
}
794
795
/**
796
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
797
 *
798
 * @param string $path the input path
799
 *
800
 * @return string returns the modified path
801
 */
802
function api_add_trailing_slash($path)
803
{
804
    return '/' === substr($path, -1) ? $path : $path.'/';
805
}
806
807
/**
808
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
809
 *
810
 * @param string $path the input path
811
 *
812
 * @return string returns the modified path
813
 */
814
function api_remove_trailing_slash($path)
815
{
816
    return '/' === substr($path, -1) ? substr($path, 0, -1) : $path;
817
}
818
819
/**
820
 * Checks the RFC 3986 syntax of a given URL.
821
 *
822
 * @param string $url      the URL to be checked
823
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
824
 *
825
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
826
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
827
 *
828
 * @see http://drupal.org
829
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
830
 * @see http://bugs.php.net/51192
831
 */
832
function api_valid_url($url, $absolute = false)
833
{
834
    if ($absolute) {
835
        if (preg_match("
836
            /^                                                      # Start at the beginning of the text
837
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
838
            (?:                                                     # Userinfo (optional) which is typically
839
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
840
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
841
            )?
842
            (?:
843
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
844
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
845
            )
846
            (?::[0-9]+)?                                            # Server port number (optional)
847
            (?:[\/|\?]
848
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
849
            *)?
850
            $/xi", $url)) {
851
            return $url;
852
        }
853
854
        return false;
855
    } else {
856
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
857
    }
858
}
859
860
/**
861
 * Checks whether a given string looks roughly like an email address.
862
 *
863
 * @param string $address the e-mail address to be checked
864
 *
865
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
866
 */
867
function api_valid_email($address)
868
{
869
    return filter_var($address, FILTER_VALIDATE_EMAIL);
870
}
871
872
/**
873
 * Function used to protect a course script.
874
 * The function blocks access when
875
 * - there is no $_SESSION["_course"] defined; or
876
 * - $is_allowed_in_course is set to false (this depends on the course
877
 * visibility and user status).
878
 *
879
 * This is only the first proposal, test and improve!
880
 *
881
 * @param bool Option to print headers when displaying error message. Default: false
882
 * @param bool whether session admins should be allowed or not
883
 * @param string $checkTool check if tool is available for users (user, group)
884
 *
885
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
886
 *
887
 * @todo replace global variable
888
 *
889
 * @author Roan Embrechts
890
 */
891
function api_protect_course_script($print_headers = false, $allow_session_admins = false, string $checkTool = '', $cid = null): bool
892
{
893
    $course_info = api_get_course_info();
894
    if (empty($course_info) && isset($_REQUEST['cid'])) {
895
        $course_info = api_get_course_info_by_id((int) $_REQUEST['cid']);
896
    }
897
898
    if (isset($cid)) {
899
        $course_info = api_get_course_info_by_id($cid);
900
    }
901
902
    if (empty($course_info)) {
903
        api_not_allowed($print_headers);
904
905
        return false;
906
    }
907
908
    if (api_is_drh()) {
909
        return true;
910
    }
911
912
    // Session admin has access to course
913
    $sessionAccess = ('true' === api_get_setting('session.session_admins_access_all_content'));
914
    if ($sessionAccess) {
915
        $allow_session_admins = true;
916
    }
917
918
    if (api_is_platform_admin($allow_session_admins)) {
919
        return true;
920
    }
921
922
    $isAllowedInCourse = api_is_allowed_in_course();
923
    $is_visible = false;
924
    if (isset($course_info) && isset($course_info['visibility'])) {
925
        switch ($course_info['visibility']) {
926
            default:
927
            case Course::CLOSED:
928
                // Completely closed: the course is only accessible to the teachers. - 0
929
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
930
                    $is_visible = true;
931
                }
932
                break;
933
            case Course::REGISTERED:
934
                // Private - access authorized to course members only - 1
935
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
936
                    $is_visible = true;
937
                }
938
                break;
939
            case Course::OPEN_PLATFORM:
940
                // Open - access allowed for users registered on the platform - 2
941
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
942
                    $is_visible = true;
943
                }
944
                break;
945
            case Course::OPEN_WORLD:
946
                //Open - access allowed for the whole world - 3
947
                $is_visible = true;
948
                break;
949
            case Course::HIDDEN:
950
                //Completely closed: the course is only accessible to the teachers. - 0
951
                if (api_is_platform_admin()) {
952
                    $is_visible = true;
953
                }
954
                break;
955
        }
956
957
        //If password is set and user is not registered to the course then the course is not visible
958
        if (false === $isAllowedInCourse &&
959
            isset($course_info['registration_code']) &&
960
            !empty($course_info['registration_code'])
961
        ) {
962
            $is_visible = false;
963
        }
964
    }
965
966
    if (!empty($checkTool)) {
967
        if (!api_is_allowed_to_edit(true, true, true)) {
968
            $toolInfo = api_get_tool_information_by_name($checkTool);
969
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && 0 == $toolInfo['visibility']) {
970
                api_not_allowed(true);
971
972
                return false;
973
            }
974
        }
975
    }
976
977
    // Check session visibility
978
    $session_id = api_get_session_id();
979
980
    if (!empty($session_id)) {
981
        // $isAllowedInCourse was set in local.inc.php
982
        if (!$isAllowedInCourse) {
983
            $is_visible = false;
984
        }
985
        // Check if course is inside session.
986
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
987
            $is_visible = false;
988
        }
989
    }
990
991
    if (!$is_visible) {
992
        api_not_allowed($print_headers);
993
994
        return false;
995
    }
996
997
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
998
        $plugin = Positioning::create();
999
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1000
        if ('true' === $block) {
1001
            $currentPath = $_SERVER['PHP_SELF'];
1002
            // Allowed only this course paths.
1003
            $paths = [
1004
                '/plugin/positioning/start.php',
1005
                '/plugin/positioning/start_student.php',
1006
                '/main/course_home/course_home.php',
1007
                '/main/exercise/overview.php',
1008
            ];
1009
1010
            if (!in_array($currentPath, $paths, true)) {
1011
                // Check if entering an exercise.
1012
                // @todo remove global $current_course_tool
1013
                /*global $current_course_tool;
1014
                if ('quiz' !== $current_course_tool) {
1015
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1016
                    if ($initialData && isset($initialData['exercise_id'])) {
1017
                        $results = Event::getExerciseResultsByUser(
1018
                            api_get_user_id(),
1019
                            $initialData['exercise_id'],
1020
                            $course_info['real_id'],
1021
                            $session_id
1022
                        );
1023
                        if (empty($results)) {
1024
                            api_not_allowed($print_headers);
1025
1026
                            return false;
1027
                        }
1028
                    }
1029
                }*/
1030
            }
1031
        }
1032
    }
1033
1034
    api_block_inactive_user();
1035
1036
    return true;
1037
}
1038
1039
/**
1040
 * Function used to protect an admin script.
1041
 *
1042
 * The function blocks access when the user has no platform admin rights
1043
 * with an error message printed on default output
1044
 *
1045
 * @param bool Whether to allow session admins as well
1046
 * @param bool Whether to allow HR directors as well
1047
 * @param string An optional message (already passed through get_lang)
1048
 *
1049
 * @return bool True if user is allowed, false otherwise.
1050
 *              The function also outputs an error message in case not allowed
1051
 *
1052
 * @author Roan Embrechts (original author)
1053
 */
1054
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1055
{
1056
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1057
        api_not_allowed(true, $message);
1058
1059
        return false;
1060
    }
1061
    api_block_inactive_user();
1062
1063
    return true;
1064
}
1065
1066
/**
1067
 * Blocks inactive users with a currently active session from accessing more pages "live".
1068
 *
1069
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1070
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1071
 */
1072
function api_block_inactive_user()
1073
{
1074
    $data = true;
1075
    if ('true' !== api_get_setting('security.security_block_inactive_users_immediately')) {
1076
        return $data;
1077
    }
1078
1079
    $userId = api_get_user_id();
1080
    $homeUrl = api_get_path(WEB_PATH);
1081
    if (0 == $userId) {
1082
        return $data;
1083
    }
1084
1085
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1086
            WHERE id = $userId";
1087
1088
    $result = Database::query($sql);
1089
    if (Database::num_rows($result) > 0) {
1090
        $result_array = Database::fetch_array($result);
1091
        $data = (bool) $result_array['active'];
1092
    }
1093
    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...
1094
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1095
        $tpl->assign('hide_login_link', 1);
1096
1097
        //api_not_allowed(true, get_lang('Account inactive'));
1098
        // we were not in a course, return to home page
1099
        $msg = Display::return_message(
1100
            get_lang('Account inactive'),
1101
            'error',
1102
            false
1103
        );
1104
1105
        $msg .= '<p class="text-center">
1106
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1107
1108
        $tpl->assign('content', $msg);
1109
        $tpl->display_one_col_template();
1110
        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...
1111
    }
1112
1113
    return $data;
1114
}
1115
1116
/**
1117
 * Function used to protect a teacher script.
1118
 * The function blocks access when the user has no teacher rights.
1119
 *
1120
 * @return bool True if the current user can access the script, false otherwise
1121
 *
1122
 * @author Yoselyn Castillo
1123
 */
1124
function api_protect_teacher_script()
1125
{
1126
    if (!api_is_allowed_to_edit()) {
1127
        api_not_allowed(true);
1128
1129
        return false;
1130
    }
1131
1132
    return true;
1133
}
1134
1135
/**
1136
 * Function used to prevent anonymous users from accessing a script.
1137
 *
1138
 * @param bool $printHeaders
1139
 *
1140
 * @return bool
1141
 */
1142
function api_block_anonymous_users($printHeaders = true)
1143
{
1144
    $isAuth = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
1145
1146
    if (false === $isAuth) {
1147
        api_not_allowed($printHeaders);
1148
1149
        return false;
1150
    }
1151
1152
    api_block_inactive_user();
1153
1154
    return true;
1155
}
1156
1157
/**
1158
 * Returns a rough evaluation of the browser's name and version based on very
1159
 * simple regexp.
1160
 *
1161
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1162
 */
1163
function api_get_navigator()
1164
{
1165
    $navigator = 'Unknown';
1166
    $version = 0;
1167
1168
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1169
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1170
    }
1171
1172
    if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
1173
        $navigator = 'Opera';
1174
        [, $version] = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1175
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
1176
        $navigator = 'Edge';
1177
        [, $version] = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1178
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
1179
        $navigator = 'Internet Explorer';
1180
        [, $version] = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1181
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
1182
        $navigator = 'Chrome';
1183
        [, $version] = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1184
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
1185
        $navigator = 'Safari';
1186
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Version/')) {
1187
            // If this Safari does have the "Version/" string in its user agent
1188
            // then use that as a version indicator rather than what's after
1189
            // "Safari/" which is rather a "build number" or something
1190
            [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1191
        } else {
1192
            [, $version] = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1193
        }
1194
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
1195
        $navigator = 'Firefox';
1196
        [, $version] = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1197
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape')) {
1198
        $navigator = 'Netscape';
1199
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/')) {
1200
            [, $version] = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1201
        } else {
1202
            [, $version] = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1203
        }
1204
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror')) {
1205
        $navigator = 'Konqueror';
1206
        [, $version] = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1207
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit')) {
1208
        $navigator = 'AppleWebKit';
1209
        [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1210
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko')) {
1211
        $navigator = 'Mozilla';
1212
        [, $version] = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1213
    }
1214
1215
    // Now cut extra stuff around (mostly *after*) the version number
1216
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1217
1218
    if (false === strpos($version, '.')) {
1219
        $version = number_format(doubleval($version), 1);
1220
    }
1221
1222
    return ['name' => $navigator, 'version' => $version];
1223
}
1224
1225
/**
1226
 * This function returns the id of the user which is stored in the $_user array.
1227
 *
1228
 * example: The function can be used to check if a user is logged in
1229
 *          if (api_get_user_id())
1230
 *
1231
 * @return int the id of the current user, 0 if is empty
1232
 */
1233
function api_get_user_id()
1234
{
1235
    $userInfo = Session::read('_user');
1236
    if ($userInfo && isset($userInfo['user_id'])) {
1237
        return (int) $userInfo['user_id'];
1238
    }
1239
1240
    return 0;
1241
}
1242
1243
/**
1244
 * Formats user information into a standard array
1245
 * This function should be only used inside api_get_user_info().
1246
 *
1247
 * @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...
1248
 * @param bool $add_password
1249
 * @param bool $loadAvatars  turn off to improve performance
1250
 *
1251
 * @return array Standard user array
1252
 */
1253
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1254
{
1255
    $result = [];
1256
1257
    if (!isset($user['id'])) {
1258
        return [];
1259
    }
1260
1261
    $result['firstname'] = null;
1262
    $result['lastname'] = null;
1263
1264
    if (isset($user['firstname']) && isset($user['lastname'])) {
1265
        // with only lowercase
1266
        $result['firstname'] = $user['firstname'];
1267
        $result['lastname'] = $user['lastname'];
1268
    } elseif (isset($user['firstName']) && isset($user['lastName'])) {
1269
        // with uppercase letters
1270
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1271
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1272
    }
1273
1274
    if (isset($user['email'])) {
1275
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1276
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1277
    } else {
1278
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1279
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1280
    }
1281
1282
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1283
    $result['complete_name_with_username'] = $result['complete_name'];
1284
1285
    if (!empty($user['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1286
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1287
    }
1288
1289
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1290
    if (!empty($user['email'])) {
1291
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1292
        if ($showEmail) {
1293
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1294
        }
1295
    } else {
1296
        $result['complete_name_with_email'] = $result['complete_name'];
1297
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1298
    }
1299
1300
    // Kept for historical reasons
1301
    $result['firstName'] = $result['firstname'];
1302
    $result['lastName'] = $result['lastname'];
1303
1304
    $attributes = [
1305
        'phone',
1306
        'address',
1307
        'picture_uri',
1308
        'official_code',
1309
        'status',
1310
        'active',
1311
        'auth_sources',
1312
        'username',
1313
        'theme',
1314
        'language',
1315
        'locale',
1316
        'creator_id',
1317
        'created_at',
1318
        'hr_dept_id',
1319
        'expiration_date',
1320
        'last_login',
1321
        'user_is_online',
1322
        'profile_completed',
1323
    ];
1324
1325
    if ('true' === api_get_setting('extended_profile')) {
1326
        $attributes[] = 'competences';
1327
        $attributes[] = 'diplomas';
1328
        $attributes[] = 'teach';
1329
        $attributes[] = 'openarea';
1330
    }
1331
1332
    foreach ($attributes as $attribute) {
1333
        $result[$attribute] = $user[$attribute] ?? null;
1334
    }
1335
1336
    $user_id = (int) $user['id'];
1337
    // Maintain the user_id index for backwards compatibility
1338
    $result['user_id'] = $result['id'] = $user_id;
1339
1340
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1341
    $result['has_certificates'] = 0;
1342
    if (!empty($hasCertificates)) {
1343
        $result['has_certificates'] = 1;
1344
    }
1345
1346
    $result['icon_status'] = '';
1347
    $result['icon_status_medium'] = '';
1348
    $result['is_admin'] = UserManager::is_admin($user_id);
1349
1350
    // Getting user avatar.
1351
    if ($loadAvatars) {
1352
        $result['avatar'] = '';
1353
        $result['avatar_no_query'] = '';
1354
        $result['avatar_small'] = '';
1355
        $result['avatar_medium'] = '';
1356
1357
        if (empty($user['avatar'])) {
1358
            $originalFile = UserManager::getUserPicture(
1359
                $user_id,
1360
                USER_IMAGE_SIZE_ORIGINAL,
1361
                null,
1362
                $result
1363
            );
1364
            $result['avatar'] = $originalFile;
1365
            $avatarString = explode('?', $result['avatar']);
1366
            $result['avatar_no_query'] = reset($avatarString);
1367
        } else {
1368
            $result['avatar'] = $user['avatar'];
1369
            $avatarString = explode('?', $user['avatar']);
1370
            $result['avatar_no_query'] = reset($avatarString);
1371
        }
1372
1373
        if (!isset($user['avatar_small'])) {
1374
            $smallFile = UserManager::getUserPicture(
1375
                $user_id,
1376
                USER_IMAGE_SIZE_SMALL,
1377
                null,
1378
                $result
1379
            );
1380
            $result['avatar_small'] = $smallFile;
1381
        } else {
1382
            $result['avatar_small'] = $user['avatar_small'];
1383
        }
1384
1385
        if (!isset($user['avatar_medium'])) {
1386
            $mediumFile = UserManager::getUserPicture(
1387
                $user_id,
1388
                USER_IMAGE_SIZE_MEDIUM,
1389
                null,
1390
                $result
1391
            );
1392
            $result['avatar_medium'] = $mediumFile;
1393
        } else {
1394
            $result['avatar_medium'] = $user['avatar_medium'];
1395
        }
1396
1397
        $urlImg = api_get_path(WEB_IMG_PATH);
1398
        $iconStatus = '';
1399
        $iconStatusMedium = '';
1400
        $label = '';
1401
1402
        switch ($result['status']) {
1403
            case STUDENT:
1404
                if ($result['has_certificates']) {
1405
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1406
                    $label = get_lang('Graduated');
1407
                } else {
1408
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1409
                    $label = get_lang('Student');
1410
                }
1411
                break;
1412
            case COURSEMANAGER:
1413
                if ($result['is_admin']) {
1414
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1415
                    $label = get_lang('Admin');
1416
                } else {
1417
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1418
                    $label = get_lang('Teacher');
1419
                }
1420
                break;
1421
            case STUDENT_BOSS:
1422
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1423
                $label = get_lang('StudentBoss');
1424
                break;
1425
        }
1426
1427
        if (!empty($iconStatus)) {
1428
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1429
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1430
        }
1431
1432
        $result['icon_status'] = $iconStatus;
1433
        $result['icon_status_label'] = $label;
1434
        $result['icon_status_medium'] = $iconStatusMedium;
1435
    }
1436
1437
    if (isset($user['user_is_online'])) {
1438
        $result['user_is_online'] = true == $user['user_is_online'] ? 1 : 0;
1439
    }
1440
    if (isset($user['user_is_online_in_chat'])) {
1441
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1442
    }
1443
1444
    if ($add_password) {
1445
        $result['password'] = $user['password'];
1446
    }
1447
1448
    if (isset($result['profile_completed'])) {
1449
        $result['profile_completed'] = $user['profile_completed'];
1450
    }
1451
1452
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1453
1454
    // Send message link
1455
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1456
    $result['complete_name_with_message_link'] = Display::url(
1457
        $result['complete_name_with_username'],
1458
        $sendMessage,
1459
        ['class' => 'ajax']
1460
    );
1461
1462
    if (isset($user['extra'])) {
1463
        $result['extra'] = $user['extra'];
1464
    }
1465
1466
    return $result;
1467
}
1468
1469
/**
1470
 * Finds all the information about a user.
1471
 * If no parameter is passed you find all the information about the current user.
1472
 *
1473
 * @param int  $user_id
1474
 * @param bool $checkIfUserOnline
1475
 * @param bool $showPassword
1476
 * @param bool $loadExtraData
1477
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1478
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1479
 * @param bool $updateCache              update apc cache if exists
1480
 *
1481
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1482
 *
1483
 * @author Patrick Cool <[email protected]>
1484
 * @author Julio Montoya
1485
 *
1486
 * @version 21 September 2004
1487
 */
1488
function api_get_user_info(
1489
    $user_id = 0,
1490
    $checkIfUserOnline = false,
1491
    $showPassword = false,
1492
    $loadExtraData = false,
1493
    $loadOnlyVisibleExtraData = false,
1494
    $loadAvatars = true,
1495
    $updateCache = false
1496
) {
1497
    // Make sure user_id is safe
1498
    $user_id = (int) $user_id;
1499
    $user = false;
1500
    if (empty($user_id)) {
1501
        $userFromSession = Session::read('_user');
1502
        if (isset($userFromSession) && !empty($userFromSession)) {
1503
            return $userFromSession;
1504
            /*
1505
            return _api_format_user(
1506
                $userFromSession,
1507
                $showPassword,
1508
                $loadAvatars
1509
            );*/
1510
        }
1511
1512
        return false;
1513
    }
1514
1515
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1516
            WHERE id = $user_id";
1517
    $result = Database::query($sql);
1518
    if (Database::num_rows($result) > 0) {
1519
        $result_array = Database::fetch_array($result);
1520
        $result_array['auth_sources'] = api_get_user_entity($result_array['id'])->getAuthSourcesAuthentications();
1521
        $result_array['user_is_online_in_chat'] = 0;
1522
        if ($checkIfUserOnline) {
1523
            $use_status_in_platform = user_is_online($user_id);
1524
            $result_array['user_is_online'] = $use_status_in_platform;
1525
            $user_online_in_chat = 0;
1526
            if ($use_status_in_platform) {
1527
                $user_status = UserManager::get_extra_user_data_by_field(
1528
                    $user_id,
1529
                    'user_chat_status',
1530
                    false,
1531
                    true
1532
                );
1533
                if (1 == (int) $user_status['user_chat_status']) {
1534
                    $user_online_in_chat = 1;
1535
                }
1536
            }
1537
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1538
        }
1539
1540
        if ($loadExtraData) {
1541
            $fieldValue = new ExtraFieldValue('user');
1542
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1543
                $user_id,
1544
                $loadOnlyVisibleExtraData
1545
            );
1546
        }
1547
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1548
    }
1549
1550
    return $user;
1551
}
1552
1553
function api_get_user_info_from_entity(
1554
    User $user,
1555
    $checkIfUserOnline = false,
1556
    $showPassword = false,
1557
    $loadExtraData = false,
1558
    $loadOnlyVisibleExtraData = false,
1559
    $loadAvatars = true,
1560
    $loadCertificate = false
1561
) {
1562
    if (!$user instanceof UserInterface) {
1563
        return false;
1564
    }
1565
1566
    // Make sure user_id is safe
1567
    $user_id = (int) $user->getId();
1568
1569
    if (empty($user_id)) {
1570
        $userFromSession = Session::read('_user');
1571
1572
        if (isset($userFromSession) && !empty($userFromSession)) {
1573
            return $userFromSession;
1574
        }
1575
1576
        return false;
1577
    }
1578
1579
    $result = [];
1580
    $result['user_is_online_in_chat'] = 0;
1581
    if ($checkIfUserOnline) {
1582
        $use_status_in_platform = user_is_online($user_id);
1583
        $result['user_is_online'] = $use_status_in_platform;
1584
        $user_online_in_chat = 0;
1585
        if ($use_status_in_platform) {
1586
            $user_status = UserManager::get_extra_user_data_by_field(
1587
                $user_id,
1588
                'user_chat_status',
1589
                false,
1590
                true
1591
            );
1592
            if (1 == (int) $user_status['user_chat_status']) {
1593
                $user_online_in_chat = 1;
1594
            }
1595
        }
1596
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1597
    }
1598
1599
    if ($loadExtraData) {
1600
        $fieldValue = new ExtraFieldValue('user');
1601
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1602
            $user_id,
1603
            $loadOnlyVisibleExtraData
1604
        );
1605
    }
1606
1607
    $result['username'] = $user->getUsername();
1608
    $result['status'] = $user->getStatus();
1609
    $result['firstname'] = $user->getFirstname();
1610
    $result['lastname'] = $user->getLastname();
1611
    $result['email'] = $result['mail'] = $user->getEmail();
1612
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1613
    $result['complete_name_with_username'] = $result['complete_name'];
1614
1615
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1616
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1617
    }
1618
1619
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1620
    if (!empty($result['email'])) {
1621
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1622
        if ($showEmail) {
1623
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1624
        }
1625
    } else {
1626
        $result['complete_name_with_email'] = $result['complete_name'];
1627
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1628
    }
1629
1630
    // Kept for historical reasons
1631
    $result['firstName'] = $result['firstname'];
1632
    $result['lastName'] = $result['lastname'];
1633
1634
    $attributes = [
1635
        'picture_uri',
1636
        'last_login',
1637
        'user_is_online',
1638
    ];
1639
1640
    $result['phone'] = $user->getPhone();
1641
    $result['address'] = $user->getAddress();
1642
    $result['official_code'] = $user->getOfficialCode();
1643
    $result['active'] = $user->isActive();
1644
    $result['auth_sources'] = $user->getAuthSourcesAuthentications();
1645
    $result['language'] = $user->getLocale();
1646
    $result['creator_id'] = $user->getCreatorId();
1647
    $result['created_at'] = $user->getCreatedAt()->format('Y-m-d H:i:s');
1648
    $result['hr_dept_id'] = $user->getHrDeptId();
1649
    $result['expiration_date'] = '';
1650
    if ($user->getExpirationDate()) {
1651
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1652
    }
1653
1654
    $result['last_login'] = null;
1655
    if ($user->getLastLogin()) {
1656
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1657
    }
1658
1659
    $result['competences'] = $user->getCompetences();
1660
    $result['diplomas'] = $user->getDiplomas();
1661
    $result['teach'] = $user->getTeach();
1662
    $result['openarea'] = $user->getOpenarea();
1663
    $user_id = (int) $user->getId();
1664
1665
    // Maintain the user_id index for backwards compatibility
1666
    $result['user_id'] = $result['id'] = $user_id;
1667
1668
    if ($loadCertificate) {
1669
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1670
        $result['has_certificates'] = 0;
1671
        if (!empty($hasCertificates)) {
1672
            $result['has_certificates'] = 1;
1673
        }
1674
    }
1675
1676
    $result['icon_status'] = '';
1677
    $result['icon_status_medium'] = '';
1678
    $result['is_admin'] = UserManager::is_admin($user_id);
1679
1680
    // Getting user avatar.
1681
    if ($loadAvatars) {
1682
        $result['avatar'] = '';
1683
        $result['avatar_no_query'] = '';
1684
        $result['avatar_small'] = '';
1685
        $result['avatar_medium'] = '';
1686
        $urlImg = '/';
1687
        $iconStatus = '';
1688
        $iconStatusMedium = '';
1689
1690
        switch ($user->getStatus()) {
1691
            case STUDENT:
1692
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1693
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1694
                } else {
1695
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1696
                }
1697
                break;
1698
            case COURSEMANAGER:
1699
                if ($result['is_admin']) {
1700
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1701
                } else {
1702
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1703
                }
1704
                break;
1705
            case STUDENT_BOSS:
1706
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1707
                break;
1708
        }
1709
1710
        if (!empty($iconStatus)) {
1711
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1712
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1713
        }
1714
1715
        $result['icon_status'] = $iconStatus;
1716
        $result['icon_status_medium'] = $iconStatusMedium;
1717
    }
1718
1719
    if (isset($result['user_is_online'])) {
1720
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1721
    }
1722
    if (isset($result['user_is_online_in_chat'])) {
1723
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1724
    }
1725
1726
    $result['password'] = '';
1727
    if ($showPassword) {
1728
        $result['password'] = $user->getPassword();
1729
    }
1730
1731
    if (isset($result['profile_completed'])) {
1732
        $result['profile_completed'] = $result['profile_completed'];
1733
    }
1734
1735
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1736
1737
    // Send message link
1738
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1739
    $result['complete_name_with_message_link'] = Display::url(
1740
        $result['complete_name_with_username'],
1741
        $sendMessage,
1742
        ['class' => 'ajax']
1743
    );
1744
1745
    if (isset($result['extra'])) {
1746
        $result['extra'] = $result['extra'];
1747
    }
1748
1749
    return $result;
1750
}
1751
1752
function api_get_lp_entity(int $id): ?CLp
1753
{
1754
    return Database::getManager()->getRepository(CLp::class)->find($id);
1755
}
1756
1757
function api_get_user_entity(int $userId = 0): ?User
1758
{
1759
    $userId = $userId ?: api_get_user_id();
1760
    $repo = Container::getUserRepository();
1761
1762
    return $repo->find($userId);
1763
}
1764
1765
function api_get_current_user(): ?User
1766
{
1767
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1768
    if (false === $isLoggedIn) {
1769
        return null;
1770
    }
1771
1772
    $token = Container::getTokenStorage()->getToken();
1773
1774
    if (null !== $token) {
1775
        return $token->getUser();
1776
    }
1777
1778
    return null;
1779
}
1780
1781
/**
1782
 * Finds all the information about a user from username instead of user id.
1783
 *
1784
 * @param string $username
1785
 *
1786
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1787
 *
1788
 * @author Yannick Warnier <[email protected]>
1789
 */
1790
function api_get_user_info_from_username($username)
1791
{
1792
    if (empty($username)) {
1793
        return false;
1794
    }
1795
    $username = trim($username);
1796
1797
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1798
            WHERE username='".Database::escape_string($username)."'";
1799
    $result = Database::query($sql);
1800
    if (Database::num_rows($result) > 0) {
1801
        $resultArray = Database::fetch_array($result);
1802
1803
        return _api_format_user($resultArray);
1804
    }
1805
1806
    return false;
1807
}
1808
1809
/**
1810
 * Get first user with an email.
1811
 *
1812
 * @param string $email
1813
 *
1814
 * @return array|bool
1815
 */
1816
function api_get_user_info_from_email($email = '')
1817
{
1818
    if (empty($email)) {
1819
        return false;
1820
    }
1821
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1822
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1823
    $result = Database::query($sql);
1824
    if (Database::num_rows($result) > 0) {
1825
        $resultArray = Database::fetch_array($result);
1826
1827
        return _api_format_user($resultArray);
1828
    }
1829
1830
    return false;
1831
}
1832
1833
/**
1834
 * @return string
1835
 */
1836
function api_get_course_id()
1837
{
1838
    return Session::read('_cid', null);
1839
}
1840
1841
/**
1842
 * Returns the current course id (integer).
1843
 *
1844
 * @param ?string $code Optional course code
1845
 *
1846
 * @return int
1847
 */
1848
function api_get_course_int_id(?string $code = null): int
1849
{
1850
    if (!empty($code)) {
1851
        $code = Database::escape_string($code);
1852
        $row = Database::select(
1853
            'id',
1854
            Database::get_main_table(TABLE_MAIN_COURSE),
1855
            ['where' => ['code = ?' => [$code]]],
1856
            'first'
1857
        );
1858
1859
        if (is_array($row) && isset($row['id'])) {
1860
            return $row['id'];
1861
        } else {
1862
            return 0;
1863
        }
1864
    }
1865
1866
    $cid = Session::read('_real_cid', 0);
1867
    if (empty($cid) && isset($_REQUEST['cid'])) {
1868
        $cid = (int) $_REQUEST['cid'];
1869
    }
1870
1871
    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...
1872
}
1873
1874
/**
1875
 * Gets a course setting from the current course_setting table. Try always using integer values.
1876
 *
1877
 * @param string       $settingName The name of the setting we want from the table
1878
 * @param Course|array $courseInfo
1879
 * @param bool         $force       force checking the value in the database
1880
 *
1881
 * @return mixed The value of that setting in that table. Return -1 if not found.
1882
 */
1883
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1884
{
1885
    if (empty($courseInfo)) {
1886
        $courseInfo = api_get_course_info();
1887
    }
1888
1889
    if (empty($courseInfo) || empty($settingName)) {
1890
        return -1;
1891
    }
1892
1893
    if ($courseInfo instanceof Course) {
1894
        $courseId = $courseInfo->getId();
1895
    } else {
1896
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1897
    }
1898
1899
    if (empty($courseId)) {
1900
        return -1;
1901
    }
1902
1903
    static $courseSettingInfo = [];
1904
1905
    if ($force) {
1906
        $courseSettingInfo = [];
1907
    }
1908
1909
    if (!isset($courseSettingInfo[$courseId])) {
1910
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1911
        $settingName = Database::escape_string($settingName);
1912
1913
        $sql = "SELECT variable, value FROM $table
1914
                WHERE c_id = $courseId ";
1915
        $res = Database::query($sql);
1916
        if (Database::num_rows($res) > 0) {
1917
            $result = Database::store_result($res, 'ASSOC');
1918
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1919
1920
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1921
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1922
                if (!is_null($value)) {
1923
                    $result = explode(',', $value);
1924
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1925
                }
1926
            }
1927
        }
1928
    }
1929
1930
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1931
        return $courseSettingInfo[$courseId][$settingName];
1932
    }
1933
1934
    return -1;
1935
}
1936
1937
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1938
{
1939
    $value = api_get_course_setting($settingName, $courseInfo, true);
1940
1941
    if (-1 === $value) {
1942
        // Check global settings
1943
        $value = api_get_plugin_setting($plugin, $settingName);
1944
        if ('true' === $value) {
1945
            return 1;
1946
        }
1947
        if ('false' === $value) {
1948
            return 0;
1949
        }
1950
        if (null === $value) {
1951
            return -1;
1952
        }
1953
    }
1954
1955
    return $value;
1956
}
1957
1958
/**
1959
 * Gets an anonymous user ID.
1960
 *
1961
 * For some tools that need tracking, like the learnpath tool, it is necessary
1962
 * to have a usable user-id to enable some kind of tracking, even if not
1963
 * perfect. An anonymous ID is taken from the users table by looking for a
1964
 * status of "6" (anonymous).
1965
 *
1966
 * @return int User ID of the anonymous user, or O if no anonymous user found
1967
 */
1968
function api_get_anonymous_id()
1969
{
1970
    // Find if another anon is connected now
1971
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1972
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1973
    $ip = Database::escape_string(api_get_real_ip());
1974
    $max = (int) api_get_setting('admin.max_anonymous_users');
1975
    if ($max >= 2) {
1976
        $sql = "SELECT * FROM $table as TEL
1977
                JOIN $tableU as U
1978
                ON U.id = TEL.login_user_id
1979
                WHERE TEL.user_ip = '$ip'
1980
                    AND U.status = ".ANONYMOUS."
1981
                    AND U.id != 2 ";
1982
1983
        $result = Database::query($sql);
1984
        if (empty(Database::num_rows($result))) {
1985
            $login = uniqid('anon_');
1986
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['created_at ASC']);
1987
            if (count($anonList) >= $max) {
1988
                foreach ($anonList as $userToDelete) {
1989
                    UserManager::delete_user($userToDelete['user_id']);
1990
                    break;
1991
                }
1992
            }
1993
1994
            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...
1995
                $login,
1996
                'anon',
1997
                ANONYMOUS,
1998
                ' anonymous@localhost',
1999
                $login,
2000
                $login
2001
            );
2002
        } else {
2003
            $row = Database::fetch_assoc($result);
2004
2005
            return $row['id'];
2006
        }
2007
    }
2008
2009
    $table = Database::get_main_table(TABLE_MAIN_USER);
2010
    $sql = "SELECT id
2011
            FROM $table
2012
            WHERE status = ".ANONYMOUS." ";
2013
    $res = Database::query($sql);
2014
    if (Database::num_rows($res) > 0) {
2015
        $row = Database::fetch_assoc($res);
2016
2017
        return $row['id'];
2018
    }
2019
2020
    // No anonymous user was found.
2021
    return 0;
2022
}
2023
2024
/**
2025
 * @param int $courseId
2026
 * @param int $sessionId
2027
 * @param int $groupId
2028
 *
2029
 * @return string
2030
 */
2031
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2032
{
2033
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2034
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2035
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2036
2037
    $url = 'cid='.$courseId;
2038
    $url .= '&sid='.$sessionId;
2039
    $url .= '&gid='.$groupId;
2040
2041
    return $url;
2042
}
2043
2044
/**
2045
 * Returns the current course url part including session, group, and gradebook params.
2046
 *
2047
 * @param bool   $addSessionId
2048
 * @param bool   $addGroupId
2049
 * @param string $origin
2050
 *
2051
 * @return string Course & session references to add to a URL
2052
 */
2053
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2054
{
2055
    $courseId = api_get_course_int_id();
2056
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2057
        $courseId = (int) $_REQUEST['cid'];
2058
    }
2059
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2060
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2061
2062
    if ($addSessionId) {
2063
        if (!empty($url)) {
2064
            $sessionId = api_get_session_id();
2065
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2066
                $sessionId = (int) $_REQUEST['sid'];
2067
            }
2068
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2069
        }
2070
    }
2071
2072
    if ($addGroupId) {
2073
        if (!empty($url)) {
2074
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2075
        }
2076
    }
2077
2078
    if (!empty($url)) {
2079
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2080
        if (false !== $origin) {
2081
            $url .= '&origin=' . $origin;
2082
        }
2083
    }
2084
2085
    return $url;
2086
}
2087
2088
/**
2089
 * Get if we visited a gradebook page.
2090
 *
2091
 * @return bool
2092
 */
2093
function api_is_in_gradebook()
2094
{
2095
    return Session::read('in_gradebook', false);
2096
}
2097
2098
/**
2099
 * Set that we are in a page inside a gradebook.
2100
 */
2101
function api_set_in_gradebook()
2102
{
2103
    Session::write('in_gradebook', true);
2104
}
2105
2106
/**
2107
 * Remove gradebook session.
2108
 */
2109
function api_remove_in_gradebook()
2110
{
2111
    Session::erase('in_gradebook');
2112
}
2113
2114
/**
2115
 * Returns the current course info array see api_format_course_array()
2116
 * If the course_code is given, the returned array gives info about that
2117
 * particular course, if none given it gets the course info from the session.
2118
 *
2119
 * @param string $courseCode
2120
 *
2121
 * @return array
2122
 */
2123
function api_get_course_info($courseCode = null)
2124
{
2125
    if (!empty($courseCode)) {
2126
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2127
2128
        return api_format_course_array($course);
2129
    }
2130
2131
    $course = Session::read('_course');
2132
    if ('-1' == $course) {
2133
        $course = [];
2134
    }
2135
2136
    if (empty($course) && isset($_REQUEST['cid'])) {
2137
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2138
    }
2139
2140
    return $course;
2141
}
2142
2143
/**
2144
 * @param int $courseId
2145
 */
2146
function api_get_course_entity($courseId = 0): ?Course
2147
{
2148
    if (empty($courseId)) {
2149
        $courseId = api_get_course_int_id();
2150
    }
2151
2152
    if (empty($courseId)) {
2153
        return null;
2154
    }
2155
2156
    return Container::getCourseRepository()->find($courseId);
2157
}
2158
2159
/**
2160
 * @param int $id
2161
 */
2162
function api_get_session_entity($id = 0): ?SessionEntity
2163
{
2164
    if (empty($id)) {
2165
        $id = api_get_session_id();
2166
    }
2167
2168
    if (empty($id)) {
2169
        return null;
2170
    }
2171
2172
    return Container::getSessionRepository()->find($id);
2173
}
2174
2175
/**
2176
 * @param int $id
2177
 */
2178
function api_get_group_entity($id = 0): ?CGroup
2179
{
2180
    if (empty($id)) {
2181
        $id = api_get_group_id();
2182
    }
2183
2184
    return Container::getGroupRepository()->find($id);
2185
}
2186
2187
/**
2188
 * @param int $id
2189
 */
2190
function api_get_url_entity($id = 0): ?AccessUrl
2191
{
2192
    if (empty($id)) {
2193
        $id = api_get_current_access_url_id();
2194
    }
2195
2196
    return Container::getAccessUrlRepository()->find($id);
2197
}
2198
2199
/**
2200
 * Returns the current course info array.
2201
2202
 * Now if the course_code is given, the returned array gives info about that
2203
 * particular course, not specially the current one.
2204
 *
2205
 * @param int $id Numeric ID of the course
2206
 *
2207
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2208
 */
2209
function api_get_course_info_by_id(?int $id = 0)
2210
{
2211
    if (empty($id)) {
2212
        $course = Session::read('_course', []);
2213
2214
        return $course;
2215
    }
2216
2217
    $course = Container::getCourseRepository()->find($id);
2218
    if (empty($course)) {
2219
        return [];
2220
    }
2221
2222
    return api_format_course_array($course);
2223
}
2224
2225
/**
2226
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2227
 * to switch from 'code' to 'id' in the array.
2228
 *
2229
 * @return array
2230
 *
2231
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2232
 */
2233
function api_format_course_array(Course $course = null)
2234
{
2235
    if (empty($course)) {
2236
        return [];
2237
    }
2238
2239
    $courseData = [];
2240
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2241
2242
    // Added
2243
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2244
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2245
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2246
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2247
    $courseData['titular'] = $course->getTutorName();
2248
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2249
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2250
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2251
2252
    $courseData['visibility'] = $course->getVisibility();
2253
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2254
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2255
    $courseData['activate_legal'] = $course->getActivateLegal();
2256
    $courseData['legal'] = $course->getLegal();
2257
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2258
    $courseData['video_url'] = $course->getVideoUrl();
2259
    $courseData['sticky'] = (int) $course->isSticky();
2260
2261
    $coursePath = '/course/';
2262
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2263
2264
    // Course password
2265
    $courseData['registration_code'] = $course->getRegistrationCode();
2266
    $courseData['disk_quota'] = $course->getDiskQuota();
2267
    $courseData['course_public_url'] = $webCourseHome;
2268
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2269
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2270
2271
    $image = Display::getMdiIcon(
2272
        ObjectIcon::COURSE,
2273
        'ch-tool-icon',
2274
        null,
2275
        ICON_SIZE_BIG
2276
    );
2277
2278
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2279
    if (!empty($illustration)) {
2280
        $image = $illustration;
2281
    }
2282
2283
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2284
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2285
2286
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2287
        $courseData['duration'] = $course->getDuration();
2288
    }
2289
2290
    return $courseData;
2291
}
2292
2293
/**
2294
 * Returns a difficult to guess password.
2295
 */
2296
function api_generate_password(int $length = 8, $useRequirements = true): string
2297
{
2298
    if ($length < 2) {
2299
        $length = 2;
2300
    }
2301
2302
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2303
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2304
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2305
    $minNumbers = 2;
2306
    $length = $length - $minNumbers;
2307
    $minLowerCase = round($length / 2);
2308
    $minUpperCase = $length - $minLowerCase;
2309
    $minSpecials = 1; // Default minimum special characters
2310
2311
    $password = '';
2312
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2313
2314
    $factory = new RandomLib\Factory();
2315
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2316
2317
    if (!empty($passwordRequirements)) {
2318
        $length = $passwordRequirements['min']['length'];
2319
        $minNumbers = $passwordRequirements['min']['numeric'];
2320
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2321
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2322
        $minSpecials = $passwordRequirements['min']['specials'];
2323
2324
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2325
        // Add the rest to fill the length requirement
2326
        if ($rest > 0) {
2327
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2328
        }
2329
    }
2330
2331
    // Min digits default 2
2332
    for ($i = 0; $i < $minNumbers; $i++) {
2333
        $password .= $generator->generateInt(2, 9);
2334
    }
2335
2336
    // Min lowercase
2337
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2338
2339
    // Min uppercase
2340
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2341
2342
    // Min special characters
2343
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2344
2345
    // Shuffle the password to ensure randomness
2346
    $password = str_shuffle($password);
2347
2348
    return $password;
2349
}
2350
2351
/**
2352
 * Checks a password to see wether it is OK to use.
2353
 *
2354
 * @param string $password
2355
 *
2356
 * @return bool if the password is acceptable, false otherwise
2357
 *              Notes about what a password "OK to use" is:
2358
 *              1. The password should be at least 5 characters long.
2359
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2360
 *              3. The password should contain at least 3 letters.
2361
 *              4. It should contain at least 2 digits.
2362
 *              Settings will change if the configuration value is set: password_requirements
2363
 */
2364
function api_check_password($password)
2365
{
2366
    $passwordRequirements = Security::getPasswordRequirements();
2367
2368
    $minLength = $passwordRequirements['min']['length'];
2369
    $minNumbers = $passwordRequirements['min']['numeric'];
2370
    // Optional
2371
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2372
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2373
2374
    $minLetters = $minLowerCase + $minUpperCase;
2375
    $passwordLength = api_strlen($password);
2376
2377
    $conditions = [
2378
        'min_length' => $passwordLength >= $minLength,
2379
    ];
2380
2381
    $digits = 0;
2382
    $lowerCase = 0;
2383
    $upperCase = 0;
2384
2385
    for ($i = 0; $i < $passwordLength; $i++) {
2386
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2387
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2388
            $upperCase++;
2389
        }
2390
2391
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2392
            $lowerCase++;
2393
        }
2394
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2395
            $digits++;
2396
        }
2397
    }
2398
2399
    // Min number of digits
2400
    $conditions['min_numeric'] = $digits >= $minNumbers;
2401
2402
    if (!empty($minUpperCase)) {
2403
        // Uppercase
2404
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2405
    }
2406
2407
    if (!empty($minLowerCase)) {
2408
        // Lowercase
2409
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2410
    }
2411
2412
    // Min letters
2413
    $letters = $upperCase + $lowerCase;
2414
    $conditions['min_letters'] = $letters >= $minLetters;
2415
2416
    $isPasswordOk = true;
2417
    foreach ($conditions as $condition) {
2418
        if (false === $condition) {
2419
            $isPasswordOk = false;
2420
            break;
2421
        }
2422
    }
2423
2424
    if (false === $isPasswordOk) {
2425
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2426
        $output .= Security::getPasswordRequirementsToString($conditions);
2427
2428
        Display::addFlash(Display::return_message($output, 'warning', false));
2429
    }
2430
2431
    return $isPasswordOk;
2432
}
2433
2434
/**
2435
 * Gets the current Chamilo (not PHP/cookie) session ID.
2436
 *
2437
 * @return int O if no active session, the session ID otherwise
2438
 */
2439
function api_get_session_id()
2440
{
2441
    return (int) Session::read('sid', 0);
2442
}
2443
2444
/**
2445
 * Gets the current Chamilo (not social network) group ID.
2446
 *
2447
 * @return int O if no active session, the session ID otherwise
2448
 */
2449
function api_get_group_id()
2450
{
2451
    return Session::read('gid', 0);
2452
}
2453
2454
/**
2455
 * Gets the current or given session name.
2456
 *
2457
 * @param   int     Session ID (optional)
2458
 *
2459
 * @return string The session name, or null if not found
2460
 */
2461
function api_get_session_name($session_id = 0)
2462
{
2463
    if (empty($session_id)) {
2464
        $session_id = api_get_session_id();
2465
        if (empty($session_id)) {
2466
            return null;
2467
        }
2468
    }
2469
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2470
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2471
    $r = Database::query($s);
2472
    $c = Database::num_rows($r);
2473
    if ($c > 0) {
2474
        //technically, there can be only one, but anyway we take the first
2475
        $rec = Database::fetch_array($r);
2476
2477
        return $rec['title'];
2478
    }
2479
2480
    return null;
2481
}
2482
2483
/**
2484
 * Gets the session info by id.
2485
 *
2486
 * @param int $id Session ID
2487
 *
2488
 * @return array information of the session
2489
 */
2490
function api_get_session_info($id)
2491
{
2492
    return SessionManager::fetch($id);
2493
}
2494
2495
/**
2496
 * Gets the session visibility by session id.
2497
 *
2498
 * @param int  $session_id
2499
 * @param int  $courseId
2500
 * @param bool $ignore_visibility_for_admins
2501
 *
2502
 * @return int
2503
 *             0 = session still available,
2504
 *             SESSION_VISIBLE_READ_ONLY = 1,
2505
 *             SESSION_VISIBLE = 2,
2506
 *             SESSION_INVISIBLE = 3
2507
 */
2508
function api_get_session_visibility(
2509
    $session_id,
2510
    $courseId = null,
2511
    $ignore_visibility_for_admins = true,
2512
    $userId = 0
2513
) {
2514
    if (api_is_platform_admin()) {
2515
        if ($ignore_visibility_for_admins) {
2516
            return SESSION_AVAILABLE;
2517
        }
2518
    }
2519
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2520
2521
    $now = time();
2522
    if (empty($session_id)) {
2523
        return 0; // Means that the session is still available.
2524
    }
2525
2526
    $session_id = (int) $session_id;
2527
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2528
2529
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2530
2531
    if (Database::num_rows($result) <= 0) {
2532
        return SESSION_INVISIBLE;
2533
    }
2534
2535
    $row = Database::fetch_assoc($result);
2536
    $visibility = $row['visibility'];
2537
2538
    // I don't care the session visibility.
2539
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2540
        // Session duration per student.
2541
        if (isset($row['duration']) && !empty($row['duration'])) {
2542
            $duration = $row['duration'] * 24 * 60 * 60;
2543
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2544
2545
            // If there is a session duration but there is no previous
2546
            // access by the user, then the session is still available
2547
            if (0 == count($courseAccess)) {
2548
                return SESSION_AVAILABLE;
2549
            }
2550
2551
            $currentTime = time();
2552
            $firstAccess = isset($courseAccess['login_course_date'])
2553
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2554
                : 0;
2555
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2556
            $userDuration = isset($userDurationData['duration'])
2557
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2558
                : 0;
2559
2560
            $totalDuration = $firstAccess + $duration + $userDuration;
2561
2562
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2563
        }
2564
2565
        return SESSION_AVAILABLE;
2566
    }
2567
2568
    // If start date was set.
2569
    if (!empty($row['access_start_date'])) {
2570
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2571
    } else {
2572
        // If there's no start date, assume it's available until the end date
2573
        $visibility = SESSION_AVAILABLE;
2574
    }
2575
2576
    // If the end date was set.
2577
    if (!empty($row['access_end_date'])) {
2578
        // Only if date_start said that it was ok
2579
        if (SESSION_AVAILABLE === $visibility) {
2580
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2581
                ? SESSION_AVAILABLE // Date still available
2582
                : $row['visibility']; // Session ends
2583
        }
2584
    }
2585
2586
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2587
    $isCoach = api_is_coach($session_id, $courseId);
2588
2589
    if ($isCoach) {
2590
        // Test start date.
2591
        if (!empty($row['coach_access_start_date'])) {
2592
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2593
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2594
        }
2595
2596
        // Test end date.
2597
        if (!empty($row['coach_access_end_date'])) {
2598
            if (SESSION_AVAILABLE === $visibility) {
2599
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2600
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2601
            }
2602
        }
2603
    }
2604
2605
    return $visibility;
2606
}
2607
2608
/**
2609
 * This function returns a (star) session icon if the session is not null and
2610
 * the user is not a student.
2611
 *
2612
 * @param int $sessionId
2613
 * @param int $statusId  User status id - if 5 (student), will return empty
2614
 *
2615
 * @return string Session icon
2616
 */
2617
function api_get_session_image($sessionId, User $user)
2618
{
2619
    $sessionId = (int) $sessionId;
2620
    $image = '';
2621
    if (!$user->hasRole('ROLE_STUDENT')) {
2622
        // Check whether is not a student
2623
        if ($sessionId > 0) {
2624
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2625
                ObjectIcon::STAR,
2626
                'ch-tool-icon',
2627
                'align:absmiddle;',
2628
                ICON_SIZE_SMALL,
2629
                get_lang('Session-specific resource')
2630
            );
2631
        }
2632
    }
2633
2634
    return $image;
2635
}
2636
2637
/**
2638
 * This function add an additional condition according to the session of the course.
2639
 *
2640
 * @param int    $session_id        session id
2641
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2642
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2643
 *                                  false for strict session condition
2644
 * @param string $session_field
2645
 *
2646
 * @return string condition of the session
2647
 */
2648
function api_get_session_condition(
2649
    $session_id,
2650
    $and = true,
2651
    $with_base_content = false,
2652
    $session_field = 'session_id'
2653
) {
2654
    $session_id = (int) $session_id;
2655
2656
    if (empty($session_field)) {
2657
        $session_field = 'session_id';
2658
    }
2659
    // Condition to show resources by session
2660
    $condition_add = $and ? ' AND ' : ' WHERE ';
2661
2662
    if ($with_base_content) {
2663
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2664
    } else {
2665
        if (empty($session_id)) {
2666
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2667
        } else {
2668
            $condition_session = $condition_add." $session_field = $session_id ";
2669
        }
2670
    }
2671
2672
    return $condition_session;
2673
}
2674
2675
/**
2676
 * Returns the value of a setting from the web-adjustable admin config settings.
2677
 *
2678
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2679
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2680
 * instead of
2681
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2682
 *
2683
 * @param string $variable The variable name
2684
 *
2685
 * @return string|array
2686
 */
2687
function api_get_setting($variable, $isArray = false, $key = null)
2688
{
2689
    $settingsManager = Container::getSettingsManager();
2690
    if (empty($settingsManager)) {
2691
        return '';
2692
    }
2693
    $variable = trim($variable);
2694
2695
    switch ($variable) {
2696
        case 'server_type':
2697
            $test = ['dev', 'test'];
2698
            $environment = Container::getEnvironment();
2699
            if (in_array($environment, $test)) {
2700
                return 'test';
2701
            }
2702
2703
            return 'prod';
2704
        // deprecated settings
2705
        // no break
2706
        case 'openid_authentication':
2707
        case 'service_ppt2lp':
2708
        case 'formLogin_hide_unhide_label':
2709
            return false;
2710
            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...
2711
        case 'tool_visible_by_default_at_creation':
2712
            $values = $settingsManager->getSetting($variable);
2713
            $newResult = [];
2714
            foreach ($values as $parameter) {
2715
                $newResult[$parameter] = 'true';
2716
            }
2717
2718
            return $newResult;
2719
            break;
2720
        default:
2721
            $settingValue = $settingsManager->getSetting($variable, true);
2722
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2723
                // Check if the value is a valid JSON string
2724
                $decodedValue = json_decode($settingValue, true);
2725
2726
                // If it's a valid JSON string and the result is an array, return it
2727
                if (is_array($decodedValue)) {
2728
                    return $decodedValue;
2729
                }
2730
2731
                // If it's not an array, continue with the normal flow
2732
                // Optional: If you need to evaluate the value using eval
2733
                $strArrayValue = rtrim($settingValue, ';');
2734
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2735
                if (is_array($value)) {
2736
                    return $value;
2737
                }
2738
            }
2739
2740
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2741
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2742
                return $settingValue[$variable][$key];
2743
            }
2744
2745
            return $settingValue;
2746
            break;
2747
    }
2748
}
2749
2750
/**
2751
 * @param string $variable
2752
 * @param string $option
2753
 *
2754
 * @return bool
2755
 */
2756
function api_get_setting_in_list($variable, $option)
2757
{
2758
    $value = api_get_setting($variable);
2759
2760
    return in_array($option, $value);
2761
}
2762
2763
/**
2764
 * @param string $plugin
2765
 * @param string $variable
2766
 *
2767
 * @return string
2768
 */
2769
function api_get_plugin_setting($plugin, $variable)
2770
{
2771
    $variableName = $plugin.'_'.$variable;
2772
    //$result = api_get_setting($variableName);
2773
    $params = [
2774
        'category = ? AND subkey = ? AND variable = ?' => [
2775
            'Plugins',
2776
            $plugin,
2777
            $variableName,
2778
        ],
2779
    ];
2780
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2781
    $result = Database::select(
2782
        'selected_value',
2783
        $table,
2784
        ['where' => $params],
2785
        'one'
2786
    );
2787
    if ($result) {
2788
        $value = $result['selected_value'];
2789
        $serializedValue = @unserialize($result['selected_value'], []);
2790
        if (false !== $serializedValue) {
2791
            $value = $serializedValue;
2792
        }
2793
2794
        return $value;
2795
    }
2796
2797
    return null;
2798
    /// Old code
2799
2800
    $variableName = $plugin.'_'.$variable;
0 ignored issues
show
Unused Code introduced by
$variableName = $plugin . '_' . $variable is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2801
    $result = api_get_setting($variableName);
2802
2803
    if (isset($result[$plugin])) {
2804
        $value = $result[$plugin];
2805
2806
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2807
2808
        if (false !== $unserialized) {
2809
            $value = $unserialized;
2810
        }
2811
2812
        return $value;
2813
    }
2814
2815
    return null;
2816
}
2817
2818
/**
2819
 * Returns the value of a setting from the web-adjustable admin config settings.
2820
 */
2821
function api_get_settings_params($params)
2822
{
2823
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2824
2825
    return Database::select('*', $table, ['where' => $params]);
2826
}
2827
2828
/**
2829
 * @param array $params example: [id = ? => '1']
2830
 *
2831
 * @return array
2832
 */
2833
function api_get_settings_params_simple($params)
2834
{
2835
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2836
2837
    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...
2838
}
2839
2840
/**
2841
 * Returns the value of a setting from the web-adjustable admin config settings.
2842
 */
2843
function api_delete_settings_params($params)
2844
{
2845
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2846
2847
    return Database::delete($table, $params);
2848
}
2849
2850
/**
2851
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2852
 *
2853
 * @return string Escaped version of $_SERVER['PHP_SELF']
2854
 */
2855
function api_get_self()
2856
{
2857
    return htmlentities($_SERVER['PHP_SELF']);
2858
}
2859
2860
/**
2861
 * Checks whether current user is a platform administrator.
2862
 *
2863
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2864
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2865
 *
2866
 * @return bool true if the user has platform admin rights,
2867
 *              false otherwise
2868
 *
2869
 * @see usermanager::is_admin(user_id) for a user-id specific function
2870
 */
2871
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2872
{
2873
    $currentUser = api_get_current_user();
2874
2875
    if (null === $currentUser) {
2876
        return false;
2877
    }
2878
2879
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2880
2881
    if ($isAdmin) {
2882
        return true;
2883
    }
2884
2885
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2886
        return true;
2887
    }
2888
2889
    if ($allowDrh && $currentUser->hasRole('ROLE_HR')) {
2890
        return true;
2891
    }
2892
2893
    return false;
2894
}
2895
2896
/**
2897
 * Checks whether the user given as user id is in the admin table.
2898
 *
2899
 * @param int $user_id If none provided, will use current user
2900
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2901
 *
2902
 * @return bool True if the user is admin, false otherwise
2903
 */
2904
function api_is_platform_admin_by_id($user_id = null, $url = null)
2905
{
2906
    $user_id = (int) $user_id;
2907
    if (empty($user_id)) {
2908
        $user_id = api_get_user_id();
2909
    }
2910
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2911
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2912
    $res = Database::query($sql);
2913
    $is_admin = 1 === Database::num_rows($res);
2914
    if (!$is_admin || !isset($url)) {
2915
        return $is_admin;
2916
    }
2917
    // We get here only if $url is set
2918
    $url = (int) $url;
2919
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2920
    $sql = "SELECT * FROM $url_user_table
2921
            WHERE access_url_id = $url AND user_id = $user_id";
2922
    $res = Database::query($sql);
2923
2924
    return 1 === Database::num_rows($res);
2925
}
2926
2927
/**
2928
 * Checks whether current user is allowed to create courses.
2929
 *
2930
 * @return bool true if the user has course creation rights,
2931
 *              false otherwise
2932
 */
2933
function api_is_allowed_to_create_course()
2934
{
2935
    if (api_is_platform_admin()) {
2936
        return true;
2937
    }
2938
2939
    // Teachers can only create courses
2940
    if (api_is_teacher()) {
2941
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2942
            return true;
2943
        } else {
2944
            return false;
2945
        }
2946
    }
2947
2948
    return Session::read('is_allowedCreateCourse');
2949
}
2950
2951
/**
2952
 * Checks whether the current user is a course administrator.
2953
 *
2954
 * @return bool True if current user is a course administrator
2955
 */
2956
function api_is_course_admin()
2957
{
2958
    if (api_is_platform_admin()) {
2959
        return true;
2960
    }
2961
2962
    $user = api_get_current_user();
2963
    if ($user) {
2964
        if (
2965
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2966
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2967
        ) {
2968
            return true;
2969
        }
2970
    }
2971
2972
    return false;
2973
}
2974
2975
/**
2976
 * Checks whether the current user is a course coach
2977
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2978
 *
2979
 * @return bool True if current user is a course coach
2980
 */
2981
function api_is_session_general_coach()
2982
{
2983
    return Session::read('is_session_general_coach');
2984
}
2985
2986
/**
2987
 * Checks whether the current user is a course tutor
2988
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2989
 *
2990
 * @return bool True if current user is a course tutor
2991
 */
2992
function api_is_course_tutor()
2993
{
2994
    return Session::read('is_courseTutor');
2995
}
2996
2997
/**
2998
 * @param int $user_id
2999
 * @param int $courseId
3000
 * @param int $session_id
3001
 *
3002
 * @return bool
3003
 */
3004
function api_is_course_session_coach($user_id, $courseId, $session_id)
3005
{
3006
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3007
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3008
3009
    $user_id = (int) $user_id;
3010
    $session_id = (int) $session_id;
3011
    $courseId = (int) $courseId;
3012
3013
    $sql = "SELECT DISTINCT session.id
3014
            FROM $session_table
3015
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3016
            ON session.id = session_rc_ru.session_id
3017
            WHERE
3018
                session_rc_ru.user_id = '".$user_id."'  AND
3019
                session_rc_ru.c_id = '$courseId' AND
3020
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3021
                session_rc_ru.session_id = '$session_id'";
3022
    $result = Database::query($sql);
3023
3024
    return Database::num_rows($result) > 0;
3025
}
3026
3027
/**
3028
 * Checks whether the current user is a course or session coach.
3029
 *
3030
 * @param int $session_id
3031
 * @param int $courseId
3032
 * @param bool  Check whether we are in student view and, if we are, return false
3033
 * @param int $userId
3034
 *
3035
 * @return bool True if current user is a course or session coach
3036
 */
3037
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3038
{
3039
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3040
3041
    if (!empty($session_id)) {
3042
        $session_id = (int) $session_id;
3043
    } else {
3044
        $session_id = api_get_session_id();
3045
    }
3046
3047
    // The student preview was on
3048
    if ($check_student_view && api_is_student_view_active()) {
3049
        return false;
3050
    }
3051
3052
    if (!empty($courseId)) {
3053
        $courseId = (int) $courseId;
3054
    } else {
3055
        $courseId = api_get_course_int_id();
3056
    }
3057
3058
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3059
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3060
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3061
    $sessionIsCoach = [];
3062
3063
    if (!empty($courseId)) {
3064
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3065
                FROM $session_table s
3066
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3067
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3068
                WHERE
3069
                    session_rc_ru.c_id = '$courseId' AND
3070
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3071
                    session_rc_ru.session_id = '$session_id'";
3072
        $result = Database::query($sql);
3073
        $sessionIsCoach = Database::store_result($result);
3074
    }
3075
3076
    if (!empty($session_id)) {
3077
        $sql = "SELECT DISTINCT s.id
3078
                FROM $session_table AS s
3079
                INNER JOIN $tblSessionRelUser sru
3080
                ON s.id = sru.session_id
3081
                WHERE
3082
                    sru.user_id = $userId AND
3083
                    s.id = $session_id AND
3084
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3085
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3086
        $result = Database::query($sql);
3087
        if (!empty($sessionIsCoach)) {
3088
            $sessionIsCoach = array_merge(
3089
                $sessionIsCoach,
3090
                Database::store_result($result)
3091
            );
3092
        } else {
3093
            $sessionIsCoach = Database::store_result($result);
3094
        }
3095
    }
3096
3097
    return count($sessionIsCoach) > 0;
3098
}
3099
3100
function api_user_has_role(string $role, ?User $user = null): bool
3101
{
3102
    if (null === $user) {
3103
        $user = api_get_current_user();
3104
    }
3105
3106
    if (null === $user) {
3107
        return false;
3108
    }
3109
3110
    return $user->hasRole($role);
3111
}
3112
3113
function api_is_allowed_in_course(): bool
3114
{
3115
    if (api_is_platform_admin()) {
3116
        return true;
3117
    }
3118
3119
    $user = api_get_current_user();
3120
    if ($user instanceof User) {
3121
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3122
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3123
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3124
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3125
        ) {
3126
            return true;
3127
        }
3128
    }
3129
3130
    return false;
3131
}
3132
3133
/**
3134
 * Checks whether current user is a student boss.
3135
 */
3136
function api_is_student_boss(?User $user = null): bool
3137
{
3138
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3139
}
3140
3141
/**
3142
 * Checks whether the current user is a session administrator.
3143
 *
3144
 * @return bool True if current user is a course administrator
3145
 */
3146
function api_is_session_admin(?User $user = null)
3147
{
3148
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3149
}
3150
3151
/**
3152
 * Checks whether the current user is a human resources manager.
3153
 *
3154
 * @return bool True if current user is a human resources manager
3155
 */
3156
function api_is_drh()
3157
{
3158
    return api_user_has_role('ROLE_HR');
3159
}
3160
3161
/**
3162
 * Checks whether the current user is a student.
3163
 *
3164
 * @return bool True if current user is a human resources manager
3165
 */
3166
function api_is_student()
3167
{
3168
    return api_user_has_role('ROLE_STUDENT');
3169
}
3170
3171
/**
3172
 * Checks whether the current user has the status 'teacher'.
3173
 *
3174
 * @return bool True if current user is a human resources manager
3175
 */
3176
function api_is_teacher()
3177
{
3178
    return api_user_has_role('ROLE_TEACHER');
3179
}
3180
3181
/**
3182
 * Checks whether the current user is a invited user.
3183
 *
3184
 * @return bool
3185
 */
3186
function api_is_invitee()
3187
{
3188
    return api_user_has_role('ROLE_INVITEE');
3189
}
3190
3191
/**
3192
 * This function checks whether a session is assigned into a category.
3193
 *
3194
 * @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...
3195
 * @param string    - category name
3196
 *
3197
 * @return bool - true if is found, otherwise false
3198
 */
3199
function api_is_session_in_category($session_id, $category_name)
3200
{
3201
    $session_id = (int) $session_id;
3202
    $category_name = Database::escape_string($category_name);
3203
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3204
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3205
3206
    $sql = "SELECT 1
3207
            FROM $tbl_session
3208
            WHERE $session_id IN (
3209
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3210
                WHERE
3211
                  s.session_category_id = sc.id AND
3212
                  sc.name LIKE '%$category_name'
3213
            )";
3214
    $rs = Database::query($sql);
3215
3216
    if (Database::num_rows($rs) > 0) {
3217
        return true;
3218
    }
3219
3220
    return false;
3221
}
3222
3223
/**
3224
 * Displays options for switching between student view and course manager view.
3225
 *
3226
 * Changes in version 1.2 (Patrick Cool)
3227
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3228
 * is changed explicitly
3229
 *
3230
 * Changes in version 1.1 (Patrick Cool)
3231
 * student view now works correctly in subfolders of the document tool
3232
 * student view works correctly in the new links tool
3233
 *
3234
 * Example code for using this in your tools:
3235
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3236
 * //   display_tool_view_option($isStudentView);
3237
 * //}
3238
 * //and in later sections, use api_is_allowed_to_edit()
3239
 *
3240
 * @author Roan Embrechts
3241
 * @author Patrick Cool
3242
 * @author Julio Montoya, changes added in Chamilo
3243
 *
3244
 * @version 1.2
3245
 *
3246
 * @todo rewrite code so it is easier to understand
3247
 */
3248
function api_display_tool_view_option()
3249
{
3250
    if ('true' != api_get_setting('student_view_enabled')) {
3251
        return '';
3252
    }
3253
3254
    $sourceurl = '';
3255
    $is_framed = false;
3256
    // Exceptions apply for all multi-frames pages
3257
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3258
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3259
        return '';
3260
    }
3261
3262
    // Uncomment to remove student view link from document view page
3263
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3264
        if (empty($_GET['lp_id'])) {
3265
            return '';
3266
        }
3267
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3268
        $sourceurl = str_replace(
3269
            'lp/lp_header.php',
3270
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3271
            $sourceurl
3272
        );
3273
        //showinframes doesn't handle student view anyway...
3274
        //return '';
3275
        $is_framed = true;
3276
    }
3277
3278
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3279
    if (!$is_framed) {
3280
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3281
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3282
        } else {
3283
            $sourceurl = $_SERVER['REQUEST_URI'];
3284
        }
3285
    }
3286
3287
    $output_string = '';
3288
    if (!empty($_SESSION['studentview'])) {
3289
        if ('studentview' == $_SESSION['studentview']) {
3290
            // We have to remove the isStudentView=true from the $sourceurl
3291
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3292
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3293
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3294
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3295
        } elseif ('teacherview' == $_SESSION['studentview']) {
3296
            // Switching to teacherview
3297
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3298
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3299
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3300
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3301
        }
3302
    } else {
3303
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3304
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3305
    }
3306
    $output_string = Security::remove_XSS($output_string);
3307
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3308
3309
    return $html;
3310
}
3311
3312
/**
3313
 * Function that removes the need to directly use is_courseAdmin global in
3314
 * tool scripts. It returns true or false depending on the user's rights in
3315
 * this particular course.
3316
 * Optionally checking for tutor and coach roles here allows us to use the
3317
 * student_view feature altogether with these roles as well.
3318
 *
3319
 * @param bool  Whether to check if the user has the tutor role
3320
 * @param bool  Whether to check if the user has the coach role
3321
 * @param bool  Whether to check if the user has the session coach role
3322
 * @param bool  check the student view or not
3323
 *
3324
 * @author Roan Embrechts
3325
 * @author Patrick Cool
3326
 * @author Julio Montoya
3327
 *
3328
 * @version 1.1, February 2004
3329
 *
3330
 * @return bool true: the user has the rights to edit, false: he does not
3331
 */
3332
function api_is_allowed_to_edit(
3333
    $tutor = false,
3334
    $coach = false,
3335
    $session_coach = false,
3336
    $check_student_view = true
3337
) {
3338
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3339
    // Admins can edit anything.
3340
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3341
        //The student preview was on
3342
        if ($check_student_view && api_is_student_view_active()) {
3343
            return false;
3344
        }
3345
3346
        return true;
3347
    }
3348
3349
    $sessionId = api_get_session_id();
3350
3351
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3352
        $efv = new ExtraFieldValue('course');
3353
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3354
            api_get_course_int_id(),
3355
            'session_courses_read_only_mode'
3356
        );
3357
3358
        if (!empty($lockExrafieldField['value'])) {
3359
            return false;
3360
        }
3361
    }
3362
3363
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3364
    $session_visibility = api_get_session_visibility($sessionId);
3365
    $is_courseAdmin = api_is_course_admin();
3366
3367
    if (!$is_courseAdmin && $tutor) {
3368
        // If we also want to check if the user is a tutor...
3369
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3370
    }
3371
3372
    if (!$is_courseAdmin && $coach) {
3373
        // If we also want to check if the user is a coach...';
3374
        // Check if session visibility is read only for coaches.
3375
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3376
            $is_allowed_coach_to_edit = false;
3377
        }
3378
3379
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3380
            // Check if coach is allowed to edit a course.
3381
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3382
        }
3383
    }
3384
3385
    if (!$is_courseAdmin && $session_coach) {
3386
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3387
    }
3388
3389
    // Check if the student_view is enabled, and if so, if it is activated.
3390
    if ('true' === api_get_setting('student_view_enabled')) {
3391
        $studentView = api_is_student_view_active();
3392
        if (!empty($sessionId)) {
3393
            // Check if session visibility is read only for coaches.
3394
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3395
                $is_allowed_coach_to_edit = false;
3396
            }
3397
3398
            $is_allowed = false;
3399
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3400
                // Check if coach is allowed to edit a course.
3401
                $is_allowed = $is_allowed_coach_to_edit;
3402
            }
3403
            if ($check_student_view) {
3404
                $is_allowed = $is_allowed && false === $studentView;
3405
            }
3406
        } else {
3407
            $is_allowed = $is_courseAdmin;
3408
            if ($check_student_view) {
3409
                $is_allowed = $is_courseAdmin && false === $studentView;
3410
            }
3411
        }
3412
3413
        return $is_allowed;
3414
    } else {
3415
        return $is_courseAdmin;
3416
    }
3417
}
3418
3419
/**
3420
 * Returns true if user is a course coach of at least one course in session.
3421
 *
3422
 * @param int $sessionId
3423
 *
3424
 * @return bool
3425
 */
3426
function api_is_coach_of_course_in_session($sessionId)
3427
{
3428
    if (api_is_platform_admin()) {
3429
        return true;
3430
    }
3431
3432
    $userId = api_get_user_id();
3433
    $courseList = UserManager::get_courses_list_by_session(
3434
        $userId,
3435
        $sessionId
3436
    );
3437
3438
    // Session visibility.
3439
    $visibility = api_get_session_visibility(
3440
        $sessionId,
3441
        null,
3442
        false
3443
    );
3444
3445
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3446
        // Course Coach session visibility.
3447
        $blockedCourseCount = 0;
3448
        $closedVisibilityList = [
3449
            COURSE_VISIBILITY_CLOSED,
3450
            COURSE_VISIBILITY_HIDDEN,
3451
        ];
3452
3453
        foreach ($courseList as $course) {
3454
            // Checking session visibility
3455
            $sessionCourseVisibility = api_get_session_visibility(
3456
                $sessionId,
3457
                $course['real_id']
3458
            );
3459
3460
            $courseIsVisible = !in_array(
3461
                $course['visibility'],
3462
                $closedVisibilityList
3463
            );
3464
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3465
                $blockedCourseCount++;
3466
            }
3467
        }
3468
3469
        // If all courses are blocked then no show in the list.
3470
        if ($blockedCourseCount === count($courseList)) {
3471
            $visibility = SESSION_INVISIBLE;
3472
        } else {
3473
            $visibility = SESSION_VISIBLE;
3474
        }
3475
    }
3476
3477
    switch ($visibility) {
3478
        case SESSION_VISIBLE_READ_ONLY:
3479
        case SESSION_VISIBLE:
3480
        case SESSION_AVAILABLE:
3481
            return true;
3482
            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...
3483
        case SESSION_INVISIBLE:
3484
            return false;
3485
    }
3486
3487
    return false;
3488
}
3489
3490
/**
3491
 * Checks if a student can edit contents in a session depending
3492
 * on the session visibility.
3493
 *
3494
 * @param bool $tutor Whether to check if the user has the tutor role
3495
 * @param bool $coach Whether to check if the user has the coach role
3496
 *
3497
 * @return bool true: the user has the rights to edit, false: he does not
3498
 */
3499
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3500
{
3501
    if (api_is_allowed_to_edit($tutor, $coach)) {
3502
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3503
        return true;
3504
    } else {
3505
        $sessionId = api_get_session_id();
3506
3507
        if (0 == $sessionId) {
3508
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3509
            return true;
3510
        } else {
3511
            // I'm in a session and I'm a student
3512
            // Get the session visibility
3513
            $session_visibility = api_get_session_visibility($sessionId);
3514
            // if 5 the session is still available
3515
            switch ($session_visibility) {
3516
                case SESSION_VISIBLE_READ_ONLY: // 1
3517
                    return false;
3518
                case SESSION_VISIBLE:           // 2
3519
                    return true;
3520
                case SESSION_INVISIBLE:         // 3
3521
                    return false;
3522
                case SESSION_AVAILABLE:         //5
3523
                    return true;
3524
            }
3525
        }
3526
    }
3527
3528
    return false;
3529
}
3530
3531
/**
3532
 * Current user is anon?
3533
 *
3534
 * @return bool true if this user is anonymous, false otherwise
3535
 */
3536
function api_is_anonymous()
3537
{
3538
    return !Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
3539
}
3540
3541
/**
3542
 * Displays message "You are not allowed here..." and exits the entire script.
3543
 *
3544
 * @param bool $print_headers Whether to print headers (default = false -> does not print them)
3545
 * @param string $message
3546
 * @param int $responseCode
3547
 *
3548
 * @throws Exception
3549
 */
3550
function api_not_allowed(
3551
    $print_headers = false,
3552
    $message = null,
3553
    $responseCode = 0
3554
): never {
3555
    throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode);
3556
}
3557
3558
/**
3559
 * @param string $languageIsoCode
3560
 *
3561
 * @return string
3562
 */
3563
function languageToCountryIsoCode($languageIsoCode)
3564
{
3565
    $allow = ('true' === api_get_setting('language.language_flags_by_country'));
3566
3567
    // @todo save in DB
3568
    switch ($languageIsoCode) {
3569
        case 'ar':
3570
            $country = 'ae';
3571
            break;
3572
        case 'bs':
3573
            $country = 'ba';
3574
            break;
3575
        case 'ca':
3576
            $country = 'es';
3577
            if ($allow) {
3578
                $country = 'catalan';
3579
            }
3580
            break;
3581
        case 'cs':
3582
            $country = 'cz';
3583
            break;
3584
        case 'da':
3585
            $country = 'dk';
3586
            break;
3587
        case 'el':
3588
            $country = 'ae';
3589
            break;
3590
        case 'en':
3591
            $country = 'gb';
3592
            break;
3593
        case 'eu': // Euskera
3594
            $country = 'es';
3595
            if ($allow) {
3596
                $country = 'basque';
3597
            }
3598
            break;
3599
        case 'gl': // galego
3600
            $country = 'es';
3601
            if ($allow) {
3602
                $country = 'galician';
3603
            }
3604
            break;
3605
        case 'he':
3606
            $country = 'il';
3607
            break;
3608
        case 'ja':
3609
            $country = 'jp';
3610
            break;
3611
        case 'ka':
3612
            $country = 'ge';
3613
            break;
3614
        case 'ko':
3615
            $country = 'kr';
3616
            break;
3617
        case 'ms':
3618
            $country = 'my';
3619
            break;
3620
        case 'pt-BR':
3621
            $country = 'br';
3622
            break;
3623
        case 'qu':
3624
            $country = 'pe';
3625
            break;
3626
        case 'sl':
3627
            $country = 'si';
3628
            break;
3629
        case 'sv':
3630
            $country = 'se';
3631
            break;
3632
        case 'uk': // Ukraine
3633
            $country = 'ua';
3634
            break;
3635
        case 'zh-TW':
3636
        case 'zh':
3637
            $country = 'cn';
3638
            break;
3639
        default:
3640
            $country = $languageIsoCode;
3641
            break;
3642
    }
3643
    $country = strtolower($country);
3644
3645
    return $country;
3646
}
3647
3648
/**
3649
 * Returns a list of all the languages that are made available by the admin.
3650
 *
3651
 * @return array An array with all languages. Structure of the array is
3652
 *               array['name'] = An array with the name of every language
3653
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
3654
 */
3655
function api_get_languages()
3656
{
3657
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3658
    $sql = "SELECT * FROM $table WHERE available='1'
3659
            ORDER BY original_name ASC";
3660
    $result = Database::query($sql);
3661
    $languages = [];
3662
    while ($row = Database::fetch_assoc($result)) {
3663
        $languages[$row['isocode']] = $row['original_name'];
3664
    }
3665
3666
    return $languages;
3667
}
3668
3669
/**
3670
 * Returns the id (the database id) of a language.
3671
 *
3672
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
3673
 *
3674
 * @return int id of the language
3675
 */
3676
function api_get_language_id($language)
3677
{
3678
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3679
    if (empty($language)) {
3680
        return null;
3681
    }
3682
3683
    // We check the language by iscocode
3684
    $langInfo = api_get_language_from_iso($language);
3685
    if (null !== $langInfo && !empty($langInfo->getId())) {
3686
        return $langInfo->getId();
3687
    }
3688
3689
    $language = Database::escape_string($language);
3690
    $sql = "SELECT id FROM $tbl_language
3691
            WHERE english_name = '$language' LIMIT 1";
3692
    $result = Database::query($sql);
3693
    $row = Database::fetch_array($result);
3694
3695
    return $row['id'];
3696
}
3697
3698
/**
3699
 * Get the language information by its id.
3700
 *
3701
 * @param int $languageId
3702
 *
3703
 * @throws Exception
3704
 *
3705
 * @return array
3706
 */
3707
function api_get_language_info($languageId)
3708
{
3709
    if (empty($languageId)) {
3710
        return [];
3711
    }
3712
3713
    $language = Database::getManager()->find(Language::class, $languageId);
3714
3715
    if (!$language) {
3716
        return [];
3717
    }
3718
3719
    return [
3720
        'id' => $language->getId(),
3721
        'original_name' => $language->getOriginalName(),
3722
        'english_name' => $language->getEnglishName(),
3723
        'isocode' => $language->getIsocode(),
3724
        'available' => $language->getAvailable(),
3725
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
3726
    ];
3727
}
3728
3729
/**
3730
 * @param string $code
3731
 *
3732
 * @return Language
3733
 */
3734
function api_get_language_from_iso($code)
3735
{
3736
    $em = Database::getManager();
3737
3738
    return $em->getRepository(Language::class)->findOneBy(['isocode' => $code]);
3739
}
3740
3741
/**
3742
 * Shortcut to ThemeHelper::getVisualTheme()
3743
 */
3744
function api_get_visual_theme(): string
3745
{
3746
    $themeHelper = Container::$container->get(ThemeHelper::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

3746
    /** @scrutinizer ignore-call */ 
3747
    $themeHelper = Container::$container->get(ThemeHelper::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...
3747
3748
    return $themeHelper->getVisualTheme();
3749
}
3750
3751
/**
3752
 * Returns a list of CSS themes currently available in the CSS folder
3753
 * The folder must have a default.css file.
3754
 *
3755
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
3756
 *
3757
 * @return array list of themes directories from the css folder
3758
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
3759
 */
3760
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
3761
{
3762
    // This configuration value is set by the vchamilo plugin
3763
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
3764
3765
    $readCssFolder = function ($dir) use ($virtualTheme) {
3766
        $finder = new Finder();
3767
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
3768
        $list = [];
3769
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
3770
        foreach ($themes as $theme) {
3771
            $folder = $theme->getFilename();
3772
            // A theme folder is consider if there's a default.css file
3773
            if (!file_exists($theme->getPathname().'/default.css')) {
3774
                continue;
3775
            }
3776
            $name = ucwords(str_replace('_', ' ', $folder));
3777
            if ($folder == $virtualTheme) {
3778
                continue;
3779
            }
3780
            $list[$folder] = $name;
3781
        }
3782
3783
        return $list;
3784
    };
3785
3786
    $dir = Container::getProjectDir().'var/themes/';
3787
    $list = $readCssFolder($dir);
3788
3789
    if (!empty($virtualTheme)) {
3790
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
3791
        if ($getOnlyThemeFromVirtualInstance) {
3792
            return $newList;
3793
        }
3794
        $list = $list + $newList;
3795
        asort($list);
3796
    }
3797
3798
    return $list;
3799
}
3800
3801
/**
3802
 * Find the largest sort value in a given user_course_category
3803
 * This function is used when we are moving a course to a different category
3804
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
3805
 *
3806
 * @param int $courseCategoryId the id of the user_course_category
3807
 * @param int $userId
3808
 *
3809
 * @return int the value of the highest sort of the user_course_category
3810
 */
3811
function api_max_sort_value($courseCategoryId, $userId)
3812
{
3813
    $user = api_get_user_entity($userId);
3814
    $userCourseCategory = Database::getManager()->getRepository(UserCourseCategory::class)->find($courseCategoryId);
3815
3816
    return null === $user ? 0 : $user->getMaxSortValue($userCourseCategory);
3817
}
3818
3819
/**
3820
 * Transforms a number of seconds in hh:mm:ss format.
3821
 *
3822
 * @author Julian Prud'homme
3823
 *
3824
 * @param int    $seconds      number of seconds
3825
 * @param string $space
3826
 * @param bool   $showSeconds
3827
 * @param bool   $roundMinutes
3828
 *
3829
 * @return string the formatted time
3830
 */
3831
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
3832
{
3833
    // $seconds = -1 means that we have wrong data in the db.
3834
    if (-1 == $seconds) {
3835
        return
3836
            get_lang('Unknown').
3837
            Display::getMdiIcon(
3838
                ActionIcon::INFORMATION,
3839
                'ch-tool-icon',
3840
                'align: absmiddle; hspace: 3px',
3841
                ICON_SIZE_SMALL,
3842
                get_lang('The datas about this user were registered when the calculation of time spent on the platform wasn\'t possible.')
3843
            );
3844
    }
3845
3846
    // How many hours ?
3847
    $hours = floor($seconds / 3600);
3848
3849
    // How many minutes ?
3850
    $min = floor(($seconds - ($hours * 3600)) / 60);
3851
3852
    if ($roundMinutes) {
3853
        if ($min >= 45) {
3854
            $min = 45;
3855
        }
3856
3857
        if ($min >= 30 && $min <= 44) {
3858
            $min = 30;
3859
        }
3860
3861
        if ($min >= 15 && $min <= 29) {
3862
            $min = 15;
3863
        }
3864
3865
        if ($min >= 0 && $min <= 14) {
3866
            $min = 0;
3867
        }
3868
    }
3869
3870
    // How many seconds
3871
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
3872
3873
    if ($hours < 10) {
3874
        $hours = "0$hours";
3875
    }
3876
3877
    if ($sec < 10) {
3878
        $sec = "0$sec";
3879
    }
3880
3881
    if ($min < 10) {
3882
        $min = "0$min";
3883
    }
3884
3885
    $seconds = '';
3886
    if ($showSeconds) {
3887
        $seconds = $space.$sec;
3888
    }
3889
3890
    return $hours.$space.$min.$seconds;
3891
}
3892
3893
/**
3894
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3895
 * The return value is based on the platform administrator's setting
3896
 * "Administration > Configuration settings > Security > Permissions for new directories".
3897
 *
3898
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
3899
 */
3900
function api_get_permissions_for_new_directories()
3901
{
3902
    static $permissions;
3903
    if (!isset($permissions)) {
3904
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
3905
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
3906
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
3907
    }
3908
3909
    return $permissions;
3910
}
3911
3912
/**
3913
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3914
 * The return value is based on the platform administrator's setting
3915
 * "Administration > Configuration settings > Security > Permissions for new files".
3916
 *
3917
 * @return int returns the permissions in the format
3918
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
3919
 */
3920
function api_get_permissions_for_new_files()
3921
{
3922
    static $permissions;
3923
    if (!isset($permissions)) {
3924
        $permissions = trim(api_get_setting('permissions_for_new_files'));
3925
        // The default value 0666 is according to that in the platform
3926
        // administration panel after fresh system installation.
3927
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
3928
    }
3929
3930
    return $permissions;
3931
}
3932
3933
/**
3934
 * Deletes a file, or a folder and its contents.
3935
 *
3936
 * @author      Aidan Lister <[email protected]>
3937
 *
3938
 * @version     1.0.3
3939
 *
3940
 * @param string $dirname Directory to delete
3941
 * @param       bool     Deletes only the content or not
3942
 * @param bool $strict if one folder/file fails stop the loop
3943
 *
3944
 * @return bool Returns TRUE on success, FALSE on failure
3945
 *
3946
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
3947
 *
3948
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
3949
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
3950
 */
3951
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
3952
{
3953
    $res = true;
3954
    // A sanity check.
3955
    if (!file_exists($dirname)) {
3956
        return false;
3957
    }
3958
    $php_errormsg = '';
3959
    // Simple delete for a file.
3960
    if (is_file($dirname) || is_link($dirname)) {
3961
        $res = unlink($dirname);
3962
        if (false === $res) {
3963
            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);
3964
        }
3965
3966
        return $res;
3967
    }
3968
3969
    // Loop through the folder.
3970
    $dir = dir($dirname);
3971
    // A sanity check.
3972
    $is_object_dir = is_object($dir);
3973
    if ($is_object_dir) {
3974
        while (false !== $entry = $dir->read()) {
3975
            // Skip pointers.
3976
            if ('.' == $entry || '..' == $entry) {
3977
                continue;
3978
            }
3979
3980
            // Recurse.
3981
            if ($strict) {
3982
                $result = rmdirr("$dirname/$entry");
3983
                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...
3984
                    $res = false;
3985
                    break;
3986
                }
3987
            } else {
3988
                rmdirr("$dirname/$entry");
3989
            }
3990
        }
3991
    }
3992
3993
    // Clean up.
3994
    if ($is_object_dir) {
3995
        $dir->close();
3996
    }
3997
3998
    if (false == $delete_only_content_in_folder) {
3999
        $res = rmdir($dirname);
4000
        if (false === $res) {
4001
            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);
4002
        }
4003
    }
4004
4005
    return $res;
4006
}
4007
4008
// TODO: This function is to be simplified. File access modes to be implemented.
4009
/**
4010
 * function adapted from a php.net comment
4011
 * copy recursively a folder.
4012
 *
4013
 * @param the source folder
4014
 * @param the dest folder
4015
 * @param an array of excluded file_name (without extension)
4016
 * @param copied_files the returned array of copied files
4017
 * @param string $source
4018
 * @param string $dest
4019
 */
4020
function copyr($source, $dest, $exclude = [], $copied_files = [])
4021
{
4022
    if (empty($dest)) {
4023
        return false;
4024
    }
4025
    // Simple copy for a file
4026
    if (is_file($source)) {
4027
        $path_info = pathinfo($source);
4028
        if (!in_array($path_info['filename'], $exclude)) {
4029
            copy($source, $dest);
4030
        }
4031
4032
        return true;
4033
    } elseif (!is_dir($source)) {
4034
        //then source is not a dir nor a file, return
4035
        return false;
4036
    }
4037
4038
    // Make destination directory.
4039
    if (!is_dir($dest)) {
4040
        mkdir($dest, api_get_permissions_for_new_directories());
4041
    }
4042
4043
    // Loop through the folder.
4044
    $dir = dir($source);
4045
    while (false !== $entry = $dir->read()) {
4046
        // Skip pointers
4047
        if ('.' == $entry || '..' == $entry) {
4048
            continue;
4049
        }
4050
4051
        // Deep copy directories.
4052
        if ($dest !== "$source/$entry") {
4053
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
4054
        }
4055
    }
4056
    // Clean up.
4057
    $dir->close();
4058
4059
    return true;
4060
}
4061
4062
/**
4063
 * @todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
4064
 * Documentation header to be added here.
4065
 *
4066
 * @param string $pathname
4067
 * @param string $base_path_document
4068
 * @param int    $session_id
4069
 *
4070
 * @return mixed True if directory already exists, false if a file already exists at
4071
 *               the destination and null if everything goes according to plan
4072
 */
4073
function copy_folder_course_session(
4074
    $pathname,
4075
    $base_path_document,
4076
    $session_id,
4077
    $course_info,
4078
    $document,
4079
    $source_course_id,
4080
    array $originalFolderNameList = [],
4081
    string $originalBaseName = ''
4082
) {
4083
    $table = Database::get_course_table(TABLE_DOCUMENT);
4084
    $session_id = intval($session_id);
4085
    $source_course_id = intval($source_course_id);
4086
4087
    // Check whether directory already exists.
4088
    if (empty($pathname) || is_dir($pathname)) {
4089
        return true;
4090
    }
4091
4092
    // Ensure that a file with the same name does not already exist.
4093
    if (is_file($pathname)) {
4094
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
4095
4096
        return false;
4097
    }
4098
4099
    $baseNoDocument = str_replace('document', '', $originalBaseName);
4100
    $folderTitles = explode('/', $baseNoDocument);
4101
    $folderTitles = array_filter($folderTitles);
4102
4103
    $table = Database::get_course_table(TABLE_DOCUMENT);
4104
    $session_id = (int) $session_id;
4105
    $source_course_id = (int) $source_course_id;
4106
4107
    $course_id = $course_info['real_id'];
4108
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
4109
    $new_pathname = $base_path_document;
4110
    $path = '';
4111
4112
    foreach ($folders as $index => $folder) {
4113
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
4114
        $path .= DIRECTORY_SEPARATOR.$folder;
4115
4116
        if (!file_exists($new_pathname)) {
4117
            $path = Database::escape_string($path);
4118
4119
            $sql = "SELECT * FROM $table
4120
                    WHERE
4121
                        c_id = $source_course_id AND
4122
                        path = '$path' AND
4123
                        filetype = 'folder' AND
4124
                        session_id = '$session_id'";
4125
            $rs1 = Database::query($sql);
4126
            $num_rows = Database::num_rows($rs1);
4127
4128
            if (0 == $num_rows) {
4129
                mkdir($new_pathname, api_get_permissions_for_new_directories());
4130
                $title = basename($new_pathname);
4131
4132
                if (isset($folderTitles[$index + 1])) {
4133
                    $checkPath = $folderTitles[$index +1];
4134
4135
                    if (isset($originalFolderNameList[$checkPath])) {
4136
                        $title = $originalFolderNameList[$checkPath];
4137
                    }
4138
                }
4139
4140
                // Insert new folder with destination session_id.
4141
                $params = [
4142
                    'c_id' => $course_id,
4143
                    'path' => $path,
4144
                    'comment' => $document->comment,
4145
                    'title' => $title,
4146
                    'filetype' => 'folder',
4147
                    'size' => '0',
4148
                    'session_id' => $session_id,
4149
                ];
4150
                Database::insert($table, $params);
4151
            }
4152
        }
4153
    } // en foreach
4154
}
4155
4156
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
4157
/**
4158
 * @param string $path
4159
 */
4160
function api_chmod_R($path, $filemode)
4161
{
4162
    if (!is_dir($path)) {
4163
        return chmod($path, $filemode);
4164
    }
4165
4166
    $handler = opendir($path);
4167
    while ($file = readdir($handler)) {
4168
        if ('.' != $file && '..' != $file) {
4169
            $fullpath = "$path/$file";
4170
            if (!is_dir($fullpath)) {
4171
                if (!chmod($fullpath, $filemode)) {
4172
                    return false;
4173
                }
4174
            } else {
4175
                if (!api_chmod_R($fullpath, $filemode)) {
4176
                    return false;
4177
                }
4178
            }
4179
        }
4180
    }
4181
4182
    closedir($handler);
4183
4184
    return chmod($path, $filemode);
4185
}
4186
4187
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
4188
/**
4189
 * Parse info file format. (e.g: file.info).
4190
 *
4191
 * Files should use an ini-like format to specify values.
4192
 * White-space generally doesn't matter, except inside values.
4193
 * e.g.
4194
 *
4195
 * @verbatim
4196
 *   key = value
4197
 *   key = "value"
4198
 *   key = 'value'
4199
 *   key = "multi-line
4200
 *
4201
 *   value"
4202
 *   key = 'multi-line
4203
 *
4204
 *   value'
4205
 *   key
4206
 *   =
4207
 *   'value'
4208
 * @endverbatim
4209
 *
4210
 * Arrays are created using a GET-like syntax:
4211
 *
4212
 * @verbatim
4213
 *   key[] = "numeric array"
4214
 *   key[index] = "associative array"
4215
 *   key[index][] = "nested numeric array"
4216
 *   key[index][index] = "nested associative array"
4217
 * @endverbatim
4218
 *
4219
 * PHP constants are substituted in, but only when used as the entire value:
4220
 *
4221
 * Comments should start with a semi-colon at the beginning of a line.
4222
 *
4223
 * This function is NOT for placing arbitrary module-specific settings. Use
4224
 * variable_get() and variable_set() for that.
4225
 *
4226
 * Information stored in the module.info file:
4227
 * - name: The real name of the module for display purposes.
4228
 * - description: A brief description of the module.
4229
 * - dependencies: An array of shortnames of other modules this module depends on.
4230
 * - package: The name of the package of modules this module belongs to.
4231
 *
4232
 * Example of .info file:
4233
 * <code>
4234
 * @verbatim
4235
 *   name = Forum
4236
 *   description = Enables threaded discussions about general topics.
4237
 *   dependencies[] = taxonomy
4238
 *   dependencies[] = comment
4239
 *   package = Core - optional
4240
 *   version = VERSION
4241
 * @endverbatim
4242
 * </code>
4243
 *
4244
 * @param string $filename
4245
 *                         The file we are parsing. Accepts file with relative or absolute path.
4246
 *
4247
 * @return
4248
 *   The info array
4249
 */
4250
function api_parse_info_file($filename)
4251
{
4252
    $info = [];
4253
4254
    if (!file_exists($filename)) {
4255
        return $info;
4256
    }
4257
4258
    $data = file_get_contents($filename);
4259
    if (preg_match_all('
4260
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
4261
        ((?:
4262
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
4263
          \[[^\[\]]*\]                  # unless they are balanced and not nested
4264
        )+?)
4265
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
4266
        (?:
4267
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
4268
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
4269
          ([^\r\n]*?)                   # Non-quoted string
4270
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
4271
        @msx', $data, $matches, PREG_SET_ORDER)) {
4272
        $key = $value1 = $value2 = $value3 = '';
4273
        foreach ($matches as $match) {
4274
            // Fetch the key and value string.
4275
            $i = 0;
4276
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
4277
                $$var = isset($match[++$i]) ? $match[$i] : '';
4278
            }
4279
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
4280
4281
            // Parse array syntax.
4282
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
4283
            $last = array_pop($keys);
4284
            $parent = &$info;
4285
4286
            // Create nested arrays.
4287
            foreach ($keys as $key) {
4288
                if ('' == $key) {
4289
                    $key = count($parent);
4290
                }
4291
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
4292
                    $parent[$key] = [];
4293
                }
4294
                $parent = &$parent[$key];
4295
            }
4296
4297
            // Handle PHP constants.
4298
            if (defined($value)) {
4299
                $value = constant($value);
4300
            }
4301
4302
            // Insert actual value.
4303
            if ('' == $last) {
4304
                $last = count($parent);
4305
            }
4306
            $parent[$last] = $value;
4307
        }
4308
    }
4309
4310
    return $info;
4311
}
4312
4313
/**
4314
 * Gets Chamilo version from the configuration files.
4315
 *
4316
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
4317
 */
4318
function api_get_version()
4319
{
4320
    return (string) api_get_configuration_value('system_version');
4321
}
4322
4323
/**
4324
 * Gets the software name (the name/brand of the Chamilo-based customized system).
4325
 *
4326
 * @return string
4327
 */
4328
function api_get_software_name()
4329
{
4330
    $name = api_get_env_variable('SOFTWARE_NAME', 'Chamilo');
4331
    return $name;
4332
}
4333
4334
function api_get_status_list()
4335
{
4336
    $list = [];
4337
    // Table of status
4338
    $list[COURSEMANAGER] = 'teacher'; // 1
4339
    $list[SESSIONADMIN] = 'session_admin'; // 3
4340
    $list[DRH] = 'drh'; // 4
4341
    $list[STUDENT] = 'user'; // 5
4342
    $list[ANONYMOUS] = 'anonymous'; // 6
4343
    $list[INVITEE] = 'invited'; // 20
4344
4345
    return $list;
4346
}
4347
4348
/**
4349
 * Checks whether status given in parameter exists in the platform.
4350
 *
4351
 * @param mixed the status (can be either int either string)
4352
 *
4353
 * @return bool if the status exists, else returns false
4354
 */
4355
function api_status_exists($status_asked)
4356
{
4357
    $list = api_get_status_list();
4358
4359
    return in_array($status_asked, $list) ? true : isset($list[$status_asked]);
4360
}
4361
4362
/**
4363
 * Checks whether status given in parameter exists in the platform. The function
4364
 * returns the status ID or false if it does not exist, but given the fact there
4365
 * is no "0" status, the return value can be checked against
4366
 * if(api_status_key()) to know if it exists.
4367
 *
4368
 * @param   mixed   The status (can be either int or string)
4369
 *
4370
 * @return mixed Status ID if exists, false otherwise
4371
 */
4372
function api_status_key($status)
4373
{
4374
    $list = api_get_status_list();
4375
4376
    return isset($list[$status]) ? $status : array_search($status, $list);
4377
}
4378
4379
/**
4380
 * Gets the status langvars list.
4381
 *
4382
 * @return string[] the list of status with their translations
4383
 */
4384
function api_get_status_langvars()
4385
{
4386
    return [
4387
        COURSEMANAGER => get_lang('Teacher'),
4388
        SESSIONADMIN => get_lang('Sessions administrator'),
4389
        DRH => get_lang('Human Resources Manager'),
4390
        STUDENT => get_lang('Learner'),
4391
        ANONYMOUS => get_lang('Anonymous'),
4392
        STUDENT_BOSS => get_lang('Student boss'),
4393
        INVITEE => get_lang('Invited'),
4394
    ];
4395
}
4396
4397
/**
4398
 * The function that retrieves all the possible settings for a certain config setting.
4399
 *
4400
 * @author Patrick Cool <[email protected]>, Ghent University
4401
 */
4402
function api_get_settings_options($var)
4403
{
4404
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4405
    $var = Database::escape_string($var);
4406
    $sql = "SELECT * FROM $table_settings_options
4407
            WHERE variable = '$var'
4408
            ORDER BY id";
4409
    $result = Database::query($sql);
4410
    $settings_options_array = [];
4411
    while ($row = Database::fetch_assoc($result)) {
4412
        $settings_options_array[] = $row;
4413
    }
4414
4415
    return $settings_options_array;
4416
}
4417
4418
/**
4419
 * @param array $params
4420
 */
4421
function api_set_setting_option($params)
4422
{
4423
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4424
    if (empty($params['id'])) {
4425
        Database::insert($table, $params);
4426
    } else {
4427
        Database::update($table, $params, ['id = ? ' => $params['id']]);
4428
    }
4429
}
4430
4431
/**
4432
 * @param array $params
4433
 */
4434
function api_set_setting_simple($params)
4435
{
4436
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4437
    $url_id = api_get_current_access_url_id();
4438
4439
    if (empty($params['id'])) {
4440
        $params['access_url'] = $url_id;
4441
        Database::insert($table, $params);
4442
    } else {
4443
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
4444
    }
4445
}
4446
4447
/**
4448
 * @param int $id
4449
 */
4450
function api_delete_setting_option($id)
4451
{
4452
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4453
    if (!empty($id)) {
4454
        Database::delete($table, ['id = ? ' => $id]);
4455
    }
4456
}
4457
4458
/**
4459
 * Sets a platform configuration setting to a given value.
4460
 *
4461
 * @param string    The variable we want to update
4462
 * @param string    The value we want to record
4463
 * @param string    The sub-variable if any (in most cases, this will remain null)
4464
 * @param string    The category if any (in most cases, this will remain null)
4465
 * @param int       The access_url for which this parameter is valid
4466
 * @param string $cat
4467
 *
4468
 * @return bool|null
4469
 */
4470
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
4471
{
4472
    if (empty($var)) {
4473
        return false;
4474
    }
4475
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
4476
    $var = Database::escape_string($var);
4477
    $value = Database::escape_string($value);
4478
    $access_url = (int) $access_url;
4479
    if (empty($access_url)) {
4480
        $access_url = 1;
4481
    }
4482
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
4483
    if (!empty($subvar)) {
4484
        $subvar = Database::escape_string($subvar);
4485
        $select .= " AND subkey = '$subvar'";
4486
    }
4487
    if (!empty($cat)) {
4488
        $cat = Database::escape_string($cat);
4489
        $select .= " AND category = '$cat'";
4490
    }
4491
    if ($access_url > 1) {
4492
        $select .= " AND access_url = $access_url";
4493
    } else {
4494
        $select .= " AND access_url = 1 ";
4495
    }
4496
4497
    $res = Database::query($select);
4498
    if (Database::num_rows($res) > 0) {
4499
        // Found item for this access_url.
4500
        $row = Database::fetch_array($res);
4501
        $sql = "UPDATE $t_settings SET selected_value = '$value'
4502
                WHERE id = ".$row['id'];
4503
        Database::query($sql);
4504
    } else {
4505
        // Item not found for this access_url, we have to check if it exist with access_url = 1
4506
        $select = "SELECT * FROM $t_settings
4507
                   WHERE variable = '$var' AND access_url = 1 ";
4508
        // Just in case
4509
        if (1 == $access_url) {
4510
            if (!empty($subvar)) {
4511
                $select .= " AND subkey = '$subvar'";
4512
            }
4513
            if (!empty($cat)) {
4514
                $select .= " AND category = '$cat'";
4515
            }
4516
            $res = Database::query($select);
4517
            if (Database::num_rows($res) > 0) {
4518
                // We have a setting for access_url 1, but none for the current one, so create one.
4519
                $row = Database::fetch_array($res);
4520
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
4521
                        VALUES
4522
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4523
                    "'".$row['type']."','".$row['category']."',".
4524
                    "'$value','".$row['title']."',".
4525
                    "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4526
                    "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
4527
                Database::query($insert);
4528
            } else {
4529
                // Such a setting does not exist.
4530
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
4531
            }
4532
        } else {
4533
            // Other access url.
4534
            if (!empty($subvar)) {
4535
                $select .= " AND subkey = '$subvar'";
4536
            }
4537
            if (!empty($cat)) {
4538
                $select .= " AND category = '$cat'";
4539
            }
4540
            $res = Database::query($select);
4541
4542
            if (Database::num_rows($res) > 0) {
4543
                // We have a setting for access_url 1, but none for the current one, so create one.
4544
                $row = Database::fetch_array($res);
4545
                if (1 == $row['access_url_changeable']) {
4546
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
4547
                            ('".$row['variable']."',".
4548
                        (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4549
                        "'".$row['type']."','".$row['category']."',".
4550
                        "'$value','".$row['title']."',".
4551
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
4552
                        (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4553
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
4554
                    Database::query($insert);
4555
                }
4556
            } else { // Such a setting does not exist.
4557
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
4558
            }
4559
        }
4560
    }
4561
}
4562
4563
/**
4564
 * Sets a whole category of settings to one specific value.
4565
 *
4566
 * @param string    Category
4567
 * @param string    Value
4568
 * @param int       Access URL. Optional. Defaults to 1
4569
 * @param array     Optional array of filters on field type
4570
 * @param string $category
4571
 * @param string $value
4572
 *
4573
 * @return bool
4574
 */
4575
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
4576
{
4577
    if (empty($category)) {
4578
        return false;
4579
    }
4580
    $category = Database::escape_string($category);
4581
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS);
4582
    $access_url = (int) $access_url;
4583
    if (empty($access_url)) {
4584
        $access_url = 1;
4585
    }
4586
    if (isset($value)) {
4587
        $value = Database::escape_string($value);
4588
        $sql = "UPDATE $t_s SET selected_value = '$value'
4589
                WHERE category = '$category' AND access_url = $access_url";
4590
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4591
            $sql .= " AND ( ";
4592
            $i = 0;
4593
            foreach ($fieldtype as $type) {
4594
                if ($i > 0) {
4595
                    $sql .= ' OR ';
4596
                }
4597
                $type = Database::escape_string($type);
4598
                $sql .= " type='".$type."' ";
4599
                $i++;
4600
            }
4601
            $sql .= ")";
4602
        }
4603
        $res = Database::query($sql);
4604
4605
        return false !== $res;
4606
    } else {
4607
        $sql = "UPDATE $t_s SET selected_value = NULL
4608
                WHERE category = '$category' AND access_url = $access_url";
4609
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4610
            $sql .= " AND ( ";
4611
            $i = 0;
4612
            foreach ($fieldtype as $type) {
4613
                if ($i > 0) {
4614
                    $sql .= ' OR ';
4615
                }
4616
                $type = Database::escape_string($type);
4617
                $sql .= " type='".$type."' ";
4618
                $i++;
4619
            }
4620
            $sql .= ")";
4621
        }
4622
        $res = Database::query($sql);
4623
4624
        return false !== $res;
4625
    }
4626
}
4627
4628
/**
4629
 * Gets all available access urls in an array (as in the database).
4630
 *
4631
 * @return array An array of database records
4632
 */
4633
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
4634
{
4635
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4636
    $from = (int) $from;
4637
    $to = (int) $to;
4638
    $order = Database::escape_string($order);
4639
    $direction = Database::escape_string($direction);
4640
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
4641
    $sql = "SELECT id, url, description, active, created_by, tms
4642
            FROM $table
4643
            ORDER BY `$order` $direction
4644
            LIMIT $to OFFSET $from";
4645
    $res = Database::query($sql);
4646
4647
    return Database::store_result($res);
4648
}
4649
4650
/**
4651
 * Gets the access url info in an array.
4652
 *
4653
 * @param int  $id            Id of the access url
4654
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
4655
 *
4656
 * @return array All the info (url, description, active, created_by, tms)
4657
 *               from the access_url table
4658
 *
4659
 * @author Julio Montoya
4660
 */
4661
function api_get_access_url($id, $returnDefault = true)
4662
{
4663
    static $staticResult;
4664
    $id = (int) $id;
4665
4666
    if (isset($staticResult[$id])) {
4667
        $result = $staticResult[$id];
4668
    } else {
4669
        // Calling the Database:: library dont work this is handmade.
4670
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4671
        $sql = "SELECT url, description, active, created_by, tms
4672
                FROM $table_access_url WHERE id = '$id' ";
4673
        $res = Database::query($sql);
4674
        $result = @Database::fetch_array($res);
4675
        $staticResult[$id] = $result;
4676
    }
4677
4678
    // If the result url is 'http://localhost/' (the default) and the root_web
4679
    // (=current url) is different, and the $id is = 1 (which might mean
4680
    // api_get_current_access_url_id() returned 1 by default), then return the
4681
    // root_web setting instead of the current URL
4682
    // This is provided as an option to avoid breaking the storage of URL-specific
4683
    // homepages in home/localhost/
4684
    if (1 === $id && false === $returnDefault) {
4685
        $currentUrl = api_get_current_access_url_id();
4686
        // only do this if we are on the main URL (=1), otherwise we could get
4687
        // information on another URL instead of the one asked as parameter
4688
        if (1 === $currentUrl) {
4689
            $rootWeb = api_get_path(WEB_PATH);
4690
            $default = AccessUrl::DEFAULT_ACCESS_URL;
4691
            if ($result['url'] === $default && $rootWeb != $default) {
4692
                $result['url'] = $rootWeb;
4693
            }
4694
        }
4695
    }
4696
4697
    return $result;
4698
}
4699
4700
/**
4701
 * Gets all the current settings for a specific access url.
4702
 *
4703
 * @param string    The category, if any, that we want to get
4704
 * @param string    Whether we want a simple list (display a category) or
4705
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
4706
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
4707
 *
4708
 * @return array Array of database results for the current settings of the current access URL
4709
 */
4710
function &api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
4711
{
4712
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4713
    $access_url = (int) $access_url;
4714
    $where_condition = '';
4715
    if (1 == $url_changeable) {
4716
        $where_condition = " AND access_url_changeable= '1' ";
4717
    }
4718
    if (empty($access_url) || -1 == $access_url) {
4719
        $access_url = 1;
4720
    }
4721
    $sql = "SELECT * FROM $table
4722
            WHERE access_url = $access_url  $where_condition ";
4723
4724
    if (!empty($cat)) {
4725
        $cat = Database::escape_string($cat);
4726
        $sql .= " AND category='$cat' ";
4727
    }
4728
    if ('group' == $ordering) {
4729
        $sql .= " ORDER BY id ASC";
4730
    } else {
4731
        $sql .= " ORDER BY 1,2 ASC";
4732
    }
4733
    $result = Database::query($sql);
4734
    if (null === $result) {
4735
        $result = [];
4736
        return $result;
4737
    }
4738
    $result = Database::store_result($result, 'ASSOC');
4739
4740
    return $result;
4741
}
4742
4743
/**
4744
 * @param string $value       The value we want to record
4745
 * @param string $variable    The variable name we want to insert
4746
 * @param string $subKey      The subkey for the variable we want to insert
4747
 * @param string $type        The type for the variable we want to insert
4748
 * @param string $category    The category for the variable we want to insert
4749
 * @param string $title       The title
4750
 * @param string $comment     The comment
4751
 * @param string $scope       The scope
4752
 * @param string $subKeyText  The subkey text
4753
 * @param int    $accessUrlId The access_url for which this parameter is valid
4754
 * @param int    $visibility  The changeability of this setting for non-master urls
4755
 *
4756
 * @return int The setting ID
4757
 */
4758
function api_add_setting(
4759
    $value,
4760
    $variable,
4761
    $subKey = '',
4762
    $type = 'textfield',
4763
    $category = '',
4764
    $title = '',
4765
    $comment = '',
4766
    $scope = '',
4767
    $subKeyText = '',
4768
    $accessUrlId = 1,
4769
    $visibility = 0
4770
) {
4771
    $em = Database::getManager();
4772
4773
    $settingRepo = $em->getRepository(SettingsCurrent::class);
4774
    $accessUrlId = (int) $accessUrlId ?: 1;
4775
4776
    if (is_array($value)) {
4777
        $value = serialize($value);
4778
    } else {
4779
        $value = trim($value);
4780
    }
4781
4782
    $criteria = ['variable' => $variable, 'url' => $accessUrlId];
4783
4784
    if (!empty($subKey)) {
4785
        $criteria['subkey'] = $subKey;
4786
    }
4787
4788
    // Check if this variable doesn't exist already
4789
    /** @var SettingsCurrent $setting */
4790
    $setting = $settingRepo->findOneBy($criteria);
4791
4792
    if ($setting) {
0 ignored issues
show
introduced by
$setting is of type Chamilo\CoreBundle\Entity\SettingsCurrent, thus it always evaluated to true.
Loading history...
4793
        $setting->setSelectedValue($value);
4794
4795
        $em->persist($setting);
4796
        $em->flush();
4797
4798
        return $setting->getId();
4799
    }
4800
4801
    // Item not found for this access_url, we have to check if the whole thing is missing
4802
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
4803
    $setting = new SettingsCurrent();
4804
    $url = api_get_url_entity();
4805
4806
    $setting
4807
        ->setVariable($variable)
4808
        ->setSelectedValue($value)
4809
        ->setType($type)
4810
        ->setCategory($category)
4811
        ->setSubkey($subKey)
4812
        ->setTitle($title)
4813
        ->setComment($comment)
4814
        ->setScope($scope)
4815
        ->setSubkeytext($subKeyText)
4816
        ->setUrl(api_get_url_entity())
4817
        ->setAccessUrlChangeable($visibility);
4818
4819
    $em->persist($setting);
4820
    $em->flush();
4821
4822
    return $setting->getId();
4823
}
4824
4825
/**
4826
 * Checks wether a user can or can't view the contents of a course.
4827
 *
4828
 * @deprecated use CourseManager::is_user_subscribed_in_course
4829
 *
4830
 * @param int $userid User id or NULL to get it from $_SESSION
4831
 * @param int $cid    course id to check whether the user is allowed
4832
 *
4833
 * @return bool
4834
 */
4835
function api_is_course_visible_for_user($userid = null, $cid = null)
4836
{
4837
    if (null === $userid) {
4838
        $userid = api_get_user_id();
4839
    }
4840
    if (empty($userid) || strval(intval($userid)) != $userid) {
4841
        if (api_is_anonymous()) {
4842
            $userid = api_get_anonymous_id();
4843
        } else {
4844
            return false;
4845
        }
4846
    }
4847
    $cid = Database::escape_string($cid);
4848
4849
    $courseInfo = api_get_course_info($cid);
4850
    $courseId = $courseInfo['real_id'];
4851
    $is_platformAdmin = api_is_platform_admin();
4852
4853
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
4854
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
4855
4856
    $sql = "SELECT
4857
                $course_cat_table.code AS category_code,
4858
                $course_table.visibility,
4859
                $course_table.code,
4860
                $course_cat_table.code
4861
            FROM $course_table
4862
            LEFT JOIN $course_cat_table
4863
                ON $course_table.category_id = $course_cat_table.id
4864
            WHERE
4865
                $course_table.code = '$cid'
4866
            LIMIT 1";
4867
4868
    $result = Database::query($sql);
4869
4870
    if (Database::num_rows($result) > 0) {
4871
        $visibility = Database::fetch_array($result);
4872
        $visibility = $visibility['visibility'];
4873
    } else {
4874
        $visibility = 0;
4875
    }
4876
    // Shortcut permissions in case the visibility is "open to the world".
4877
    if (COURSE_VISIBILITY_OPEN_WORLD === $visibility) {
4878
        return true;
4879
    }
4880
4881
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4882
4883
    $sql = "SELECT
4884
                is_tutor, status
4885
            FROM $tbl_course_user
4886
            WHERE
4887
                user_id  = '$userid' AND
4888
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
4889
                c_id = $courseId
4890
            LIMIT 1";
4891
4892
    $result = Database::query($sql);
4893
4894
    if (Database::num_rows($result) > 0) {
4895
        // This user has got a recorded state for this course.
4896
        $cuData = Database::fetch_array($result);
4897
        $is_courseMember = true;
4898
        $is_courseAdmin = (1 == $cuData['status']);
4899
    }
4900
4901
    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...
4902
        // This user has no status related to this course.
4903
        // Is it the session coach or the session admin?
4904
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4905
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4906
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4907
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4908
4909
        $sql = "SELECT sru_2.user_id AS session_admin_id, sru_1.user_id AS session_coach_id
4910
                FROM $tbl_session AS s
4911
                INNER JOIN $tblSessionRelUser sru_1
4912
                ON (sru_1.session_id = s.id AND sru_1.relation_type = ".SessionEntity::GENERAL_COACH.")
4913
                INNER JOIN $tblSessionRelUser sru_2
4914
                ON (sru_2.session_id = s.id AND sru_2.relation_type = ".SessionEntity::SESSION_ADMIN.")
4915
                INNER JOIN $tbl_session_course src
4916
                ON (src.session_id = s.id AND src.c_id = $courseId)";
4917
4918
        $result = Database::query($sql);
4919
        $row = Database::store_result($result);
4920
        $sessionAdminsId = array_column($row, 'session_admin_id');
4921
        $sessionCoachesId = array_column($row, 'session_coach_id');
4922
4923
        if (in_array($userid, $sessionCoachesId)) {
4924
            $is_courseMember = true;
4925
            $is_courseAdmin = false;
4926
        } elseif (in_array($userid, $sessionAdminsId)) {
4927
            $is_courseMember = false;
4928
            $is_courseAdmin = false;
4929
        } else {
4930
            // Check if the current user is the course coach.
4931
            $sql = "SELECT 1
4932
                    FROM $tbl_session_course
4933
                    WHERE session_rel_course.c_id = '$courseId'
4934
                    AND session_rel_course.id_coach = '$userid'
4935
                    LIMIT 1";
4936
4937
            $result = Database::query($sql);
4938
4939
            //if ($row = Database::fetch_array($result)) {
4940
            if (Database::num_rows($result) > 0) {
4941
                $is_courseMember = true;
4942
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4943
4944
                $sql = "SELECT status FROM $tbl_user
4945
                        WHERE id = $userid
4946
                        LIMIT 1";
4947
4948
                $result = Database::query($sql);
4949
4950
                if (1 == Database::result($result, 0, 0)) {
4951
                    $is_courseAdmin = true;
4952
                } else {
4953
                    $is_courseAdmin = false;
4954
                }
4955
            } else {
4956
                // Check if the user is a student is this session.
4957
                $sql = "SELECT  id
4958
                        FROM $tbl_session_course_user
4959
                        WHERE
4960
                            user_id  = '$userid' AND
4961
                            c_id = '$courseId'
4962
                        LIMIT 1";
4963
4964
                if (Database::num_rows($result) > 0) {
4965
                    // This user haa got a recorded state for this course.
4966
                    while ($row = Database::fetch_array($result)) {
4967
                        $is_courseMember = true;
4968
                        $is_courseAdmin = false;
4969
                    }
4970
                }
4971
            }
4972
        }
4973
    }
4974
4975
    switch ($visibility) {
4976
        case Course::OPEN_WORLD:
4977
            return true;
4978
        case Course::OPEN_PLATFORM:
4979
            return isset($userid);
4980
        case Course::REGISTERED:
4981
        case Course::CLOSED:
4982
            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...
4983
        case Course::HIDDEN:
4984
            return $is_platformAdmin;
4985
    }
4986
4987
    return false;
4988
}
4989
4990
/**
4991
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
4992
 *
4993
 * @param string the tool of the element
4994
 * @param int the element id in database
4995
 * @param int the session_id to compare with element session id
4996
 *
4997
 * @return bool true if the element is in the session, false else
4998
 */
4999
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
5000
{
5001
    if (is_null($session_id)) {
5002
        $session_id = api_get_session_id();
5003
    }
5004
5005
    $element_id = (int) $element_id;
5006
5007
    if (empty($element_id)) {
5008
        return false;
5009
    }
5010
5011
    // Get information to build query depending of the tool.
5012
    switch ($tool) {
5013
        case TOOL_SURVEY:
5014
            $table_tool = Database::get_course_table(TABLE_SURVEY);
5015
            $key_field = 'survey_id';
5016
            break;
5017
        case TOOL_ANNOUNCEMENT:
5018
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
5019
            $key_field = 'id';
5020
            break;
5021
        case TOOL_AGENDA:
5022
            $table_tool = Database::get_course_table(TABLE_AGENDA);
5023
            $key_field = 'id';
5024
            break;
5025
        case TOOL_GROUP:
5026
            $table_tool = Database::get_course_table(TABLE_GROUP);
5027
            $key_field = 'id';
5028
            break;
5029
        default:
5030
            return false;
5031
    }
5032
    $course_id = api_get_course_int_id();
5033
5034
    $sql = "SELECT session_id FROM $table_tool
5035
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
5036
    $rs = Database::query($sql);
5037
    if ($element_session_id = Database::result($rs, 0, 0)) {
5038
        if ($element_session_id == intval($session_id)) {
5039
            // The element belongs to the session.
5040
            return true;
5041
        }
5042
    }
5043
5044
    return false;
5045
}
5046
5047
/**
5048
 * Replaces "forbidden" characters in a filename string.
5049
 *
5050
 * @param string $filename
5051
 * @param bool   $treat_spaces_as_hyphens
5052
 *
5053
 * @return string
5054
 */
5055
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
5056
{
5057
    // Some non-properly encoded file names can cause the whole file to be
5058
    // skipped when uploaded. Avoid this by detecting the encoding and
5059
    // converting to UTF-8, setting the source as ASCII (a reasonably
5060
    // limited characters set) if nothing could be found (BT#
5061
    $encoding = api_detect_encoding($filename);
5062
    if (empty($encoding)) {
5063
        $encoding = 'ASCII';
5064
        if (!api_is_valid_ascii($filename)) {
5065
            // try iconv and try non standard ASCII a.k.a CP437
5066
            // see BT#15022
5067
            if (function_exists('iconv')) {
5068
                $result = iconv('CP437', 'UTF-8', $filename);
5069
                if (api_is_valid_utf8($result)) {
5070
                    $filename = $result;
5071
                    $encoding = 'UTF-8';
5072
                }
5073
            }
5074
        }
5075
    }
5076
5077
    $filename = api_to_system_encoding($filename, $encoding);
5078
5079
    $url = URLify::filter(
5080
        $filename,
5081
        250,
5082
        '',
5083
        true,
5084
        false,
5085
        false
5086
    );
5087
5088
    // Replace multiple dots at the end.
5089
    $regex = "/\.+$/";
5090
5091
    return preg_replace($regex, '', $url);
5092
}
5093
5094
/**
5095
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
5096
 *
5097
 * @author Ivan Tcholakov, 28-JUN-2006.
5098
 */
5099
function api_request_uri()
5100
{
5101
    if (!empty($_SERVER['REQUEST_URI'])) {
5102
        return $_SERVER['REQUEST_URI'];
5103
    }
5104
    $uri = $_SERVER['SCRIPT_NAME'];
5105
    if (!empty($_SERVER['QUERY_STRING'])) {
5106
        $uri .= '?'.$_SERVER['QUERY_STRING'];
5107
    }
5108
    $_SERVER['REQUEST_URI'] = $uri;
5109
5110
    return $uri;
5111
}
5112
5113
/**
5114
 * Gets the current access_url id of the Chamilo Platform.
5115
 *
5116
 * @return int access_url_id of the current Chamilo Installation
5117
 *
5118
 * @author Julio Montoya <[email protected]>
5119
 * @throws Exception
5120
 */
5121
function api_get_current_access_url_id(): int
5122
{
5123
    if (false === api_get_multiple_access_url()) {
5124
        return 1;
5125
    }
5126
5127
    static $id;
5128
    if (!empty($id)) {
5129
        return $id;
5130
    }
5131
5132
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5133
    $path = Database::escape_string(api_get_path(WEB_PATH));
5134
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
5135
    $result = Database::query($sql);
5136
    if (Database::num_rows($result) > 0) {
5137
        $id = Database::result($result, 0, 0);
5138
        if (false === $id) {
5139
            return -1;
5140
        }
5141
5142
        return (int) $id;
5143
    }
5144
5145
    $id = 1;
5146
5147
    //if the url in WEB_PATH was not found, it can only mean that there is
5148
    // either a configuration problem or the first URL has not been defined yet
5149
    // (by default it is http://localhost/). Thus the more sensible thing we can
5150
    // do is return 1 (the main URL) as the user cannot hack this value anyway
5151
    return 1;
5152
}
5153
5154
/**
5155
 * Gets the registered urls from a given user id.
5156
 *
5157
 * @author Julio Montoya <[email protected]>
5158
 *
5159
 * @param int $user_id
5160
 *
5161
 * @return array
5162
 */
5163
function api_get_access_url_from_user($user_id)
5164
{
5165
    $user_id = (int) $user_id;
5166
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5167
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5168
    $sql = "SELECT access_url_id
5169
            FROM $table_url_rel_user url_rel_user
5170
            INNER JOIN $table_url u
5171
            ON (url_rel_user.access_url_id = u.id)
5172
            WHERE user_id = ".$user_id;
5173
    $result = Database::query($sql);
5174
    $list = [];
5175
    while ($row = Database::fetch_assoc($result)) {
5176
        $list[] = $row['access_url_id'];
5177
    }
5178
5179
    return $list;
5180
}
5181
5182
/**
5183
 * Checks whether the curent user is in a group or not.
5184
 *
5185
 * @param string        The group id - optional (takes it from session if not given)
5186
 * @param string        The course code - optional (no additional check by course if course code is not given)
5187
 *
5188
 * @return bool
5189
 *
5190
 * @author Ivan Tcholakov
5191
 */
5192
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
5193
{
5194
    if (!empty($courseCodeParam)) {
5195
        $courseCode = api_get_course_id();
5196
        if (!empty($courseCode)) {
5197
            if ($courseCodeParam != $courseCode) {
5198
                return false;
5199
            }
5200
        } else {
5201
            return false;
5202
        }
5203
    }
5204
5205
    $groupId = api_get_group_id();
5206
5207
    if (isset($groupId) && '' != $groupId) {
5208
        if (!empty($groupIdParam)) {
5209
            return $groupIdParam == $groupId;
5210
        } else {
5211
            return true;
5212
        }
5213
    }
5214
5215
    return false;
5216
}
5217
5218
/**
5219
 * Checks whether a secret key is valid.
5220
 *
5221
 * @param string $original_key_secret - secret key from (webservice) client
5222
 * @param string $security_key        - security key from Chamilo
5223
 *
5224
 * @return bool - true if secret key is valid, false otherwise
5225
 */
5226
function api_is_valid_secret_key($original_key_secret, $security_key)
5227
{
5228
    if (empty($original_key_secret) || empty($security_key)) {
5229
        return false;
5230
    }
5231
5232
    return (string) $original_key_secret === hash('sha512', $security_key);
5233
}
5234
5235
/**
5236
 * Checks whether the server's operating system is Windows (TM).
5237
 *
5238
 * @return bool - true if the operating system is Windows, false otherwise
5239
 */
5240
function api_is_windows_os()
5241
{
5242
    if (function_exists('php_uname')) {
5243
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
5244
        // We expect that this function will always work for Chamilo 1.8.x.
5245
        $os = php_uname();
5246
    }
5247
    // The following methods are not needed, but let them stay, just in case.
5248
    elseif (isset($_ENV['OS'])) {
5249
        // Sometimes $_ENV['OS'] may not be present (bugs?)
5250
        $os = $_ENV['OS'];
5251
    } elseif (defined('PHP_OS')) {
5252
        // PHP_OS means on which OS PHP was compiled, this is why
5253
        // using PHP_OS is the last choice for detection.
5254
        $os = PHP_OS;
5255
    } else {
5256
        return false;
5257
    }
5258
5259
    return 'win' == strtolower(substr((string) $os, 0, 3));
5260
}
5261
5262
/**
5263
 * This function informs whether the sent request is XMLHttpRequest.
5264
 */
5265
function api_is_xml_http_request()
5266
{
5267
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH']);
5268
}
5269
5270
/**
5271
 * Returns a list of Chamilo's tools or
5272
 * checks whether a given identificator is a valid Chamilo's tool.
5273
 *
5274
 * @author Isaac flores paz
5275
 *
5276
 * @param string The tool name to filter
5277
 *
5278
 * @return mixed Filtered string or array
5279
 */
5280
function api_get_tools_lists($my_tool = null)
5281
{
5282
    $tools_list = [
5283
        TOOL_DOCUMENT,
5284
        TOOL_THUMBNAIL,
5285
        TOOL_HOTPOTATOES,
5286
        TOOL_CALENDAR_EVENT,
5287
        TOOL_LINK,
5288
        TOOL_COURSE_DESCRIPTION,
5289
        TOOL_SEARCH,
5290
        TOOL_LEARNPATH,
5291
        TOOL_ANNOUNCEMENT,
5292
        TOOL_FORUM,
5293
        TOOL_THREAD,
5294
        TOOL_POST,
5295
        TOOL_DROPBOX,
5296
        TOOL_QUIZ,
5297
        TOOL_USER,
5298
        TOOL_GROUP,
5299
        TOOL_BLOGS,
5300
        TOOL_CHAT,
5301
        TOOL_STUDENTPUBLICATION,
5302
        TOOL_TRACKING,
5303
        TOOL_HOMEPAGE_LINK,
5304
        TOOL_COURSE_SETTING,
5305
        TOOL_BACKUP,
5306
        TOOL_COPY_COURSE_CONTENT,
5307
        TOOL_RECYCLE_COURSE,
5308
        TOOL_COURSE_HOMEPAGE,
5309
        TOOL_COURSE_RIGHTS_OVERVIEW,
5310
        TOOL_UPLOAD,
5311
        TOOL_COURSE_MAINTENANCE,
5312
        TOOL_SURVEY,
5313
        //TOOL_WIKI,
5314
        TOOL_GLOSSARY,
5315
        TOOL_GRADEBOOK,
5316
        TOOL_NOTEBOOK,
5317
        TOOL_ATTENDANCE,
5318
        TOOL_COURSE_PROGRESS,
5319
    ];
5320
    if (empty($my_tool)) {
5321
        return $tools_list;
5322
    }
5323
5324
    return in_array($my_tool, $tools_list) ? $my_tool : '';
5325
}
5326
5327
/**
5328
 * Checks whether we already approved the last version term and condition.
5329
 *
5330
 * @param int user id
5331
 *
5332
 * @return bool true if we pass false otherwise
5333
 */
5334
function api_check_term_condition($userId)
5335
{
5336
    if ('true' === api_get_setting('allow_terms_conditions')) {
5337
        // Check if exists terms and conditions
5338
        if (0 == LegalManager::count()) {
5339
            return true;
5340
        }
5341
5342
        $extraFieldValue = new ExtraFieldValue('user');
5343
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
5344
            $userId,
5345
            'legal_accept'
5346
        );
5347
5348
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
5349
            $result = $data['value'];
5350
            $user_conditions = explode(':', $result);
5351
            $version = $user_conditions[0];
5352
            $langId = $user_conditions[1];
5353
            $realVersion = LegalManager::get_last_version($langId);
5354
5355
            return $version >= $realVersion;
5356
        }
5357
5358
        return false;
5359
    }
5360
5361
    return false;
5362
}
5363
5364
/**
5365
 * Gets all information of a tool into course.
5366
 *
5367
 * @param int The tool id
5368
 *
5369
 * @return array
5370
 */
5371
function api_get_tool_information_by_name($name)
5372
{
5373
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
5374
    $course_id = api_get_course_int_id();
5375
5376
    $sql = "SELECT id FROM tool
5377
            WHERE title = '".Database::escape_string($name)."' ";
5378
    $rs = Database::query($sql);
5379
    $data = Database::fetch_array($rs);
5380
    if ($data) {
5381
        $tool = $data['id'];
5382
        $sql = "SELECT * FROM $t_tool
5383
                WHERE c_id = $course_id  AND tool_id = '".$tool."' ";
5384
        $rs = Database::query($sql);
5385
5386
        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...
5387
    }
5388
5389
    return [];
5390
}
5391
5392
/**
5393
 * Function used to protect a "global" admin script.
5394
 * The function blocks access when the user has no global platform admin rights.
5395
 * Global admins are the admins that are registered in the main.admin table
5396
 * AND the users who have access to the "principal" portal.
5397
 * That means that there is a record in the main.access_url_rel_user table
5398
 * with his user id and the access_url_id=1.
5399
 *
5400
 * @author Julio Montoya
5401
 *
5402
 * @param int $user_id
5403
 *
5404
 * @return bool
5405
 */
5406
function api_is_global_platform_admin($user_id = null)
5407
{
5408
    $user_id = (int) $user_id;
5409
    if (empty($user_id)) {
5410
        $user_id = api_get_user_id();
5411
    }
5412
    if (api_is_platform_admin_by_id($user_id)) {
5413
        $urlList = api_get_access_url_from_user($user_id);
5414
        // The admin is registered in the first "main" site with access_url_id = 1
5415
        if (in_array(1, $urlList)) {
5416
            return true;
5417
        }
5418
    }
5419
5420
    return false;
5421
}
5422
5423
/**
5424
 * @param int  $admin_id_to_check
5425
 * @param int  $userId
5426
 * @param bool $allow_session_admin
5427
 *
5428
 * @return bool
5429
 */
5430
function api_global_admin_can_edit_admin(
5431
    $admin_id_to_check,
5432
    $userId = 0,
5433
    $allow_session_admin = false
5434
) {
5435
    if (empty($userId)) {
5436
        $userId = api_get_user_id();
5437
    }
5438
5439
    $iam_a_global_admin = api_is_global_platform_admin($userId);
5440
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
5441
5442
    if ($iam_a_global_admin) {
5443
        // Global admin can edit everything
5444
        return true;
5445
    }
5446
5447
    // If i'm a simple admin
5448
    $is_platform_admin = api_is_platform_admin_by_id($userId);
5449
5450
    if ($allow_session_admin && !$is_platform_admin) {
5451
        $user = api_get_user_entity($userId);
5452
        $is_platform_admin = $user->hasRole('ROLE_SESSION_MANAGER');
5453
    }
5454
5455
    if ($is_platform_admin) {
5456
        if ($user_is_global_admin) {
5457
            return false;
5458
        } else {
5459
            return true;
5460
        }
5461
    }
5462
5463
    return false;
5464
}
5465
5466
/**
5467
 * @param int  $admin_id_to_check
5468
 * @param int  $userId
5469
 * @param bool $allow_session_admin
5470
 *
5471
 * @return bool|null
5472
 */
5473
function api_protect_super_admin($admin_id_to_check, $userId = null, $allow_session_admin = false)
5474
{
5475
    if (api_global_admin_can_edit_admin($admin_id_to_check, $userId, $allow_session_admin)) {
5476
        return true;
5477
    } else {
5478
        api_not_allowed();
5479
    }
5480
}
5481
5482
/**
5483
 * Function used to protect a global admin script.
5484
 * The function blocks access when the user has no global platform admin rights.
5485
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
5486
 *
5487
 * @author Julio Montoya
5488
 */
5489
function api_protect_global_admin_script()
5490
{
5491
    if (!api_is_global_platform_admin()) {
5492
        api_not_allowed();
5493
5494
        return false;
5495
    }
5496
5497
    return true;
5498
}
5499
5500
/**
5501
 * Check browser support for specific file types or features
5502
 * This function checks if the user's browser supports a file format or given
5503
 * feature, or returns the current browser and major version when
5504
 * $format=check_browser. Only a limited number of formats and features are
5505
 * checked by this method. Make sure you check its definition first.
5506
 *
5507
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
5508
 *
5509
 * @deprecated
5510
 *
5511
 * @return bool or return text array if $format=check_browser
5512
 *
5513
 * @author Juan Carlos Raña Trabado
5514
 */
5515
function api_browser_support($format = '')
5516
{
5517
    return true;
5518
5519
    $browser = new Browser();
0 ignored issues
show
Unused Code introduced by
$browser = new Browser() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5520
    $current_browser = $browser->getBrowser();
5521
    $a_versiontemp = explode('.', $browser->getVersion());
5522
    $current_majorver = $a_versiontemp[0];
5523
5524
    static $result;
5525
5526
    if (isset($result[$format])) {
5527
        return $result[$format];
5528
    }
5529
5530
    // Native svg support
5531
    if ('svg' == $format) {
5532
        if (('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5533
            ('Firefox' == $current_browser && $current_majorver > 1) ||
5534
            ('Safari' == $current_browser && $current_majorver >= 4) ||
5535
            ('Chrome' == $current_browser && $current_majorver >= 1) ||
5536
            ('Opera' == $current_browser && $current_majorver >= 9)
5537
        ) {
5538
            $result[$format] = true;
5539
5540
            return true;
5541
        } else {
5542
            $result[$format] = false;
5543
5544
            return false;
5545
        }
5546
    } elseif ('pdf' == $format) {
5547
        // native pdf support
5548
        if ('Chrome' == $current_browser && $current_majorver >= 6) {
5549
            $result[$format] = true;
5550
5551
            return true;
5552
        } else {
5553
            $result[$format] = false;
5554
5555
            return false;
5556
        }
5557
    } elseif ('tif' == $format || 'tiff' == $format) {
5558
        //native tif support
5559
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5560
            $result[$format] = true;
5561
5562
            return true;
5563
        } else {
5564
            $result[$format] = false;
5565
5566
            return false;
5567
        }
5568
    } elseif ('ogg' == $format || 'ogx' == $format || 'ogv' == $format || 'oga' == $format) {
5569
        //native ogg, ogv,oga support
5570
        if (('Firefox' == $current_browser && $current_majorver >= 3) ||
5571
            ('Chrome' == $current_browser && $current_majorver >= 3) ||
5572
            ('Opera' == $current_browser && $current_majorver >= 9)) {
5573
            $result[$format] = true;
5574
5575
            return true;
5576
        } else {
5577
            $result[$format] = false;
5578
5579
            return false;
5580
        }
5581
    } elseif ('mpg' == $format || 'mpeg' == $format) {
5582
        //native mpg support
5583
        if (('Safari' == $current_browser && $current_majorver >= 5)) {
5584
            $result[$format] = true;
5585
5586
            return true;
5587
        } else {
5588
            $result[$format] = false;
5589
5590
            return false;
5591
        }
5592
    } elseif ('mp4' == $format) {
5593
        //native mp4 support (TODO: Android, iPhone)
5594
        if ('Android' == $current_browser || 'iPhone' == $current_browser) {
5595
            $result[$format] = true;
5596
5597
            return true;
5598
        } else {
5599
            $result[$format] = false;
5600
5601
            return false;
5602
        }
5603
    } elseif ('mov' == $format) {
5604
        //native mov support( TODO:check iPhone)
5605
        if ('Safari' == $current_browser && $current_majorver >= 5 || 'iPhone' == $current_browser) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ('Safari' == $current_br...ne' == $current_browser, Probably Intended Meaning: 'Safari' == $current_bro...e' == $current_browser)
Loading history...
5606
            $result[$format] = true;
5607
5608
            return true;
5609
        } else {
5610
            $result[$format] = false;
5611
5612
            return false;
5613
        }
5614
    } elseif ('avi' == $format) {
5615
        //native avi support
5616
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5617
            $result[$format] = true;
5618
5619
            return true;
5620
        } else {
5621
            $result[$format] = false;
5622
5623
            return false;
5624
        }
5625
    } elseif ('wmv' == $format) {
5626
        //native wmv support
5627
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5628
            $result[$format] = true;
5629
5630
            return true;
5631
        } else {
5632
            $result[$format] = false;
5633
5634
            return false;
5635
        }
5636
    } elseif ('webm' == $format) {
5637
        //native webm support (TODO:check IE9, Chrome9, Android)
5638
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5639
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5640
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5641
            ('Chrome' == $current_browser && $current_majorver >= 9) ||
5642
            'Android' == $current_browser
5643
        ) {
5644
            $result[$format] = true;
5645
5646
            return true;
5647
        } else {
5648
            $result[$format] = false;
5649
5650
            return false;
5651
        }
5652
    } elseif ('wav' == $format) {
5653
        //native wav support (only some codecs !)
5654
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5655
            ('Safari' == $current_browser && $current_majorver >= 5) ||
5656
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5657
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5658
            ('Chrome' == $current_browser && $current_majorver > 9) ||
5659
            'Android' == $current_browser ||
5660
            'iPhone' == $current_browser
5661
        ) {
5662
            $result[$format] = true;
5663
5664
            return true;
5665
        } else {
5666
            $result[$format] = false;
5667
5668
            return false;
5669
        }
5670
    } elseif ('mid' == $format || 'kar' == $format) {
5671
        //native midi support (TODO:check Android)
5672
        if ('Opera' == $current_browser && $current_majorver >= 9 || 'Android' == $current_browser) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ('Opera' == $current_bro...id' == $current_browser, Probably Intended Meaning: 'Opera' == $current_brow...d' == $current_browser)
Loading history...
5673
            $result[$format] = true;
5674
5675
            return true;
5676
        } else {
5677
            $result[$format] = false;
5678
5679
            return false;
5680
        }
5681
    } elseif ('wma' == $format) {
5682
        //native wma support
5683
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5684
            $result[$format] = true;
5685
5686
            return true;
5687
        } else {
5688
            $result[$format] = false;
5689
5690
            return false;
5691
        }
5692
    } elseif ('au' == $format) {
5693
        //native au support
5694
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5695
            $result[$format] = true;
5696
5697
            return true;
5698
        } else {
5699
            $result[$format] = false;
5700
5701
            return false;
5702
        }
5703
    } elseif ('mp3' == $format) {
5704
        //native mp3 support (TODO:check Android, iPhone)
5705
        if (('Safari' == $current_browser && $current_majorver >= 5) ||
5706
            ('Chrome' == $current_browser && $current_majorver >= 6) ||
5707
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5708
            'Android' == $current_browser ||
5709
            'iPhone' == $current_browser ||
5710
            'Firefox' == $current_browser
5711
        ) {
5712
            $result[$format] = true;
5713
5714
            return true;
5715
        } else {
5716
            $result[$format] = false;
5717
5718
            return false;
5719
        }
5720
    } elseif ('autocapitalize' == $format) {
5721
        // Help avoiding showing the autocapitalize option if the browser doesn't
5722
        // support it: this attribute is against the HTML5 standard
5723
        if ('Safari' == $current_browser || 'iPhone' == $current_browser) {
5724
            return true;
5725
        } else {
5726
            return false;
5727
        }
5728
    } elseif ("check_browser" == $format) {
5729
        $array_check_browser = [$current_browser, $current_majorver];
5730
5731
        return $array_check_browser;
5732
    } else {
5733
        $result[$format] = false;
5734
5735
        return false;
5736
    }
5737
}
5738
5739
/**
5740
 * This function checks if exist path and file browscap.ini
5741
 * In order for this to work, your browscap configuration setting in php.ini
5742
 * must point to the correct location of the browscap.ini file on your system
5743
 * http://php.net/manual/en/function.get-browser.php.
5744
 *
5745
 * @return bool
5746
 *
5747
 * @author Juan Carlos Raña Trabado
5748
 */
5749
function api_check_browscap()
5750
{
5751
    $setting = ini_get('browscap');
5752
    if ($setting) {
5753
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
5754
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
5755
            return true;
5756
        }
5757
    }
5758
5759
    return false;
5760
}
5761
5762
/**
5763
 * Returns the <script> HTML tag.
5764
 */
5765
function api_get_js($file)
5766
{
5767
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
5768
}
5769
5770
function api_get_build_js($file)
5771
{
5772
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'"></script>'."\n";
5773
}
5774
5775
function api_get_build_css($file, $media = 'screen')
5776
{
5777
    return '<link
5778
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5779
}
5780
5781
/**
5782
 * Returns the <script> HTML tag.
5783
 *
5784
 * @return string
5785
 */
5786
function api_get_asset($file)
5787
{
5788
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"></script>'."\n";
5789
}
5790
5791
/**
5792
 * Returns the <script> HTML tag.
5793
 *
5794
 * @param string $file
5795
 * @param string $media
5796
 *
5797
 * @return string
5798
 */
5799
function api_get_css_asset($file, $media = 'screen')
5800
{
5801
    return '<link
5802
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"
5803
        rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5804
}
5805
5806
/**
5807
 * Returns the <link> HTML tag.
5808
 *
5809
 * @param string $file
5810
 * @param string $media
5811
 */
5812
function api_get_css($file, $media = 'screen')
5813
{
5814
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5815
}
5816
5817
function api_get_bootstrap_and_font_awesome($returnOnlyPath = false, $returnFileLocation = false)
5818
{
5819
    $url = api_get_path(WEB_PUBLIC_PATH).'build/css/bootstrap.css';
5820
5821
    if ($returnOnlyPath) {
5822
        if ($returnFileLocation) {
5823
            return api_get_path(SYS_PUBLIC_PATH).'build/css/bootstrap.css';
5824
        }
5825
5826
        return $url;
5827
    }
5828
5829
    return '<link href="'.$url.'" rel="stylesheet" type="text/css" />'."\n";
5830
}
5831
5832
/**
5833
 * Returns the js header to include the jquery library.
5834
 */
5835
function api_get_jquery_js()
5836
{
5837
    return api_get_asset('jquery/jquery.min.js');
5838
}
5839
5840
/**
5841
 * Returns the jquery path.
5842
 *
5843
 * @return string
5844
 */
5845
function api_get_jquery_web_path()
5846
{
5847
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/jquery.min.js';
5848
}
5849
5850
/**
5851
 * @return string
5852
 */
5853
function api_get_jquery_ui_js_web_path()
5854
{
5855
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
5856
}
5857
5858
/**
5859
 * @return string
5860
 */
5861
function api_get_jquery_ui_css_web_path()
5862
{
5863
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
5864
}
5865
5866
/**
5867
 * Returns the jquery-ui library js headers.
5868
 *
5869
 * @return string html tags
5870
 */
5871
function api_get_jquery_ui_js()
5872
{
5873
    $libraries = [];
5874
5875
    return api_get_jquery_libraries_js($libraries);
5876
}
5877
5878
function api_get_jqgrid_js()
5879
{
5880
    return api_get_build_css('legacy_free-jqgrid.css').PHP_EOL
5881
        .api_get_build_js('legacy_free-jqgrid.js');
5882
}
5883
5884
/**
5885
 * Returns the jquery library js and css headers.
5886
 *
5887
 * @param   array   list of jquery libraries supported jquery-ui
5888
 * @param   bool    add the jquery library
5889
 *
5890
 * @return string html tags
5891
 */
5892
function api_get_jquery_libraries_js($libraries)
5893
{
5894
    $js = '';
5895
5896
    //Document multiple upload funcionality
5897
    if (in_array('jquery-uploadzs', $libraries)) {
5898
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
5899
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
5900
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
5901
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
5902
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
5903
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
5904
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
5905
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
5906
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
5907
5908
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
5909
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
5910
    }
5911
5912
    // jquery datepicker
5913
    if (in_array('datepicker', $libraries)) {
5914
        $languaje = 'en-GB';
5915
        $platform_isocode = strtolower(api_get_language_isocode());
5916
5917
        $datapicker_langs = [
5918
            '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',
5919
        ];
5920
        if (in_array($platform_isocode, $datapicker_langs)) {
5921
            $languaje = $platform_isocode;
5922
        }
5923
5924
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
5925
        $script = '<script>
5926
        $(function(){
5927
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
5928
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
5929
        });
5930
        </script>
5931
        ';
5932
        $js .= $script;
5933
    }
5934
5935
    return $js;
5936
}
5937
5938
/**
5939
 * Returns the URL to the course or session, removing the complexity of the URL
5940
 * building piece by piece.
5941
 *
5942
 * This function relies on api_get_course_info()
5943
 *
5944
 * @param int $courseId  The course code - optional (takes it from context if not given)
5945
 * @param int $sessionId The session ID  - optional (takes it from context if not given)
5946
 * @param int $groupId   The group ID - optional (takes it from context if not given)
5947
 *
5948
 * @return string The URL to a course, a session, or empty string if nothing works
5949
 *                e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
5950
 *
5951
 * @author  Julio Montoya
5952
 */
5953
function api_get_course_url($courseId = null, $sessionId = null, $groupId = null)
5954
{
5955
    $url = '';
5956
    // If courseCode not set, get context or []
5957
    if (empty($courseId)) {
5958
        $courseId = api_get_course_int_id();
5959
    }
5960
5961
    // If sessionId not set, get context or 0
5962
    if (empty($sessionId)) {
5963
        $sessionId = api_get_session_id();
5964
    }
5965
5966
    // If groupId not set, get context or 0
5967
    if (empty($groupId)) {
5968
        $groupId = api_get_group_id();
5969
    }
5970
5971
    // Build the URL
5972
    if (!empty($courseId)) {
5973
        $webCourseHome = '/course/'.$courseId.'/home';
5974
        // directory not empty, so we do have a course
5975
        $url = $webCourseHome.'?sid='.$sessionId.'&gid='.$groupId;
5976
    } else {
5977
        if (!empty($sessionId) && 'true' !== api_get_setting('session.remove_session_url')) {
5978
            // if the course was unset and the session was set, send directly to the session
5979
            $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
5980
        }
5981
    }
5982
5983
    // if not valid combination was found, return an empty string
5984
    return $url;
5985
}
5986
5987
/**
5988
 * Check if there is more than the default URL defined in the access_url table.
5989
 */
5990
function api_get_multiple_access_url(): bool
5991
{
5992
    return Container::getAccessUrlHelper()->isMultiple();
5993
}
5994
5995
/**
5996
 * @deprecated Use AccessUrlHelper::isMultiple
5997
 */
5998
function api_is_multiple_url_enabled(): bool
5999
{
6000
    return api_get_multiple_access_url();
6001
}
6002
6003
/**
6004
 * Returns a md5 unique id.
6005
 *
6006
 * @todo add more parameters
6007
 */
6008
function api_get_unique_id()
6009
{
6010
    return md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
6011
}
6012
6013
/**
6014
 * @param int Course id
6015
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
6016
 * @param int the item id (tool id, exercise id, lp id)
6017
 *
6018
 * @return bool
6019
 */
6020
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
6021
{
6022
    if (api_is_platform_admin()) {
6023
        return false;
6024
    }
6025
    if ('true' === api_get_setting('gradebook_locking_enabled')) {
6026
        if (empty($course_code)) {
6027
            $course_code = api_get_course_id();
6028
        }
6029
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
6030
        $item_id = (int) $item_id;
6031
        $link_type = (int) $link_type;
6032
        $course_code = Database::escape_string($course_code);
6033
        $sql = "SELECT locked FROM $table
6034
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
6035
        $result = Database::query($sql);
6036
        if (Database::num_rows($result)) {
6037
            return true;
6038
        }
6039
    }
6040
6041
    return false;
6042
}
6043
6044
/**
6045
 * Blocks a page if the item was added in a gradebook.
6046
 *
6047
 * @param int       exercise id, work id, thread id,
6048
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
6049
 * see gradebook/lib/be/linkfactory
6050
 * @param string    course code
6051
 *
6052
 * @return false|null
6053
 */
6054
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
6055
{
6056
    if (api_is_platform_admin()) {
6057
        return false;
6058
    }
6059
6060
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
6061
        $message = Display::return_message(
6062
            get_lang(
6063
                '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.'
6064
            ),
6065
            'warning'
6066
        );
6067
        api_not_allowed(true, $message);
6068
    }
6069
}
6070
6071
/**
6072
 * Checks the PHP version installed is enough to run Chamilo.
6073
 *
6074
 * @param string Include path (used to load the error page)
6075
 */
6076
function api_check_php_version()
6077
{
6078
    if (!function_exists('version_compare') ||
6079
        version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')
6080
    ) {
6081
        throw new Exception('Wrong PHP version');
6082
    }
6083
}
6084
6085
/**
6086
 * Checks whether the Archive directory is present and writeable. If not,
6087
 * prints a warning message.
6088
 */
6089
function api_check_archive_dir()
6090
{
6091
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
6092
        $message = Display::return_message(
6093
            get_lang(
6094
                'The var/cache/ directory, used by this tool, is not writeable. Please contact your platform administrator.'
6095
            ),
6096
            'warning'
6097
        );
6098
        api_not_allowed(true, $message);
6099
    }
6100
}
6101
6102
/**
6103
 * Returns an array of global configuration settings which should be ignored
6104
 * when printing the configuration settings screens.
6105
 *
6106
 * @return array Array of strings, each identifying one of the excluded settings
6107
 */
6108
function api_get_locked_settings()
6109
{
6110
    return [
6111
        'permanently_remove_deleted_files',
6112
        'account_valid_duration',
6113
        'service_ppt2lp',
6114
        'wcag_anysurfer_public_pages',
6115
        'upload_extensions_list_type',
6116
        'upload_extensions_blacklist',
6117
        'upload_extensions_whitelist',
6118
        'upload_extensions_skip',
6119
        'upload_extensions_replace_by',
6120
        'hide_dltt_markup',
6121
        'split_users_upload_directory',
6122
        'permissions_for_new_directories',
6123
        'permissions_for_new_files',
6124
        'platform_charset',
6125
        'ldap_description',
6126
        'cas_activate',
6127
        'cas_server',
6128
        'cas_server_uri',
6129
        'cas_port',
6130
        'cas_protocol',
6131
        'cas_add_user_activate',
6132
        'update_user_info_cas_with_ldap',
6133
        'languagePriority1',
6134
        'languagePriority2',
6135
        'languagePriority3',
6136
        'languagePriority4',
6137
        'login_is_email',
6138
        'chamilo_database_version',
6139
    ];
6140
}
6141
6142
/**
6143
 * Guess the real ip for register in the database, even in reverse proxy cases.
6144
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
6145
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
6146
 * Note: the result of this function is not SQL-safe. Please escape it before
6147
 * inserting in a database.
6148
 *
6149
 * @return string the user's real ip (unsafe - escape it before inserting to db)
6150
 *
6151
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
6152
 *
6153
 * @version CEV CHANGE 24APR2012
6154
 * @throws RuntimeException
6155
 */
6156
function api_get_real_ip(): string
6157
{
6158
    if ('cli' === PHP_SAPI) {
6159
        $ip = '127.0.0.1';
6160
    } else {
6161
        $ip = trim($_SERVER['REMOTE_ADDR']);
6162
        if (empty($ip)) {
6163
            throw new RuntimeException("Unable to retrieve remote IP address.");
6164
        }
6165
    }
6166
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
6167
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
6168
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
6169
        } else {
6170
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
6171
        }
6172
        $ip = trim($ip1);
6173
    }
6174
6175
    return $ip;
6176
}
6177
6178
/**
6179
 * Checks whether an IP is included inside an IP range.
6180
 *
6181
 * @param string IP address
6182
 * @param string IP range
6183
 * @param string $ip
6184
 *
6185
 * @return bool True if IP is in the range, false otherwise
6186
 *
6187
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
6188
 * @author Yannick Warnier for improvements and managment of multiple ranges
6189
 *
6190
 * @todo check for IPv6 support
6191
 */
6192
function api_check_ip_in_range($ip, $range)
6193
{
6194
    if (empty($ip) or empty($range)) {
6195
        return false;
6196
    }
6197
    $ip_ip = ip2long($ip);
6198
    // divide range param into array of elements
6199
    if (false !== strpos($range, ',')) {
6200
        $ranges = explode(',', $range);
6201
    } else {
6202
        $ranges = [$range];
6203
    }
6204
    foreach ($ranges as $range) {
0 ignored issues
show
introduced by
$range is overwriting one of the parameters of this function.
Loading history...
6205
        $range = trim($range);
6206
        if (empty($range)) {
6207
            continue;
6208
        }
6209
        if (false === strpos($range, '/')) {
6210
            if (0 === strcmp($ip, $range)) {
6211
                return true; // there is a direct IP match, return OK
6212
            }
6213
            continue; //otherwise, get to the next range
6214
        }
6215
        // the range contains a "/", so analyse completely
6216
        [$net, $mask] = explode("/", $range);
6217
6218
        $ip_net = ip2long($net);
6219
        // mask binary magic
6220
        $ip_mask = ~((1 << (32 - $mask)) - 1);
6221
6222
        $ip_ip_net = $ip_ip & $ip_mask;
6223
        if ($ip_ip_net == $ip_net) {
6224
            return true;
6225
        }
6226
    }
6227
6228
    return false;
6229
}
6230
6231
function api_check_user_access_to_legal($courseInfo)
6232
{
6233
    if (empty($courseInfo)) {
6234
        return false;
6235
    }
6236
6237
    $visibility = (int) $courseInfo['visibility'];
6238
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
6239
6240
    return
6241
        in_array($visibility, $visibilityList) ||
6242
        api_is_drh() ||
6243
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
6244
}
6245
6246
/**
6247
 * Checks if the global chat is enabled or not.
6248
 *
6249
 * @return bool
6250
 */
6251
function api_is_global_chat_enabled()
6252
{
6253
    return
6254
        !api_is_anonymous() &&
6255
        'true' === api_get_setting('allow_global_chat') &&
6256
        'true' === api_get_setting('allow_social_tool');
6257
}
6258
6259
/**
6260
 * @param int   $item_id
6261
 * @param int   $tool_id
6262
 * @param int   $group_id   id
6263
 * @param array $courseInfo
6264
 * @param int   $sessionId
6265
 * @param int   $userId
6266
 *
6267
 * @deprecated
6268
 */
6269
function api_set_default_visibility(
6270
    $item_id,
6271
    $tool_id,
6272
    $group_id = 0,
6273
    $courseInfo = [],
6274
    $sessionId = 0,
6275
    $userId = 0
6276
) {
6277
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
6278
    $courseId = $courseInfo['real_id'];
6279
    $courseCode = $courseInfo['code'];
6280
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6281
    $userId = empty($userId) ? api_get_user_id() : $userId;
6282
6283
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
6284
    if (is_null($group_id)) {
6285
        $group_id = 0;
6286
    } else {
6287
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
6288
    }
6289
6290
    $groupInfo = [];
6291
    if (!empty($group_id)) {
6292
        $groupInfo = GroupManager::get_group_properties($group_id);
6293
    }
6294
    $original_tool_id = $tool_id;
6295
6296
    switch ($tool_id) {
6297
        case TOOL_LINK:
6298
        case TOOL_LINK_CATEGORY:
6299
            $tool_id = 'links';
6300
            break;
6301
        case TOOL_DOCUMENT:
6302
            $tool_id = 'documents';
6303
            break;
6304
        case TOOL_LEARNPATH:
6305
            $tool_id = 'learning';
6306
            break;
6307
        case TOOL_ANNOUNCEMENT:
6308
            $tool_id = 'announcements';
6309
            break;
6310
        case TOOL_FORUM:
6311
        case TOOL_FORUM_CATEGORY:
6312
        case TOOL_FORUM_THREAD:
6313
            $tool_id = 'forums';
6314
            break;
6315
        case TOOL_QUIZ:
6316
            $tool_id = 'quiz';
6317
            break;
6318
    }
6319
    $setting = api_get_setting('tool_visible_by_default_at_creation');
6320
6321
    if (isset($setting[$tool_id])) {
6322
        $visibility = 'invisible';
6323
        if ('true' === $setting[$tool_id]) {
6324
            $visibility = 'visible';
6325
        }
6326
6327
        // Read the portal and course default visibility
6328
        if ('documents' === $tool_id) {
6329
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
6330
        }
6331
6332
        // Fixes default visibility for tests
6333
        switch ($original_tool_id) {
6334
            case TOOL_QUIZ:
6335
                if (empty($sessionId)) {
6336
                    $objExerciseTmp = new Exercise($courseId);
6337
                    $objExerciseTmp->read($item_id);
6338
                    if ('visible' === $visibility) {
6339
                        $objExerciseTmp->enable();
6340
                        $objExerciseTmp->save();
6341
                    } else {
6342
                        $objExerciseTmp->disable();
6343
                        $objExerciseTmp->save();
6344
                    }
6345
                }
6346
                break;
6347
        }
6348
    }
6349
}
6350
6351
function api_get_roles()
6352
{
6353
    $hierarchy = Container::$container->getParameter('security.role_hierarchy.roles');
6354
    $roles = [];
6355
    array_walk_recursive($hierarchy, function ($role) use (&$roles) {
6356
        $roles[$role] = $role;
6357
    });
6358
6359
    return $roles;
6360
}
6361
6362
function api_get_user_roles(): array
6363
{
6364
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
6365
6366
    $roles = $permissionService->getUserRoles();
6367
6368
    return array_combine($roles, $roles);
6369
}
6370
6371
/**
6372
 * @param string $file
6373
 *
6374
 * @return string
6375
 */
6376
function api_get_js_simple($file)
6377
{
6378
    return '<script type="text/javascript" src="'.$file.'"></script>'."\n";
6379
}
6380
6381
/**
6382
 * Modify default memory_limit and max_execution_time limits
6383
 * Needed when processing long tasks.
6384
 */
6385
function api_set_more_memory_and_time_limits()
6386
{
6387
    if (function_exists('ini_set')) {
6388
        api_set_memory_limit('256M');
6389
        ini_set('max_execution_time', 1800);
6390
    }
6391
}
6392
6393
/**
6394
 * Tries to set memory limit, if authorized and new limit is higher than current.
6395
 *
6396
 * @param string $mem New memory limit
6397
 *
6398
 * @return bool True on success, false on failure or current is higher than suggested
6399
 * @assert (null) === false
6400
 * @assert (-1) === false
6401
 * @assert (0) === true
6402
 * @assert ('1G') === true
6403
 */
6404
function api_set_memory_limit($mem)
6405
{
6406
    //if ini_set() not available, this function is useless
6407
    if (!function_exists('ini_set') || is_null($mem) || -1 == $mem) {
6408
        return false;
6409
    }
6410
6411
    $memory_limit = ini_get('memory_limit');
6412
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
6413
        ini_set('memory_limit', $mem);
6414
6415
        return true;
6416
    }
6417
6418
    return false;
6419
}
6420
6421
/**
6422
 * Gets memory limit in bytes.
6423
 *
6424
 * @param string The memory size (128M, 1G, 1000K, etc)
6425
 *
6426
 * @return int
6427
 * @assert (null) === false
6428
 * @assert ('1t')  === 1099511627776
6429
 * @assert ('1g')  === 1073741824
6430
 * @assert ('1m')  === 1048576
6431
 * @assert ('100k') === 102400
6432
 */
6433
function api_get_bytes_memory_limit($mem)
6434
{
6435
    $size = strtolower(substr($mem, -1));
6436
6437
    switch ($size) {
6438
        case 't':
6439
            $mem = (int) substr($mem, -1) * 1024 * 1024 * 1024 * 1024;
6440
            break;
6441
        case 'g':
6442
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024 * 1024;
6443
            break;
6444
        case 'm':
6445
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024;
6446
            break;
6447
        case 'k':
6448
            $mem = (int) substr($mem, 0, -1) * 1024;
6449
            break;
6450
        default:
6451
            // we assume it's integer only
6452
            $mem = (int) $mem;
6453
            break;
6454
    }
6455
6456
    return $mem;
6457
}
6458
6459
/**
6460
 * Finds all the information about a user from username instead of user id.
6461
 *
6462
 * @param string $officialCode
6463
 *
6464
 * @return array $user_info user_id, lastname, firstname, username, email, ...
6465
 *
6466
 * @author Yannick Warnier <[email protected]>
6467
 */
6468
function api_get_user_info_from_official_code($officialCode)
6469
{
6470
    if (empty($officialCode)) {
6471
        return false;
6472
    }
6473
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
6474
            WHERE official_code ='".Database::escape_string($officialCode)."'";
6475
    $result = Database::query($sql);
6476
    if (Database::num_rows($result) > 0) {
6477
        $result_array = Database::fetch_array($result);
6478
6479
        return _api_format_user($result_array);
6480
    }
6481
6482
    return false;
6483
}
6484
6485
/**
6486
 * @param string $usernameInputId
6487
 * @param string $passwordInputId
6488
 *
6489
 * @return string|null
6490
 */
6491
function api_get_password_checker_js($usernameInputId, $passwordInputId)
6492
{
6493
    $checkPass = api_get_setting('allow_strength_pass_checker');
6494
    $useStrengthPassChecker = 'true' === $checkPass;
6495
6496
    if (false === $useStrengthPassChecker) {
6497
        return null;
6498
    }
6499
6500
    $minRequirements = Security::getPasswordRequirements()['min'];
6501
6502
    $options = [
6503
        'rules' => [],
6504
    ];
6505
6506
    if ($minRequirements['length'] > 0) {
6507
        $options['rules'][] = [
6508
            'minChar' => $minRequirements['length'],
6509
            'pattern' => '.',
6510
            'helpText' => sprintf(
6511
                get_lang('Minimum %s characters in total'),
6512
                $minRequirements['length']
6513
            ),
6514
        ];
6515
    }
6516
6517
    if ($minRequirements['lowercase'] > 0) {
6518
        $options['rules'][] = [
6519
            'minChar' => $minRequirements['lowercase'],
6520
            'pattern' => '[a-z]',
6521
            'helpText' => sprintf(
6522
                get_lang('Minimum %s lowercase characters'),
6523
                $minRequirements['lowercase']
6524
            ),
6525
        ];
6526
    }
6527
6528
    if ($minRequirements['uppercase'] > 0) {
6529
        $options['rules'][] = [
6530
            'minChar' => $minRequirements['uppercase'],
6531
            'pattern' => '[A-Z]',
6532
            'helpText' => sprintf(
6533
                get_lang('Minimum %s uppercase characters'),
6534
                $minRequirements['uppercase']
6535
            ),
6536
        ];
6537
    }
6538
6539
    if ($minRequirements['numeric'] > 0) {
6540
        $options['rules'][] = [
6541
            'minChar' => $minRequirements['numeric'],
6542
            'pattern' => '[0-9]',
6543
            'helpText' => sprintf(
6544
                get_lang('Minimum %s numerical (0-9) characters'),
6545
                $minRequirements['numeric']
6546
            ),
6547
        ];
6548
    }
6549
6550
    if ($minRequirements['specials'] > 0) {
6551
        $options['rules'][] = [
6552
            'minChar' => $minRequirements['specials'],
6553
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
6554
            'helpText' => sprintf(
6555
                get_lang('Minimum %s special characters'),
6556
                $minRequirements['specials']
6557
            ),
6558
        ];
6559
    }
6560
6561
    $js = api_get_js('password-checker/password-checker.js');
6562
    $js .= "<script>
6563
    $(function() {
6564
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
6565
    });
6566
    </script>";
6567
6568
    return $js;
6569
}
6570
6571
/**
6572
 * create an user extra field called 'captcha_blocked_until_date'.
6573
 *
6574
 * @param string $username
6575
 *
6576
 * @return bool
6577
 */
6578
function api_block_account_captcha($username)
6579
{
6580
    $userInfo = api_get_user_info_from_username($username);
6581
    if (empty($userInfo)) {
6582
        return false;
6583
    }
6584
    $minutesToBlock = api_get_setting('captcha_time_to_block');
6585
    $time = time() + $minutesToBlock * 60;
6586
    UserManager::update_extra_field_value(
6587
        $userInfo['user_id'],
6588
        'captcha_blocked_until_date',
6589
        api_get_utc_datetime($time)
6590
    );
6591
6592
    return true;
6593
}
6594
6595
/**
6596
 * @param string $username
6597
 *
6598
 * @return bool
6599
 */
6600
function api_clean_account_captcha($username)
6601
{
6602
    $userInfo = api_get_user_info_from_username($username);
6603
    if (empty($userInfo)) {
6604
        return false;
6605
    }
6606
    Session::erase('loginFailedCount');
6607
    UserManager::update_extra_field_value(
6608
        $userInfo['user_id'],
6609
        'captcha_blocked_until_date',
6610
        null
6611
    );
6612
6613
    return true;
6614
}
6615
6616
/**
6617
 * @param string $username
6618
 *
6619
 * @return bool
6620
 */
6621
function api_get_user_blocked_by_captcha($username)
6622
{
6623
    $userInfo = api_get_user_info_from_username($username);
6624
    if (empty($userInfo)) {
6625
        return false;
6626
    }
6627
    $data = UserManager::get_extra_user_data_by_field(
6628
        $userInfo['user_id'],
6629
        'captcha_blocked_until_date'
6630
    );
6631
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
6632
        return $data['captcha_blocked_until_date'];
6633
    }
6634
6635
    return false;
6636
}
6637
6638
/**
6639
 * If true, the drh can access all content (courses, users) inside a session.
6640
 *
6641
 * @return bool
6642
 */
6643
function api_drh_can_access_all_session_content()
6644
{
6645
    return 'true' === api_get_setting('drh_can_access_all_session_content');
6646
}
6647
6648
/**
6649
 * Checks if user can login as another user.
6650
 *
6651
 * @param int $loginAsUserId the user id to log in
6652
 * @param int $userId        my user id
6653
 *
6654
 * @return bool
6655
 */
6656
function api_can_login_as($loginAsUserId, $userId = null)
6657
{
6658
    $loginAsUserId = (int) $loginAsUserId;
6659
6660
    if (empty($loginAsUserId)) {
6661
        return false;
6662
    }
6663
6664
    if (empty($userId)) {
6665
        $userId = api_get_user_id();
6666
    }
6667
6668
    if ($loginAsUserId == $userId) {
6669
        return false;
6670
    }
6671
6672
    // Check if the user to login is an admin
6673
    if (api_is_platform_admin_by_id($loginAsUserId)) {
6674
        // Only super admins can login to admin accounts
6675
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
6676
            return false;
6677
        }
6678
    }
6679
6680
    $userInfo = api_get_user_info($loginAsUserId);
6681
6682
    $isDrh = function () use ($loginAsUserId) {
6683
        if (api_is_drh()) {
6684
            if (api_drh_can_access_all_session_content()) {
6685
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
6686
                    'drh_all',
6687
                    api_get_user_id()
6688
                );
6689
                $userList = [];
6690
                if (is_array($users)) {
6691
                    foreach ($users as $user) {
6692
                        $userList[] = $user['id'];
6693
                    }
6694
                }
6695
                if (in_array($loginAsUserId, $userList)) {
6696
                    return true;
6697
                }
6698
            } else {
6699
                if (api_is_drh() &&
6700
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
6701
                ) {
6702
                    return true;
6703
                }
6704
            }
6705
        }
6706
6707
        return false;
6708
    };
6709
6710
    $loginAsStatusForSessionAdmins = [STUDENT];
6711
6712
    if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
6713
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
6714
    }
6715
6716
    return api_is_platform_admin() ||
6717
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
6718
        $isDrh();
6719
}
6720
6721
/**
6722
 * Return true on https install.
6723
 *
6724
 * @return bool
6725
 */
6726
function api_is_https()
6727
{
6728
    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...
6729
        'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] || !empty(api_get_configuration_value('force_https_forwarded_proto'))
6730
    ) {
6731
        $isSecured = true;
6732
    } else {
6733
        if (!empty($_SERVER['HTTPS']) && 'off' != $_SERVER['HTTPS']) {
6734
            $isSecured = true;
6735
        } else {
6736
            $isSecured = false;
6737
            // last chance
6738
            if (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) {
6739
                $isSecured = true;
6740
            }
6741
        }
6742
    }
6743
6744
    return $isSecured;
6745
}
6746
6747
/**
6748
 * Return protocol (http or https).
6749
 *
6750
 * @return string
6751
 */
6752
function api_get_protocol()
6753
{
6754
    return api_is_https() ? 'https' : 'http';
6755
}
6756
6757
/**
6758
 * Get origin.
6759
 *
6760
 * @param string
6761
 *
6762
 * @return string
6763
 */
6764
function api_get_origin()
6765
{
6766
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
6767
}
6768
6769
/**
6770
 * Warns an user that the portal reach certain limit.
6771
 *
6772
 * @param string $limitName
6773
 */
6774
function api_warn_hosting_contact($limitName)
6775
{
6776
    $hostingParams = api_get_configuration_value(1);
6777
    $email = null;
6778
6779
    if (!empty($hostingParams)) {
6780
        if (isset($hostingParams['hosting_contact_mail'])) {
6781
            $email = $hostingParams['hosting_contact_mail'];
6782
        }
6783
    }
6784
6785
    if (!empty($email)) {
6786
        $subject = get_lang('Hosting warning reached');
6787
        $body = get_lang('Portal name').': '.api_get_path(WEB_PATH)." \n ";
6788
        $body .= get_lang('Portal\'s limit type').': '.$limitName." \n ";
6789
        if (isset($hostingParams[$limitName])) {
6790
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
6791
        }
6792
        api_mail_html(null, $email, $subject, $body);
6793
    }
6794
}
6795
6796
/**
6797
 * Gets value of a variable from config/configuration.php
6798
 * Variables that are not set in the configuration.php file but set elsewhere:
6799
 * - virtual_css_theme_folder (vchamilo plugin)
6800
 * - access_url (global.inc.php)
6801
 * - apc/apc_prefix (global.inc.php).
6802
 *
6803
 * @param string $variable
6804
 *
6805
 * @return bool|mixed
6806
 */
6807
function api_get_configuration_value($variable)
6808
{
6809
    global $_configuration;
6810
    // Check the current url id, id = 1 by default
6811
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
6812
6813
    $variable = trim($variable);
6814
6815
    // Check if variable exists
6816
    if (isset($_configuration[$variable])) {
6817
        if (is_array($_configuration[$variable])) {
6818
            // Check if it exists for the sub portal
6819
            if (array_key_exists($urlId, $_configuration[$variable])) {
6820
                return $_configuration[$variable][$urlId];
6821
            } else {
6822
                // Try to found element with id = 1 (master portal)
6823
                if (array_key_exists(1, $_configuration[$variable])) {
6824
                    return $_configuration[$variable][1];
6825
                }
6826
            }
6827
        }
6828
6829
        return $_configuration[$variable];
6830
    }
6831
6832
    return false;
6833
}
6834
6835
/**
6836
 * Gets a specific hosting limit.
6837
 *
6838
 * @param int $urlId The URL ID.
6839
 * @param string $limitName The name of the limit.
6840
 * @return mixed The value of the limit, or null if not found.
6841
 */
6842
function get_hosting_limit(int $urlId, string $limitName): mixed
6843
{
6844
    if (!Container::$container->hasParameter('settings_overrides')) {
6845
        return [];
6846
    }
6847
6848
    $settingsOverrides = Container::$container->getParameter('settings_overrides');
6849
6850
    $limits = $settingsOverrides[$urlId]['hosting_limit'] ?? $settingsOverrides['default']['hosting_limit'];
6851
6852
    foreach ($limits as $limitArray) {
6853
        if (isset($limitArray[$limitName])) {
6854
            return $limitArray[$limitName];
6855
        }
6856
    }
6857
6858
    return null;
6859
}
6860
6861
6862
/**
6863
 * Retrieves an environment variable value with validation and handles boolean conversion.
6864
 *
6865
 * @param string $variable The name of the environment variable.
6866
 * @param mixed $default The default value to return if the variable is not set.
6867
 * @return mixed The value of the environment variable, converted to boolean if necessary, or the default value.
6868
 */
6869
function api_get_env_variable(string $variable, mixed $default = null): mixed
6870
{
6871
    $variable = strtolower($variable);
6872
    if (Container::$container->hasParameter($variable)) {
6873
        return Container::$container->getParameter($variable);
6874
    }
6875
6876
    return $default;
6877
}
6878
6879
/**
6880
 * Retreives and returns a value in a hierarchical configuration array
6881
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
6882
 *
6883
 * @param string $path      the successive array keys, separated by the separator
6884
 * @param mixed  $default   value to be returned if not found, null by default
6885
 * @param string $separator '/' by default
6886
 * @param array  $array     the active configuration array by default
6887
 *
6888
 * @return mixed the found value or $default
6889
 */
6890
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
6891
{
6892
    $pos = strpos($path, $separator);
6893
    if (false === $pos) {
6894
        if (is_null($array)) {
6895
            return api_get_configuration_value($path);
6896
        }
6897
        if (is_array($array) && array_key_exists($path, $array)) {
6898
            return $array[$path];
6899
        }
6900
6901
        return $default;
6902
    }
6903
    $key = substr($path, 0, $pos);
6904
    if (is_null($array)) {
6905
        $newArray = api_get_configuration_value($key);
6906
    } elseif (is_array($array) && array_key_exists($key, $array)) {
6907
        $newArray = $array[$key];
6908
    } else {
6909
        return $default;
6910
    }
6911
    if (is_array($newArray)) {
6912
        $newPath = substr($path, $pos + 1);
6913
6914
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
6915
    }
6916
6917
    return $default;
6918
}
6919
6920
/**
6921
 * Retrieves and returns a value in a hierarchical configuration array
6922
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
6923
 *
6924
 * @param array  $array     the recursive array that contains the value to be returned (or not)
6925
 * @param string $path      the successive array keys, separated by the separator
6926
 * @param mixed  $default   the value to be returned if not found
6927
 * @param string $separator the separator substring
6928
 *
6929
 * @return mixed the found value or $default
6930
 */
6931
function api_array_sub_value($array, $path, $default = null, $separator = '/')
6932
{
6933
    $pos = strpos($path, $separator);
6934
    if (false === $pos) {
6935
        if (is_array($array) && array_key_exists($path, $array)) {
6936
            return $array[$path];
6937
        }
6938
6939
        return $default;
6940
    }
6941
    $key = substr($path, 0, $pos);
6942
    if (is_array($array) && array_key_exists($key, $array)) {
6943
        $newArray = $array[$key];
6944
    } else {
6945
        return $default;
6946
    }
6947
    if (is_array($newArray)) {
6948
        $newPath = substr($path, $pos + 1);
6949
6950
        return api_array_sub_value($newArray, $newPath, $default);
6951
    }
6952
6953
    return $default;
6954
}
6955
6956
/**
6957
 * Returns supported image extensions in the portal.
6958
 *
6959
 * @param bool $supportVectors Whether vector images should also be accepted or not
6960
 *
6961
 * @return array Supported image extensions in the portal
6962
 */
6963
function api_get_supported_image_extensions($supportVectors = true)
6964
{
6965
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
6966
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
6967
    if ($supportVectors) {
6968
        array_push($supportedImageExtensions, 'svg');
6969
    }
6970
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
6971
        array_push($supportedImageExtensions, 'webp');
6972
    }
6973
6974
    return $supportedImageExtensions;
6975
}
6976
6977
/**
6978
 * This setting changes the registration status for the campus.
6979
 *
6980
 * @author Patrick Cool <[email protected]>, Ghent University
6981
 *
6982
 * @version August 2006
6983
 *
6984
 * @param bool $listCampus Whether we authorize
6985
 *
6986
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
6987
 */
6988
function api_register_campus($listCampus = true)
6989
{
6990
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
6991
6992
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
6993
    Database::query($sql);
6994
6995
    if (!$listCampus) {
6996
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
6997
        Database::query($sql);
6998
    }
6999
}
7000
7001
/**
7002
 * Check whether the user type should be exclude.
7003
 * Such as invited or anonymous users.
7004
 *
7005
 * @param bool $checkDB Optional. Whether check the user status
7006
 * @param int  $userId  Options. The user id
7007
 *
7008
 * @return bool
7009
 */
7010
function api_is_excluded_user_type($checkDB = false, $userId = 0)
7011
{
7012
    if ($checkDB) {
7013
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
7014
7015
        if (0 == $userId) {
7016
            return true;
7017
        }
7018
7019
        $userInfo = api_get_user_info($userId);
7020
7021
        switch ($userInfo['status']) {
7022
            case INVITEE:
7023
            case ANONYMOUS:
7024
                return true;
7025
            default:
7026
                return false;
7027
        }
7028
    }
7029
7030
    $isInvited = api_is_invitee();
7031
    $isAnonymous = api_is_anonymous();
7032
7033
    if ($isInvited || $isAnonymous) {
7034
        return true;
7035
    }
7036
7037
    return false;
7038
}
7039
7040
/**
7041
 * Get the user status to ignore in reports.
7042
 *
7043
 * @param string $format Optional. The result type (array or string)
7044
 *
7045
 * @return array|string
7046
 */
7047
function api_get_users_status_ignored_in_reports($format = 'array')
7048
{
7049
    $excludedTypes = [
7050
        INVITEE,
7051
        ANONYMOUS,
7052
    ];
7053
7054
    if ('string' == $format) {
7055
        return implode(', ', $excludedTypes);
7056
    }
7057
7058
    return $excludedTypes;
7059
}
7060
7061
/**
7062
 * Set the Site Use Cookie Warning for 1 year.
7063
 */
7064
function api_set_site_use_cookie_warning_cookie()
7065
{
7066
    setcookie('ChamiloUsesCookies', 'ok', time() + 31556926);
7067
}
7068
7069
/**
7070
 * Return true if the Site Use Cookie Warning Cookie warning exists.
7071
 *
7072
 * @return bool
7073
 */
7074
function api_site_use_cookie_warning_cookie_exist()
7075
{
7076
    return isset($_COOKIE['ChamiloUsesCookies']);
7077
}
7078
7079
/**
7080
 * Given a number of seconds, format the time to show hours, minutes and seconds.
7081
 *
7082
 * @param int    $time         The time in seconds
7083
 * @param string $originFormat Optional. PHP o JS
7084
 *
7085
 * @return string (00h00'00")
7086
 */
7087
function api_format_time($time, $originFormat = 'php')
7088
{
7089
    $h = get_lang('h');
7090
    $hours = $time / 3600;
7091
    $mins = ($time % 3600) / 60;
7092
    $secs = ($time % 60);
7093
7094
    if ($time < 0) {
7095
        $hours = 0;
7096
        $mins = 0;
7097
        $secs = 0;
7098
    }
7099
7100
    if ('js' === $originFormat) {
7101
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
7102
    } else {
7103
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
7104
    }
7105
7106
    return $formattedTime;
7107
}
7108
7109
function api_set_noreply_and_from_address_to_mailer(
7110
    TemplatedEmail $email,
7111
    array $sender,
7112
    array $replyToAddress = []
7113
): void {
7114
    $validator = Container::getLegacyHelper()->getValidator();
7115
    $emailConstraint = new Assert\Email();
7116
7117
    $noReplyAddress = api_get_setting('noreply_email_address');
7118
    $avoidReplyToAddress = false;
7119
7120
    if (!empty($noReplyAddress)) {
7121
        // $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
7122
    }
7123
7124
    // Default values
7125
    $notification = new Notification();
7126
    $defaultSenderName = $notification->getDefaultPlatformSenderName();
7127
    $defaultSenderEmail = $notification->getDefaultPlatformSenderEmail();
7128
7129
    // If the parameter is set don't use the admin.
7130
    $senderName = !empty($sender['name']) ? $sender['name'] : $defaultSenderName;
7131
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $defaultSenderEmail;
7132
7133
    // Send errors to the platform admin
7134
    $adminEmail = api_get_setting('admin.administrator_email');
7135
7136
    $adminEmailValidation = $validator->validate($adminEmail, $emailConstraint);
7137
7138
    if (!empty($adminEmail) && 0 === $adminEmailValidation->count()) {
7139
        $email
7140
            ->getHeaders()
7141
            ->addIdHeader('Errors-To', $adminEmail)
7142
        ;
7143
    }
7144
7145
    if (!$avoidReplyToAddress && !empty($replyToAddress)) {
7146
        $replyToEmailValidation = $validator->validate($replyToAddress['mail'], $emailConstraint);
7147
7148
        if (0 === $replyToEmailValidation->count()) {
7149
            $email->addReplyTo(new Address($replyToAddress['mail'], $replyToAddress['name']));
7150
        }
7151
    }
7152
7153
    if ('true' === api_get_setting('mail.smtp_unique_sender')) {
7154
        $senderName = $defaultSenderName;
7155
        $senderEmail = $defaultSenderEmail;
7156
7157
        $email->sender(new Address($senderEmail, $senderName));
7158
    }
7159
7160
    if ($senderEmail) {
7161
        $email->from(new Address($senderEmail, $senderName));
7162
    }
7163
}
7164
7165
/**
7166
 * Sends an email
7167
 * Sender name and email can be specified, if not specified
7168
 * name and email of the platform admin are used.
7169
 *
7170
 * @param string    name of recipient
7171
 * @param string    email of recipient
7172
 * @param string    email subject
7173
 * @param string    email body
7174
 * @param string    sender name
7175
 * @param string    sender e-mail
7176
 * @param array     extra headers in form $headers = array($name => $value) to allow parsing
7177
 * @param array     data file (path and filename)
7178
 * @param bool      True for attaching a embedded file inside content html (optional)
7179
 * @param array     Additional parameters
7180
 *
7181
 * @return bool true if mail was sent
7182
 */
7183
function api_mail_html(
7184
    $recipientName,
7185
    $recipientEmail,
7186
    $subject,
7187
    $body,
7188
    $senderName = '',
7189
    $senderEmail = '',
7190
    $extra_headers = [],
7191
    $data_file = [],
7192
    $embeddedImage = false,
7193
    $additionalParameters = [],
7194
    string $sendErrorTo = null
7195
) {
7196
    $mailHelper = Container::$container->get(MailHelper::class);
7197
7198
    return $mailHelper->send(
7199
        $recipientName,
7200
        $recipientEmail,
7201
        $subject,
7202
        $body,
7203
        $senderName,
7204
        $senderEmail,
7205
        $extra_headers,
7206
        $data_file,
7207
        $embeddedImage,
7208
        $additionalParameters,
7209
        $sendErrorTo
7210
    );
7211
}
7212
7213
/**
7214
 * @param int  $tool       Possible values: GroupManager::GROUP_TOOL_*
7215
 * @param bool $showHeader
7216
 */
7217
function api_protect_course_group($tool, $showHeader = true)
7218
{
7219
    $groupId = api_get_group_id();
7220
    if (!empty($groupId)) {
7221
        if (api_is_platform_admin()) {
7222
            return true;
7223
        }
7224
7225
        if (api_is_allowed_to_edit(false, true, true)) {
7226
            return true;
7227
        }
7228
7229
        $userId = api_get_user_id();
7230
        $sessionId = api_get_session_id();
7231
        if (!empty($sessionId)) {
7232
            if (api_is_coach($sessionId, api_get_course_int_id())) {
7233
                return true;
7234
            }
7235
7236
            if (api_is_drh()) {
7237
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
7238
                    return true;
7239
                }
7240
            }
7241
        }
7242
7243
        $group = api_get_group_entity($groupId);
7244
7245
        // Group doesn't exists
7246
        if (null === $group) {
7247
            api_not_allowed($showHeader);
7248
        }
7249
7250
        // Check group access
7251
        $allow = GroupManager::userHasAccess(
7252
            $userId,
7253
            $group,
7254
            $tool
7255
        );
7256
7257
        if (!$allow) {
7258
            api_not_allowed($showHeader);
7259
        }
7260
    }
7261
7262
    return false;
7263
}
7264
7265
/**
7266
 * Check if a date is in a date range.
7267
 *
7268
 * @param datetime $startDate
7269
 * @param datetime $endDate
7270
 * @param datetime $currentDate
7271
 *
7272
 * @return bool true if date is in rage, false otherwise
7273
 */
7274
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
7275
{
7276
    $startDate = strtotime(api_get_local_time($startDate));
7277
    $endDate = strtotime(api_get_local_time($endDate));
7278
    $currentDate = strtotime(api_get_local_time($currentDate));
7279
7280
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
7281
        return true;
7282
    }
7283
7284
    return false;
7285
}
7286
7287
/**
7288
 * Eliminate the duplicates of a multidimensional array by sending the key.
7289
 *
7290
 * @param array $array multidimensional array
7291
 * @param int   $key   key to find to compare
7292
 *
7293
 * @return array
7294
 */
7295
function api_unique_multidim_array($array, $key)
7296
{
7297
    $temp_array = [];
7298
    $i = 0;
7299
    $key_array = [];
7300
7301
    foreach ($array as $val) {
7302
        if (!in_array($val[$key], $key_array)) {
7303
            $key_array[$i] = $val[$key];
7304
            $temp_array[$i] = $val;
7305
        }
7306
        $i++;
7307
    }
7308
7309
    return $temp_array;
7310
}
7311
7312
/**
7313
 * Limit the access to Session Admins when the limit_session_admin_role
7314
 * configuration variable is set to true.
7315
 */
7316
function api_protect_limit_for_session_admin()
7317
{
7318
    $limitAdmin = api_get_setting('limit_session_admin_role');
7319
    if (api_is_session_admin() && 'true' === $limitAdmin) {
7320
        api_not_allowed(true);
7321
    }
7322
}
7323
7324
/**
7325
 * Limits that a session admin has access to list users.
7326
 * When limit_session_admin_list_users configuration variable is set to true.
7327
 */
7328
function api_protect_session_admin_list_users()
7329
{
7330
    $limitAdmin = ('true' === api_get_setting('session.limit_session_admin_list_users'));
7331
7332
    if (api_is_session_admin() && true === $limitAdmin) {
7333
        api_not_allowed(true);
7334
    }
7335
}
7336
7337
/**
7338
 * @return bool
7339
 */
7340
function api_is_student_view_active(): bool
7341
{
7342
    $studentView = Session::read('studentview');
7343
7344
    return 'studentview' === $studentView;
7345
}
7346
7347
/**
7348
 * Converts string value to float value.
7349
 *
7350
 * 3.141516 => 3.141516
7351
 * 3,141516 => 3.141516
7352
 *
7353
 * @todo WIP
7354
 *
7355
 * @param string $number
7356
 *
7357
 * @return float
7358
 */
7359
function api_float_val($number)
7360
{
7361
    return (float) str_replace(',', '.', trim($number));
7362
}
7363
7364
/**
7365
 * Converts float values
7366
 * Example if $decimals = 2.
7367
 *
7368
 * 3.141516 => 3.14
7369
 * 3,141516 => 3,14
7370
 *
7371
 * @param string $number            number in iso code
7372
 * @param int    $decimals
7373
 * @param string $decimalSeparator
7374
 * @param string $thousandSeparator
7375
 *
7376
 * @return bool|string
7377
 */
7378
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
7379
{
7380
    $number = api_float_val($number);
7381
7382
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
7383
}
7384
7385
/**
7386
 * Set location url with a exit break by default.
7387
 *
7388
 * @param string $url
7389
 * @param bool   $exit
7390
 */
7391
function api_location($url, $exit = true)
7392
{
7393
    header('Location: '.$url);
7394
7395
    if ($exit) {
7396
        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...
7397
    }
7398
}
7399
7400
/**
7401
 * @param string $from
7402
 * @param string $to
7403
 *
7404
 * @return string
7405
 */
7406
function api_get_relative_path($from, $to)
7407
{
7408
    // some compatibility fixes for Windows paths
7409
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
7410
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
7411
    $from = str_replace('\\', '/', $from);
7412
    $to = str_replace('\\', '/', $to);
7413
7414
    $from = explode('/', $from);
7415
    $to = explode('/', $to);
7416
    $relPath = $to;
7417
7418
    foreach ($from as $depth => $dir) {
7419
        // find first non-matching dir
7420
        if ($dir === $to[$depth]) {
7421
            // ignore this directory
7422
            array_shift($relPath);
7423
        } else {
7424
            // get number of remaining dirs to $from
7425
            $remaining = count($from) - $depth;
7426
            if ($remaining > 1) {
7427
                // add traversals up to first matching dir
7428
                $padLength = (count($relPath) + $remaining - 1) * -1;
7429
                $relPath = array_pad($relPath, $padLength, '..');
7430
                break;
7431
            } else {
7432
                $relPath[0] = './'.$relPath[0];
7433
            }
7434
        }
7435
    }
7436
7437
    return implode('/', $relPath);
7438
}
7439
7440
/**
7441
 * @param string $template
7442
 *
7443
 * @return string
7444
 */
7445
function api_find_template($template)
7446
{
7447
    return Template::findTemplateFilePath($template);
7448
}
7449
7450
/**
7451
 * @return array
7452
 */
7453
function api_get_language_list_for_flag()
7454
{
7455
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
7456
    $sql = "SELECT english_name, isocode FROM $table
7457
            ORDER BY original_name ASC";
7458
    static $languages = [];
7459
    if (empty($languages)) {
7460
        $result = Database::query($sql);
7461
        while ($row = Database::fetch_array($result)) {
7462
            $languages[$row['english_name']] = $row['isocode'];
7463
        }
7464
        $languages['english'] = 'gb';
7465
    }
7466
7467
    return $languages;
7468
}
7469
7470
function api_create_zip(string $name): ZipStream
7471
{
7472
    $zipStreamOptions = new Archive();
7473
    $zipStreamOptions->setSendHttpHeaders(true);
7474
    $zipStreamOptions->setContentDisposition('attachment');
7475
    $zipStreamOptions->setContentType('application/x-zip');
7476
7477
    return new ZipStream($name, $zipStreamOptions);
7478
}
7479
7480
function api_get_language_translate_html(): string
7481
{
7482
    $translate = 'true' === api_get_setting('editor.translate_html');
7483
7484
    if (!$translate) {
7485
        return '';
7486
    }
7487
7488
    /*$languageList = api_get_languages();
7489
    $hideAll = '';
7490
    foreach ($languageList as $isocode => $name) {
7491
        $hideAll .= '
7492
        $(".mce-translatehtml").hide();
7493
        $("span:lang('.$isocode.')").filter(
7494
            function(e, val) {
7495
                // Only find the spans if they have set the lang
7496
                if ($(this).attr("lang") == null) {
7497
                    return false;
7498
                }
7499
                // Ignore ckeditor classes
7500
                return !this.className.match(/cke(.*)/);
7501
        }).hide();'."\n";
7502
    }*/
7503
7504
    $userInfo = api_get_user_info();
7505
    if (!empty($userInfo['language'])) {
7506
        $isoCode = $userInfo['language'];
7507
7508
        return '
7509
            $(function() {
7510
                $(".mce-translatehtml").hide();
7511
                var defaultLanguageFromUser = "'.$isoCode.'";
7512
                $("span:lang('.$isoCode.')").show();
7513
            });
7514
        ';
7515
    }
7516
7517
    return '';
7518
}
7519
7520
function api_protect_webservices()
7521
{
7522
    if (api_get_configuration_value('disable_webservices')) {
7523
        echo "Webservices are disabled. \n";
7524
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
7525
        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...
7526
    }
7527
}
7528
7529
function api_filename_has_blacklisted_stream_wrapper(string $filename) {
7530
    if (strpos($filename, '://') > 0) {
7531
        $wrappers = stream_get_wrappers();
7532
        $allowedWrappers = ['http', 'https', 'file'];
7533
        foreach ($wrappers as $wrapper) {
7534
            if (in_array($wrapper, $allowedWrappers)) {
7535
                continue;
7536
            }
7537
            if (stripos($filename, $wrapper . '://') === 0) {
7538
                return true;
7539
            }
7540
        }
7541
    }
7542
    return false;
7543
}
7544
7545
/**
7546
 * Checks if a set of roles have a specific permission.
7547
 *
7548
 * @param string $permissionSlug The slug of the permission to check.
7549
 * @param array  $roles          An array of role codes to check against.
7550
 * @return bool True if any of the roles have the permission, false otherwise.
7551
 */
7552
function api_get_permission(string $permissionSlug, array $roles): bool
7553
{
7554
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
7555
7556
    return $permissionService->hasPermission($permissionSlug, $roles);
7557
}
7558
7559
/**
7560
 * Calculate the percentage of change between two numbers.
7561
 *
7562
 * @param int $newValue
7563
 * @param int $oldValue
7564
 * @return string
7565
 */
7566
function api_calculate_increment_percent(int $newValue, int $oldValue): string
7567
{
7568
    if ($oldValue <= 0) {
7569
        $result = " - ";
7570
    } else {
7571
        $result = ' '.round(100 * (($newValue / $oldValue) - 1), 2).' %';
7572
    }
7573
    return $result;
7574
}
7575