Passed
Pull Request — master (#6042)
by Yannick
08:29
created

api_calculate_increment_percent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 8
rs 10
c 1
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\AccessUrl;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\Language;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\SettingsCurrent;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\UserCourseCategory;
12
use Chamilo\CoreBundle\Exception\NotAllowedException;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
15
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
16
use Chamilo\CoreBundle\ServiceHelper\PermissionServiceHelper;
17
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
18
use Chamilo\CourseBundle\Entity\CGroup;
19
use Chamilo\CourseBundle\Entity\CLp;
20
use ChamiloSession as Session;
21
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
22
use Symfony\Component\Finder\Finder;
23
use Symfony\Component\Mime\Address;
24
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Validator\Constraints as Assert;
27
use Symfony\Component\Yaml\Yaml;
28
use ZipStream\Option\Archive;
29
use ZipStream\ZipStream;
30
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
31
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
32
33
/**
34
 * This is a code library for Chamilo.
35
 * It is included by default in every Chamilo file (through including the global.inc.php)
36
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
37
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
38
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
39
 */
40
41
// PHP version requirement.
42
define('REQUIRED_PHP_VERSION', '8.2');
43
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
44
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
45
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
46
47
// USER STATUS CONSTANTS
48
/** global status of a user: student */
49
define('STUDENT', 5);
50
/** global status of a user: course manager */
51
define('COURSEMANAGER', 1);
52
/** global status of a user: session admin */
53
define('SESSIONADMIN', 3);
54
/** global status of a user: human resources manager */
55
define('DRH', 4);
56
/** global status of a user: anonymous visitor */
57
define('ANONYMOUS', 6);
58
/** global status of a user: low security, necessary for inserting data from
59
 * the teacher through HTMLPurifier */
60
define('COURSEMANAGERLOWSECURITY', 10);
61
// Soft user status
62
define('PLATFORM_ADMIN', 11);
63
define('SESSION_COURSE_COACH', 12);
64
define('SESSION_GENERAL_COACH', 13);
65
define('COURSE_STUDENT', 14); //student subscribed in a course
66
define('SESSION_STUDENT', 15); //student subscribed in a session course
67
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
68
define('STUDENT_BOSS', 17); // student is boss
69
define('INVITEE', 20);
70
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
71
72
// COURSE VISIBILITY CONSTANTS
73
/** only visible for course admin */
74
define('COURSE_VISIBILITY_CLOSED', 0);
75
/** only visible for users registered in the course */
76
define('COURSE_VISIBILITY_REGISTERED', 1);
77
/** Open for all registered users on the platform */
78
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
79
/** Open for the whole world */
80
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
81
/** Invisible to all except admin */
82
define('COURSE_VISIBILITY_HIDDEN', 4);
83
84
define('COURSE_REQUEST_PENDING', 0);
85
define('COURSE_REQUEST_ACCEPTED', 1);
86
define('COURSE_REQUEST_REJECTED', 2);
87
define('DELETE_ACTION_ENABLED', false);
88
89
// EMAIL SENDING RECIPIENT CONSTANTS
90
define('SEND_EMAIL_EVERYONE', 1);
91
define('SEND_EMAIL_STUDENTS', 2);
92
define('SEND_EMAIL_TEACHERS', 3);
93
94
// SESSION VISIBILITY CONSTANTS
95
define('SESSION_VISIBLE_READ_ONLY', 1);
96
define('SESSION_VISIBLE', 2);
97
define('SESSION_INVISIBLE', 3); // not available
98
define('SESSION_AVAILABLE', 4);
99
100
define('SESSION_LINK_TARGET', '_self');
101
102
define('SUBSCRIBE_ALLOWED', 1);
103
define('SUBSCRIBE_NOT_ALLOWED', 0);
104
define('UNSUBSCRIBE_ALLOWED', 1);
105
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
106
107
// SURVEY VISIBILITY CONSTANTS
108
define('SURVEY_VISIBLE_TUTOR', 0);
109
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
110
define('SURVEY_VISIBLE_PUBLIC', 2);
111
112
// CONSTANTS defining all tools, using the english version
113
/* When you add a new tool you must add it into function api_get_tools_lists() too */
114
define('TOOL_DOCUMENT', 'document');
115
define('TOOL_LP_FINAL_ITEM', 'final_item');
116
define('TOOL_READOUT_TEXT', 'readout_text');
117
define('TOOL_THUMBNAIL', 'thumbnail');
118
define('TOOL_HOTPOTATOES', 'hotpotatoes');
119
define('TOOL_CALENDAR_EVENT', 'calendar_event');
120
define('TOOL_LINK', 'link');
121
define('TOOL_LINK_CATEGORY', 'link_category');
122
define('TOOL_COURSE_DESCRIPTION', 'course_description');
123
define('TOOL_SEARCH', 'search');
124
define('TOOL_LEARNPATH', 'learnpath');
125
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
126
define('TOOL_AGENDA', 'agenda');
127
define('TOOL_ANNOUNCEMENT', 'announcement');
128
define('TOOL_FORUM', 'forum');
129
define('TOOL_FORUM_CATEGORY', 'forum_category');
130
define('TOOL_FORUM_THREAD', 'forum_thread');
131
define('TOOL_FORUM_POST', 'forum_post');
132
define('TOOL_FORUM_ATTACH', 'forum_attachment');
133
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
134
define('TOOL_THREAD', 'thread');
135
define('TOOL_POST', 'post');
136
define('TOOL_DROPBOX', 'dropbox');
137
define('TOOL_QUIZ', 'quiz');
138
define('TOOL_TEST_CATEGORY', 'test_category');
139
define('TOOL_USER', 'user');
140
define('TOOL_GROUP', 'group');
141
define('TOOL_BLOGS', 'blog_management');
142
define('TOOL_CHAT', 'chat');
143
define('TOOL_STUDENTPUBLICATION', 'student_publication');
144
define('TOOL_TRACKING', 'tracking');
145
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
146
define('TOOL_COURSE_SETTING', 'course_setting');
147
define('TOOL_BACKUP', 'backup');
148
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
149
define('TOOL_RECYCLE_COURSE', 'recycle_course');
150
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
151
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
152
define('TOOL_UPLOAD', 'file_upload');
153
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
154
define('TOOL_SURVEY', 'survey');
155
//define('TOOL_WIKI', 'wiki');
156
define('TOOL_GLOSSARY', 'glossary');
157
define('TOOL_GRADEBOOK', 'gradebook');
158
define('TOOL_NOTEBOOK', 'notebook');
159
define('TOOL_ATTENDANCE', 'attendance');
160
define('TOOL_COURSE_PROGRESS', 'course_progress');
161
define('TOOL_PORTFOLIO', 'portfolio');
162
define('TOOL_PLAGIARISM', 'compilatio');
163
define('TOOL_XAPI', 'xapi');
164
165
// CONSTANTS defining Chamilo interface sections
166
define('SECTION_CAMPUS', 'mycampus');
167
define('SECTION_COURSES', 'mycourses');
168
define('SECTION_CATALOG', 'catalog');
169
define('SECTION_MYPROFILE', 'myprofile');
170
define('SECTION_MYAGENDA', 'myagenda');
171
define('SECTION_COURSE_ADMIN', 'course_admin');
172
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
173
define('SECTION_MYGRADEBOOK', 'mygradebook');
174
define('SECTION_TRACKING', 'session_my_space');
175
define('SECTION_SOCIAL', 'social-network');
176
define('SECTION_DASHBOARD', 'dashboard');
177
define('SECTION_REPORTS', 'reports');
178
define('SECTION_GLOBAL', 'global');
179
define('SECTION_INCLUDE', 'include');
180
define('SECTION_CUSTOMPAGE', 'custompage');
181
182
// CONSTANT name for local authentication source
183
define('PLATFORM_AUTH_SOURCE', 'platform');
184
define('CAS_AUTH_SOURCE', 'cas');
185
define('LDAP_AUTH_SOURCE', 'extldap');
186
187
// event logs types
188
define('LOG_COURSE_DELETE', 'course_deleted');
189
define('LOG_COURSE_CREATE', 'course_created');
190
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
191
192
// @todo replace 'soc_gr' with social_group
193
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
194
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
195
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
196
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
197
198
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
199
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
200
201
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
202
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
203
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
204
205
define('LOG_MESSAGE_DATA', 'message_data');
206
define('LOG_MESSAGE_DELETE', 'msg_deleted');
207
208
const LOG_RESOURCE_LINK_DELETE = 'resource_link_deleted';
209
const LOG_RESOURCE_LINK_SOFT_DELETE = 'resource_link_soft_deleted';
210
const LOG_RESOURCE_NODE = 'resource_node_id';
211
const LOG_RESOURCE_LINK = 'resource_link_id';
212
const LOG_RESOURCE_NODE_AND_RESOURCE_LINK = 'resource_node_id_and_resource_link_id';
213
214
define('LOG_USER_DELETE', 'user_deleted');
215
define('LOG_USER_PREDELETE', 'user_predeleted');
216
define('LOG_USER_CREATE', 'user_created');
217
define('LOG_USER_UPDATE', 'user_updated');
218
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
219
define('LOG_USER_ENABLE', 'user_enable');
220
define('LOG_USER_DISABLE', 'user_disable');
221
define('LOG_USER_ANONYMIZE', 'user_anonymized');
222
define('LOG_USER_FIELD_CREATE', 'user_field_created');
223
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
224
define('LOG_SESSION_CREATE', 'session_created');
225
define('LOG_SESSION_DELETE', 'session_deleted');
226
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
227
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
228
define('LOG_SESSION_ADD_USER', 'session_add_user');
229
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
230
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
231
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
232
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
233
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
234
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
235
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
236
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
237
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
238
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
239
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
240
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
241
define('LOG_PROMOTION_CREATE', 'promotion_created');
242
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
243
define('LOG_CAREER_CREATE', 'career_created');
244
define('LOG_CAREER_DELETE', 'career_deleted');
245
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
246
//define('LOG_WIKI_ACCESS', 'wiki_page_view');
247
// All results from an exercise
248
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
249
// Logs only the one attempt
250
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
251
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
252
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
253
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
254
255
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
256
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
257
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
258
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
259
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
260
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
261
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
262
263
// Event logs data types (max 20 chars)
264
define('LOG_COURSE_CODE', 'course_code');
265
define('LOG_COURSE_ID', 'course_id');
266
define('LOG_USER_ID', 'user_id');
267
define('LOG_USER_OBJECT', 'user_object');
268
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
269
define('LOG_SESSION_ID', 'session_id');
270
271
define('LOG_QUESTION_ID', 'question_id');
272
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
273
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
274
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
275
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
276
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
277
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
278
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
279
define('LOG_CAREER_ID', 'career_id');
280
define('LOG_PROMOTION_ID', 'promotion_id');
281
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
282
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
283
define('LOG_GRADEBOOK_ID', 'gradebook_id');
284
//define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
285
define('LOG_EXERCISE_ID', 'exercise_id');
286
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
287
define('LOG_LP_ID', 'lp_id');
288
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
289
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
290
291
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
292
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
293
define('LOG_WORK_DATA', 'work_data_array');
294
295
define('LOG_MY_FOLDER_PATH', 'path');
296
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
297
298
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
299
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
300
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
301
302
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
303
304
define('LOG_QUESTION_CREATED', 'question_created');
305
define('LOG_QUESTION_UPDATED', 'question_updated');
306
define('LOG_QUESTION_DELETED', 'question_deleted');
307
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
308
309
define('LOG_SURVEY_ID', 'survey_id');
310
define('LOG_SURVEY_CREATED', 'survey_created');
311
define('LOG_SURVEY_DELETED', 'survey_deleted');
312
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
313
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
314
315
//used when login_is_email setting is true
316
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
317
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
318
319
// This constant is a result of Windows OS detection, it has a boolean value:
320
// true whether the server runs on Windows OS, false otherwise.
321
define('IS_WINDOWS_OS', api_is_windows_os());
322
323
// Patterns for processing paths. Examples.
324
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
325
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
326
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
327
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
328
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
329
// basic (leaf elements)
330
define('REL_CODE_PATH', 'REL_CODE_PATH');
331
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
332
define('REL_HOME_PATH', 'REL_HOME_PATH');
333
334
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
335
define('WEB_PATH', 'WEB_PATH');
336
define('SYS_PATH', 'SYS_PATH');
337
define('SYMFONY_SYS_PATH', 'SYMFONY_SYS_PATH');
338
339
define('REL_PATH', 'REL_PATH');
340
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
341
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
342
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
343
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
344
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
345
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
346
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
347
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
348
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
349
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
350
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
351
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
352
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
353
define('LIBRARY_PATH', 'LIBRARY_PATH');
354
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
355
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
356
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
357
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
358
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
359
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
360
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
361
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
362
363
// Relations type with Course manager
364
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
365
366
// Relations type with Human resources manager
367
define('COURSE_RELATION_TYPE_RRHH', 1);
368
369
// User image sizes
370
define('USER_IMAGE_SIZE_ORIGINAL', 1);
371
define('USER_IMAGE_SIZE_BIG', 2);
372
define('USER_IMAGE_SIZE_MEDIUM', 3);
373
define('USER_IMAGE_SIZE_SMALL', 4);
374
375
// Gradebook link constants
376
// Please do not change existing values, they are used in the database !
377
define('GRADEBOOK_ITEM_LIMIT', 1000);
378
379
define('LINK_EXERCISE', 1);
380
define('LINK_DROPBOX', 2);
381
define('LINK_STUDENTPUBLICATION', 3);
382
define('LINK_LEARNPATH', 4);
383
define('LINK_FORUM_THREAD', 5);
384
//define('LINK_WORK',6);
385
define('LINK_ATTENDANCE', 7);
386
define('LINK_SURVEY', 8);
387
define('LINK_HOTPOTATOES', 9);
388
define('LINK_PORTFOLIO', 10);
389
390
// Score display types constants
391
define('SCORE_DIV', 1); // X / Y
392
define('SCORE_PERCENT', 2); // XX %
393
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
394
define('SCORE_AVERAGE', 4); // XX %
395
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
396
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
397
define('SCORE_SIMPLE', 7); // X
398
define('SCORE_IGNORE_SPLIT', 8); //  ??
399
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
400
define('SCORE_CUSTOM', 10); // Good!
401
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
402
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
403
define('SCORE_ONLY_SCORE', 13); // X - Good!
404
define('SCORE_NUMERIC', 14);
405
406
define('SCORE_BOTH', 1);
407
define('SCORE_ONLY_DEFAULT', 2);
408
define('SCORE_ONLY_CUSTOM', 3);
409
410
// From display.lib.php
411
412
define('MAX_LENGTH_BREADCRUMB', 100);
413
define('ICON_SIZE_ATOM', 8);
414
define('ICON_SIZE_TINY', 16);
415
define('ICON_SIZE_SMALL', 22);
416
define('ICON_SIZE_MEDIUM', 32);
417
define('ICON_SIZE_LARGE', 48);
418
define('ICON_SIZE_BIG', 64);
419
define('ICON_SIZE_HUGE', 128);
420
define('SHOW_TEXT_NEAR_ICONS', false);
421
422
// Session catalog
423
define('CATALOG_COURSES', 0);
424
define('CATALOG_SESSIONS', 1);
425
define('CATALOG_COURSES_SESSIONS', 2);
426
427
// Hook type events, pre-process and post-process.
428
// All means to be executed for both hook event types
429
define('HOOK_EVENT_TYPE_PRE', 0);
430
define('HOOK_EVENT_TYPE_POST', 1);
431
define('HOOK_EVENT_TYPE_ALL', 10);
432
433
// Group permissions
434
define('GROUP_PERMISSION_OPEN', '1');
435
define('GROUP_PERMISSION_CLOSED', '2');
436
437
// Group user permissions
438
define('GROUP_USER_PERMISSION_ADMIN', 1); // the admin of a group
439
define('GROUP_USER_PERMISSION_READER', 2); // a normal user
440
define('GROUP_USER_PERMISSION_PENDING_INVITATION', 3); // When an admin/moderator invites a user
441
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', 4); // an user joins a group
442
define('GROUP_USER_PERMISSION_MODERATOR', 5); // a moderator
443
define('GROUP_USER_PERMISSION_ANONYMOUS', 6); // an anonymous user
444
define('GROUP_USER_PERMISSION_HRM', 7); // a human resources manager
445
446
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
447
define('GROUP_IMAGE_SIZE_BIG', 2);
448
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
449
define('GROUP_IMAGE_SIZE_SMALL', 4);
450
define('GROUP_TITLE_LENGTH', 50);
451
452
// Exercise
453
// @todo move into a class
454
define('ALL_ON_ONE_PAGE', 1);
455
define('ONE_PER_PAGE', 2);
456
457
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
458
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
459
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
460
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
461
462
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
463
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
464
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
465
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
466
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
467
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
468
define('RESULT_DISABLE_RANKING', 6);
469
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
470
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
471
define('RESULT_DISABLE_RADAR', 9);
472
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
473
474
define('EXERCISE_MAX_NAME_SIZE', 80);
475
476
// Question types (edit next array as well when adding values)
477
// @todo move into a class
478
define('UNIQUE_ANSWER', 1);
479
define('MULTIPLE_ANSWER', 2);
480
define('FILL_IN_BLANKS', 3);
481
define('MATCHING', 4);
482
define('FREE_ANSWER', 5);
483
define('HOT_SPOT', 6);
484
define('HOT_SPOT_ORDER', 7);
485
define('HOT_SPOT_DELINEATION', 8);
486
define('MULTIPLE_ANSWER_COMBINATION', 9);
487
define('UNIQUE_ANSWER_NO_OPTION', 10);
488
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
489
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
490
define('ORAL_EXPRESSION', 13);
491
define('GLOBAL_MULTIPLE_ANSWER', 14);
492
define('MEDIA_QUESTION', 15);
493
define('CALCULATED_ANSWER', 16);
494
define('UNIQUE_ANSWER_IMAGE', 17);
495
define('DRAGGABLE', 18);
496
define('MATCHING_DRAGGABLE', 19);
497
define('ANNOTATION', 20);
498
define('READING_COMPREHENSION', 21);
499
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
500
501
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
502
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
503
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
504
505
// Question selection type
506
define('EX_Q_SELECTION_ORDERED', 1);
507
define('EX_Q_SELECTION_RANDOM', 2);
508
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
509
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
510
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
511
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
512
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
513
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
514
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
515
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
516
517
// Used to save the skill_rel_item table
518
define('ITEM_TYPE_EXERCISE', 1);
519
define('ITEM_TYPE_HOTPOTATOES', 2);
520
define('ITEM_TYPE_LINK', 3);
521
define('ITEM_TYPE_LEARNPATH', 4);
522
define('ITEM_TYPE_GRADEBOOK', 5);
523
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
524
//define('ITEM_TYPE_FORUM', 7);
525
define('ITEM_TYPE_ATTENDANCE', 8);
526
define('ITEM_TYPE_SURVEY', 9);
527
define('ITEM_TYPE_FORUM_THREAD', 10);
528
define('ITEM_TYPE_PORTFOLIO', 11);
529
530
// Course description blocks.
531
define('ADD_BLOCK', 8);
532
533
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
534
define(
535
    'QUESTION_TYPES',
536
    UNIQUE_ANSWER.':'.
537
    MULTIPLE_ANSWER.':'.
538
    FILL_IN_BLANKS.':'.
539
    MATCHING.':'.
540
    FREE_ANSWER.':'.
541
    HOT_SPOT.':'.
542
    HOT_SPOT_ORDER.':'.
543
    HOT_SPOT_DELINEATION.':'.
544
    MULTIPLE_ANSWER_COMBINATION.':'.
545
    UNIQUE_ANSWER_NO_OPTION.':'.
546
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
547
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
548
    ORAL_EXPRESSION.':'.
549
    GLOBAL_MULTIPLE_ANSWER.':'.
550
    MEDIA_QUESTION.':'.
551
    CALCULATED_ANSWER.':'.
552
    UNIQUE_ANSWER_IMAGE.':'.
553
    DRAGGABLE.':'.
554
    MATCHING_DRAGGABLE.':'.
555
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
556
    ANNOTATION
557
);
558
559
//Some alias used in the QTI exports
560
define('MCUA', 1);
561
define('TF', 1);
562
define('MCMA', 2);
563
define('FIB', 3);
564
565
// Message
566
define('MESSAGE_STATUS_INVITATION_PENDING', 5);
567
define('MESSAGE_STATUS_INVITATION_ACCEPTED', 6);
568
define('MESSAGE_STATUS_INVITATION_DENIED', 7);
569
define('MESSAGE_STATUS_WALL', 8);
570
571
define('MESSAGE_STATUS_WALL_DELETE', 9);
572
define('MESSAGE_STATUS_WALL_POST', 10);
573
574
define('MESSAGE_STATUS_FORUM', 12);
575
define('MESSAGE_STATUS_PROMOTED', 13);
576
577
// Images
578
define('IMAGE_WALL_SMALL_SIZE', 200);
579
define('IMAGE_WALL_MEDIUM_SIZE', 500);
580
define('IMAGE_WALL_BIG_SIZE', 2000);
581
define('IMAGE_WALL_SMALL', 'small');
582
define('IMAGE_WALL_MEDIUM', 'medium');
583
define('IMAGE_WALL_BIG', 'big');
584
585
// Social PLUGIN PLACES
586
define('SOCIAL_LEFT_PLUGIN', 1);
587
define('SOCIAL_CENTER_PLUGIN', 2);
588
define('SOCIAL_RIGHT_PLUGIN', 3);
589
define('CUT_GROUP_NAME', 50);
590
591
/**
592
 * FormValidator Filter.
593
 */
594
define('NO_HTML', 1);
595
define('STUDENT_HTML', 2);
596
define('TEACHER_HTML', 3);
597
define('STUDENT_HTML_FULLPAGE', 4);
598
define('TEACHER_HTML_FULLPAGE', 5);
599
600
// Timeline
601
define('TIMELINE_STATUS_ACTIVE', '1');
602
define('TIMELINE_STATUS_INACTIVE', '2');
603
604
// Event email template class
605
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
606
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
607
608
// Course home
609
define('SHORTCUTS_HORIZONTAL', 0);
610
define('SHORTCUTS_VERTICAL', 1);
611
612
// Course copy
613
define('FILE_SKIP', 1);
614
define('FILE_RENAME', 2);
615
define('FILE_OVERWRITE', 3);
616
define('UTF8_CONVERT', false); //false by default
617
618
define('DOCUMENT', 'file');
619
define('FOLDER', 'folder');
620
621
define('RESOURCE_ASSET', 'asset');
622
define('RESOURCE_DOCUMENT', 'document');
623
define('RESOURCE_GLOSSARY', 'glossary');
624
define('RESOURCE_EVENT', 'calendar_event');
625
define('RESOURCE_LINK', 'link');
626
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
627
define('RESOURCE_LEARNPATH', 'learnpath');
628
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
629
define('RESOURCE_ANNOUNCEMENT', 'announcement');
630
define('RESOURCE_FORUM', 'forum');
631
define('RESOURCE_FORUMTOPIC', 'thread');
632
define('RESOURCE_FORUMPOST', 'post');
633
define('RESOURCE_QUIZ', 'quiz');
634
define('RESOURCE_TEST_CATEGORY', 'test_category');
635
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
636
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
637
define('RESOURCE_LINKCATEGORY', 'Link_Category');
638
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
639
define('RESOURCE_SCORM', 'Scorm');
640
define('RESOURCE_SURVEY', 'survey');
641
define('RESOURCE_SURVEYQUESTION', 'survey_question');
642
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
643
define('RESOURCE_WIKI', 'wiki');
644
define('RESOURCE_THEMATIC', 'thematic');
645
define('RESOURCE_ATTENDANCE', 'attendance');
646
define('RESOURCE_WORK', 'work');
647
define('RESOURCE_SESSION_COURSE', 'session_course');
648
define('RESOURCE_GRADEBOOK', 'gradebook');
649
define('ADD_THEMATIC_PLAN', 6);
650
651
// Max online users to show per page (whoisonline)
652
define('MAX_ONLINE_USERS', 12);
653
654
define('TOOL_AUTHORING', 'toolauthoring');
655
define('TOOL_INTERACTION', 'toolinteraction');
656
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
657
define('TOOL_ADMIN', 'tooladmin');
658
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
659
define('TOOL_DRH', 'tool_drh');
660
define('TOOL_STUDENT_VIEW', 'toolstudentview');
661
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
662
663
// Search settings (from main/inc/lib/search/IndexableChunk.class.php )
664
// some constants to avoid serialize string keys on serialized data array
665
define('SE_COURSE_ID', 0);
666
define('SE_TOOL_ID', 1);
667
define('SE_DATA', 2);
668
define('SE_USER', 3);
669
670
// in some cases we need top differenciate xapian documents of the same tool
671
define('SE_DOCTYPE_EXERCISE_EXERCISE', 0);
672
define('SE_DOCTYPE_EXERCISE_QUESTION', 1);
673
674
// xapian prefixes
675
define('XAPIAN_PREFIX_COURSEID', 'C');
676
define('XAPIAN_PREFIX_TOOLID', 'O');
677
678
// User active field constants
679
define('USER_ACTIVE', 1);
680
define('USER_INACTIVE', 0);
681
define('USER_INACTIVE_AUTOMATIC', -1);
682
define('USER_SOFT_DELETED', -2);
683
684
/**
685
 * Returns a path to a certain resource within Chamilo.
686
 *
687
 * @param string $path A path which type is to be converted. Also, it may be a defined constant for a path.
688
 *
689
 * @return string the requested path or the converted path
690
 *
691
 * Notes about the current behaviour model:
692
 * 1. Windows back-slashes are converted to slashes in the result.
693
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
694
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
695
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
696
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
697
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
698
 * It has not been identified as needed yet.
699
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
700
 *
701
 * Vchamilo changes : allow using an alternate configuration
702
 * to get vchamilo  instance paths
703
 */
704
function api_get_path($path = '', $configuration = [])
705
{
706
    global $paths;
707
708
    // get proper configuration data if exists
709
    global $_configuration;
710
711
    $emptyConfigurationParam = false;
712
    if (empty($configuration)) {
713
        $configuration = (array) $_configuration;
714
        $emptyConfigurationParam = true;
715
    }
716
717
    $root_sys = Container::getProjectDir();
718
    $root_web = '';
719
    if (isset(Container::$container)) {
720
        $root_web = Container::$container->get('router')->generate(
721
            'index',
722
            [],
723
            UrlGeneratorInterface::ABSOLUTE_URL
724
        );
725
    }
726
727
    /*if (api_get_multiple_access_url()) {
728
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the main_api.lib.php
729
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
730
            // We look into the DB the function api_get_access_url
731
            $urlInfo = api_get_access_url($configuration['access_url']);
732
            // Avoid default value
733
            $defaultValues = ['http://localhost/', 'https://localhost/'];
734
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
735
                $root_web = 1 == $urlInfo['active'] ? $urlInfo['url'] : $configuration['root_web'];
736
            }
737
        }
738
    }*/
739
740
    $paths = [
741
        WEB_PATH => $root_web,
742
        SYMFONY_SYS_PATH => $root_sys,
743
        SYS_PATH => $root_sys.'public/',
744
        REL_PATH => '',
745
        CONFIGURATION_PATH => 'app/config/',
746
        LIBRARY_PATH => $root_sys.'public/main/inc/lib/',
747
748
        REL_COURSE_PATH => '',
749
        REL_CODE_PATH => '/main/',
750
751
        SYS_CODE_PATH => $root_sys.'public/main/',
752
        SYS_CSS_PATH => $root_sys.'public/build/css/',
753
        SYS_PLUGIN_PATH => $root_sys.'public/plugin/',
754
        SYS_ARCHIVE_PATH => $root_sys.'var/cache/',
755
        SYS_TEST_PATH => $root_sys.'tests/',
756
        SYS_TEMPLATE_PATH => $root_sys.'public/main/template/',
757
        SYS_PUBLIC_PATH => $root_sys.'public/',
758
        SYS_FONTS_PATH => $root_sys.'public/fonts/',
759
760
        WEB_CODE_PATH => $root_web.'main/',
761
        WEB_PLUGIN_ASSET_PATH => $root_web.'plugins/',
762
        WEB_COURSE_PATH => $root_web.'course/',
763
        WEB_IMG_PATH => $root_web.'img/',
764
        WEB_CSS_PATH => $root_web.'build/css/',
765
        WEB_AJAX_PATH => $root_web.'main/inc/ajax/',
766
        WEB_LIBRARY_PATH => $root_web.'main/inc/lib/',
767
        WEB_LIBRARY_JS_PATH => $root_web.'main/inc/lib/javascript/',
768
        WEB_PLUGIN_PATH => $root_web.'plugin/',
769
        WEB_PUBLIC_PATH => $root_web,
770
    ];
771
772
    $root_rel = '';
773
774
    global $virtualChamilo;
775
    if (!empty($virtualChamilo)) {
776
        $paths[SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
777
        //$paths[SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
778
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
779
        $paths[WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
780
        //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
781
782
        // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
783
784
        // RewriteEngine On
785
        // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
786
787
        //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
788
        //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
789
        //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
790
    }
791
792
    $path = trim($path);
793
794
    // Retrieving a common-purpose path.
795
    if (isset($paths[$path])) {
796
        return $paths[$path];
797
    }
798
799
    return false;
800
}
801
802
/**
803
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
804
 *
805
 * @param string $path the input path
806
 *
807
 * @return string returns the modified path
808
 */
809
function api_add_trailing_slash($path)
810
{
811
    return '/' === substr($path, -1) ? $path : $path.'/';
812
}
813
814
/**
815
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
816
 *
817
 * @param string $path the input path
818
 *
819
 * @return string returns the modified path
820
 */
821
function api_remove_trailing_slash($path)
822
{
823
    return '/' === substr($path, -1) ? substr($path, 0, -1) : $path;
824
}
825
826
/**
827
 * Checks the RFC 3986 syntax of a given URL.
828
 *
829
 * @param string $url      the URL to be checked
830
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
831
 *
832
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
833
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
834
 *
835
 * @see http://drupal.org
836
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
837
 * @see http://bugs.php.net/51192
838
 */
839
function api_valid_url($url, $absolute = false)
840
{
841
    if ($absolute) {
842
        if (preg_match("
843
            /^                                                      # Start at the beginning of the text
844
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
845
            (?:                                                     # Userinfo (optional) which is typically
846
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
847
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
848
            )?
849
            (?:
850
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
851
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
852
            )
853
            (?::[0-9]+)?                                            # Server port number (optional)
854
            (?:[\/|\?]
855
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
856
            *)?
857
            $/xi", $url)) {
858
            return $url;
859
        }
860
861
        return false;
862
    } else {
863
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
864
    }
865
}
866
867
/**
868
 * Checks whether a given string looks roughly like an email address.
869
 *
870
 * @param string $address the e-mail address to be checked
871
 *
872
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
873
 */
874
function api_valid_email($address)
875
{
876
    return filter_var($address, FILTER_VALIDATE_EMAIL);
877
}
878
879
/**
880
 * Function used to protect a course script.
881
 * The function blocks access when
882
 * - there is no $_SESSION["_course"] defined; or
883
 * - $is_allowed_in_course is set to false (this depends on the course
884
 * visibility and user status).
885
 *
886
 * This is only the first proposal, test and improve!
887
 *
888
 * @param bool Option to print headers when displaying error message. Default: false
889
 * @param bool whether session admins should be allowed or not
890
 * @param string $checkTool check if tool is available for users (user, group)
891
 *
892
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
893
 *
894
 * @todo replace global variable
895
 *
896
 * @author Roan Embrechts
897
 */
898
function api_protect_course_script($print_headers = false, $allow_session_admins = false, string $checkTool = '', $cid = null): bool
899
{
900
    $course_info = api_get_course_info();
901
    if (empty($course_info) && isset($_REQUEST['cid'])) {
902
        $course_info = api_get_course_info_by_id((int) $_REQUEST['cid']);
903
    }
904
905
    if (isset($cid)) {
906
        $course_info = api_get_course_info_by_id($cid);
907
    }
908
909
    if (empty($course_info)) {
910
        api_not_allowed($print_headers);
911
912
        return false;
913
    }
914
915
    if (api_is_drh()) {
916
        return true;
917
    }
918
919
    // Session admin has access to course
920
    $sessionAccess = ('true' === api_get_setting('session.session_admins_access_all_content'));
921
    if ($sessionAccess) {
922
        $allow_session_admins = true;
923
    }
924
925
    if (api_is_platform_admin($allow_session_admins)) {
926
        return true;
927
    }
928
929
    $isAllowedInCourse = api_is_allowed_in_course();
930
    $is_visible = false;
931
    if (isset($course_info) && isset($course_info['visibility'])) {
932
        switch ($course_info['visibility']) {
933
            default:
934
            case Course::CLOSED:
935
                // Completely closed: the course is only accessible to the teachers. - 0
936
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
937
                    $is_visible = true;
938
                }
939
                break;
940
            case Course::REGISTERED:
941
                // Private - access authorized to course members only - 1
942
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
943
                    $is_visible = true;
944
                }
945
                break;
946
            case Course::OPEN_PLATFORM:
947
                // Open - access allowed for users registered on the platform - 2
948
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
949
                    $is_visible = true;
950
                }
951
                break;
952
            case Course::OPEN_WORLD:
953
                //Open - access allowed for the whole world - 3
954
                $is_visible = true;
955
                break;
956
            case Course::HIDDEN:
957
                //Completely closed: the course is only accessible to the teachers. - 0
958
                if (api_is_platform_admin()) {
959
                    $is_visible = true;
960
                }
961
                break;
962
        }
963
964
        //If password is set and user is not registered to the course then the course is not visible
965
        if (false === $isAllowedInCourse &&
966
            isset($course_info['registration_code']) &&
967
            !empty($course_info['registration_code'])
968
        ) {
969
            $is_visible = false;
970
        }
971
    }
972
973
    if (!empty($checkTool)) {
974
        if (!api_is_allowed_to_edit(true, true, true)) {
975
            $toolInfo = api_get_tool_information_by_name($checkTool);
976
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && 0 == $toolInfo['visibility']) {
977
                api_not_allowed(true);
978
979
                return false;
980
            }
981
        }
982
    }
983
984
    // Check session visibility
985
    $session_id = api_get_session_id();
986
987
    if (!empty($session_id)) {
988
        // $isAllowedInCourse was set in local.inc.php
989
        if (!$isAllowedInCourse) {
990
            $is_visible = false;
991
        }
992
        // Check if course is inside session.
993
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
994
            $is_visible = false;
995
        }
996
    }
997
998
    if (!$is_visible) {
999
        api_not_allowed($print_headers);
1000
1001
        return false;
1002
    }
1003
1004
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
1005
        $plugin = Positioning::create();
1006
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1007
        if ('true' === $block) {
1008
            $currentPath = $_SERVER['PHP_SELF'];
1009
            // Allowed only this course paths.
1010
            $paths = [
1011
                '/plugin/positioning/start.php',
1012
                '/plugin/positioning/start_student.php',
1013
                '/main/course_home/course_home.php',
1014
                '/main/exercise/overview.php',
1015
            ];
1016
1017
            if (!in_array($currentPath, $paths, true)) {
1018
                // Check if entering an exercise.
1019
                // @todo remove global $current_course_tool
1020
                /*global $current_course_tool;
1021
                if ('quiz' !== $current_course_tool) {
1022
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1023
                    if ($initialData && isset($initialData['exercise_id'])) {
1024
                        $results = Event::getExerciseResultsByUser(
1025
                            api_get_user_id(),
1026
                            $initialData['exercise_id'],
1027
                            $course_info['real_id'],
1028
                            $session_id
1029
                        );
1030
                        if (empty($results)) {
1031
                            api_not_allowed($print_headers);
1032
1033
                            return false;
1034
                        }
1035
                    }
1036
                }*/
1037
            }
1038
        }
1039
    }
1040
1041
    api_block_inactive_user();
1042
1043
    return true;
1044
}
1045
1046
/**
1047
 * Function used to protect an admin script.
1048
 *
1049
 * The function blocks access when the user has no platform admin rights
1050
 * with an error message printed on default output
1051
 *
1052
 * @param bool Whether to allow session admins as well
1053
 * @param bool Whether to allow HR directors as well
1054
 * @param string An optional message (already passed through get_lang)
1055
 *
1056
 * @return bool True if user is allowed, false otherwise.
1057
 *              The function also outputs an error message in case not allowed
1058
 *
1059
 * @author Roan Embrechts (original author)
1060
 */
1061
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1062
{
1063
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1064
        api_not_allowed(true, $message);
1065
1066
        return false;
1067
    }
1068
    api_block_inactive_user();
1069
1070
    return true;
1071
}
1072
1073
/**
1074
 * Blocks inactive users with a currently active session from accessing more pages "live".
1075
 *
1076
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1077
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1078
 */
1079
function api_block_inactive_user()
1080
{
1081
    $data = true;
1082
    if ('true' !== api_get_setting('security.security_block_inactive_users_immediately')) {
1083
        return $data;
1084
    }
1085
1086
    $userId = api_get_user_id();
1087
    $homeUrl = api_get_path(WEB_PATH);
1088
    if (0 == $userId) {
1089
        return $data;
1090
    }
1091
1092
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1093
            WHERE id = $userId";
1094
1095
    $result = Database::query($sql);
1096
    if (Database::num_rows($result) > 0) {
1097
        $result_array = Database::fetch_array($result);
1098
        $data = (bool) $result_array['active'];
1099
    }
1100
    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...
1101
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1102
        $tpl->assign('hide_login_link', 1);
1103
1104
        //api_not_allowed(true, get_lang('Account inactive'));
1105
        // we were not in a course, return to home page
1106
        $msg = Display::return_message(
1107
            get_lang('Account inactive'),
1108
            'error',
1109
            false
1110
        );
1111
1112
        $msg .= '<p class="text-center">
1113
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1114
1115
        $tpl->assign('content', $msg);
1116
        $tpl->display_one_col_template();
1117
        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...
1118
    }
1119
1120
    return $data;
1121
}
1122
1123
/**
1124
 * Function used to protect a teacher script.
1125
 * The function blocks access when the user has no teacher rights.
1126
 *
1127
 * @return bool True if the current user can access the script, false otherwise
1128
 *
1129
 * @author Yoselyn Castillo
1130
 */
1131
function api_protect_teacher_script()
1132
{
1133
    if (!api_is_allowed_to_edit()) {
1134
        api_not_allowed(true);
1135
1136
        return false;
1137
    }
1138
1139
    return true;
1140
}
1141
1142
/**
1143
 * Function used to prevent anonymous users from accessing a script.
1144
 *
1145
 * @param bool $printHeaders
1146
 *
1147
 * @return bool
1148
 */
1149
function api_block_anonymous_users($printHeaders = true)
1150
{
1151
    $isAuth = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
1152
1153
    if (false === $isAuth) {
1154
        api_not_allowed($printHeaders);
1155
1156
        return false;
1157
    }
1158
1159
    api_block_inactive_user();
1160
1161
    return true;
1162
}
1163
1164
/**
1165
 * Returns a rough evaluation of the browser's name and version based on very
1166
 * simple regexp.
1167
 *
1168
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1169
 */
1170
function api_get_navigator()
1171
{
1172
    $navigator = 'Unknown';
1173
    $version = 0;
1174
1175
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1176
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1177
    }
1178
1179
    if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
1180
        $navigator = 'Opera';
1181
        [, $version] = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1182
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
1183
        $navigator = 'Edge';
1184
        [, $version] = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1185
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
1186
        $navigator = 'Internet Explorer';
1187
        [, $version] = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1188
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
1189
        $navigator = 'Chrome';
1190
        [, $version] = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1191
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
1192
        $navigator = 'Safari';
1193
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Version/')) {
1194
            // If this Safari does have the "Version/" string in its user agent
1195
            // then use that as a version indicator rather than what's after
1196
            // "Safari/" which is rather a "build number" or something
1197
            [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1198
        } else {
1199
            [, $version] = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1200
        }
1201
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
1202
        $navigator = 'Firefox';
1203
        [, $version] = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1204
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape')) {
1205
        $navigator = 'Netscape';
1206
        if (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/')) {
1207
            [, $version] = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1208
        } else {
1209
            [, $version] = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1210
        }
1211
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror')) {
1212
        $navigator = 'Konqueror';
1213
        [, $version] = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1214
    } elseif (false !== stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit')) {
1215
        $navigator = 'AppleWebKit';
1216
        [, $version] = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1217
    } elseif (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko')) {
1218
        $navigator = 'Mozilla';
1219
        [, $version] = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1220
    }
1221
1222
    // Now cut extra stuff around (mostly *after*) the version number
1223
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1224
1225
    if (false === strpos($version, '.')) {
1226
        $version = number_format(doubleval($version), 1);
1227
    }
1228
1229
    return ['name' => $navigator, 'version' => $version];
1230
}
1231
1232
/**
1233
 * This function returns the id of the user which is stored in the $_user array.
1234
 *
1235
 * example: The function can be used to check if a user is logged in
1236
 *          if (api_get_user_id())
1237
 *
1238
 * @return int the id of the current user, 0 if is empty
1239
 */
1240
function api_get_user_id()
1241
{
1242
    $userInfo = Session::read('_user');
1243
    if ($userInfo && isset($userInfo['user_id'])) {
1244
        return (int) $userInfo['user_id'];
1245
    }
1246
1247
    return 0;
1248
}
1249
1250
/**
1251
 * Formats user information into a standard array
1252
 * This function should be only used inside api_get_user_info().
1253
 *
1254
 * @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...
1255
 * @param bool $add_password
1256
 * @param bool $loadAvatars  turn off to improve performance
1257
 *
1258
 * @return array Standard user array
1259
 */
1260
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1261
{
1262
    $result = [];
1263
1264
    if (!isset($user['id'])) {
1265
        return [];
1266
    }
1267
1268
    $result['firstname'] = null;
1269
    $result['lastname'] = null;
1270
1271
    if (isset($user['firstname']) && isset($user['lastname'])) {
1272
        // with only lowercase
1273
        $result['firstname'] = $user['firstname'];
1274
        $result['lastname'] = $user['lastname'];
1275
    } elseif (isset($user['firstName']) && isset($user['lastName'])) {
1276
        // with uppercase letters
1277
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1278
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1279
    }
1280
1281
    if (isset($user['email'])) {
1282
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1283
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1284
    } else {
1285
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1286
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1287
    }
1288
1289
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1290
    $result['complete_name_with_username'] = $result['complete_name'];
1291
1292
    if (!empty($user['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1293
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1294
    }
1295
1296
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1297
    if (!empty($user['email'])) {
1298
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1299
        if ($showEmail) {
1300
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1301
        }
1302
    } else {
1303
        $result['complete_name_with_email'] = $result['complete_name'];
1304
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1305
    }
1306
1307
    // Kept for historical reasons
1308
    $result['firstName'] = $result['firstname'];
1309
    $result['lastName'] = $result['lastname'];
1310
1311
    $attributes = [
1312
        'phone',
1313
        'address',
1314
        'picture_uri',
1315
        'official_code',
1316
        'status',
1317
        'active',
1318
        'auth_source',
1319
        'username',
1320
        'theme',
1321
        'language',
1322
        'locale',
1323
        'creator_id',
1324
        'registration_date',
1325
        'hr_dept_id',
1326
        'expiration_date',
1327
        'last_login',
1328
        'user_is_online',
1329
        'profile_completed',
1330
    ];
1331
1332
    if ('true' === api_get_setting('extended_profile')) {
1333
        $attributes[] = 'competences';
1334
        $attributes[] = 'diplomas';
1335
        $attributes[] = 'teach';
1336
        $attributes[] = 'openarea';
1337
    }
1338
1339
    foreach ($attributes as $attribute) {
1340
        $result[$attribute] = $user[$attribute] ?? null;
1341
    }
1342
1343
    $user_id = (int) $user['id'];
1344
    // Maintain the user_id index for backwards compatibility
1345
    $result['user_id'] = $result['id'] = $user_id;
1346
1347
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1348
    $result['has_certificates'] = 0;
1349
    if (!empty($hasCertificates)) {
1350
        $result['has_certificates'] = 1;
1351
    }
1352
1353
    $result['icon_status'] = '';
1354
    $result['icon_status_medium'] = '';
1355
    $result['is_admin'] = UserManager::is_admin($user_id);
1356
1357
    // Getting user avatar.
1358
    if ($loadAvatars) {
1359
        $result['avatar'] = '';
1360
        $result['avatar_no_query'] = '';
1361
        $result['avatar_small'] = '';
1362
        $result['avatar_medium'] = '';
1363
1364
        if (empty($user['avatar'])) {
1365
            $originalFile = UserManager::getUserPicture(
1366
                $user_id,
1367
                USER_IMAGE_SIZE_ORIGINAL,
1368
                null,
1369
                $result
1370
            );
1371
            $result['avatar'] = $originalFile;
1372
            $avatarString = explode('?', $result['avatar']);
1373
            $result['avatar_no_query'] = reset($avatarString);
1374
        } else {
1375
            $result['avatar'] = $user['avatar'];
1376
            $avatarString = explode('?', $user['avatar']);
1377
            $result['avatar_no_query'] = reset($avatarString);
1378
        }
1379
1380
        if (!isset($user['avatar_small'])) {
1381
            $smallFile = UserManager::getUserPicture(
1382
                $user_id,
1383
                USER_IMAGE_SIZE_SMALL,
1384
                null,
1385
                $result
1386
            );
1387
            $result['avatar_small'] = $smallFile;
1388
        } else {
1389
            $result['avatar_small'] = $user['avatar_small'];
1390
        }
1391
1392
        if (!isset($user['avatar_medium'])) {
1393
            $mediumFile = UserManager::getUserPicture(
1394
                $user_id,
1395
                USER_IMAGE_SIZE_MEDIUM,
1396
                null,
1397
                $result
1398
            );
1399
            $result['avatar_medium'] = $mediumFile;
1400
        } else {
1401
            $result['avatar_medium'] = $user['avatar_medium'];
1402
        }
1403
1404
        $urlImg = api_get_path(WEB_IMG_PATH);
1405
        $iconStatus = '';
1406
        $iconStatusMedium = '';
1407
        $label = '';
1408
1409
        switch ($result['status']) {
1410
            case STUDENT:
1411
                if ($result['has_certificates']) {
1412
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1413
                    $label = get_lang('Graduated');
1414
                } else {
1415
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1416
                    $label = get_lang('Student');
1417
                }
1418
                break;
1419
            case COURSEMANAGER:
1420
                if ($result['is_admin']) {
1421
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1422
                    $label = get_lang('Admin');
1423
                } else {
1424
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1425
                    $label = get_lang('Teacher');
1426
                }
1427
                break;
1428
            case STUDENT_BOSS:
1429
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1430
                $label = get_lang('StudentBoss');
1431
                break;
1432
        }
1433
1434
        if (!empty($iconStatus)) {
1435
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1436
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1437
        }
1438
1439
        $result['icon_status'] = $iconStatus;
1440
        $result['icon_status_label'] = $label;
1441
        $result['icon_status_medium'] = $iconStatusMedium;
1442
    }
1443
1444
    if (isset($user['user_is_online'])) {
1445
        $result['user_is_online'] = true == $user['user_is_online'] ? 1 : 0;
1446
    }
1447
    if (isset($user['user_is_online_in_chat'])) {
1448
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1449
    }
1450
1451
    if ($add_password) {
1452
        $result['password'] = $user['password'];
1453
    }
1454
1455
    if (isset($result['profile_completed'])) {
1456
        $result['profile_completed'] = $user['profile_completed'];
1457
    }
1458
1459
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1460
1461
    // Send message link
1462
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1463
    $result['complete_name_with_message_link'] = Display::url(
1464
        $result['complete_name_with_username'],
1465
        $sendMessage,
1466
        ['class' => 'ajax']
1467
    );
1468
1469
    if (isset($user['extra'])) {
1470
        $result['extra'] = $user['extra'];
1471
    }
1472
1473
    return $result;
1474
}
1475
1476
/**
1477
 * Finds all the information about a user.
1478
 * If no parameter is passed you find all the information about the current user.
1479
 *
1480
 * @param int  $user_id
1481
 * @param bool $checkIfUserOnline
1482
 * @param bool $showPassword
1483
 * @param bool $loadExtraData
1484
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1485
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1486
 * @param bool $updateCache              update apc cache if exists
1487
 *
1488
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1489
 *
1490
 * @author Patrick Cool <[email protected]>
1491
 * @author Julio Montoya
1492
 *
1493
 * @version 21 September 2004
1494
 */
1495
function api_get_user_info(
1496
    $user_id = 0,
1497
    $checkIfUserOnline = false,
1498
    $showPassword = false,
1499
    $loadExtraData = false,
1500
    $loadOnlyVisibleExtraData = false,
1501
    $loadAvatars = true,
1502
    $updateCache = false
1503
) {
1504
    // Make sure user_id is safe
1505
    $user_id = (int) $user_id;
1506
    $user = false;
1507
    if (empty($user_id)) {
1508
        $userFromSession = Session::read('_user');
1509
        if (isset($userFromSession) && !empty($userFromSession)) {
1510
            return $userFromSession;
1511
            /*
1512
            return _api_format_user(
1513
                $userFromSession,
1514
                $showPassword,
1515
                $loadAvatars
1516
            );*/
1517
        }
1518
1519
        return false;
1520
    }
1521
1522
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1523
            WHERE id = $user_id";
1524
    $result = Database::query($sql);
1525
    if (Database::num_rows($result) > 0) {
1526
        $result_array = Database::fetch_array($result);
1527
        $result_array['user_is_online_in_chat'] = 0;
1528
        if ($checkIfUserOnline) {
1529
            $use_status_in_platform = user_is_online($user_id);
1530
            $result_array['user_is_online'] = $use_status_in_platform;
1531
            $user_online_in_chat = 0;
1532
            if ($use_status_in_platform) {
1533
                $user_status = UserManager::get_extra_user_data_by_field(
1534
                    $user_id,
1535
                    'user_chat_status',
1536
                    false,
1537
                    true
1538
                );
1539
                if (1 == (int) $user_status['user_chat_status']) {
1540
                    $user_online_in_chat = 1;
1541
                }
1542
            }
1543
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1544
        }
1545
1546
        if ($loadExtraData) {
1547
            $fieldValue = new ExtraFieldValue('user');
1548
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1549
                $user_id,
1550
                $loadOnlyVisibleExtraData
1551
            );
1552
        }
1553
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1554
    }
1555
1556
    return $user;
1557
}
1558
1559
function api_get_user_info_from_entity(
1560
    User $user,
1561
    $checkIfUserOnline = false,
1562
    $showPassword = false,
1563
    $loadExtraData = false,
1564
    $loadOnlyVisibleExtraData = false,
1565
    $loadAvatars = true,
1566
    $loadCertificate = false
1567
) {
1568
    if (!$user instanceof UserInterface) {
1569
        return false;
1570
    }
1571
1572
    // Make sure user_id is safe
1573
    $user_id = (int) $user->getId();
1574
1575
    if (empty($user_id)) {
1576
        $userFromSession = Session::read('_user');
1577
1578
        if (isset($userFromSession) && !empty($userFromSession)) {
1579
            return $userFromSession;
1580
        }
1581
1582
        return false;
1583
    }
1584
1585
    $result = [];
1586
    $result['user_is_online_in_chat'] = 0;
1587
    if ($checkIfUserOnline) {
1588
        $use_status_in_platform = user_is_online($user_id);
1589
        $result['user_is_online'] = $use_status_in_platform;
1590
        $user_online_in_chat = 0;
1591
        if ($use_status_in_platform) {
1592
            $user_status = UserManager::get_extra_user_data_by_field(
1593
                $user_id,
1594
                'user_chat_status',
1595
                false,
1596
                true
1597
            );
1598
            if (1 == (int) $user_status['user_chat_status']) {
1599
                $user_online_in_chat = 1;
1600
            }
1601
        }
1602
        $result['user_is_online_in_chat'] = $user_online_in_chat;
1603
    }
1604
1605
    if ($loadExtraData) {
1606
        $fieldValue = new ExtraFieldValue('user');
1607
        $result['extra'] = $fieldValue->getAllValuesForAnItem(
1608
            $user_id,
1609
            $loadOnlyVisibleExtraData
1610
        );
1611
    }
1612
1613
    $result['username'] = $user->getUsername();
1614
    $result['status'] = $user->getStatus();
1615
    $result['firstname'] = $user->getFirstname();
1616
    $result['lastname'] = $user->getLastname();
1617
    $result['email'] = $result['mail'] = $user->getEmail();
1618
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1619
    $result['complete_name_with_username'] = $result['complete_name'];
1620
1621
    if (!empty($result['username']) && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
1622
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$result['username'].')';
1623
    }
1624
1625
    $showEmail = 'true' === api_get_setting('show_email_addresses');
1626
    if (!empty($result['email'])) {
1627
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$result['email'].')';
1628
        if ($showEmail) {
1629
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$result['email'].')';
1630
        }
1631
    } else {
1632
        $result['complete_name_with_email'] = $result['complete_name'];
1633
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1634
    }
1635
1636
    // Kept for historical reasons
1637
    $result['firstName'] = $result['firstname'];
1638
    $result['lastName'] = $result['lastname'];
1639
1640
    $attributes = [
1641
        'picture_uri',
1642
        'last_login',
1643
        'user_is_online',
1644
    ];
1645
1646
    $result['phone'] = $user->getPhone();
1647
    $result['address'] = $user->getAddress();
1648
    $result['official_code'] = $user->getOfficialCode();
1649
    $result['active'] = $user->isActive();
1650
    $result['auth_source'] = $user->getAuthSource();
1651
    $result['language'] = $user->getLocale();
1652
    $result['creator_id'] = $user->getCreatorId();
1653
    $result['registration_date'] = $user->getRegistrationDate()->format('Y-m-d H:i:s');
1654
    $result['hr_dept_id'] = $user->getHrDeptId();
1655
    $result['expiration_date'] = '';
1656
    if ($user->getExpirationDate()) {
1657
        $result['expiration_date'] = $user->getExpirationDate()->format('Y-m-d H:i:s');
1658
    }
1659
1660
    $result['last_login'] = null;
1661
    if ($user->getLastLogin()) {
1662
        $result['last_login'] = $user->getLastLogin()->format('Y-m-d H:i:s');
1663
    }
1664
1665
    $result['competences'] = $user->getCompetences();
1666
    $result['diplomas'] = $user->getDiplomas();
1667
    $result['teach'] = $user->getTeach();
1668
    $result['openarea'] = $user->getOpenarea();
1669
    $user_id = (int) $user->getId();
1670
1671
    // Maintain the user_id index for backwards compatibility
1672
    $result['user_id'] = $result['id'] = $user_id;
1673
1674
    if ($loadCertificate) {
1675
        $hasCertificates = Certificate::getCertificateByUser($user_id);
1676
        $result['has_certificates'] = 0;
1677
        if (!empty($hasCertificates)) {
1678
            $result['has_certificates'] = 1;
1679
        }
1680
    }
1681
1682
    $result['icon_status'] = '';
1683
    $result['icon_status_medium'] = '';
1684
    $result['is_admin'] = UserManager::is_admin($user_id);
1685
1686
    // Getting user avatar.
1687
    if ($loadAvatars) {
1688
        $result['avatar'] = '';
1689
        $result['avatar_no_query'] = '';
1690
        $result['avatar_small'] = '';
1691
        $result['avatar_medium'] = '';
1692
        $urlImg = '/';
1693
        $iconStatus = '';
1694
        $iconStatusMedium = '';
1695
1696
        switch ($user->getStatus()) {
1697
            case STUDENT:
1698
                if (isset($result['has_certificates']) && $result['has_certificates']) {
1699
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1700
                } else {
1701
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1702
                }
1703
                break;
1704
            case COURSEMANAGER:
1705
                if ($result['is_admin']) {
1706
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1707
                } else {
1708
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1709
                }
1710
                break;
1711
            case STUDENT_BOSS:
1712
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1713
                break;
1714
        }
1715
1716
        if (!empty($iconStatus)) {
1717
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1718
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1719
        }
1720
1721
        $result['icon_status'] = $iconStatus;
1722
        $result['icon_status_medium'] = $iconStatusMedium;
1723
    }
1724
1725
    if (isset($result['user_is_online'])) {
1726
        $result['user_is_online'] = true == $result['user_is_online'] ? 1 : 0;
1727
    }
1728
    if (isset($result['user_is_online_in_chat'])) {
1729
        $result['user_is_online_in_chat'] = $result['user_is_online_in_chat'];
1730
    }
1731
1732
    $result['password'] = '';
1733
    if ($showPassword) {
1734
        $result['password'] = $user->getPassword();
1735
    }
1736
1737
    if (isset($result['profile_completed'])) {
1738
        $result['profile_completed'] = $result['profile_completed'];
1739
    }
1740
1741
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1742
1743
    // Send message link
1744
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1745
    $result['complete_name_with_message_link'] = Display::url(
1746
        $result['complete_name_with_username'],
1747
        $sendMessage,
1748
        ['class' => 'ajax']
1749
    );
1750
1751
    if (isset($result['extra'])) {
1752
        $result['extra'] = $result['extra'];
1753
    }
1754
1755
    return $result;
1756
}
1757
1758
function api_get_lp_entity(int $id): ?CLp
1759
{
1760
    return Database::getManager()->getRepository(CLp::class)->find($id);
1761
}
1762
1763
function api_get_user_entity(int $userId = 0): ?User
1764
{
1765
    $userId = $userId ?: api_get_user_id();
1766
    $repo = Container::getUserRepository();
1767
1768
    return $repo->find($userId);
1769
}
1770
1771
function api_get_current_user(): ?User
1772
{
1773
    $isLoggedIn = Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED_REMEMBERED');
1774
    if (false === $isLoggedIn) {
1775
        return null;
1776
    }
1777
1778
    $token = Container::getTokenStorage()->getToken();
1779
1780
    if (null !== $token) {
1781
        return $token->getUser();
1782
    }
1783
1784
    return null;
1785
}
1786
1787
/**
1788
 * Finds all the information about a user from username instead of user id.
1789
 *
1790
 * @param string $username
1791
 *
1792
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1793
 *
1794
 * @author Yannick Warnier <[email protected]>
1795
 */
1796
function api_get_user_info_from_username($username)
1797
{
1798
    if (empty($username)) {
1799
        return false;
1800
    }
1801
    $username = trim($username);
1802
1803
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1804
            WHERE username='".Database::escape_string($username)."'";
1805
    $result = Database::query($sql);
1806
    if (Database::num_rows($result) > 0) {
1807
        $resultArray = Database::fetch_array($result);
1808
1809
        return _api_format_user($resultArray);
1810
    }
1811
1812
    return false;
1813
}
1814
1815
/**
1816
 * Get first user with an email.
1817
 *
1818
 * @param string $email
1819
 *
1820
 * @return array|bool
1821
 */
1822
function api_get_user_info_from_email($email = '')
1823
{
1824
    if (empty($email)) {
1825
        return false;
1826
    }
1827
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1828
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1829
    $result = Database::query($sql);
1830
    if (Database::num_rows($result) > 0) {
1831
        $resultArray = Database::fetch_array($result);
1832
1833
        return _api_format_user($resultArray);
1834
    }
1835
1836
    return false;
1837
}
1838
1839
/**
1840
 * @return string
1841
 */
1842
function api_get_course_id()
1843
{
1844
    return Session::read('_cid', null);
1845
}
1846
1847
/**
1848
 * Returns the current course id (integer).
1849
 *
1850
 * @param ?string $code Optional course code
1851
 *
1852
 * @return int
1853
 */
1854
function api_get_course_int_id(?string $code = null): int
1855
{
1856
    if (!empty($code)) {
1857
        $code = Database::escape_string($code);
1858
        $row = Database::select(
1859
            'id',
1860
            Database::get_main_table(TABLE_MAIN_COURSE),
1861
            ['where' => ['code = ?' => [$code]]],
1862
            'first'
1863
        );
1864
1865
        if (is_array($row) && isset($row['id'])) {
1866
            return $row['id'];
1867
        } else {
1868
            return 0;
1869
        }
1870
    }
1871
1872
    $cid = Session::read('_real_cid', 0);
1873
    if (empty($cid) && isset($_REQUEST['cid'])) {
1874
        $cid = (int) $_REQUEST['cid'];
1875
    }
1876
1877
    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...
1878
}
1879
1880
/**
1881
 * Gets a course setting from the current course_setting table. Try always using integer values.
1882
 *
1883
 * @param string       $settingName The name of the setting we want from the table
1884
 * @param Course|array $courseInfo
1885
 * @param bool         $force       force checking the value in the database
1886
 *
1887
 * @return mixed The value of that setting in that table. Return -1 if not found.
1888
 */
1889
function api_get_course_setting($settingName, $courseInfo = null, $force = false)
1890
{
1891
    if (empty($courseInfo)) {
1892
        $courseInfo = api_get_course_info();
1893
    }
1894
1895
    if (empty($courseInfo) || empty($settingName)) {
1896
        return -1;
1897
    }
1898
1899
    if ($courseInfo instanceof Course) {
1900
        $courseId = $courseInfo->getId();
1901
    } else {
1902
        $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
1903
    }
1904
1905
    if (empty($courseId)) {
1906
        return -1;
1907
    }
1908
1909
    static $courseSettingInfo = [];
1910
1911
    if ($force) {
1912
        $courseSettingInfo = [];
1913
    }
1914
1915
    if (!isset($courseSettingInfo[$courseId])) {
1916
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
1917
        $settingName = Database::escape_string($settingName);
1918
1919
        $sql = "SELECT variable, value FROM $table
1920
                WHERE c_id = $courseId ";
1921
        $res = Database::query($sql);
1922
        if (Database::num_rows($res) > 0) {
1923
            $result = Database::store_result($res, 'ASSOC');
1924
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
1925
1926
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
1927
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
1928
                if (!is_null($value)) {
1929
                    $result = explode(',', $value);
1930
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
1931
                }
1932
            }
1933
        }
1934
    }
1935
1936
    if (isset($courseSettingInfo[$courseId]) && isset($courseSettingInfo[$courseId][$settingName])) {
1937
        return $courseSettingInfo[$courseId][$settingName];
1938
    }
1939
1940
    return -1;
1941
}
1942
1943
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
1944
{
1945
    $value = api_get_course_setting($settingName, $courseInfo, true);
1946
1947
    if (-1 === $value) {
1948
        // Check global settings
1949
        $value = api_get_plugin_setting($plugin, $settingName);
1950
        if ('true' === $value) {
1951
            return 1;
1952
        }
1953
        if ('false' === $value) {
1954
            return 0;
1955
        }
1956
        if (null === $value) {
1957
            return -1;
1958
        }
1959
    }
1960
1961
    return $value;
1962
}
1963
1964
/**
1965
 * Gets an anonymous user ID.
1966
 *
1967
 * For some tools that need tracking, like the learnpath tool, it is necessary
1968
 * to have a usable user-id to enable some kind of tracking, even if not
1969
 * perfect. An anonymous ID is taken from the users table by looking for a
1970
 * status of "6" (anonymous).
1971
 *
1972
 * @return int User ID of the anonymous user, or O if no anonymous user found
1973
 */
1974
function api_get_anonymous_id()
1975
{
1976
    // Find if another anon is connected now
1977
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1978
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
1979
    $ip = Database::escape_string(api_get_real_ip());
1980
    $max = (int) api_get_setting('admin.max_anonymous_users');
1981
    if ($max >= 2) {
1982
        $sql = "SELECT * FROM $table as TEL
1983
                JOIN $tableU as U
1984
                ON U.id = TEL.login_user_id
1985
                WHERE TEL.user_ip = '$ip'
1986
                    AND U.status = ".ANONYMOUS."
1987
                    AND U.id != 2 ";
1988
1989
        $result = Database::query($sql);
1990
        if (empty(Database::num_rows($result))) {
1991
            $login = uniqid('anon_');
1992
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['registration_date ASC']);
1993
            if (count($anonList) >= $max) {
1994
                foreach ($anonList as $userToDelete) {
1995
                    UserManager::delete_user($userToDelete['user_id']);
1996
                    break;
1997
                }
1998
            }
1999
2000
            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...
2001
                $login,
2002
                'anon',
2003
                ANONYMOUS,
2004
                ' anonymous@localhost',
2005
                $login,
2006
                $login
2007
            );
2008
        } else {
2009
            $row = Database::fetch_assoc($result);
2010
2011
            return $row['id'];
2012
        }
2013
    }
2014
2015
    $table = Database::get_main_table(TABLE_MAIN_USER);
2016
    $sql = "SELECT id
2017
            FROM $table
2018
            WHERE status = ".ANONYMOUS." ";
2019
    $res = Database::query($sql);
2020
    if (Database::num_rows($res) > 0) {
2021
        $row = Database::fetch_assoc($res);
2022
2023
        return $row['id'];
2024
    }
2025
2026
    // No anonymous user was found.
2027
    return 0;
2028
}
2029
2030
/**
2031
 * @param int $courseId
2032
 * @param int $sessionId
2033
 * @param int $groupId
2034
 *
2035
 * @return string
2036
 */
2037
function api_get_cidreq_params($courseId, $sessionId = 0, $groupId = 0)
2038
{
2039
    $courseId = !empty($courseId) ? (int) $courseId : 0;
2040
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2041
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2042
2043
    $url = 'cid='.$courseId;
2044
    $url .= '&sid='.$sessionId;
2045
    $url .= '&gid='.$groupId;
2046
2047
    return $url;
2048
}
2049
2050
/**
2051
 * Returns the current course url part including session, group, and gradebook params.
2052
 *
2053
 * @param bool   $addSessionId
2054
 * @param bool   $addGroupId
2055
 * @param string $origin
2056
 *
2057
 * @return string Course & session references to add to a URL
2058
 */
2059
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2060
{
2061
    $courseId = api_get_course_int_id();
2062
    if (0 === $courseId && isset($_REQUEST['cid'])) {
2063
        $courseId = (int) $_REQUEST['cid'];
2064
    }
2065
    $url = empty($courseId) ? '' : 'cid='.$courseId;
2066
    $origin = empty($origin) ? api_get_origin() : Security::remove_XSS($origin);
2067
2068
    if ($addSessionId) {
2069
        if (!empty($url)) {
2070
            $sessionId = api_get_session_id();
2071
            if (0 === $sessionId && isset($_REQUEST['sid'])) {
2072
                $sessionId = (int) $_REQUEST['sid'];
2073
            }
2074
            $url .= 0 === $sessionId ? '&sid=0' : '&sid='.$sessionId;
2075
        }
2076
    }
2077
2078
    if ($addGroupId) {
2079
        if (!empty($url)) {
2080
            $url .= 0 == api_get_group_id() ? '&gid=0' : '&gid='.api_get_group_id();
2081
        }
2082
    }
2083
2084
    if (!empty($url)) {
2085
        $url .= '&gradebook='.(int) api_is_in_gradebook();
2086
        if (false !== $origin) {
2087
            $url .= '&origin=' . $origin;
0 ignored issues
show
Bug introduced by
Are you sure $origin of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

2087
            $url .= '&origin=' . /** @scrutinizer ignore-type */ $origin;
Loading history...
2088
        }
2089
    }
2090
2091
    return $url;
2092
}
2093
2094
/**
2095
 * Get if we visited a gradebook page.
2096
 *
2097
 * @return bool
2098
 */
2099
function api_is_in_gradebook()
2100
{
2101
    return Session::read('in_gradebook', false);
2102
}
2103
2104
/**
2105
 * Set that we are in a page inside a gradebook.
2106
 */
2107
function api_set_in_gradebook()
2108
{
2109
    Session::write('in_gradebook', true);
2110
}
2111
2112
/**
2113
 * Remove gradebook session.
2114
 */
2115
function api_remove_in_gradebook()
2116
{
2117
    Session::erase('in_gradebook');
2118
}
2119
2120
/**
2121
 * Returns the current course info array see api_format_course_array()
2122
 * If the course_code is given, the returned array gives info about that
2123
 * particular course, if none given it gets the course info from the session.
2124
 *
2125
 * @param string $courseCode
2126
 *
2127
 * @return array
2128
 */
2129
function api_get_course_info($courseCode = null)
2130
{
2131
    if (!empty($courseCode)) {
2132
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2133
2134
        return api_format_course_array($course);
2135
    }
2136
2137
    $course = Session::read('_course');
2138
    if ('-1' == $course) {
2139
        $course = [];
2140
    }
2141
2142
    if (empty($course) && isset($_REQUEST['cid'])) {
2143
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2144
    }
2145
2146
    return $course;
2147
}
2148
2149
/**
2150
 * @param int $courseId
2151
 */
2152
function api_get_course_entity($courseId = 0): ?Course
2153
{
2154
    if (empty($courseId)) {
2155
        $courseId = api_get_course_int_id();
2156
    }
2157
2158
    if (empty($courseId)) {
2159
        return null;
2160
    }
2161
2162
    return Container::getCourseRepository()->find($courseId);
2163
}
2164
2165
/**
2166
 * @param int $id
2167
 */
2168
function api_get_session_entity($id = 0): ?SessionEntity
2169
{
2170
    if (empty($id)) {
2171
        $id = api_get_session_id();
2172
    }
2173
2174
    if (empty($id)) {
2175
        return null;
2176
    }
2177
2178
    return Container::getSessionRepository()->find($id);
2179
}
2180
2181
/**
2182
 * @param int $id
2183
 */
2184
function api_get_group_entity($id = 0): ?CGroup
2185
{
2186
    if (empty($id)) {
2187
        $id = api_get_group_id();
2188
    }
2189
2190
    return Container::getGroupRepository()->find($id);
2191
}
2192
2193
/**
2194
 * @param int $id
2195
 */
2196
function api_get_url_entity($id = 0): ?AccessUrl
2197
{
2198
    if (empty($id)) {
2199
        $id = api_get_current_access_url_id();
2200
    }
2201
2202
    return Container::getAccessUrlRepository()->find($id);
2203
}
2204
2205
/**
2206
 * Returns the current course info array.
2207
2208
 * Now if the course_code is given, the returned array gives info about that
2209
 * particular course, not specially the current one.
2210
 *
2211
 * @param int $id Numeric ID of the course
2212
 *
2213
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2214
 */
2215
function api_get_course_info_by_id(?int $id = 0)
2216
{
2217
    if (empty($id)) {
2218
        $course = Session::read('_course', []);
2219
2220
        return $course;
2221
    }
2222
2223
    $course = Container::getCourseRepository()->find($id);
2224
    if (empty($course)) {
2225
        return [];
2226
    }
2227
2228
    return api_format_course_array($course);
2229
}
2230
2231
/**
2232
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2233
 * to switch from 'code' to 'id' in the array.
2234
 *
2235
 * @return array
2236
 *
2237
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2238
 */
2239
function api_format_course_array(Course $course = null)
2240
{
2241
    if (empty($course)) {
2242
        return [];
2243
    }
2244
2245
    $courseData = [];
2246
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2247
2248
    // Added
2249
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2250
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2251
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2252
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2253
    $courseData['titular'] = $course->getTutorName();
2254
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2255
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2256
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2257
2258
    $courseData['visibility'] = $course->getVisibility();
2259
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2260
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2261
    $courseData['activate_legal'] = $course->getActivateLegal();
2262
    $courseData['legal'] = $course->getLegal();
2263
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2264
    $courseData['video_url'] = $course->getVideoUrl();
2265
    $courseData['sticky'] = (int) $course->isSticky();
2266
2267
    $coursePath = '/course/';
2268
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2269
2270
    // Course password
2271
    $courseData['registration_code'] = $course->getRegistrationCode();
2272
    $courseData['disk_quota'] = $course->getDiskQuota();
2273
    $courseData['course_public_url'] = $webCourseHome;
2274
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2275
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2276
2277
    $image = Display::getMdiIcon(
2278
        ObjectIcon::COURSE,
2279
        'ch-tool-icon',
2280
        null,
2281
        ICON_SIZE_BIG
2282
    );
2283
2284
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2285
    if (!empty($illustration)) {
2286
        $image = $illustration;
2287
    }
2288
2289
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2290
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2291
2292
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2293
        $courseData['duration'] = $course->getDuration();
2294
    }
2295
2296
    return $courseData;
2297
}
2298
2299
/**
2300
 * Returns a difficult to guess password.
2301
 */
2302
function api_generate_password(int $length = 8, $useRequirements = true): string
2303
{
2304
    if ($length < 2) {
2305
        $length = 2;
2306
    }
2307
2308
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2309
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2310
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2311
    $minNumbers = 2;
2312
    $length = $length - $minNumbers;
2313
    $minLowerCase = round($length / 2);
2314
    $minUpperCase = $length - $minLowerCase;
2315
    $minSpecials = 1; // Default minimum special characters
2316
2317
    $password = '';
2318
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2319
2320
    $factory = new RandomLib\Factory();
2321
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2322
2323
    if (!empty($passwordRequirements)) {
2324
        $length = $passwordRequirements['min']['length'];
2325
        $minNumbers = $passwordRequirements['min']['numeric'];
2326
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2327
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2328
        $minSpecials = $passwordRequirements['min']['specials'];
2329
2330
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2331
        // Add the rest to fill the length requirement
2332
        if ($rest > 0) {
2333
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2334
        }
2335
    }
2336
2337
    // Min digits default 2
2338
    for ($i = 0; $i < $minNumbers; $i++) {
2339
        $password .= $generator->generateInt(2, 9);
2340
    }
2341
2342
    // Min lowercase
2343
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2344
2345
    // Min uppercase
2346
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2347
2348
    // Min special characters
2349
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2350
2351
    // Shuffle the password to ensure randomness
2352
    $password = str_shuffle($password);
2353
2354
    return $password;
2355
}
2356
2357
/**
2358
 * Checks a password to see wether it is OK to use.
2359
 *
2360
 * @param string $password
2361
 *
2362
 * @return bool if the password is acceptable, false otherwise
2363
 *              Notes about what a password "OK to use" is:
2364
 *              1. The password should be at least 5 characters long.
2365
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2366
 *              3. The password should contain at least 3 letters.
2367
 *              4. It should contain at least 2 digits.
2368
 *              Settings will change if the configuration value is set: password_requirements
2369
 */
2370
function api_check_password($password)
2371
{
2372
    $passwordRequirements = Security::getPasswordRequirements();
2373
2374
    $minLength = $passwordRequirements['min']['length'];
2375
    $minNumbers = $passwordRequirements['min']['numeric'];
2376
    // Optional
2377
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2378
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2379
2380
    $minLetters = $minLowerCase + $minUpperCase;
2381
    $passwordLength = api_strlen($password);
2382
2383
    $conditions = [
2384
        'min_length' => $passwordLength >= $minLength,
2385
    ];
2386
2387
    $digits = 0;
2388
    $lowerCase = 0;
2389
    $upperCase = 0;
2390
2391
    for ($i = 0; $i < $passwordLength; $i++) {
2392
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2393
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2394
            $upperCase++;
2395
        }
2396
2397
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2398
            $lowerCase++;
2399
        }
2400
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2401
            $digits++;
2402
        }
2403
    }
2404
2405
    // Min number of digits
2406
    $conditions['min_numeric'] = $digits >= $minNumbers;
2407
2408
    if (!empty($minUpperCase)) {
2409
        // Uppercase
2410
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2411
    }
2412
2413
    if (!empty($minLowerCase)) {
2414
        // Lowercase
2415
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2416
    }
2417
2418
    // Min letters
2419
    $letters = $upperCase + $lowerCase;
2420
    $conditions['min_letters'] = $letters >= $minLetters;
2421
2422
    $isPasswordOk = true;
2423
    foreach ($conditions as $condition) {
2424
        if (false === $condition) {
2425
            $isPasswordOk = false;
2426
            break;
2427
        }
2428
    }
2429
2430
    if (false === $isPasswordOk) {
2431
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2432
        $output .= Security::getPasswordRequirementsToString($conditions);
2433
2434
        Display::addFlash(Display::return_message($output, 'warning', false));
2435
    }
2436
2437
    return $isPasswordOk;
2438
}
2439
2440
/**
2441
 * Gets the current Chamilo (not PHP/cookie) session ID.
2442
 *
2443
 * @return int O if no active session, the session ID otherwise
2444
 */
2445
function api_get_session_id()
2446
{
2447
    return (int) Session::read('sid', 0);
2448
}
2449
2450
/**
2451
 * Gets the current Chamilo (not social network) group ID.
2452
 *
2453
 * @return int O if no active session, the session ID otherwise
2454
 */
2455
function api_get_group_id()
2456
{
2457
    return Session::read('gid', 0);
2458
}
2459
2460
/**
2461
 * Gets the current or given session name.
2462
 *
2463
 * @param   int     Session ID (optional)
2464
 *
2465
 * @return string The session name, or null if not found
2466
 */
2467
function api_get_session_name($session_id = 0)
2468
{
2469
    if (empty($session_id)) {
2470
        $session_id = api_get_session_id();
2471
        if (empty($session_id)) {
2472
            return null;
2473
        }
2474
    }
2475
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2476
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2477
    $r = Database::query($s);
2478
    $c = Database::num_rows($r);
2479
    if ($c > 0) {
2480
        //technically, there can be only one, but anyway we take the first
2481
        $rec = Database::fetch_array($r);
2482
2483
        return $rec['title'];
2484
    }
2485
2486
    return null;
2487
}
2488
2489
/**
2490
 * Gets the session info by id.
2491
 *
2492
 * @param int $id Session ID
2493
 *
2494
 * @return array information of the session
2495
 */
2496
function api_get_session_info($id)
2497
{
2498
    return SessionManager::fetch($id);
2499
}
2500
2501
/**
2502
 * Gets the session visibility by session id.
2503
 *
2504
 * @param int  $session_id
2505
 * @param int  $courseId
2506
 * @param bool $ignore_visibility_for_admins
2507
 *
2508
 * @return int
2509
 *             0 = session still available,
2510
 *             SESSION_VISIBLE_READ_ONLY = 1,
2511
 *             SESSION_VISIBLE = 2,
2512
 *             SESSION_INVISIBLE = 3
2513
 */
2514
function api_get_session_visibility(
2515
    $session_id,
2516
    $courseId = null,
2517
    $ignore_visibility_for_admins = true,
2518
    $userId = 0
2519
) {
2520
    if (api_is_platform_admin()) {
2521
        if ($ignore_visibility_for_admins) {
2522
            return SESSION_AVAILABLE;
2523
        }
2524
    }
2525
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2526
2527
    $now = time();
2528
    if (empty($session_id)) {
2529
        return 0; // Means that the session is still available.
2530
    }
2531
2532
    $session_id = (int) $session_id;
2533
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2534
2535
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2536
2537
    if (Database::num_rows($result) <= 0) {
2538
        return SESSION_INVISIBLE;
2539
    }
2540
2541
    $row = Database::fetch_assoc($result);
2542
    $visibility = $row['visibility'];
2543
2544
    // I don't care the session visibility.
2545
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2546
        // Session duration per student.
2547
        if (isset($row['duration']) && !empty($row['duration'])) {
2548
            $duration = $row['duration'] * 24 * 60 * 60;
2549
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2550
2551
            // If there is a session duration but there is no previous
2552
            // access by the user, then the session is still available
2553
            if (0 == count($courseAccess)) {
2554
                return SESSION_AVAILABLE;
2555
            }
2556
2557
            $currentTime = time();
2558
            $firstAccess = isset($courseAccess['login_course_date'])
2559
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2560
                : 0;
2561
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2562
            $userDuration = isset($userDurationData['duration'])
2563
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2564
                : 0;
2565
2566
            $totalDuration = $firstAccess + $duration + $userDuration;
2567
2568
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2569
        }
2570
2571
        return SESSION_AVAILABLE;
2572
    }
2573
2574
    // If start date was set.
2575
    if (!empty($row['access_start_date'])) {
2576
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2577
    } else {
2578
        // If there's no start date, assume it's available until the end date
2579
        $visibility = SESSION_AVAILABLE;
2580
    }
2581
2582
    // If the end date was set.
2583
    if (!empty($row['access_end_date'])) {
2584
        // Only if date_start said that it was ok
2585
        if (SESSION_AVAILABLE === $visibility) {
2586
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2587
                ? SESSION_AVAILABLE // Date still available
2588
                : $row['visibility']; // Session ends
2589
        }
2590
    }
2591
2592
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2593
    $isCoach = api_is_coach($session_id, $courseId);
2594
2595
    if ($isCoach) {
2596
        // Test start date.
2597
        if (!empty($row['coach_access_start_date'])) {
2598
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2599
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2600
        }
2601
2602
        // Test end date.
2603
        if (!empty($row['coach_access_end_date'])) {
2604
            if (SESSION_AVAILABLE === $visibility) {
2605
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2606
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2607
            }
2608
        }
2609
    }
2610
2611
    return $visibility;
2612
}
2613
2614
/**
2615
 * This function returns a (star) session icon if the session is not null and
2616
 * the user is not a student.
2617
 *
2618
 * @param int $sessionId
2619
 * @param int $statusId  User status id - if 5 (student), will return empty
2620
 *
2621
 * @return string Session icon
2622
 */
2623
function api_get_session_image($sessionId, User $user)
2624
{
2625
    $sessionId = (int) $sessionId;
2626
    $image = '';
2627
    if (!$user->hasRole('ROLE_STUDENT')) {
2628
        // Check whether is not a student
2629
        if ($sessionId > 0) {
2630
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2631
                ObjectIcon::STAR,
2632
                'ch-tool-icon',
2633
                'align:absmiddle;',
2634
                ICON_SIZE_SMALL,
2635
                get_lang('Session-specific resource')
2636
            );
2637
        }
2638
    }
2639
2640
    return $image;
2641
}
2642
2643
/**
2644
 * This function add an additional condition according to the session of the course.
2645
 *
2646
 * @param int    $session_id        session id
2647
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2648
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2649
 *                                  false for strict session condition
2650
 * @param string $session_field
2651
 *
2652
 * @return string condition of the session
2653
 */
2654
function api_get_session_condition(
2655
    $session_id,
2656
    $and = true,
2657
    $with_base_content = false,
2658
    $session_field = 'session_id'
2659
) {
2660
    $session_id = (int) $session_id;
2661
2662
    if (empty($session_field)) {
2663
        $session_field = 'session_id';
2664
    }
2665
    // Condition to show resources by session
2666
    $condition_add = $and ? ' AND ' : ' WHERE ';
2667
2668
    if ($with_base_content) {
2669
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2670
    } else {
2671
        if (empty($session_id)) {
2672
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2673
        } else {
2674
            $condition_session = $condition_add." $session_field = $session_id ";
2675
        }
2676
    }
2677
2678
    return $condition_session;
2679
}
2680
2681
/**
2682
 * Returns the value of a setting from the web-adjustable admin config settings.
2683
 *
2684
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2685
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2686
 * instead of
2687
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2688
 *
2689
 * @param string $variable The variable name
2690
 *
2691
 * @return string|array
2692
 */
2693
function api_get_setting($variable, $isArray = false, $key = null)
2694
{
2695
    $settingsManager = Container::getSettingsManager();
2696
    if (empty($settingsManager)) {
2697
        return '';
2698
    }
2699
    $variable = trim($variable);
2700
2701
    switch ($variable) {
2702
        case 'server_type':
2703
            $test = ['dev', 'test'];
2704
            $environment = Container::getEnvironment();
2705
            if (in_array($environment, $test)) {
2706
                return 'test';
2707
            }
2708
2709
            return 'prod';
2710
        // deprecated settings
2711
        // no break
2712
        case 'openid_authentication':
2713
        case 'service_ppt2lp':
2714
        case 'formLogin_hide_unhide_label':
2715
            return false;
2716
            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...
2717
        case 'tool_visible_by_default_at_creation':
2718
            $values = $settingsManager->getSetting($variable);
2719
            $newResult = [];
2720
            foreach ($values as $parameter) {
2721
                $newResult[$parameter] = 'true';
2722
            }
2723
2724
            return $newResult;
2725
            break;
2726
        default:
2727
            $settingValue = $settingsManager->getSetting($variable, true);
2728
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2729
                // Check if the value is a valid JSON string
2730
                $decodedValue = json_decode($settingValue, true);
2731
2732
                // If it's a valid JSON string and the result is an array, return it
2733
                if (is_array($decodedValue)) {
2734
                    return $decodedValue;
2735
                }
2736
2737
                // If it's not an array, continue with the normal flow
2738
                // Optional: If you need to evaluate the value using eval
2739
                $strArrayValue = rtrim($settingValue, ';');
2740
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2741
                if (is_array($value)) {
2742
                    return $value;
2743
                }
2744
            }
2745
2746
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2747
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2748
                return $settingValue[$variable][$key];
2749
            }
2750
2751
            return $settingValue;
2752
            break;
2753
    }
2754
}
2755
2756
/**
2757
 * @param string $variable
2758
 * @param string $option
2759
 *
2760
 * @return bool
2761
 */
2762
function api_get_setting_in_list($variable, $option)
2763
{
2764
    $value = api_get_setting($variable);
2765
2766
    return in_array($option, $value);
2767
}
2768
2769
/**
2770
 * @param string $plugin
2771
 * @param string $variable
2772
 *
2773
 * @return string
2774
 */
2775
function api_get_plugin_setting($plugin, $variable)
2776
{
2777
    $variableName = $plugin.'_'.$variable;
2778
    //$result = api_get_setting($variableName);
2779
    $params = [
2780
        'category = ? AND subkey = ? AND variable = ?' => [
2781
            'Plugins',
2782
            $plugin,
2783
            $variableName,
2784
        ],
2785
    ];
2786
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2787
    $result = Database::select(
2788
        'selected_value',
2789
        $table,
2790
        ['where' => $params],
2791
        'one'
2792
    );
2793
    if ($result) {
2794
        $value = $result['selected_value'];
2795
        $serializedValue = @unserialize($result['selected_value'], []);
2796
        if (false !== $serializedValue) {
2797
            $value = $serializedValue;
2798
        }
2799
2800
        return $value;
2801
    }
2802
2803
    return null;
2804
    /// Old code
2805
2806
    $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...
2807
    $result = api_get_setting($variableName);
2808
2809
    if (isset($result[$plugin])) {
2810
        $value = $result[$plugin];
2811
2812
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2813
2814
        if (false !== $unserialized) {
2815
            $value = $unserialized;
2816
        }
2817
2818
        return $value;
2819
    }
2820
2821
    return null;
2822
}
2823
2824
/**
2825
 * Returns the value of a setting from the web-adjustable admin config settings.
2826
 */
2827
function api_get_settings_params($params)
2828
{
2829
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2830
2831
    return Database::select('*', $table, ['where' => $params]);
2832
}
2833
2834
/**
2835
 * @param array $params example: [id = ? => '1']
2836
 *
2837
 * @return array
2838
 */
2839
function api_get_settings_params_simple($params)
2840
{
2841
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2842
2843
    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...
2844
}
2845
2846
/**
2847
 * Returns the value of a setting from the web-adjustable admin config settings.
2848
 */
2849
function api_delete_settings_params($params)
2850
{
2851
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2852
2853
    return Database::delete($table, $params);
2854
}
2855
2856
/**
2857
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2858
 *
2859
 * @return string Escaped version of $_SERVER['PHP_SELF']
2860
 */
2861
function api_get_self()
2862
{
2863
    return htmlentities($_SERVER['PHP_SELF']);
2864
}
2865
2866
/**
2867
 * Checks whether current user is a platform administrator.
2868
 *
2869
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2870
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2871
 *
2872
 * @return bool true if the user has platform admin rights,
2873
 *              false otherwise
2874
 *
2875
 * @see usermanager::is_admin(user_id) for a user-id specific function
2876
 */
2877
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2878
{
2879
    $currentUser = api_get_current_user();
2880
2881
    if (null === $currentUser) {
2882
        return false;
2883
    }
2884
2885
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2886
2887
    if ($isAdmin) {
2888
        return true;
2889
    }
2890
2891
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2892
        return true;
2893
    }
2894
2895
    if ($allowDrh && $currentUser->hasRole('ROLE_HR')) {
2896
        return true;
2897
    }
2898
2899
    return false;
2900
}
2901
2902
/**
2903
 * Checks whether the user given as user id is in the admin table.
2904
 *
2905
 * @param int $user_id If none provided, will use current user
2906
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2907
 *
2908
 * @return bool True if the user is admin, false otherwise
2909
 */
2910
function api_is_platform_admin_by_id($user_id = null, $url = null)
2911
{
2912
    $user_id = (int) $user_id;
2913
    if (empty($user_id)) {
2914
        $user_id = api_get_user_id();
2915
    }
2916
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2917
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2918
    $res = Database::query($sql);
2919
    $is_admin = 1 === Database::num_rows($res);
2920
    if (!$is_admin || !isset($url)) {
2921
        return $is_admin;
2922
    }
2923
    // We get here only if $url is set
2924
    $url = (int) $url;
2925
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2926
    $sql = "SELECT * FROM $url_user_table
2927
            WHERE access_url_id = $url AND user_id = $user_id";
2928
    $res = Database::query($sql);
2929
2930
    return 1 === Database::num_rows($res);
2931
}
2932
2933
/**
2934
 * Checks whether current user is allowed to create courses.
2935
 *
2936
 * @return bool true if the user has course creation rights,
2937
 *              false otherwise
2938
 */
2939
function api_is_allowed_to_create_course()
2940
{
2941
    if (api_is_platform_admin()) {
2942
        return true;
2943
    }
2944
2945
    // Teachers can only create courses
2946
    if (api_is_teacher()) {
2947
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2948
            return true;
2949
        } else {
2950
            return false;
2951
        }
2952
    }
2953
2954
    return Session::read('is_allowedCreateCourse');
2955
}
2956
2957
/**
2958
 * Checks whether the current user is a course administrator.
2959
 *
2960
 * @return bool True if current user is a course administrator
2961
 */
2962
function api_is_course_admin()
2963
{
2964
    if (api_is_platform_admin()) {
2965
        return true;
2966
    }
2967
2968
    $user = api_get_current_user();
2969
    if ($user) {
2970
        if (
2971
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2972
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2973
        ) {
2974
            return true;
2975
        }
2976
    }
2977
2978
    return false;
2979
}
2980
2981
/**
2982
 * Checks whether the current user is a course coach
2983
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2984
 *
2985
 * @return bool True if current user is a course coach
2986
 */
2987
function api_is_session_general_coach()
2988
{
2989
    return Session::read('is_session_general_coach');
2990
}
2991
2992
/**
2993
 * Checks whether the current user is a course tutor
2994
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2995
 *
2996
 * @return bool True if current user is a course tutor
2997
 */
2998
function api_is_course_tutor()
2999
{
3000
    return Session::read('is_courseTutor');
3001
}
3002
3003
/**
3004
 * @param int $user_id
3005
 * @param int $courseId
3006
 * @param int $session_id
3007
 *
3008
 * @return bool
3009
 */
3010
function api_is_course_session_coach($user_id, $courseId, $session_id)
3011
{
3012
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3013
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3014
3015
    $user_id = (int) $user_id;
3016
    $session_id = (int) $session_id;
3017
    $courseId = (int) $courseId;
3018
3019
    $sql = "SELECT DISTINCT session.id
3020
            FROM $session_table
3021
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3022
            ON session.id = session_rc_ru.session_id
3023
            WHERE
3024
                session_rc_ru.user_id = '".$user_id."'  AND
3025
                session_rc_ru.c_id = '$courseId' AND
3026
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3027
                session_rc_ru.session_id = '$session_id'";
3028
    $result = Database::query($sql);
3029
3030
    return Database::num_rows($result) > 0;
3031
}
3032
3033
/**
3034
 * Checks whether the current user is a course or session coach.
3035
 *
3036
 * @param int $session_id
3037
 * @param int $courseId
3038
 * @param bool  Check whether we are in student view and, if we are, return false
3039
 * @param int $userId
3040
 *
3041
 * @return bool True if current user is a course or session coach
3042
 */
3043
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3044
{
3045
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3046
3047
    if (!empty($session_id)) {
3048
        $session_id = (int) $session_id;
3049
    } else {
3050
        $session_id = api_get_session_id();
3051
    }
3052
3053
    // The student preview was on
3054
    if ($check_student_view && api_is_student_view_active()) {
3055
        return false;
3056
    }
3057
3058
    if (!empty($courseId)) {
3059
        $courseId = (int) $courseId;
3060
    } else {
3061
        $courseId = api_get_course_int_id();
3062
    }
3063
3064
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3065
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3066
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3067
    $sessionIsCoach = [];
3068
3069
    if (!empty($courseId)) {
3070
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3071
                FROM $session_table s
3072
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3073
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3074
                WHERE
3075
                    session_rc_ru.c_id = '$courseId' AND
3076
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3077
                    session_rc_ru.session_id = '$session_id'";
3078
        $result = Database::query($sql);
3079
        $sessionIsCoach = Database::store_result($result);
3080
    }
3081
3082
    if (!empty($session_id)) {
3083
        $sql = "SELECT DISTINCT s.id
3084
                FROM $session_table AS s
3085
                INNER JOIN $tblSessionRelUser sru
3086
                ON s.id = sru.session_id
3087
                WHERE
3088
                    sru.user_id = $userId AND
3089
                    s.id = $session_id AND
3090
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3091
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3092
        $result = Database::query($sql);
3093
        if (!empty($sessionIsCoach)) {
3094
            $sessionIsCoach = array_merge(
3095
                $sessionIsCoach,
3096
                Database::store_result($result)
3097
            );
3098
        } else {
3099
            $sessionIsCoach = Database::store_result($result);
3100
        }
3101
    }
3102
3103
    return count($sessionIsCoach) > 0;
3104
}
3105
3106
function api_user_has_role(string $role, ?User $user = null): bool
3107
{
3108
    if (null === $user) {
3109
        $user = api_get_current_user();
3110
    }
3111
3112
    if (null === $user) {
3113
        return false;
3114
    }
3115
3116
    return $user->hasRole($role);
3117
}
3118
3119
function api_is_allowed_in_course(): bool
3120
{
3121
    if (api_is_platform_admin()) {
3122
        return true;
3123
    }
3124
3125
    $user = api_get_current_user();
3126
    if ($user instanceof User) {
3127
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3128
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3129
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3130
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3131
        ) {
3132
            return true;
3133
        }
3134
    }
3135
3136
    return false;
3137
}
3138
3139
/**
3140
 * Checks whether current user is a student boss.
3141
 */
3142
function api_is_student_boss(?User $user = null): bool
3143
{
3144
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3145
}
3146
3147
/**
3148
 * Checks whether the current user is a session administrator.
3149
 *
3150
 * @return bool True if current user is a course administrator
3151
 */
3152
function api_is_session_admin(?User $user = null)
3153
{
3154
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3155
}
3156
3157
/**
3158
 * Checks whether the current user is a human resources manager.
3159
 *
3160
 * @return bool True if current user is a human resources manager
3161
 */
3162
function api_is_drh()
3163
{
3164
    return api_user_has_role('ROLE_HR');
3165
}
3166
3167
/**
3168
 * Checks whether the current user is a student.
3169
 *
3170
 * @return bool True if current user is a human resources manager
3171
 */
3172
function api_is_student()
3173
{
3174
    return api_user_has_role('ROLE_STUDENT');
3175
}
3176
3177
/**
3178
 * Checks whether the current user has the status 'teacher'.
3179
 *
3180
 * @return bool True if current user is a human resources manager
3181
 */
3182
function api_is_teacher()
3183
{
3184
    return api_user_has_role('ROLE_TEACHER');
3185
}
3186
3187
/**
3188
 * Checks whether the current user is a invited user.
3189
 *
3190
 * @return bool
3191
 */
3192
function api_is_invitee()
3193
{
3194
    return api_user_has_role('ROLE_INVITEE');
3195
}
3196
3197
/**
3198
 * This function checks whether a session is assigned into a category.
3199
 *
3200
 * @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...
3201
 * @param string    - category name
3202
 *
3203
 * @return bool - true if is found, otherwise false
3204
 */
3205
function api_is_session_in_category($session_id, $category_name)
3206
{
3207
    $session_id = (int) $session_id;
3208
    $category_name = Database::escape_string($category_name);
3209
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3210
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3211
3212
    $sql = "SELECT 1
3213
            FROM $tbl_session
3214
            WHERE $session_id IN (
3215
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3216
                WHERE
3217
                  s.session_category_id = sc.id AND
3218
                  sc.name LIKE '%$category_name'
3219
            )";
3220
    $rs = Database::query($sql);
3221
3222
    if (Database::num_rows($rs) > 0) {
3223
        return true;
3224
    }
3225
3226
    return false;
3227
}
3228
3229
/**
3230
 * Displays options for switching between student view and course manager view.
3231
 *
3232
 * Changes in version 1.2 (Patrick Cool)
3233
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3234
 * is changed explicitly
3235
 *
3236
 * Changes in version 1.1 (Patrick Cool)
3237
 * student view now works correctly in subfolders of the document tool
3238
 * student view works correctly in the new links tool
3239
 *
3240
 * Example code for using this in your tools:
3241
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3242
 * //   display_tool_view_option($isStudentView);
3243
 * //}
3244
 * //and in later sections, use api_is_allowed_to_edit()
3245
 *
3246
 * @author Roan Embrechts
3247
 * @author Patrick Cool
3248
 * @author Julio Montoya, changes added in Chamilo
3249
 *
3250
 * @version 1.2
3251
 *
3252
 * @todo rewrite code so it is easier to understand
3253
 */
3254
function api_display_tool_view_option()
3255
{
3256
    if ('true' != api_get_setting('student_view_enabled')) {
3257
        return '';
3258
    }
3259
3260
    $sourceurl = '';
3261
    $is_framed = false;
3262
    // Exceptions apply for all multi-frames pages
3263
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3264
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3265
        return '';
3266
    }
3267
3268
    // Uncomment to remove student view link from document view page
3269
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3270
        if (empty($_GET['lp_id'])) {
3271
            return '';
3272
        }
3273
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3274
        $sourceurl = str_replace(
3275
            'lp/lp_header.php',
3276
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3277
            $sourceurl
3278
        );
3279
        //showinframes doesn't handle student view anyway...
3280
        //return '';
3281
        $is_framed = true;
3282
    }
3283
3284
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3285
    if (!$is_framed) {
3286
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3287
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3288
        } else {
3289
            $sourceurl = $_SERVER['REQUEST_URI'];
3290
        }
3291
    }
3292
3293
    $output_string = '';
3294
    if (!empty($_SESSION['studentview'])) {
3295
        if ('studentview' == $_SESSION['studentview']) {
3296
            // We have to remove the isStudentView=true from the $sourceurl
3297
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3298
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3299
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3300
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3301
        } elseif ('teacherview' == $_SESSION['studentview']) {
3302
            // Switching to teacherview
3303
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3304
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3305
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3306
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3307
        }
3308
    } else {
3309
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3310
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3311
    }
3312
    $output_string = Security::remove_XSS($output_string);
3313
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3314
3315
    return $html;
3316
}
3317
3318
/**
3319
 * Function that removes the need to directly use is_courseAdmin global in
3320
 * tool scripts. It returns true or false depending on the user's rights in
3321
 * this particular course.
3322
 * Optionally checking for tutor and coach roles here allows us to use the
3323
 * student_view feature altogether with these roles as well.
3324
 *
3325
 * @param bool  Whether to check if the user has the tutor role
3326
 * @param bool  Whether to check if the user has the coach role
3327
 * @param bool  Whether to check if the user has the session coach role
3328
 * @param bool  check the student view or not
3329
 *
3330
 * @author Roan Embrechts
3331
 * @author Patrick Cool
3332
 * @author Julio Montoya
3333
 *
3334
 * @version 1.1, February 2004
3335
 *
3336
 * @return bool true: the user has the rights to edit, false: he does not
3337
 */
3338
function api_is_allowed_to_edit(
3339
    $tutor = false,
3340
    $coach = false,
3341
    $session_coach = false,
3342
    $check_student_view = true
3343
) {
3344
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3345
    // Admins can edit anything.
3346
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3347
        //The student preview was on
3348
        if ($check_student_view && api_is_student_view_active()) {
3349
            return false;
3350
        }
3351
3352
        return true;
3353
    }
3354
3355
    $sessionId = api_get_session_id();
3356
3357
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3358
        $efv = new ExtraFieldValue('course');
3359
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3360
            api_get_course_int_id(),
3361
            'session_courses_read_only_mode'
3362
        );
3363
3364
        if (!empty($lockExrafieldField['value'])) {
3365
            return false;
3366
        }
3367
    }
3368
3369
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3370
    $session_visibility = api_get_session_visibility($sessionId);
3371
    $is_courseAdmin = api_is_course_admin();
3372
3373
    if (!$is_courseAdmin && $tutor) {
3374
        // If we also want to check if the user is a tutor...
3375
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3376
    }
3377
3378
    if (!$is_courseAdmin && $coach) {
3379
        // If we also want to check if the user is a coach...';
3380
        // Check if session visibility is read only for coaches.
3381
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3382
            $is_allowed_coach_to_edit = false;
3383
        }
3384
3385
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3386
            // Check if coach is allowed to edit a course.
3387
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3388
        }
3389
    }
3390
3391
    if (!$is_courseAdmin && $session_coach) {
3392
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3393
    }
3394
3395
    // Check if the student_view is enabled, and if so, if it is activated.
3396
    if ('true' === api_get_setting('student_view_enabled')) {
3397
        $studentView = api_is_student_view_active();
3398
        if (!empty($sessionId)) {
3399
            // Check if session visibility is read only for coaches.
3400
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3401
                $is_allowed_coach_to_edit = false;
3402
            }
3403
3404
            $is_allowed = false;
3405
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3406
                // Check if coach is allowed to edit a course.
3407
                $is_allowed = $is_allowed_coach_to_edit;
3408
            }
3409
            if ($check_student_view) {
3410
                $is_allowed = $is_allowed && false === $studentView;
3411
            }
3412
        } else {
3413
            $is_allowed = $is_courseAdmin;
3414
            if ($check_student_view) {
3415
                $is_allowed = $is_courseAdmin && false === $studentView;
3416
            }
3417
        }
3418
3419
        return $is_allowed;
3420
    } else {
3421
        return $is_courseAdmin;
3422
    }
3423
}
3424
3425
/**
3426
 * Returns true if user is a course coach of at least one course in session.
3427
 *
3428
 * @param int $sessionId
3429
 *
3430
 * @return bool
3431
 */
3432
function api_is_coach_of_course_in_session($sessionId)
3433
{
3434
    if (api_is_platform_admin()) {
3435
        return true;
3436
    }
3437
3438
    $userId = api_get_user_id();
3439
    $courseList = UserManager::get_courses_list_by_session(
3440
        $userId,
3441
        $sessionId
3442
    );
3443
3444
    // Session visibility.
3445
    $visibility = api_get_session_visibility(
3446
        $sessionId,
3447
        null,
3448
        false
3449
    );
3450
3451
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3452
        // Course Coach session visibility.
3453
        $blockedCourseCount = 0;
3454
        $closedVisibilityList = [
3455
            COURSE_VISIBILITY_CLOSED,
3456
            COURSE_VISIBILITY_HIDDEN,
3457
        ];
3458
3459
        foreach ($courseList as $course) {
3460
            // Checking session visibility
3461
            $sessionCourseVisibility = api_get_session_visibility(
3462
                $sessionId,
3463
                $course['real_id']
3464
            );
3465
3466
            $courseIsVisible = !in_array(
3467
                $course['visibility'],
3468
                $closedVisibilityList
3469
            );
3470
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3471
                $blockedCourseCount++;
3472
            }
3473
        }
3474
3475
        // If all courses are blocked then no show in the list.
3476
        if ($blockedCourseCount === count($courseList)) {
3477
            $visibility = SESSION_INVISIBLE;
3478
        } else {
3479
            $visibility = SESSION_VISIBLE;
3480
        }
3481
    }
3482
3483
    switch ($visibility) {
3484
        case SESSION_VISIBLE_READ_ONLY:
3485
        case SESSION_VISIBLE:
3486
        case SESSION_AVAILABLE:
3487
            return true;
3488
            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...
3489
        case SESSION_INVISIBLE:
3490
            return false;
3491
    }
3492
3493
    return false;
3494
}
3495
3496
/**
3497
 * Checks if a student can edit contents in a session depending
3498
 * on the session visibility.
3499
 *
3500
 * @param bool $tutor Whether to check if the user has the tutor role
3501
 * @param bool $coach Whether to check if the user has the coach role
3502
 *
3503
 * @return bool true: the user has the rights to edit, false: he does not
3504
 */
3505
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3506
{
3507
    if (api_is_allowed_to_edit($tutor, $coach)) {
3508
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3509
        return true;
3510
    } else {
3511
        $sessionId = api_get_session_id();
3512
3513
        if (0 == $sessionId) {
3514
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3515
            return true;
3516
        } else {
3517
            // I'm in a session and I'm a student
3518
            // Get the session visibility
3519
            $session_visibility = api_get_session_visibility($sessionId);
3520
            // if 5 the session is still available
3521
            switch ($session_visibility) {
3522
                case SESSION_VISIBLE_READ_ONLY: // 1
3523
                    return false;
3524
                case SESSION_VISIBLE:           // 2
3525
                    return true;
3526
                case SESSION_INVISIBLE:         // 3
3527
                    return false;
3528
                case SESSION_AVAILABLE:         //5
3529
                    return true;
3530
            }
3531
        }
3532
    }
3533
3534
    return false;
3535
}
3536
3537
/**
3538
 * Current user is anon?
3539
 *
3540
 * @return bool true if this user is anonymous, false otherwise
3541
 */
3542
function api_is_anonymous()
3543
{
3544
    return !Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
3545
}
3546
3547
/**
3548
 * Displays message "You are not allowed here..." and exits the entire script.
3549
 *
3550
 * @param bool $print_headers Whether to print headers (default = false -> does not print them)
3551
 * @param string $message
3552
 * @param int $responseCode
3553
 *
3554
 * @throws Exception
3555
 */
3556
function api_not_allowed(
3557
    $print_headers = false,
3558
    $message = null,
3559
    $responseCode = 0
3560
): never {
3561
    throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode);
3562
}
3563
3564
/**
3565
 * @param string $languageIsoCode
3566
 *
3567
 * @return string
3568
 */
3569
function languageToCountryIsoCode($languageIsoCode)
3570
{
3571
    $allow = ('true' === api_get_setting('language.language_flags_by_country'));
3572
3573
    // @todo save in DB
3574
    switch ($languageIsoCode) {
3575
        case 'ar':
3576
            $country = 'ae';
3577
            break;
3578
        case 'bs':
3579
            $country = 'ba';
3580
            break;
3581
        case 'ca':
3582
            $country = 'es';
3583
            if ($allow) {
3584
                $country = 'catalan';
3585
            }
3586
            break;
3587
        case 'cs':
3588
            $country = 'cz';
3589
            break;
3590
        case 'da':
3591
            $country = 'dk';
3592
            break;
3593
        case 'el':
3594
            $country = 'ae';
3595
            break;
3596
        case 'en':
3597
            $country = 'gb';
3598
            break;
3599
        case 'eu': // Euskera
3600
            $country = 'es';
3601
            if ($allow) {
3602
                $country = 'basque';
3603
            }
3604
            break;
3605
        case 'gl': // galego
3606
            $country = 'es';
3607
            if ($allow) {
3608
                $country = 'galician';
3609
            }
3610
            break;
3611
        case 'he':
3612
            $country = 'il';
3613
            break;
3614
        case 'ja':
3615
            $country = 'jp';
3616
            break;
3617
        case 'ka':
3618
            $country = 'ge';
3619
            break;
3620
        case 'ko':
3621
            $country = 'kr';
3622
            break;
3623
        case 'ms':
3624
            $country = 'my';
3625
            break;
3626
        case 'pt-BR':
3627
            $country = 'br';
3628
            break;
3629
        case 'qu':
3630
            $country = 'pe';
3631
            break;
3632
        case 'sl':
3633
            $country = 'si';
3634
            break;
3635
        case 'sv':
3636
            $country = 'se';
3637
            break;
3638
        case 'uk': // Ukraine
3639
            $country = 'ua';
3640
            break;
3641
        case 'zh-TW':
3642
        case 'zh':
3643
            $country = 'cn';
3644
            break;
3645
        default:
3646
            $country = $languageIsoCode;
3647
            break;
3648
    }
3649
    $country = strtolower($country);
3650
3651
    return $country;
3652
}
3653
3654
/**
3655
 * Returns a list of all the languages that are made available by the admin.
3656
 *
3657
 * @return array An array with all languages. Structure of the array is
3658
 *               array['name'] = An array with the name of every language
3659
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
3660
 */
3661
function api_get_languages()
3662
{
3663
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3664
    $sql = "SELECT * FROM $table WHERE available='1'
3665
            ORDER BY original_name ASC";
3666
    $result = Database::query($sql);
3667
    $languages = [];
3668
    while ($row = Database::fetch_assoc($result)) {
3669
        $languages[$row['isocode']] = $row['original_name'];
3670
    }
3671
3672
    return $languages;
3673
}
3674
3675
/**
3676
 * Returns the id (the database id) of a language.
3677
 *
3678
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
3679
 *
3680
 * @return int id of the language
3681
 */
3682
function api_get_language_id($language)
3683
{
3684
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3685
    if (empty($language)) {
3686
        return null;
3687
    }
3688
3689
    // We check the language by iscocode
3690
    $langInfo = api_get_language_from_iso($language);
3691
    if (null !== $langInfo && !empty($langInfo->getId())) {
3692
        return $langInfo->getId();
3693
    }
3694
3695
    $language = Database::escape_string($language);
3696
    $sql = "SELECT id FROM $tbl_language
3697
            WHERE english_name = '$language' LIMIT 1";
3698
    $result = Database::query($sql);
3699
    $row = Database::fetch_array($result);
3700
3701
    return $row['id'];
3702
}
3703
3704
/**
3705
 * Get the language information by its id.
3706
 *
3707
 * @param int $languageId
3708
 *
3709
 * @throws Exception
3710
 *
3711
 * @return array
3712
 */
3713
function api_get_language_info($languageId)
3714
{
3715
    if (empty($languageId)) {
3716
        return [];
3717
    }
3718
3719
    $language = Database::getManager()->find(Language::class, $languageId);
3720
3721
    if (!$language) {
3722
        return [];
3723
    }
3724
3725
    return [
3726
        'id' => $language->getId(),
3727
        'original_name' => $language->getOriginalName(),
3728
        'english_name' => $language->getEnglishName(),
3729
        'isocode' => $language->getIsocode(),
3730
        'available' => $language->getAvailable(),
3731
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
3732
    ];
3733
}
3734
3735
/**
3736
 * @param string $code
3737
 *
3738
 * @return Language
3739
 */
3740
function api_get_language_from_iso($code)
3741
{
3742
    $em = Database::getManager();
3743
3744
    return $em->getRepository(Language::class)->findOneBy(['isocode' => $code]);
3745
}
3746
3747
/**
3748
 * Shortcut to ThemeHelper::getVisualTheme()
3749
 */
3750
function api_get_visual_theme(): string
3751
{
3752
    $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

3752
    /** @scrutinizer ignore-call */ 
3753
    $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...
3753
3754
    return $themeHelper->getVisualTheme();
3755
}
3756
3757
/**
3758
 * Returns a list of CSS themes currently available in the CSS folder
3759
 * The folder must have a default.css file.
3760
 *
3761
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
3762
 *
3763
 * @return array list of themes directories from the css folder
3764
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
3765
 */
3766
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
3767
{
3768
    // This configuration value is set by the vchamilo plugin
3769
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
3770
3771
    $readCssFolder = function ($dir) use ($virtualTheme) {
3772
        $finder = new Finder();
3773
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
3774
        $list = [];
3775
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
3776
        foreach ($themes as $theme) {
3777
            $folder = $theme->getFilename();
3778
            // A theme folder is consider if there's a default.css file
3779
            if (!file_exists($theme->getPathname().'/default.css')) {
3780
                continue;
3781
            }
3782
            $name = ucwords(str_replace('_', ' ', $folder));
3783
            if ($folder == $virtualTheme) {
3784
                continue;
3785
            }
3786
            $list[$folder] = $name;
3787
        }
3788
3789
        return $list;
3790
    };
3791
3792
    $dir = Container::getProjectDir().'var/themes/';
3793
    $list = $readCssFolder($dir);
3794
3795
    if (!empty($virtualTheme)) {
3796
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
3797
        if ($getOnlyThemeFromVirtualInstance) {
3798
            return $newList;
3799
        }
3800
        $list = $list + $newList;
3801
        asort($list);
3802
    }
3803
3804
    return $list;
3805
}
3806
3807
/**
3808
 * Find the largest sort value in a given user_course_category
3809
 * This function is used when we are moving a course to a different category
3810
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
3811
 *
3812
 * @param int $courseCategoryId the id of the user_course_category
3813
 * @param int $userId
3814
 *
3815
 * @return int the value of the highest sort of the user_course_category
3816
 */
3817
function api_max_sort_value($courseCategoryId, $userId)
3818
{
3819
    $user = api_get_user_entity($userId);
3820
    $userCourseCategory = Database::getManager()->getRepository(UserCourseCategory::class)->find($courseCategoryId);
3821
3822
    return null === $user ? 0 : $user->getMaxSortValue($userCourseCategory);
3823
}
3824
3825
/**
3826
 * Transforms a number of seconds in hh:mm:ss format.
3827
 *
3828
 * @author Julian Prud'homme
3829
 *
3830
 * @param int    $seconds      number of seconds
3831
 * @param string $space
3832
 * @param bool   $showSeconds
3833
 * @param bool   $roundMinutes
3834
 *
3835
 * @return string the formatted time
3836
 */
3837
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
3838
{
3839
    // $seconds = -1 means that we have wrong data in the db.
3840
    if (-1 == $seconds) {
3841
        return
3842
            get_lang('Unknown').
3843
            Display::getMdiIcon(
3844
                ActionIcon::INFORMATION,
3845
                'ch-tool-icon',
3846
                'align: absmiddle; hspace: 3px',
3847
                ICON_SIZE_SMALL,
3848
                get_lang('The datas about this user were registered when the calculation of time spent on the platform wasn\'t possible.')
3849
            );
3850
    }
3851
3852
    // How many hours ?
3853
    $hours = floor($seconds / 3600);
3854
3855
    // How many minutes ?
3856
    $min = floor(($seconds - ($hours * 3600)) / 60);
3857
3858
    if ($roundMinutes) {
3859
        if ($min >= 45) {
3860
            $min = 45;
3861
        }
3862
3863
        if ($min >= 30 && $min <= 44) {
3864
            $min = 30;
3865
        }
3866
3867
        if ($min >= 15 && $min <= 29) {
3868
            $min = 15;
3869
        }
3870
3871
        if ($min >= 0 && $min <= 14) {
3872
            $min = 0;
3873
        }
3874
    }
3875
3876
    // How many seconds
3877
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
3878
3879
    if ($hours < 10) {
3880
        $hours = "0$hours";
3881
    }
3882
3883
    if ($sec < 10) {
3884
        $sec = "0$sec";
3885
    }
3886
3887
    if ($min < 10) {
3888
        $min = "0$min";
3889
    }
3890
3891
    $seconds = '';
3892
    if ($showSeconds) {
3893
        $seconds = $space.$sec;
3894
    }
3895
3896
    return $hours.$space.$min.$seconds;
3897
}
3898
3899
/**
3900
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3901
 * The return value is based on the platform administrator's setting
3902
 * "Administration > Configuration settings > Security > Permissions for new directories".
3903
 *
3904
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
3905
 */
3906
function api_get_permissions_for_new_directories()
3907
{
3908
    static $permissions;
3909
    if (!isset($permissions)) {
3910
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
3911
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
3912
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
3913
    }
3914
3915
    return $permissions;
3916
}
3917
3918
/**
3919
 * Returns the permissions to be assigned to every newly created directory by the web-server.
3920
 * The return value is based on the platform administrator's setting
3921
 * "Administration > Configuration settings > Security > Permissions for new files".
3922
 *
3923
 * @return int returns the permissions in the format
3924
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
3925
 */
3926
function api_get_permissions_for_new_files()
3927
{
3928
    static $permissions;
3929
    if (!isset($permissions)) {
3930
        $permissions = trim(api_get_setting('permissions_for_new_files'));
3931
        // The default value 0666 is according to that in the platform
3932
        // administration panel after fresh system installation.
3933
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
3934
    }
3935
3936
    return $permissions;
3937
}
3938
3939
/**
3940
 * Deletes a file, or a folder and its contents.
3941
 *
3942
 * @author      Aidan Lister <[email protected]>
3943
 *
3944
 * @version     1.0.3
3945
 *
3946
 * @param string $dirname Directory to delete
3947
 * @param       bool     Deletes only the content or not
3948
 * @param bool $strict if one folder/file fails stop the loop
3949
 *
3950
 * @return bool Returns TRUE on success, FALSE on failure
3951
 *
3952
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
3953
 *
3954
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
3955
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
3956
 */
3957
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
3958
{
3959
    $res = true;
3960
    // A sanity check.
3961
    if (!file_exists($dirname)) {
3962
        return false;
3963
    }
3964
    $php_errormsg = '';
3965
    // Simple delete for a file.
3966
    if (is_file($dirname) || is_link($dirname)) {
3967
        $res = unlink($dirname);
3968
        if (false === $res) {
3969
            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);
3970
        }
3971
3972
        return $res;
3973
    }
3974
3975
    // Loop through the folder.
3976
    $dir = dir($dirname);
3977
    // A sanity check.
3978
    $is_object_dir = is_object($dir);
3979
    if ($is_object_dir) {
3980
        while (false !== $entry = $dir->read()) {
3981
            // Skip pointers.
3982
            if ('.' == $entry || '..' == $entry) {
3983
                continue;
3984
            }
3985
3986
            // Recurse.
3987
            if ($strict) {
3988
                $result = rmdirr("$dirname/$entry");
3989
                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...
3990
                    $res = false;
3991
                    break;
3992
                }
3993
            } else {
3994
                rmdirr("$dirname/$entry");
3995
            }
3996
        }
3997
    }
3998
3999
    // Clean up.
4000
    if ($is_object_dir) {
4001
        $dir->close();
4002
    }
4003
4004
    if (false == $delete_only_content_in_folder) {
4005
        $res = rmdir($dirname);
4006
        if (false === $res) {
4007
            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);
4008
        }
4009
    }
4010
4011
    return $res;
4012
}
4013
4014
// TODO: This function is to be simplified. File access modes to be implemented.
4015
/**
4016
 * function adapted from a php.net comment
4017
 * copy recursively a folder.
4018
 *
4019
 * @param the source folder
4020
 * @param the dest folder
4021
 * @param an array of excluded file_name (without extension)
4022
 * @param copied_files the returned array of copied files
4023
 * @param string $source
4024
 * @param string $dest
4025
 */
4026
function copyr($source, $dest, $exclude = [], $copied_files = [])
4027
{
4028
    if (empty($dest)) {
4029
        return false;
4030
    }
4031
    // Simple copy for a file
4032
    if (is_file($source)) {
4033
        $path_info = pathinfo($source);
4034
        if (!in_array($path_info['filename'], $exclude)) {
4035
            copy($source, $dest);
4036
        }
4037
4038
        return true;
4039
    } elseif (!is_dir($source)) {
4040
        //then source is not a dir nor a file, return
4041
        return false;
4042
    }
4043
4044
    // Make destination directory.
4045
    if (!is_dir($dest)) {
4046
        mkdir($dest, api_get_permissions_for_new_directories());
4047
    }
4048
4049
    // Loop through the folder.
4050
    $dir = dir($source);
4051
    while (false !== $entry = $dir->read()) {
4052
        // Skip pointers
4053
        if ('.' == $entry || '..' == $entry) {
4054
            continue;
4055
        }
4056
4057
        // Deep copy directories.
4058
        if ($dest !== "$source/$entry") {
4059
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
4060
        }
4061
    }
4062
    // Clean up.
4063
    $dir->close();
4064
4065
    return true;
4066
}
4067
4068
/**
4069
 * @todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
4070
 * Documentation header to be added here.
4071
 *
4072
 * @param string $pathname
4073
 * @param string $base_path_document
4074
 * @param int    $session_id
4075
 *
4076
 * @return mixed True if directory already exists, false if a file already exists at
4077
 *               the destination and null if everything goes according to plan
4078
 */
4079
function copy_folder_course_session(
4080
    $pathname,
4081
    $base_path_document,
4082
    $session_id,
4083
    $course_info,
4084
    $document,
4085
    $source_course_id,
4086
    array $originalFolderNameList = [],
4087
    string $originalBaseName = ''
4088
) {
4089
    $table = Database::get_course_table(TABLE_DOCUMENT);
4090
    $session_id = intval($session_id);
4091
    $source_course_id = intval($source_course_id);
4092
4093
    // Check whether directory already exists.
4094
    if (empty($pathname) || is_dir($pathname)) {
4095
        return true;
4096
    }
4097
4098
    // Ensure that a file with the same name does not already exist.
4099
    if (is_file($pathname)) {
4100
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
4101
4102
        return false;
4103
    }
4104
4105
    $baseNoDocument = str_replace('document', '', $originalBaseName);
4106
    $folderTitles = explode('/', $baseNoDocument);
4107
    $folderTitles = array_filter($folderTitles);
4108
4109
    $table = Database::get_course_table(TABLE_DOCUMENT);
4110
    $session_id = (int) $session_id;
4111
    $source_course_id = (int) $source_course_id;
4112
4113
    $course_id = $course_info['real_id'];
4114
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
4115
    $new_pathname = $base_path_document;
4116
    $path = '';
4117
4118
    foreach ($folders as $index => $folder) {
4119
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
4120
        $path .= DIRECTORY_SEPARATOR.$folder;
4121
4122
        if (!file_exists($new_pathname)) {
4123
            $path = Database::escape_string($path);
4124
4125
            $sql = "SELECT * FROM $table
4126
                    WHERE
4127
                        c_id = $source_course_id AND
4128
                        path = '$path' AND
4129
                        filetype = 'folder' AND
4130
                        session_id = '$session_id'";
4131
            $rs1 = Database::query($sql);
4132
            $num_rows = Database::num_rows($rs1);
4133
4134
            if (0 == $num_rows) {
4135
                mkdir($new_pathname, api_get_permissions_for_new_directories());
4136
                $title = basename($new_pathname);
4137
4138
                if (isset($folderTitles[$index + 1])) {
4139
                    $checkPath = $folderTitles[$index +1];
4140
4141
                    if (isset($originalFolderNameList[$checkPath])) {
4142
                        $title = $originalFolderNameList[$checkPath];
4143
                    }
4144
                }
4145
4146
                // Insert new folder with destination session_id.
4147
                $params = [
4148
                    'c_id' => $course_id,
4149
                    'path' => $path,
4150
                    'comment' => $document->comment,
4151
                    'title' => $title,
4152
                    'filetype' => 'folder',
4153
                    'size' => '0',
4154
                    'session_id' => $session_id,
4155
                ];
4156
                Database::insert($table, $params);
4157
            }
4158
        }
4159
    } // en foreach
4160
}
4161
4162
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
4163
/**
4164
 * @param string $path
4165
 */
4166
function api_chmod_R($path, $filemode)
4167
{
4168
    if (!is_dir($path)) {
4169
        return chmod($path, $filemode);
4170
    }
4171
4172
    $handler = opendir($path);
4173
    while ($file = readdir($handler)) {
4174
        if ('.' != $file && '..' != $file) {
4175
            $fullpath = "$path/$file";
4176
            if (!is_dir($fullpath)) {
4177
                if (!chmod($fullpath, $filemode)) {
4178
                    return false;
4179
                }
4180
            } else {
4181
                if (!api_chmod_R($fullpath, $filemode)) {
4182
                    return false;
4183
                }
4184
            }
4185
        }
4186
    }
4187
4188
    closedir($handler);
4189
4190
    return chmod($path, $filemode);
4191
}
4192
4193
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
4194
/**
4195
 * Parse info file format. (e.g: file.info).
4196
 *
4197
 * Files should use an ini-like format to specify values.
4198
 * White-space generally doesn't matter, except inside values.
4199
 * e.g.
4200
 *
4201
 * @verbatim
4202
 *   key = value
4203
 *   key = "value"
4204
 *   key = 'value'
4205
 *   key = "multi-line
4206
 *
4207
 *   value"
4208
 *   key = 'multi-line
4209
 *
4210
 *   value'
4211
 *   key
4212
 *   =
4213
 *   'value'
4214
 * @endverbatim
4215
 *
4216
 * Arrays are created using a GET-like syntax:
4217
 *
4218
 * @verbatim
4219
 *   key[] = "numeric array"
4220
 *   key[index] = "associative array"
4221
 *   key[index][] = "nested numeric array"
4222
 *   key[index][index] = "nested associative array"
4223
 * @endverbatim
4224
 *
4225
 * PHP constants are substituted in, but only when used as the entire value:
4226
 *
4227
 * Comments should start with a semi-colon at the beginning of a line.
4228
 *
4229
 * This function is NOT for placing arbitrary module-specific settings. Use
4230
 * variable_get() and variable_set() for that.
4231
 *
4232
 * Information stored in the module.info file:
4233
 * - name: The real name of the module for display purposes.
4234
 * - description: A brief description of the module.
4235
 * - dependencies: An array of shortnames of other modules this module depends on.
4236
 * - package: The name of the package of modules this module belongs to.
4237
 *
4238
 * Example of .info file:
4239
 * <code>
4240
 * @verbatim
4241
 *   name = Forum
4242
 *   description = Enables threaded discussions about general topics.
4243
 *   dependencies[] = taxonomy
4244
 *   dependencies[] = comment
4245
 *   package = Core - optional
4246
 *   version = VERSION
4247
 * @endverbatim
4248
 * </code>
4249
 *
4250
 * @param string $filename
4251
 *                         The file we are parsing. Accepts file with relative or absolute path.
4252
 *
4253
 * @return
4254
 *   The info array
4255
 */
4256
function api_parse_info_file($filename)
4257
{
4258
    $info = [];
4259
4260
    if (!file_exists($filename)) {
4261
        return $info;
4262
    }
4263
4264
    $data = file_get_contents($filename);
4265
    if (preg_match_all('
4266
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
4267
        ((?:
4268
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
4269
          \[[^\[\]]*\]                  # unless they are balanced and not nested
4270
        )+?)
4271
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
4272
        (?:
4273
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
4274
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
4275
          ([^\r\n]*?)                   # Non-quoted string
4276
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
4277
        @msx', $data, $matches, PREG_SET_ORDER)) {
4278
        $key = $value1 = $value2 = $value3 = '';
4279
        foreach ($matches as $match) {
4280
            // Fetch the key and value string.
4281
            $i = 0;
4282
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
4283
                $$var = isset($match[++$i]) ? $match[$i] : '';
4284
            }
4285
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
4286
4287
            // Parse array syntax.
4288
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
4289
            $last = array_pop($keys);
4290
            $parent = &$info;
4291
4292
            // Create nested arrays.
4293
            foreach ($keys as $key) {
4294
                if ('' == $key) {
4295
                    $key = count($parent);
4296
                }
4297
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
4298
                    $parent[$key] = [];
4299
                }
4300
                $parent = &$parent[$key];
4301
            }
4302
4303
            // Handle PHP constants.
4304
            if (defined($value)) {
4305
                $value = constant($value);
4306
            }
4307
4308
            // Insert actual value.
4309
            if ('' == $last) {
4310
                $last = count($parent);
4311
            }
4312
            $parent[$last] = $value;
4313
        }
4314
    }
4315
4316
    return $info;
4317
}
4318
4319
/**
4320
 * Gets Chamilo version from the configuration files.
4321
 *
4322
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
4323
 */
4324
function api_get_version()
4325
{
4326
    return (string) api_get_configuration_value('system_version');
4327
}
4328
4329
/**
4330
 * Gets the software name (the name/brand of the Chamilo-based customized system).
4331
 *
4332
 * @return string
4333
 */
4334
function api_get_software_name()
4335
{
4336
    $name = api_get_env_variable('SOFTWARE_NAME', 'Chamilo');
4337
    return $name;
4338
}
4339
4340
function api_get_status_list()
4341
{
4342
    $list = [];
4343
    // Table of status
4344
    $list[COURSEMANAGER] = 'teacher'; // 1
4345
    $list[SESSIONADMIN] = 'session_admin'; // 3
4346
    $list[DRH] = 'drh'; // 4
4347
    $list[STUDENT] = 'user'; // 5
4348
    $list[ANONYMOUS] = 'anonymous'; // 6
4349
    $list[INVITEE] = 'invited'; // 20
4350
4351
    return $list;
4352
}
4353
4354
/**
4355
 * Checks whether status given in parameter exists in the platform.
4356
 *
4357
 * @param mixed the status (can be either int either string)
4358
 *
4359
 * @return bool if the status exists, else returns false
4360
 */
4361
function api_status_exists($status_asked)
4362
{
4363
    $list = api_get_status_list();
4364
4365
    return in_array($status_asked, $list) ? true : isset($list[$status_asked]);
4366
}
4367
4368
/**
4369
 * Checks whether status given in parameter exists in the platform. The function
4370
 * returns the status ID or false if it does not exist, but given the fact there
4371
 * is no "0" status, the return value can be checked against
4372
 * if(api_status_key()) to know if it exists.
4373
 *
4374
 * @param   mixed   The status (can be either int or string)
4375
 *
4376
 * @return mixed Status ID if exists, false otherwise
4377
 */
4378
function api_status_key($status)
4379
{
4380
    $list = api_get_status_list();
4381
4382
    return isset($list[$status]) ? $status : array_search($status, $list);
4383
}
4384
4385
/**
4386
 * Gets the status langvars list.
4387
 *
4388
 * @return string[] the list of status with their translations
4389
 */
4390
function api_get_status_langvars()
4391
{
4392
    return [
4393
        COURSEMANAGER => get_lang('Teacher'),
4394
        SESSIONADMIN => get_lang('Sessions administrator'),
4395
        DRH => get_lang('Human Resources Manager'),
4396
        STUDENT => get_lang('Learner'),
4397
        ANONYMOUS => get_lang('Anonymous'),
4398
        STUDENT_BOSS => get_lang('Student boss'),
4399
        INVITEE => get_lang('Invited'),
4400
    ];
4401
}
4402
4403
/**
4404
 * The function that retrieves all the possible settings for a certain config setting.
4405
 *
4406
 * @author Patrick Cool <[email protected]>, Ghent University
4407
 */
4408
function api_get_settings_options($var)
4409
{
4410
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4411
    $var = Database::escape_string($var);
4412
    $sql = "SELECT * FROM $table_settings_options
4413
            WHERE variable = '$var'
4414
            ORDER BY id";
4415
    $result = Database::query($sql);
4416
    $settings_options_array = [];
4417
    while ($row = Database::fetch_assoc($result)) {
4418
        $settings_options_array[] = $row;
4419
    }
4420
4421
    return $settings_options_array;
4422
}
4423
4424
/**
4425
 * @param array $params
4426
 */
4427
function api_set_setting_option($params)
4428
{
4429
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4430
    if (empty($params['id'])) {
4431
        Database::insert($table, $params);
4432
    } else {
4433
        Database::update($table, $params, ['id = ? ' => $params['id']]);
4434
    }
4435
}
4436
4437
/**
4438
 * @param array $params
4439
 */
4440
function api_set_setting_simple($params)
4441
{
4442
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4443
    $url_id = api_get_current_access_url_id();
4444
4445
    if (empty($params['id'])) {
4446
        $params['access_url'] = $url_id;
4447
        Database::insert($table, $params);
4448
    } else {
4449
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
4450
    }
4451
}
4452
4453
/**
4454
 * @param int $id
4455
 */
4456
function api_delete_setting_option($id)
4457
{
4458
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
4459
    if (!empty($id)) {
4460
        Database::delete($table, ['id = ? ' => $id]);
4461
    }
4462
}
4463
4464
/**
4465
 * Sets a platform configuration setting to a given value.
4466
 *
4467
 * @param string    The variable we want to update
4468
 * @param string    The value we want to record
4469
 * @param string    The sub-variable if any (in most cases, this will remain null)
4470
 * @param string    The category if any (in most cases, this will remain null)
4471
 * @param int       The access_url for which this parameter is valid
4472
 * @param string $cat
4473
 *
4474
 * @return bool|null
4475
 */
4476
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
4477
{
4478
    if (empty($var)) {
4479
        return false;
4480
    }
4481
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
4482
    $var = Database::escape_string($var);
4483
    $value = Database::escape_string($value);
4484
    $access_url = (int) $access_url;
4485
    if (empty($access_url)) {
4486
        $access_url = 1;
4487
    }
4488
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
4489
    if (!empty($subvar)) {
4490
        $subvar = Database::escape_string($subvar);
4491
        $select .= " AND subkey = '$subvar'";
4492
    }
4493
    if (!empty($cat)) {
4494
        $cat = Database::escape_string($cat);
4495
        $select .= " AND category = '$cat'";
4496
    }
4497
    if ($access_url > 1) {
4498
        $select .= " AND access_url = $access_url";
4499
    } else {
4500
        $select .= " AND access_url = 1 ";
4501
    }
4502
4503
    $res = Database::query($select);
4504
    if (Database::num_rows($res) > 0) {
4505
        // Found item for this access_url.
4506
        $row = Database::fetch_array($res);
4507
        $sql = "UPDATE $t_settings SET selected_value = '$value'
4508
                WHERE id = ".$row['id'];
4509
        Database::query($sql);
4510
    } else {
4511
        // Item not found for this access_url, we have to check if it exist with access_url = 1
4512
        $select = "SELECT * FROM $t_settings
4513
                   WHERE variable = '$var' AND access_url = 1 ";
4514
        // Just in case
4515
        if (1 == $access_url) {
4516
            if (!empty($subvar)) {
4517
                $select .= " AND subkey = '$subvar'";
4518
            }
4519
            if (!empty($cat)) {
4520
                $select .= " AND category = '$cat'";
4521
            }
4522
            $res = Database::query($select);
4523
            if (Database::num_rows($res) > 0) {
4524
                // We have a setting for access_url 1, but none for the current one, so create one.
4525
                $row = Database::fetch_array($res);
4526
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
4527
                        VALUES
4528
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4529
                    "'".$row['type']."','".$row['category']."',".
4530
                    "'$value','".$row['title']."',".
4531
                    "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4532
                    "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
4533
                Database::query($insert);
4534
            } else {
4535
                // Such a setting does not exist.
4536
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
4537
            }
4538
        } else {
4539
            // Other access url.
4540
            if (!empty($subvar)) {
4541
                $select .= " AND subkey = '$subvar'";
4542
            }
4543
            if (!empty($cat)) {
4544
                $select .= " AND category = '$cat'";
4545
            }
4546
            $res = Database::query($select);
4547
4548
            if (Database::num_rows($res) > 0) {
4549
                // We have a setting for access_url 1, but none for the current one, so create one.
4550
                $row = Database::fetch_array($res);
4551
                if (1 == $row['access_url_changeable']) {
4552
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
4553
                            ('".$row['variable']."',".
4554
                        (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
4555
                        "'".$row['type']."','".$row['category']."',".
4556
                        "'$value','".$row['title']."',".
4557
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
4558
                        (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
4559
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
4560
                    Database::query($insert);
4561
                }
4562
            } else { // Such a setting does not exist.
4563
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
4564
            }
4565
        }
4566
    }
4567
}
4568
4569
/**
4570
 * Sets a whole category of settings to one specific value.
4571
 *
4572
 * @param string    Category
4573
 * @param string    Value
4574
 * @param int       Access URL. Optional. Defaults to 1
4575
 * @param array     Optional array of filters on field type
4576
 * @param string $category
4577
 * @param string $value
4578
 *
4579
 * @return bool
4580
 */
4581
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
4582
{
4583
    if (empty($category)) {
4584
        return false;
4585
    }
4586
    $category = Database::escape_string($category);
4587
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS);
4588
    $access_url = (int) $access_url;
4589
    if (empty($access_url)) {
4590
        $access_url = 1;
4591
    }
4592
    if (isset($value)) {
4593
        $value = Database::escape_string($value);
4594
        $sql = "UPDATE $t_s SET selected_value = '$value'
4595
                WHERE category = '$category' AND access_url = $access_url";
4596
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4597
            $sql .= " AND ( ";
4598
            $i = 0;
4599
            foreach ($fieldtype as $type) {
4600
                if ($i > 0) {
4601
                    $sql .= ' OR ';
4602
                }
4603
                $type = Database::escape_string($type);
4604
                $sql .= " type='".$type."' ";
4605
                $i++;
4606
            }
4607
            $sql .= ")";
4608
        }
4609
        $res = Database::query($sql);
4610
4611
        return false !== $res;
4612
    } else {
4613
        $sql = "UPDATE $t_s SET selected_value = NULL
4614
                WHERE category = '$category' AND access_url = $access_url";
4615
        if (is_array($fieldtype) && count($fieldtype) > 0) {
4616
            $sql .= " AND ( ";
4617
            $i = 0;
4618
            foreach ($fieldtype as $type) {
4619
                if ($i > 0) {
4620
                    $sql .= ' OR ';
4621
                }
4622
                $type = Database::escape_string($type);
4623
                $sql .= " type='".$type."' ";
4624
                $i++;
4625
            }
4626
            $sql .= ")";
4627
        }
4628
        $res = Database::query($sql);
4629
4630
        return false !== $res;
4631
    }
4632
}
4633
4634
/**
4635
 * Gets all available access urls in an array (as in the database).
4636
 *
4637
 * @return array An array of database records
4638
 */
4639
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
4640
{
4641
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4642
    $from = (int) $from;
4643
    $to = (int) $to;
4644
    $order = Database::escape_string($order);
4645
    $direction = Database::escape_string($direction);
4646
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
4647
    $sql = "SELECT id, url, description, active, created_by, tms
4648
            FROM $table
4649
            ORDER BY `$order` $direction
4650
            LIMIT $to OFFSET $from";
4651
    $res = Database::query($sql);
4652
4653
    return Database::store_result($res);
4654
}
4655
4656
/**
4657
 * Gets the access url info in an array.
4658
 *
4659
 * @param int  $id            Id of the access url
4660
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
4661
 *
4662
 * @return array All the info (url, description, active, created_by, tms)
4663
 *               from the access_url table
4664
 *
4665
 * @author Julio Montoya
4666
 */
4667
function api_get_access_url($id, $returnDefault = true)
4668
{
4669
    static $staticResult;
4670
    $id = (int) $id;
4671
4672
    if (isset($staticResult[$id])) {
4673
        $result = $staticResult[$id];
4674
    } else {
4675
        // Calling the Database:: library dont work this is handmade.
4676
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
4677
        $sql = "SELECT url, description, active, created_by, tms
4678
                FROM $table_access_url WHERE id = '$id' ";
4679
        $res = Database::query($sql);
4680
        $result = @Database::fetch_array($res);
4681
        $staticResult[$id] = $result;
4682
    }
4683
4684
    // If the result url is 'http://localhost/' (the default) and the root_web
4685
    // (=current url) is different, and the $id is = 1 (which might mean
4686
    // api_get_current_access_url_id() returned 1 by default), then return the
4687
    // root_web setting instead of the current URL
4688
    // This is provided as an option to avoid breaking the storage of URL-specific
4689
    // homepages in home/localhost/
4690
    if (1 === $id && false === $returnDefault) {
4691
        $currentUrl = api_get_current_access_url_id();
4692
        // only do this if we are on the main URL (=1), otherwise we could get
4693
        // information on another URL instead of the one asked as parameter
4694
        if (1 === $currentUrl) {
4695
            $rootWeb = api_get_path(WEB_PATH);
4696
            $default = AccessUrl::DEFAULT_ACCESS_URL;
4697
            if ($result['url'] === $default && $rootWeb != $default) {
4698
                $result['url'] = $rootWeb;
4699
            }
4700
        }
4701
    }
4702
4703
    return $result;
4704
}
4705
4706
/**
4707
 * Gets all the current settings for a specific access url.
4708
 *
4709
 * @param string    The category, if any, that we want to get
4710
 * @param string    Whether we want a simple list (display a category) or
4711
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
4712
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
4713
 *
4714
 * @return array Array of database results for the current settings of the current access URL
4715
 */
4716
function &api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
4717
{
4718
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
4719
    $access_url = (int) $access_url;
4720
    $where_condition = '';
4721
    if (1 == $url_changeable) {
4722
        $where_condition = " AND access_url_changeable= '1' ";
4723
    }
4724
    if (empty($access_url) || -1 == $access_url) {
4725
        $access_url = 1;
4726
    }
4727
    $sql = "SELECT * FROM $table
4728
            WHERE access_url = $access_url  $where_condition ";
4729
4730
    if (!empty($cat)) {
4731
        $cat = Database::escape_string($cat);
4732
        $sql .= " AND category='$cat' ";
4733
    }
4734
    if ('group' == $ordering) {
4735
        $sql .= " ORDER BY id ASC";
4736
    } else {
4737
        $sql .= " ORDER BY 1,2 ASC";
4738
    }
4739
    $result = Database::query($sql);
4740
    if (null === $result) {
4741
        $result = [];
4742
        return $result;
4743
    }
4744
    $result = Database::store_result($result, 'ASSOC');
4745
4746
    return $result;
4747
}
4748
4749
/**
4750
 * @param string $value       The value we want to record
4751
 * @param string $variable    The variable name we want to insert
4752
 * @param string $subKey      The subkey for the variable we want to insert
4753
 * @param string $type        The type for the variable we want to insert
4754
 * @param string $category    The category for the variable we want to insert
4755
 * @param string $title       The title
4756
 * @param string $comment     The comment
4757
 * @param string $scope       The scope
4758
 * @param string $subKeyText  The subkey text
4759
 * @param int    $accessUrlId The access_url for which this parameter is valid
4760
 * @param int    $visibility  The changeability of this setting for non-master urls
4761
 *
4762
 * @return int The setting ID
4763
 */
4764
function api_add_setting(
4765
    $value,
4766
    $variable,
4767
    $subKey = '',
4768
    $type = 'textfield',
4769
    $category = '',
4770
    $title = '',
4771
    $comment = '',
4772
    $scope = '',
4773
    $subKeyText = '',
4774
    $accessUrlId = 1,
4775
    $visibility = 0
4776
) {
4777
    $em = Database::getManager();
4778
4779
    $settingRepo = $em->getRepository(SettingsCurrent::class);
4780
    $accessUrlId = (int) $accessUrlId ?: 1;
4781
4782
    if (is_array($value)) {
4783
        $value = serialize($value);
4784
    } else {
4785
        $value = trim($value);
4786
    }
4787
4788
    $criteria = ['variable' => $variable, 'url' => $accessUrlId];
4789
4790
    if (!empty($subKey)) {
4791
        $criteria['subkey'] = $subKey;
4792
    }
4793
4794
    // Check if this variable doesn't exist already
4795
    /** @var SettingsCurrent $setting */
4796
    $setting = $settingRepo->findOneBy($criteria);
4797
4798
    if ($setting) {
0 ignored issues
show
introduced by
$setting is of type Chamilo\CoreBundle\Entity\SettingsCurrent, thus it always evaluated to true.
Loading history...
4799
        $setting->setSelectedValue($value);
4800
4801
        $em->persist($setting);
4802
        $em->flush();
4803
4804
        return $setting->getId();
4805
    }
4806
4807
    // Item not found for this access_url, we have to check if the whole thing is missing
4808
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
4809
    $setting = new SettingsCurrent();
4810
    $url = api_get_url_entity();
4811
4812
    $setting
4813
        ->setVariable($variable)
4814
        ->setSelectedValue($value)
4815
        ->setType($type)
4816
        ->setCategory($category)
4817
        ->setSubkey($subKey)
4818
        ->setTitle($title)
4819
        ->setComment($comment)
4820
        ->setScope($scope)
4821
        ->setSubkeytext($subKeyText)
4822
        ->setUrl(api_get_url_entity())
4823
        ->setAccessUrlChangeable($visibility);
4824
4825
    $em->persist($setting);
4826
    $em->flush();
4827
4828
    return $setting->getId();
4829
}
4830
4831
/**
4832
 * Checks wether a user can or can't view the contents of a course.
4833
 *
4834
 * @deprecated use CourseManager::is_user_subscribed_in_course
4835
 *
4836
 * @param int $userid User id or NULL to get it from $_SESSION
4837
 * @param int $cid    course id to check whether the user is allowed
4838
 *
4839
 * @return bool
4840
 */
4841
function api_is_course_visible_for_user($userid = null, $cid = null)
4842
{
4843
    if (null === $userid) {
4844
        $userid = api_get_user_id();
4845
    }
4846
    if (empty($userid) || strval(intval($userid)) != $userid) {
4847
        if (api_is_anonymous()) {
4848
            $userid = api_get_anonymous_id();
4849
        } else {
4850
            return false;
4851
        }
4852
    }
4853
    $cid = Database::escape_string($cid);
4854
4855
    $courseInfo = api_get_course_info($cid);
4856
    $courseId = $courseInfo['real_id'];
4857
    $is_platformAdmin = api_is_platform_admin();
4858
4859
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
4860
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
4861
4862
    $sql = "SELECT
4863
                $course_cat_table.code AS category_code,
4864
                $course_table.visibility,
4865
                $course_table.code,
4866
                $course_cat_table.code
4867
            FROM $course_table
4868
            LEFT JOIN $course_cat_table
4869
                ON $course_table.category_id = $course_cat_table.id
4870
            WHERE
4871
                $course_table.code = '$cid'
4872
            LIMIT 1";
4873
4874
    $result = Database::query($sql);
4875
4876
    if (Database::num_rows($result) > 0) {
4877
        $visibility = Database::fetch_array($result);
4878
        $visibility = $visibility['visibility'];
4879
    } else {
4880
        $visibility = 0;
4881
    }
4882
    // Shortcut permissions in case the visibility is "open to the world".
4883
    if (COURSE_VISIBILITY_OPEN_WORLD === $visibility) {
4884
        return true;
4885
    }
4886
4887
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4888
4889
    $sql = "SELECT
4890
                is_tutor, status
4891
            FROM $tbl_course_user
4892
            WHERE
4893
                user_id  = '$userid' AND
4894
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
4895
                c_id = $courseId
4896
            LIMIT 1";
4897
4898
    $result = Database::query($sql);
4899
4900
    if (Database::num_rows($result) > 0) {
4901
        // This user has got a recorded state for this course.
4902
        $cuData = Database::fetch_array($result);
4903
        $is_courseMember = true;
4904
        $is_courseAdmin = (1 == $cuData['status']);
4905
    }
4906
4907
    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...
4908
        // This user has no status related to this course.
4909
        // Is it the session coach or the session admin?
4910
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4911
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4912
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4913
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4914
4915
        $sql = "SELECT sru_2.user_id AS session_admin_id, sru_1.user_id AS session_coach_id
4916
                FROM $tbl_session AS s
4917
                INNER JOIN $tblSessionRelUser sru_1
4918
                ON (sru_1.session_id = s.id AND sru_1.relation_type = ".SessionEntity::GENERAL_COACH.")
4919
                INNER JOIN $tblSessionRelUser sru_2
4920
                ON (sru_2.session_id = s.id AND sru_2.relation_type = ".SessionEntity::SESSION_ADMIN.")
4921
                INNER JOIN $tbl_session_course src
4922
                ON (src.session_id = s.id AND src.c_id = $courseId)";
4923
4924
        $result = Database::query($sql);
4925
        $row = Database::store_result($result);
4926
        $sessionAdminsId = array_column($row, 'session_admin_id');
4927
        $sessionCoachesId = array_column($row, 'session_coach_id');
4928
4929
        if (in_array($userid, $sessionCoachesId)) {
4930
            $is_courseMember = true;
4931
            $is_courseAdmin = false;
4932
        } elseif (in_array($userid, $sessionAdminsId)) {
4933
            $is_courseMember = false;
4934
            $is_courseAdmin = false;
4935
        } else {
4936
            // Check if the current user is the course coach.
4937
            $sql = "SELECT 1
4938
                    FROM $tbl_session_course
4939
                    WHERE session_rel_course.c_id = '$courseId'
4940
                    AND session_rel_course.id_coach = '$userid'
4941
                    LIMIT 1";
4942
4943
            $result = Database::query($sql);
4944
4945
            //if ($row = Database::fetch_array($result)) {
4946
            if (Database::num_rows($result) > 0) {
4947
                $is_courseMember = true;
4948
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4949
4950
                $sql = "SELECT status FROM $tbl_user
4951
                        WHERE id = $userid
4952
                        LIMIT 1";
4953
4954
                $result = Database::query($sql);
4955
4956
                if (1 == Database::result($result, 0, 0)) {
4957
                    $is_courseAdmin = true;
4958
                } else {
4959
                    $is_courseAdmin = false;
4960
                }
4961
            } else {
4962
                // Check if the user is a student is this session.
4963
                $sql = "SELECT  id
4964
                        FROM $tbl_session_course_user
4965
                        WHERE
4966
                            user_id  = '$userid' AND
4967
                            c_id = '$courseId'
4968
                        LIMIT 1";
4969
4970
                if (Database::num_rows($result) > 0) {
4971
                    // This user haa got a recorded state for this course.
4972
                    while ($row = Database::fetch_array($result)) {
4973
                        $is_courseMember = true;
4974
                        $is_courseAdmin = false;
4975
                    }
4976
                }
4977
            }
4978
        }
4979
    }
4980
4981
    switch ($visibility) {
4982
        case Course::OPEN_WORLD:
4983
            return true;
4984
        case Course::OPEN_PLATFORM:
4985
            return isset($userid);
4986
        case Course::REGISTERED:
4987
        case Course::CLOSED:
4988
            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...
4989
        case Course::HIDDEN:
4990
            return $is_platformAdmin;
4991
    }
4992
4993
    return false;
4994
}
4995
4996
/**
4997
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
4998
 *
4999
 * @param string the tool of the element
5000
 * @param int the element id in database
5001
 * @param int the session_id to compare with element session id
5002
 *
5003
 * @return bool true if the element is in the session, false else
5004
 */
5005
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
5006
{
5007
    if (is_null($session_id)) {
5008
        $session_id = api_get_session_id();
5009
    }
5010
5011
    $element_id = (int) $element_id;
5012
5013
    if (empty($element_id)) {
5014
        return false;
5015
    }
5016
5017
    // Get information to build query depending of the tool.
5018
    switch ($tool) {
5019
        case TOOL_SURVEY:
5020
            $table_tool = Database::get_course_table(TABLE_SURVEY);
5021
            $key_field = 'survey_id';
5022
            break;
5023
        case TOOL_ANNOUNCEMENT:
5024
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
5025
            $key_field = 'id';
5026
            break;
5027
        case TOOL_AGENDA:
5028
            $table_tool = Database::get_course_table(TABLE_AGENDA);
5029
            $key_field = 'id';
5030
            break;
5031
        case TOOL_GROUP:
5032
            $table_tool = Database::get_course_table(TABLE_GROUP);
5033
            $key_field = 'id';
5034
            break;
5035
        default:
5036
            return false;
5037
    }
5038
    $course_id = api_get_course_int_id();
5039
5040
    $sql = "SELECT session_id FROM $table_tool
5041
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
5042
    $rs = Database::query($sql);
5043
    if ($element_session_id = Database::result($rs, 0, 0)) {
5044
        if ($element_session_id == intval($session_id)) {
5045
            // The element belongs to the session.
5046
            return true;
5047
        }
5048
    }
5049
5050
    return false;
5051
}
5052
5053
/**
5054
 * Replaces "forbidden" characters in a filename string.
5055
 *
5056
 * @param string $filename
5057
 * @param bool   $treat_spaces_as_hyphens
5058
 *
5059
 * @return string
5060
 */
5061
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
5062
{
5063
    // Some non-properly encoded file names can cause the whole file to be
5064
    // skipped when uploaded. Avoid this by detecting the encoding and
5065
    // converting to UTF-8, setting the source as ASCII (a reasonably
5066
    // limited characters set) if nothing could be found (BT#
5067
    $encoding = api_detect_encoding($filename);
5068
    if (empty($encoding)) {
5069
        $encoding = 'ASCII';
5070
        if (!api_is_valid_ascii($filename)) {
5071
            // try iconv and try non standard ASCII a.k.a CP437
5072
            // see BT#15022
5073
            if (function_exists('iconv')) {
5074
                $result = iconv('CP437', 'UTF-8', $filename);
5075
                if (api_is_valid_utf8($result)) {
5076
                    $filename = $result;
5077
                    $encoding = 'UTF-8';
5078
                }
5079
            }
5080
        }
5081
    }
5082
5083
    $filename = api_to_system_encoding($filename, $encoding);
5084
5085
    $url = URLify::filter(
5086
        $filename,
5087
        250,
5088
        '',
5089
        true,
5090
        false,
5091
        false
5092
    );
5093
5094
    // Replace multiple dots at the end.
5095
    $regex = "/\.+$/";
5096
5097
    return preg_replace($regex, '', $url);
5098
}
5099
5100
/**
5101
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
5102
 *
5103
 * @author Ivan Tcholakov, 28-JUN-2006.
5104
 */
5105
function api_request_uri()
5106
{
5107
    if (!empty($_SERVER['REQUEST_URI'])) {
5108
        return $_SERVER['REQUEST_URI'];
5109
    }
5110
    $uri = $_SERVER['SCRIPT_NAME'];
5111
    if (!empty($_SERVER['QUERY_STRING'])) {
5112
        $uri .= '?'.$_SERVER['QUERY_STRING'];
5113
    }
5114
    $_SERVER['REQUEST_URI'] = $uri;
5115
5116
    return $uri;
5117
}
5118
5119
/**
5120
 * Gets the current access_url id of the Chamilo Platform.
5121
 *
5122
 * @return int access_url_id of the current Chamilo Installation
5123
 *
5124
 * @author Julio Montoya <[email protected]>
5125
 * @throws Exception
5126
 */
5127
function api_get_current_access_url_id(): int
5128
{
5129
    if (false === api_get_multiple_access_url()) {
5130
        return 1;
5131
    }
5132
5133
    static $id;
5134
    if (!empty($id)) {
5135
        return $id;
5136
    }
5137
5138
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5139
    $path = Database::escape_string(api_get_path(WEB_PATH));
5140
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
5141
    $result = Database::query($sql);
5142
    if (Database::num_rows($result) > 0) {
5143
        $id = Database::result($result, 0, 0);
5144
        if (false === $id) {
5145
            return -1;
5146
        }
5147
5148
        return (int) $id;
5149
    }
5150
5151
    $id = 1;
5152
5153
    //if the url in WEB_PATH was not found, it can only mean that there is
5154
    // either a configuration problem or the first URL has not been defined yet
5155
    // (by default it is http://localhost/). Thus the more sensible thing we can
5156
    // do is return 1 (the main URL) as the user cannot hack this value anyway
5157
    return 1;
5158
}
5159
5160
/**
5161
 * Gets the registered urls from a given user id.
5162
 *
5163
 * @author Julio Montoya <[email protected]>
5164
 *
5165
 * @param int $user_id
5166
 *
5167
 * @return array
5168
 */
5169
function api_get_access_url_from_user($user_id)
5170
{
5171
    $user_id = (int) $user_id;
5172
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5173
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
5174
    $sql = "SELECT access_url_id
5175
            FROM $table_url_rel_user url_rel_user
5176
            INNER JOIN $table_url u
5177
            ON (url_rel_user.access_url_id = u.id)
5178
            WHERE user_id = ".$user_id;
5179
    $result = Database::query($sql);
5180
    $list = [];
5181
    while ($row = Database::fetch_assoc($result)) {
5182
        $list[] = $row['access_url_id'];
5183
    }
5184
5185
    return $list;
5186
}
5187
5188
/**
5189
 * Checks whether the curent user is in a group or not.
5190
 *
5191
 * @param string        The group id - optional (takes it from session if not given)
5192
 * @param string        The course code - optional (no additional check by course if course code is not given)
5193
 *
5194
 * @return bool
5195
 *
5196
 * @author Ivan Tcholakov
5197
 */
5198
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
5199
{
5200
    if (!empty($courseCodeParam)) {
5201
        $courseCode = api_get_course_id();
5202
        if (!empty($courseCode)) {
5203
            if ($courseCodeParam != $courseCode) {
5204
                return false;
5205
            }
5206
        } else {
5207
            return false;
5208
        }
5209
    }
5210
5211
    $groupId = api_get_group_id();
5212
5213
    if (isset($groupId) && '' != $groupId) {
5214
        if (!empty($groupIdParam)) {
5215
            return $groupIdParam == $groupId;
5216
        } else {
5217
            return true;
5218
        }
5219
    }
5220
5221
    return false;
5222
}
5223
5224
/**
5225
 * Checks whether a secret key is valid.
5226
 *
5227
 * @param string $original_key_secret - secret key from (webservice) client
5228
 * @param string $security_key        - security key from Chamilo
5229
 *
5230
 * @return bool - true if secret key is valid, false otherwise
5231
 */
5232
function api_is_valid_secret_key($original_key_secret, $security_key)
5233
{
5234
    if (empty($original_key_secret) || empty($security_key)) {
5235
        return false;
5236
    }
5237
5238
    return (string) $original_key_secret === hash('sha512', $security_key);
5239
}
5240
5241
/**
5242
 * Checks whether the server's operating system is Windows (TM).
5243
 *
5244
 * @return bool - true if the operating system is Windows, false otherwise
5245
 */
5246
function api_is_windows_os()
5247
{
5248
    if (function_exists('php_uname')) {
5249
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
5250
        // We expect that this function will always work for Chamilo 1.8.x.
5251
        $os = php_uname();
5252
    }
5253
    // The following methods are not needed, but let them stay, just in case.
5254
    elseif (isset($_ENV['OS'])) {
5255
        // Sometimes $_ENV['OS'] may not be present (bugs?)
5256
        $os = $_ENV['OS'];
5257
    } elseif (defined('PHP_OS')) {
5258
        // PHP_OS means on which OS PHP was compiled, this is why
5259
        // using PHP_OS is the last choice for detection.
5260
        $os = PHP_OS;
5261
    } else {
5262
        return false;
5263
    }
5264
5265
    return 'win' == strtolower(substr((string) $os, 0, 3));
5266
}
5267
5268
/**
5269
 * This function informs whether the sent request is XMLHttpRequest.
5270
 */
5271
function api_is_xml_http_request()
5272
{
5273
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH']);
5274
}
5275
5276
/**
5277
 * Returns a list of Chamilo's tools or
5278
 * checks whether a given identificator is a valid Chamilo's tool.
5279
 *
5280
 * @author Isaac flores paz
5281
 *
5282
 * @param string The tool name to filter
5283
 *
5284
 * @return mixed Filtered string or array
5285
 */
5286
function api_get_tools_lists($my_tool = null)
5287
{
5288
    $tools_list = [
5289
        TOOL_DOCUMENT,
5290
        TOOL_THUMBNAIL,
5291
        TOOL_HOTPOTATOES,
5292
        TOOL_CALENDAR_EVENT,
5293
        TOOL_LINK,
5294
        TOOL_COURSE_DESCRIPTION,
5295
        TOOL_SEARCH,
5296
        TOOL_LEARNPATH,
5297
        TOOL_ANNOUNCEMENT,
5298
        TOOL_FORUM,
5299
        TOOL_THREAD,
5300
        TOOL_POST,
5301
        TOOL_DROPBOX,
5302
        TOOL_QUIZ,
5303
        TOOL_USER,
5304
        TOOL_GROUP,
5305
        TOOL_BLOGS,
5306
        TOOL_CHAT,
5307
        TOOL_STUDENTPUBLICATION,
5308
        TOOL_TRACKING,
5309
        TOOL_HOMEPAGE_LINK,
5310
        TOOL_COURSE_SETTING,
5311
        TOOL_BACKUP,
5312
        TOOL_COPY_COURSE_CONTENT,
5313
        TOOL_RECYCLE_COURSE,
5314
        TOOL_COURSE_HOMEPAGE,
5315
        TOOL_COURSE_RIGHTS_OVERVIEW,
5316
        TOOL_UPLOAD,
5317
        TOOL_COURSE_MAINTENANCE,
5318
        TOOL_SURVEY,
5319
        //TOOL_WIKI,
5320
        TOOL_GLOSSARY,
5321
        TOOL_GRADEBOOK,
5322
        TOOL_NOTEBOOK,
5323
        TOOL_ATTENDANCE,
5324
        TOOL_COURSE_PROGRESS,
5325
    ];
5326
    if (empty($my_tool)) {
5327
        return $tools_list;
5328
    }
5329
5330
    return in_array($my_tool, $tools_list) ? $my_tool : '';
5331
}
5332
5333
/**
5334
 * Checks whether we already approved the last version term and condition.
5335
 *
5336
 * @param int user id
5337
 *
5338
 * @return bool true if we pass false otherwise
5339
 */
5340
function api_check_term_condition($userId)
5341
{
5342
    if ('true' === api_get_setting('allow_terms_conditions')) {
5343
        // Check if exists terms and conditions
5344
        if (0 == LegalManager::count()) {
5345
            return true;
5346
        }
5347
5348
        $extraFieldValue = new ExtraFieldValue('user');
5349
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
5350
            $userId,
5351
            'legal_accept'
5352
        );
5353
5354
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
5355
            $result = $data['value'];
5356
            $user_conditions = explode(':', $result);
5357
            $version = $user_conditions[0];
5358
            $langId = $user_conditions[1];
5359
            $realVersion = LegalManager::get_last_version($langId);
5360
5361
            return $version >= $realVersion;
5362
        }
5363
5364
        return false;
5365
    }
5366
5367
    return false;
5368
}
5369
5370
/**
5371
 * Gets all information of a tool into course.
5372
 *
5373
 * @param int The tool id
5374
 *
5375
 * @return array
5376
 */
5377
function api_get_tool_information_by_name($name)
5378
{
5379
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
5380
    $course_id = api_get_course_int_id();
5381
5382
    $sql = "SELECT id FROM tool
5383
            WHERE title = '".Database::escape_string($name)."' ";
5384
    $rs = Database::query($sql);
5385
    $data = Database::fetch_array($rs);
5386
    if ($data) {
5387
        $tool = $data['id'];
5388
        $sql = "SELECT * FROM $t_tool
5389
                WHERE c_id = $course_id  AND tool_id = '".$tool."' ";
5390
        $rs = Database::query($sql);
5391
5392
        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...
5393
    }
5394
5395
    return [];
5396
}
5397
5398
/**
5399
 * Function used to protect a "global" admin script.
5400
 * The function blocks access when the user has no global platform admin rights.
5401
 * Global admins are the admins that are registered in the main.admin table
5402
 * AND the users who have access to the "principal" portal.
5403
 * That means that there is a record in the main.access_url_rel_user table
5404
 * with his user id and the access_url_id=1.
5405
 *
5406
 * @author Julio Montoya
5407
 *
5408
 * @param int $user_id
5409
 *
5410
 * @return bool
5411
 */
5412
function api_is_global_platform_admin($user_id = null)
5413
{
5414
    $user_id = (int) $user_id;
5415
    if (empty($user_id)) {
5416
        $user_id = api_get_user_id();
5417
    }
5418
    if (api_is_platform_admin_by_id($user_id)) {
5419
        $urlList = api_get_access_url_from_user($user_id);
5420
        // The admin is registered in the first "main" site with access_url_id = 1
5421
        if (in_array(1, $urlList)) {
5422
            return true;
5423
        }
5424
    }
5425
5426
    return false;
5427
}
5428
5429
/**
5430
 * @param int  $admin_id_to_check
5431
 * @param int  $userId
5432
 * @param bool $allow_session_admin
5433
 *
5434
 * @return bool
5435
 */
5436
function api_global_admin_can_edit_admin(
5437
    $admin_id_to_check,
5438
    $userId = 0,
5439
    $allow_session_admin = false
5440
) {
5441
    if (empty($userId)) {
5442
        $userId = api_get_user_id();
5443
    }
5444
5445
    $iam_a_global_admin = api_is_global_platform_admin($userId);
5446
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
5447
5448
    if ($iam_a_global_admin) {
5449
        // Global admin can edit everything
5450
        return true;
5451
    }
5452
5453
    // If i'm a simple admin
5454
    $is_platform_admin = api_is_platform_admin_by_id($userId);
5455
5456
    if ($allow_session_admin && !$is_platform_admin) {
5457
        $user = api_get_user_entity($userId);
5458
        $is_platform_admin = $user->hasRole('ROLE_SESSION_MANAGER');
5459
    }
5460
5461
    if ($is_platform_admin) {
5462
        if ($user_is_global_admin) {
5463
            return false;
5464
        } else {
5465
            return true;
5466
        }
5467
    }
5468
5469
    return false;
5470
}
5471
5472
/**
5473
 * @param int  $admin_id_to_check
5474
 * @param int  $userId
5475
 * @param bool $allow_session_admin
5476
 *
5477
 * @return bool|null
5478
 */
5479
function api_protect_super_admin($admin_id_to_check, $userId = null, $allow_session_admin = false)
5480
{
5481
    if (api_global_admin_can_edit_admin($admin_id_to_check, $userId, $allow_session_admin)) {
5482
        return true;
5483
    } else {
5484
        api_not_allowed();
5485
    }
5486
}
5487
5488
/**
5489
 * Function used to protect a global admin script.
5490
 * The function blocks access when the user has no global platform admin rights.
5491
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
5492
 *
5493
 * @author Julio Montoya
5494
 */
5495
function api_protect_global_admin_script()
5496
{
5497
    if (!api_is_global_platform_admin()) {
5498
        api_not_allowed();
5499
5500
        return false;
5501
    }
5502
5503
    return true;
5504
}
5505
5506
/**
5507
 * Check browser support for specific file types or features
5508
 * This function checks if the user's browser supports a file format or given
5509
 * feature, or returns the current browser and major version when
5510
 * $format=check_browser. Only a limited number of formats and features are
5511
 * checked by this method. Make sure you check its definition first.
5512
 *
5513
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
5514
 *
5515
 * @deprecated
5516
 *
5517
 * @return bool or return text array if $format=check_browser
5518
 *
5519
 * @author Juan Carlos Raña Trabado
5520
 */
5521
function api_browser_support($format = '')
5522
{
5523
    return true;
5524
5525
    $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...
5526
    $current_browser = $browser->getBrowser();
5527
    $a_versiontemp = explode('.', $browser->getVersion());
5528
    $current_majorver = $a_versiontemp[0];
5529
5530
    static $result;
5531
5532
    if (isset($result[$format])) {
5533
        return $result[$format];
5534
    }
5535
5536
    // Native svg support
5537
    if ('svg' == $format) {
5538
        if (('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5539
            ('Firefox' == $current_browser && $current_majorver > 1) ||
5540
            ('Safari' == $current_browser && $current_majorver >= 4) ||
5541
            ('Chrome' == $current_browser && $current_majorver >= 1) ||
5542
            ('Opera' == $current_browser && $current_majorver >= 9)
5543
        ) {
5544
            $result[$format] = true;
5545
5546
            return true;
5547
        } else {
5548
            $result[$format] = false;
5549
5550
            return false;
5551
        }
5552
    } elseif ('pdf' == $format) {
5553
        // native pdf support
5554
        if ('Chrome' == $current_browser && $current_majorver >= 6) {
5555
            $result[$format] = true;
5556
5557
            return true;
5558
        } else {
5559
            $result[$format] = false;
5560
5561
            return false;
5562
        }
5563
    } elseif ('tif' == $format || 'tiff' == $format) {
5564
        //native tif support
5565
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5566
            $result[$format] = true;
5567
5568
            return true;
5569
        } else {
5570
            $result[$format] = false;
5571
5572
            return false;
5573
        }
5574
    } elseif ('ogg' == $format || 'ogx' == $format || 'ogv' == $format || 'oga' == $format) {
5575
        //native ogg, ogv,oga support
5576
        if (('Firefox' == $current_browser && $current_majorver >= 3) ||
5577
            ('Chrome' == $current_browser && $current_majorver >= 3) ||
5578
            ('Opera' == $current_browser && $current_majorver >= 9)) {
5579
            $result[$format] = true;
5580
5581
            return true;
5582
        } else {
5583
            $result[$format] = false;
5584
5585
            return false;
5586
        }
5587
    } elseif ('mpg' == $format || 'mpeg' == $format) {
5588
        //native mpg support
5589
        if (('Safari' == $current_browser && $current_majorver >= 5)) {
5590
            $result[$format] = true;
5591
5592
            return true;
5593
        } else {
5594
            $result[$format] = false;
5595
5596
            return false;
5597
        }
5598
    } elseif ('mp4' == $format) {
5599
        //native mp4 support (TODO: Android, iPhone)
5600
        if ('Android' == $current_browser || 'iPhone' == $current_browser) {
5601
            $result[$format] = true;
5602
5603
            return true;
5604
        } else {
5605
            $result[$format] = false;
5606
5607
            return false;
5608
        }
5609
    } elseif ('mov' == $format) {
5610
        //native mov support( TODO:check iPhone)
5611
        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...
5612
            $result[$format] = true;
5613
5614
            return true;
5615
        } else {
5616
            $result[$format] = false;
5617
5618
            return false;
5619
        }
5620
    } elseif ('avi' == $format) {
5621
        //native avi support
5622
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5623
            $result[$format] = true;
5624
5625
            return true;
5626
        } else {
5627
            $result[$format] = false;
5628
5629
            return false;
5630
        }
5631
    } elseif ('wmv' == $format) {
5632
        //native wmv support
5633
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5634
            $result[$format] = true;
5635
5636
            return true;
5637
        } else {
5638
            $result[$format] = false;
5639
5640
            return false;
5641
        }
5642
    } elseif ('webm' == $format) {
5643
        //native webm support (TODO:check IE9, Chrome9, Android)
5644
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5645
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5646
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5647
            ('Chrome' == $current_browser && $current_majorver >= 9) ||
5648
            'Android' == $current_browser
5649
        ) {
5650
            $result[$format] = true;
5651
5652
            return true;
5653
        } else {
5654
            $result[$format] = false;
5655
5656
            return false;
5657
        }
5658
    } elseif ('wav' == $format) {
5659
        //native wav support (only some codecs !)
5660
        if (('Firefox' == $current_browser && $current_majorver >= 4) ||
5661
            ('Safari' == $current_browser && $current_majorver >= 5) ||
5662
            ('Opera' == $current_browser && $current_majorver >= 9) ||
5663
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5664
            ('Chrome' == $current_browser && $current_majorver > 9) ||
5665
            'Android' == $current_browser ||
5666
            'iPhone' == $current_browser
5667
        ) {
5668
            $result[$format] = true;
5669
5670
            return true;
5671
        } else {
5672
            $result[$format] = false;
5673
5674
            return false;
5675
        }
5676
    } elseif ('mid' == $format || 'kar' == $format) {
5677
        //native midi support (TODO:check Android)
5678
        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...
5679
            $result[$format] = true;
5680
5681
            return true;
5682
        } else {
5683
            $result[$format] = false;
5684
5685
            return false;
5686
        }
5687
    } elseif ('wma' == $format) {
5688
        //native wma support
5689
        if ('Firefox' == $current_browser && $current_majorver >= 4) {
5690
            $result[$format] = true;
5691
5692
            return true;
5693
        } else {
5694
            $result[$format] = false;
5695
5696
            return false;
5697
        }
5698
    } elseif ('au' == $format) {
5699
        //native au support
5700
        if ('Safari' == $current_browser && $current_majorver >= 5) {
5701
            $result[$format] = true;
5702
5703
            return true;
5704
        } else {
5705
            $result[$format] = false;
5706
5707
            return false;
5708
        }
5709
    } elseif ('mp3' == $format) {
5710
        //native mp3 support (TODO:check Android, iPhone)
5711
        if (('Safari' == $current_browser && $current_majorver >= 5) ||
5712
            ('Chrome' == $current_browser && $current_majorver >= 6) ||
5713
            ('Internet Explorer' == $current_browser && $current_majorver >= 9) ||
5714
            'Android' == $current_browser ||
5715
            'iPhone' == $current_browser ||
5716
            'Firefox' == $current_browser
5717
        ) {
5718
            $result[$format] = true;
5719
5720
            return true;
5721
        } else {
5722
            $result[$format] = false;
5723
5724
            return false;
5725
        }
5726
    } elseif ('autocapitalize' == $format) {
5727
        // Help avoiding showing the autocapitalize option if the browser doesn't
5728
        // support it: this attribute is against the HTML5 standard
5729
        if ('Safari' == $current_browser || 'iPhone' == $current_browser) {
5730
            return true;
5731
        } else {
5732
            return false;
5733
        }
5734
    } elseif ("check_browser" == $format) {
5735
        $array_check_browser = [$current_browser, $current_majorver];
5736
5737
        return $array_check_browser;
5738
    } else {
5739
        $result[$format] = false;
5740
5741
        return false;
5742
    }
5743
}
5744
5745
/**
5746
 * This function checks if exist path and file browscap.ini
5747
 * In order for this to work, your browscap configuration setting in php.ini
5748
 * must point to the correct location of the browscap.ini file on your system
5749
 * http://php.net/manual/en/function.get-browser.php.
5750
 *
5751
 * @return bool
5752
 *
5753
 * @author Juan Carlos Raña Trabado
5754
 */
5755
function api_check_browscap()
5756
{
5757
    $setting = ini_get('browscap');
5758
    if ($setting) {
5759
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
5760
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
5761
            return true;
5762
        }
5763
    }
5764
5765
    return false;
5766
}
5767
5768
/**
5769
 * Returns the <script> HTML tag.
5770
 */
5771
function api_get_js($file)
5772
{
5773
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
5774
}
5775
5776
function api_get_build_js($file)
5777
{
5778
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'"></script>'."\n";
5779
}
5780
5781
function api_get_build_css($file, $media = 'screen')
5782
{
5783
    return '<link
5784
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5785
}
5786
5787
/**
5788
 * Returns the <script> HTML tag.
5789
 *
5790
 * @return string
5791
 */
5792
function api_get_asset($file)
5793
{
5794
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"></script>'."\n";
5795
}
5796
5797
/**
5798
 * Returns the <script> HTML tag.
5799
 *
5800
 * @param string $file
5801
 * @param string $media
5802
 *
5803
 * @return string
5804
 */
5805
function api_get_css_asset($file, $media = 'screen')
5806
{
5807
    return '<link
5808
        href="'.api_get_path(WEB_PUBLIC_PATH).'build/libs/'.$file.'"
5809
        rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5810
}
5811
5812
/**
5813
 * Returns the <link> HTML tag.
5814
 *
5815
 * @param string $file
5816
 * @param string $media
5817
 */
5818
function api_get_css($file, $media = 'screen')
5819
{
5820
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
5821
}
5822
5823
function api_get_bootstrap_and_font_awesome($returnOnlyPath = false, $returnFileLocation = false)
5824
{
5825
    $url = api_get_path(WEB_PUBLIC_PATH).'build/css/bootstrap.css';
5826
5827
    if ($returnOnlyPath) {
5828
        if ($returnFileLocation) {
5829
            return api_get_path(SYS_PUBLIC_PATH).'build/css/bootstrap.css';
5830
        }
5831
5832
        return $url;
5833
    }
5834
5835
    return '<link href="'.$url.'" rel="stylesheet" type="text/css" />'."\n";
5836
}
5837
5838
/**
5839
 * Returns the js header to include the jquery library.
5840
 */
5841
function api_get_jquery_js()
5842
{
5843
    return api_get_asset('jquery/jquery.min.js');
5844
}
5845
5846
/**
5847
 * Returns the jquery path.
5848
 *
5849
 * @return string
5850
 */
5851
function api_get_jquery_web_path()
5852
{
5853
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/jquery.min.js';
5854
}
5855
5856
/**
5857
 * @return string
5858
 */
5859
function api_get_jquery_ui_js_web_path()
5860
{
5861
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
5862
}
5863
5864
/**
5865
 * @return string
5866
 */
5867
function api_get_jquery_ui_css_web_path()
5868
{
5869
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
5870
}
5871
5872
/**
5873
 * Returns the jquery-ui library js headers.
5874
 *
5875
 * @return string html tags
5876
 */
5877
function api_get_jquery_ui_js()
5878
{
5879
    $libraries = [];
5880
5881
    return api_get_jquery_libraries_js($libraries);
5882
}
5883
5884
function api_get_jqgrid_js()
5885
{
5886
    return api_get_build_css('legacy_free-jqgrid.css').PHP_EOL
5887
        .api_get_build_js('legacy_free-jqgrid.js');
5888
}
5889
5890
/**
5891
 * Returns the jquery library js and css headers.
5892
 *
5893
 * @param   array   list of jquery libraries supported jquery-ui
5894
 * @param   bool    add the jquery library
5895
 *
5896
 * @return string html tags
5897
 */
5898
function api_get_jquery_libraries_js($libraries)
5899
{
5900
    $js = '';
5901
5902
    //Document multiple upload funcionality
5903
    if (in_array('jquery-uploadzs', $libraries)) {
5904
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
5905
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
5906
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
5907
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
5908
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
5909
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
5910
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
5911
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
5912
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
5913
5914
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
5915
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
5916
    }
5917
5918
    // jquery datepicker
5919
    if (in_array('datepicker', $libraries)) {
5920
        $languaje = 'en-GB';
5921
        $platform_isocode = strtolower(api_get_language_isocode());
5922
5923
        $datapicker_langs = [
5924
            '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',
5925
        ];
5926
        if (in_array($platform_isocode, $datapicker_langs)) {
5927
            $languaje = $platform_isocode;
5928
        }
5929
5930
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
5931
        $script = '<script>
5932
        $(function(){
5933
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
5934
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
5935
        });
5936
        </script>
5937
        ';
5938
        $js .= $script;
5939
    }
5940
5941
    return $js;
5942
}
5943
5944
/**
5945
 * Returns the URL to the course or session, removing the complexity of the URL
5946
 * building piece by piece.
5947
 *
5948
 * This function relies on api_get_course_info()
5949
 *
5950
 * @param int $courseId  The course code - optional (takes it from context if not given)
5951
 * @param int $sessionId The session ID  - optional (takes it from context if not given)
5952
 * @param int $groupId   The group ID - optional (takes it from context if not given)
5953
 *
5954
 * @return string The URL to a course, a session, or empty string if nothing works
5955
 *                e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
5956
 *
5957
 * @author  Julio Montoya
5958
 */
5959
function api_get_course_url($courseId = null, $sessionId = null, $groupId = null)
5960
{
5961
    $url = '';
5962
    // If courseCode not set, get context or []
5963
    if (empty($courseId)) {
5964
        $courseId = api_get_course_int_id();
5965
    }
5966
5967
    // If sessionId not set, get context or 0
5968
    if (empty($sessionId)) {
5969
        $sessionId = api_get_session_id();
5970
    }
5971
5972
    // If groupId not set, get context or 0
5973
    if (empty($groupId)) {
5974
        $groupId = api_get_group_id();
5975
    }
5976
5977
    // Build the URL
5978
    if (!empty($courseId)) {
5979
        $webCourseHome = '/course/'.$courseId.'/home';
5980
        // directory not empty, so we do have a course
5981
        $url = $webCourseHome.'?sid='.$sessionId.'&gid='.$groupId;
5982
    } else {
5983
        if (!empty($sessionId) && 'true' !== api_get_setting('session.remove_session_url')) {
5984
            // if the course was unset and the session was set, send directly to the session
5985
            $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
5986
        }
5987
    }
5988
5989
    // if not valid combination was found, return an empty string
5990
    return $url;
5991
}
5992
5993
/**
5994
 * Check if there is more than the default URL defined in the access_url table.
5995
 */
5996
function api_get_multiple_access_url(): bool
5997
{
5998
    return Container::$container->get(AccessUrlHelper::class)->isMultiple();
5999
}
6000
6001
/**
6002
 * @deprecated Use AccessUrlHelper::isMultiple
6003
 */
6004
function api_is_multiple_url_enabled(): bool
6005
{
6006
    return api_get_multiple_access_url();
6007
}
6008
6009
/**
6010
 * Returns a md5 unique id.
6011
 *
6012
 * @todo add more parameters
6013
 */
6014
function api_get_unique_id()
6015
{
6016
    return md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
6017
}
6018
6019
/**
6020
 * @param int Course id
6021
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
6022
 * @param int the item id (tool id, exercise id, lp id)
6023
 *
6024
 * @return bool
6025
 */
6026
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
6027
{
6028
    if (api_is_platform_admin()) {
6029
        return false;
6030
    }
6031
    if ('true' === api_get_setting('gradebook_locking_enabled')) {
6032
        if (empty($course_code)) {
6033
            $course_code = api_get_course_id();
6034
        }
6035
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
6036
        $item_id = (int) $item_id;
6037
        $link_type = (int) $link_type;
6038
        $course_code = Database::escape_string($course_code);
6039
        $sql = "SELECT locked FROM $table
6040
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
6041
        $result = Database::query($sql);
6042
        if (Database::num_rows($result)) {
6043
            return true;
6044
        }
6045
    }
6046
6047
    return false;
6048
}
6049
6050
/**
6051
 * Blocks a page if the item was added in a gradebook.
6052
 *
6053
 * @param int       exercise id, work id, thread id,
6054
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
6055
 * see gradebook/lib/be/linkfactory
6056
 * @param string    course code
6057
 *
6058
 * @return false|null
6059
 */
6060
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
6061
{
6062
    if (api_is_platform_admin()) {
6063
        return false;
6064
    }
6065
6066
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
6067
        $message = Display::return_message(
6068
            get_lang(
6069
                '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.'
6070
            ),
6071
            'warning'
6072
        );
6073
        api_not_allowed(true, $message);
6074
    }
6075
}
6076
6077
/**
6078
 * Checks the PHP version installed is enough to run Chamilo.
6079
 *
6080
 * @param string Include path (used to load the error page)
6081
 */
6082
function api_check_php_version()
6083
{
6084
    if (!function_exists('version_compare') ||
6085
        version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')
6086
    ) {
6087
        throw new Exception('Wrong PHP version');
6088
    }
6089
}
6090
6091
/**
6092
 * Checks whether the Archive directory is present and writeable. If not,
6093
 * prints a warning message.
6094
 */
6095
function api_check_archive_dir()
6096
{
6097
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
6098
        $message = Display::return_message(
6099
            get_lang(
6100
                'The var/cache/ directory, used by this tool, is not writeable. Please contact your platform administrator.'
6101
            ),
6102
            'warning'
6103
        );
6104
        api_not_allowed(true, $message);
6105
    }
6106
}
6107
6108
/**
6109
 * Returns an array of global configuration settings which should be ignored
6110
 * when printing the configuration settings screens.
6111
 *
6112
 * @return array Array of strings, each identifying one of the excluded settings
6113
 */
6114
function api_get_locked_settings()
6115
{
6116
    return [
6117
        'permanently_remove_deleted_files',
6118
        'account_valid_duration',
6119
        'service_ppt2lp',
6120
        'wcag_anysurfer_public_pages',
6121
        'upload_extensions_list_type',
6122
        'upload_extensions_blacklist',
6123
        'upload_extensions_whitelist',
6124
        'upload_extensions_skip',
6125
        'upload_extensions_replace_by',
6126
        'hide_dltt_markup',
6127
        'split_users_upload_directory',
6128
        'permissions_for_new_directories',
6129
        'permissions_for_new_files',
6130
        'platform_charset',
6131
        'ldap_description',
6132
        'cas_activate',
6133
        'cas_server',
6134
        'cas_server_uri',
6135
        'cas_port',
6136
        'cas_protocol',
6137
        'cas_add_user_activate',
6138
        'update_user_info_cas_with_ldap',
6139
        'languagePriority1',
6140
        'languagePriority2',
6141
        'languagePriority3',
6142
        'languagePriority4',
6143
        'login_is_email',
6144
        'chamilo_database_version',
6145
    ];
6146
}
6147
6148
/**
6149
 * Guess the real ip for register in the database, even in reverse proxy cases.
6150
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
6151
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
6152
 * Note: the result of this function is not SQL-safe. Please escape it before
6153
 * inserting in a database.
6154
 *
6155
 * @return string the user's real ip (unsafe - escape it before inserting to db)
6156
 *
6157
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
6158
 *
6159
 * @version CEV CHANGE 24APR2012
6160
 * @throws RuntimeException
6161
 */
6162
function api_get_real_ip(): string
6163
{
6164
    if ('cli' === PHP_SAPI) {
6165
        $ip = '127.0.0.1';
6166
    } else {
6167
        $ip = trim($_SERVER['REMOTE_ADDR']);
6168
        if (empty($ip)) {
6169
            throw new RuntimeException("Unable to retrieve remote IP address.");
6170
        }
6171
    }
6172
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
6173
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
6174
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
6175
        } else {
6176
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
6177
        }
6178
        $ip = trim($ip1);
6179
    }
6180
6181
    return $ip;
6182
}
6183
6184
/**
6185
 * Checks whether an IP is included inside an IP range.
6186
 *
6187
 * @param string IP address
6188
 * @param string IP range
6189
 * @param string $ip
6190
 *
6191
 * @return bool True if IP is in the range, false otherwise
6192
 *
6193
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
6194
 * @author Yannick Warnier for improvements and managment of multiple ranges
6195
 *
6196
 * @todo check for IPv6 support
6197
 */
6198
function api_check_ip_in_range($ip, $range)
6199
{
6200
    if (empty($ip) or empty($range)) {
6201
        return false;
6202
    }
6203
    $ip_ip = ip2long($ip);
6204
    // divide range param into array of elements
6205
    if (false !== strpos($range, ',')) {
6206
        $ranges = explode(',', $range);
6207
    } else {
6208
        $ranges = [$range];
6209
    }
6210
    foreach ($ranges as $range) {
0 ignored issues
show
introduced by
$range is overwriting one of the parameters of this function.
Loading history...
6211
        $range = trim($range);
6212
        if (empty($range)) {
6213
            continue;
6214
        }
6215
        if (false === strpos($range, '/')) {
6216
            if (0 === strcmp($ip, $range)) {
6217
                return true; // there is a direct IP match, return OK
6218
            }
6219
            continue; //otherwise, get to the next range
6220
        }
6221
        // the range contains a "/", so analyse completely
6222
        [$net, $mask] = explode("/", $range);
6223
6224
        $ip_net = ip2long($net);
6225
        // mask binary magic
6226
        $ip_mask = ~((1 << (32 - $mask)) - 1);
6227
6228
        $ip_ip_net = $ip_ip & $ip_mask;
6229
        if ($ip_ip_net == $ip_net) {
6230
            return true;
6231
        }
6232
    }
6233
6234
    return false;
6235
}
6236
6237
function api_check_user_access_to_legal($courseInfo)
6238
{
6239
    if (empty($courseInfo)) {
6240
        return false;
6241
    }
6242
6243
    $visibility = (int) $courseInfo['visibility'];
6244
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
6245
6246
    return
6247
        in_array($visibility, $visibilityList) ||
6248
        api_is_drh() ||
6249
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
6250
}
6251
6252
/**
6253
 * Checks if the global chat is enabled or not.
6254
 *
6255
 * @return bool
6256
 */
6257
function api_is_global_chat_enabled()
6258
{
6259
    return
6260
        !api_is_anonymous() &&
6261
        'true' === api_get_setting('allow_global_chat') &&
6262
        'true' === api_get_setting('allow_social_tool');
6263
}
6264
6265
/**
6266
 * @param int   $item_id
6267
 * @param int   $tool_id
6268
 * @param int   $group_id   id
6269
 * @param array $courseInfo
6270
 * @param int   $sessionId
6271
 * @param int   $userId
6272
 *
6273
 * @deprecated
6274
 */
6275
function api_set_default_visibility(
6276
    $item_id,
6277
    $tool_id,
6278
    $group_id = 0,
6279
    $courseInfo = [],
6280
    $sessionId = 0,
6281
    $userId = 0
6282
) {
6283
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
6284
    $courseId = $courseInfo['real_id'];
6285
    $courseCode = $courseInfo['code'];
6286
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6287
    $userId = empty($userId) ? api_get_user_id() : $userId;
6288
6289
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
6290
    if (is_null($group_id)) {
6291
        $group_id = 0;
6292
    } else {
6293
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
6294
    }
6295
6296
    $groupInfo = [];
6297
    if (!empty($group_id)) {
6298
        $groupInfo = GroupManager::get_group_properties($group_id);
6299
    }
6300
    $original_tool_id = $tool_id;
6301
6302
    switch ($tool_id) {
6303
        case TOOL_LINK:
6304
        case TOOL_LINK_CATEGORY:
6305
            $tool_id = 'links';
6306
            break;
6307
        case TOOL_DOCUMENT:
6308
            $tool_id = 'documents';
6309
            break;
6310
        case TOOL_LEARNPATH:
6311
            $tool_id = 'learning';
6312
            break;
6313
        case TOOL_ANNOUNCEMENT:
6314
            $tool_id = 'announcements';
6315
            break;
6316
        case TOOL_FORUM:
6317
        case TOOL_FORUM_CATEGORY:
6318
        case TOOL_FORUM_THREAD:
6319
            $tool_id = 'forums';
6320
            break;
6321
        case TOOL_QUIZ:
6322
            $tool_id = 'quiz';
6323
            break;
6324
    }
6325
    $setting = api_get_setting('tool_visible_by_default_at_creation');
6326
6327
    if (isset($setting[$tool_id])) {
6328
        $visibility = 'invisible';
6329
        if ('true' === $setting[$tool_id]) {
6330
            $visibility = 'visible';
6331
        }
6332
6333
        // Read the portal and course default visibility
6334
        if ('documents' === $tool_id) {
6335
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
6336
        }
6337
6338
        // Fixes default visibility for tests
6339
        switch ($original_tool_id) {
6340
            case TOOL_QUIZ:
6341
                if (empty($sessionId)) {
6342
                    $objExerciseTmp = new Exercise($courseId);
6343
                    $objExerciseTmp->read($item_id);
6344
                    if ('visible' === $visibility) {
6345
                        $objExerciseTmp->enable();
6346
                        $objExerciseTmp->save();
6347
                    } else {
6348
                        $objExerciseTmp->disable();
6349
                        $objExerciseTmp->save();
6350
                    }
6351
                }
6352
                break;
6353
        }
6354
    }
6355
}
6356
6357
function api_get_roles()
6358
{
6359
    $hierarchy = Container::$container->getParameter('security.role_hierarchy.roles');
6360
    $roles = [];
6361
    array_walk_recursive($hierarchy, function ($role) use (&$roles) {
6362
        $roles[$role] = $role;
6363
    });
6364
6365
    return $roles;
6366
}
6367
6368
function api_get_user_roles(): array
6369
{
6370
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
6371
6372
    $roles = $permissionService->getUserRoles();
6373
6374
    return array_combine($roles, $roles);
6375
}
6376
6377
/**
6378
 * @param string $file
6379
 *
6380
 * @return string
6381
 */
6382
function api_get_js_simple($file)
6383
{
6384
    return '<script type="text/javascript" src="'.$file.'"></script>'."\n";
6385
}
6386
6387
/**
6388
 * Modify default memory_limit and max_execution_time limits
6389
 * Needed when processing long tasks.
6390
 */
6391
function api_set_more_memory_and_time_limits()
6392
{
6393
    if (function_exists('ini_set')) {
6394
        api_set_memory_limit('256M');
6395
        ini_set('max_execution_time', 1800);
6396
    }
6397
}
6398
6399
/**
6400
 * Tries to set memory limit, if authorized and new limit is higher than current.
6401
 *
6402
 * @param string $mem New memory limit
6403
 *
6404
 * @return bool True on success, false on failure or current is higher than suggested
6405
 * @assert (null) === false
6406
 * @assert (-1) === false
6407
 * @assert (0) === true
6408
 * @assert ('1G') === true
6409
 */
6410
function api_set_memory_limit($mem)
6411
{
6412
    //if ini_set() not available, this function is useless
6413
    if (!function_exists('ini_set') || is_null($mem) || -1 == $mem) {
6414
        return false;
6415
    }
6416
6417
    $memory_limit = ini_get('memory_limit');
6418
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
6419
        ini_set('memory_limit', $mem);
6420
6421
        return true;
6422
    }
6423
6424
    return false;
6425
}
6426
6427
/**
6428
 * Gets memory limit in bytes.
6429
 *
6430
 * @param string The memory size (128M, 1G, 1000K, etc)
6431
 *
6432
 * @return int
6433
 * @assert (null) === false
6434
 * @assert ('1t')  === 1099511627776
6435
 * @assert ('1g')  === 1073741824
6436
 * @assert ('1m')  === 1048576
6437
 * @assert ('100k') === 102400
6438
 */
6439
function api_get_bytes_memory_limit($mem)
6440
{
6441
    $size = strtolower(substr($mem, -1));
6442
6443
    switch ($size) {
6444
        case 't':
6445
            $mem = (int) substr($mem, -1) * 1024 * 1024 * 1024 * 1024;
6446
            break;
6447
        case 'g':
6448
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024 * 1024;
6449
            break;
6450
        case 'm':
6451
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024;
6452
            break;
6453
        case 'k':
6454
            $mem = (int) substr($mem, 0, -1) * 1024;
6455
            break;
6456
        default:
6457
            // we assume it's integer only
6458
            $mem = (int) $mem;
6459
            break;
6460
    }
6461
6462
    return $mem;
6463
}
6464
6465
/**
6466
 * Finds all the information about a user from username instead of user id.
6467
 *
6468
 * @param string $officialCode
6469
 *
6470
 * @return array $user_info user_id, lastname, firstname, username, email, ...
6471
 *
6472
 * @author Yannick Warnier <[email protected]>
6473
 */
6474
function api_get_user_info_from_official_code($officialCode)
6475
{
6476
    if (empty($officialCode)) {
6477
        return false;
6478
    }
6479
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
6480
            WHERE official_code ='".Database::escape_string($officialCode)."'";
6481
    $result = Database::query($sql);
6482
    if (Database::num_rows($result) > 0) {
6483
        $result_array = Database::fetch_array($result);
6484
6485
        return _api_format_user($result_array);
6486
    }
6487
6488
    return false;
6489
}
6490
6491
/**
6492
 * @param string $usernameInputId
6493
 * @param string $passwordInputId
6494
 *
6495
 * @return string|null
6496
 */
6497
function api_get_password_checker_js($usernameInputId, $passwordInputId)
6498
{
6499
    $checkPass = api_get_setting('allow_strength_pass_checker');
6500
    $useStrengthPassChecker = 'true' === $checkPass;
6501
6502
    if (false === $useStrengthPassChecker) {
6503
        return null;
6504
    }
6505
6506
    $minRequirements = Security::getPasswordRequirements()['min'];
6507
6508
    $options = [
6509
        'rules' => [],
6510
    ];
6511
6512
    if ($minRequirements['length'] > 0) {
6513
        $options['rules'][] = [
6514
            'minChar' => $minRequirements['length'],
6515
            'pattern' => '.',
6516
            'helpText' => sprintf(
6517
                get_lang('Minimum %s characters in total'),
6518
                $minRequirements['length']
6519
            ),
6520
        ];
6521
    }
6522
6523
    if ($minRequirements['lowercase'] > 0) {
6524
        $options['rules'][] = [
6525
            'minChar' => $minRequirements['lowercase'],
6526
            'pattern' => '[a-z]',
6527
            'helpText' => sprintf(
6528
                get_lang('Minimum %s lowercase characters'),
6529
                $minRequirements['lowercase']
6530
            ),
6531
        ];
6532
    }
6533
6534
    if ($minRequirements['uppercase'] > 0) {
6535
        $options['rules'][] = [
6536
            'minChar' => $minRequirements['uppercase'],
6537
            'pattern' => '[A-Z]',
6538
            'helpText' => sprintf(
6539
                get_lang('Minimum %s uppercase characters'),
6540
                $minRequirements['uppercase']
6541
            ),
6542
        ];
6543
    }
6544
6545
    if ($minRequirements['numeric'] > 0) {
6546
        $options['rules'][] = [
6547
            'minChar' => $minRequirements['numeric'],
6548
            'pattern' => '[0-9]',
6549
            'helpText' => sprintf(
6550
                get_lang('Minimum %s numerical (0-9) characters'),
6551
                $minRequirements['numeric']
6552
            ),
6553
        ];
6554
    }
6555
6556
    if ($minRequirements['specials'] > 0) {
6557
        $options['rules'][] = [
6558
            'minChar' => $minRequirements['specials'],
6559
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
6560
            'helpText' => sprintf(
6561
                get_lang('Minimum %s special characters'),
6562
                $minRequirements['specials']
6563
            ),
6564
        ];
6565
    }
6566
6567
    $js = api_get_js('password-checker/password-checker.js');
6568
    $js .= "<script>
6569
    $(function() {
6570
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
6571
    });
6572
    </script>";
6573
6574
    return $js;
6575
}
6576
6577
/**
6578
 * create an user extra field called 'captcha_blocked_until_date'.
6579
 *
6580
 * @param string $username
6581
 *
6582
 * @return bool
6583
 */
6584
function api_block_account_captcha($username)
6585
{
6586
    $userInfo = api_get_user_info_from_username($username);
6587
    if (empty($userInfo)) {
6588
        return false;
6589
    }
6590
    $minutesToBlock = api_get_setting('captcha_time_to_block');
6591
    $time = time() + $minutesToBlock * 60;
6592
    UserManager::update_extra_field_value(
6593
        $userInfo['user_id'],
6594
        'captcha_blocked_until_date',
6595
        api_get_utc_datetime($time)
6596
    );
6597
6598
    return true;
6599
}
6600
6601
/**
6602
 * @param string $username
6603
 *
6604
 * @return bool
6605
 */
6606
function api_clean_account_captcha($username)
6607
{
6608
    $userInfo = api_get_user_info_from_username($username);
6609
    if (empty($userInfo)) {
6610
        return false;
6611
    }
6612
    Session::erase('loginFailedCount');
6613
    UserManager::update_extra_field_value(
6614
        $userInfo['user_id'],
6615
        'captcha_blocked_until_date',
6616
        null
6617
    );
6618
6619
    return true;
6620
}
6621
6622
/**
6623
 * @param string $username
6624
 *
6625
 * @return bool
6626
 */
6627
function api_get_user_blocked_by_captcha($username)
6628
{
6629
    $userInfo = api_get_user_info_from_username($username);
6630
    if (empty($userInfo)) {
6631
        return false;
6632
    }
6633
    $data = UserManager::get_extra_user_data_by_field(
6634
        $userInfo['user_id'],
6635
        'captcha_blocked_until_date'
6636
    );
6637
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
6638
        return $data['captcha_blocked_until_date'];
6639
    }
6640
6641
    return false;
6642
}
6643
6644
/**
6645
 * If true, the drh can access all content (courses, users) inside a session.
6646
 *
6647
 * @return bool
6648
 */
6649
function api_drh_can_access_all_session_content()
6650
{
6651
    return 'true' === api_get_setting('drh_can_access_all_session_content');
6652
}
6653
6654
/**
6655
 * Checks if user can login as another user.
6656
 *
6657
 * @param int $loginAsUserId the user id to log in
6658
 * @param int $userId        my user id
6659
 *
6660
 * @return bool
6661
 */
6662
function api_can_login_as($loginAsUserId, $userId = null)
6663
{
6664
    $loginAsUserId = (int) $loginAsUserId;
6665
6666
    if (empty($loginAsUserId)) {
6667
        return false;
6668
    }
6669
6670
    if (empty($userId)) {
6671
        $userId = api_get_user_id();
6672
    }
6673
6674
    if ($loginAsUserId == $userId) {
6675
        return false;
6676
    }
6677
6678
    // Check if the user to login is an admin
6679
    if (api_is_platform_admin_by_id($loginAsUserId)) {
6680
        // Only super admins can login to admin accounts
6681
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
6682
            return false;
6683
        }
6684
    }
6685
6686
    $userInfo = api_get_user_info($loginAsUserId);
6687
6688
    $isDrh = function () use ($loginAsUserId) {
6689
        if (api_is_drh()) {
6690
            if (api_drh_can_access_all_session_content()) {
6691
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
6692
                    'drh_all',
6693
                    api_get_user_id()
6694
                );
6695
                $userList = [];
6696
                if (is_array($users)) {
6697
                    foreach ($users as $user) {
6698
                        $userList[] = $user['id'];
6699
                    }
6700
                }
6701
                if (in_array($loginAsUserId, $userList)) {
6702
                    return true;
6703
                }
6704
            } else {
6705
                if (api_is_drh() &&
6706
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
6707
                ) {
6708
                    return true;
6709
                }
6710
            }
6711
        }
6712
6713
        return false;
6714
    };
6715
6716
    $loginAsStatusForSessionAdmins = [STUDENT];
6717
6718
    if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
6719
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
6720
    }
6721
6722
    return api_is_platform_admin() ||
6723
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
6724
        $isDrh();
6725
}
6726
6727
/**
6728
 * Return true on https install.
6729
 *
6730
 * @return bool
6731
 */
6732
function api_is_https()
6733
{
6734
    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...
6735
        'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] || !empty(api_get_configuration_value('force_https_forwarded_proto'))
6736
    ) {
6737
        $isSecured = true;
6738
    } else {
6739
        if (!empty($_SERVER['HTTPS']) && 'off' != $_SERVER['HTTPS']) {
6740
            $isSecured = true;
6741
        } else {
6742
            $isSecured = false;
6743
            // last chance
6744
            if (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) {
6745
                $isSecured = true;
6746
            }
6747
        }
6748
    }
6749
6750
    return $isSecured;
6751
}
6752
6753
/**
6754
 * Return protocol (http or https).
6755
 *
6756
 * @return string
6757
 */
6758
function api_get_protocol()
6759
{
6760
    return api_is_https() ? 'https' : 'http';
6761
}
6762
6763
/**
6764
 * Get origin.
6765
 *
6766
 * @param string
6767
 *
6768
 * @return string
6769
 */
6770
function api_get_origin()
6771
{
6772
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
6773
}
6774
6775
/**
6776
 * Warns an user that the portal reach certain limit.
6777
 *
6778
 * @param string $limitName
6779
 */
6780
function api_warn_hosting_contact($limitName)
6781
{
6782
    $hostingParams = api_get_configuration_value(1);
6783
    $email = null;
6784
6785
    if (!empty($hostingParams)) {
6786
        if (isset($hostingParams['hosting_contact_mail'])) {
6787
            $email = $hostingParams['hosting_contact_mail'];
6788
        }
6789
    }
6790
6791
    if (!empty($email)) {
6792
        $subject = get_lang('Hosting warning reached');
6793
        $body = get_lang('Portal name').': '.api_get_path(WEB_PATH)." \n ";
6794
        $body .= get_lang('Portal\'s limit type').': '.$limitName." \n ";
6795
        if (isset($hostingParams[$limitName])) {
6796
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
6797
        }
6798
        api_mail_html(null, $email, $subject, $body);
6799
    }
6800
}
6801
6802
/**
6803
 * Gets value of a variable from config/configuration.php
6804
 * Variables that are not set in the configuration.php file but set elsewhere:
6805
 * - virtual_css_theme_folder (vchamilo plugin)
6806
 * - access_url (global.inc.php)
6807
 * - apc/apc_prefix (global.inc.php).
6808
 *
6809
 * @param string $variable
6810
 *
6811
 * @return bool|mixed
6812
 */
6813
function api_get_configuration_value($variable)
6814
{
6815
    global $_configuration;
6816
    // Check the current url id, id = 1 by default
6817
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
6818
6819
    $variable = trim($variable);
6820
6821
    // Check if variable exists
6822
    if (isset($_configuration[$variable])) {
6823
        if (is_array($_configuration[$variable])) {
6824
            // Check if it exists for the sub portal
6825
            if (array_key_exists($urlId, $_configuration[$variable])) {
6826
                return $_configuration[$variable][$urlId];
6827
            } else {
6828
                // Try to found element with id = 1 (master portal)
6829
                if (array_key_exists(1, $_configuration[$variable])) {
6830
                    return $_configuration[$variable][1];
6831
                }
6832
            }
6833
        }
6834
6835
        return $_configuration[$variable];
6836
    }
6837
6838
    return false;
6839
}
6840
6841
/**
6842
 * Loads hosting limits from the YAML file.
6843
 *
6844
 * @return array The hosting limits.
6845
 */
6846
function load_hosting_limits(): array
6847
{
6848
    $container = Container::$container;
6849
6850
    $hostingLimits = $container->getParameter('hosting_limits');
0 ignored issues
show
Bug introduced by
The method getParameter() 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

6850
    /** @scrutinizer ignore-call */ 
6851
    $hostingLimits = $container->getParameter('hosting_limits');

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...
6851
6852
    return $hostingLimits['urls'] ?? [];
6853
}
6854
6855
/**
6856
 * Gets a specific hosting limit.
6857
 *
6858
 * @param int $urlId The URL ID.
6859
 * @param string $limitName The name of the limit.
6860
 * @return mixed The value of the limit, or null if not found.
6861
 */
6862
function get_hosting_limit(int $urlId, string $limitName): mixed
6863
{
6864
    $limits = load_hosting_limits();
6865
6866
    foreach ($limits[$urlId] as $limitArray) {
6867
        if (isset($limitArray[$limitName])) {
6868
            return $limitArray[$limitName];
6869
        }
6870
    }
6871
6872
    return null;
6873
}
6874
6875
6876
/**
6877
 * Retrieves an environment variable value with validation and handles boolean conversion.
6878
 *
6879
 * @param string $variable The name of the environment variable.
6880
 * @param mixed $default The default value to return if the variable is not set.
6881
 * @return mixed The value of the environment variable, converted to boolean if necessary, or the default value.
6882
 */
6883
function api_get_env_variable(string $variable, mixed $default = null): mixed
6884
{
6885
    $variable = strtolower($variable);
6886
    if (Container::$container->hasParameter($variable)) {
6887
        return Container::$container->getParameter($variable);
6888
    }
6889
6890
    return $default;
6891
}
6892
6893
/**
6894
 * Retreives and returns a value in a hierarchical configuration array
6895
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
6896
 *
6897
 * @param string $path      the successive array keys, separated by the separator
6898
 * @param mixed  $default   value to be returned if not found, null by default
6899
 * @param string $separator '/' by default
6900
 * @param array  $array     the active configuration array by default
6901
 *
6902
 * @return mixed the found value or $default
6903
 */
6904
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
6905
{
6906
    $pos = strpos($path, $separator);
6907
    if (false === $pos) {
6908
        if (is_null($array)) {
6909
            return api_get_configuration_value($path);
6910
        }
6911
        if (is_array($array) && array_key_exists($path, $array)) {
6912
            return $array[$path];
6913
        }
6914
6915
        return $default;
6916
    }
6917
    $key = substr($path, 0, $pos);
6918
    if (is_null($array)) {
6919
        $newArray = api_get_configuration_value($key);
6920
    } elseif (is_array($array) && array_key_exists($key, $array)) {
6921
        $newArray = $array[$key];
6922
    } else {
6923
        return $default;
6924
    }
6925
    if (is_array($newArray)) {
6926
        $newPath = substr($path, $pos + 1);
6927
6928
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
6929
    }
6930
6931
    return $default;
6932
}
6933
6934
/**
6935
 * Retrieves and returns a value in a hierarchical configuration array
6936
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
6937
 *
6938
 * @param array  $array     the recursive array that contains the value to be returned (or not)
6939
 * @param string $path      the successive array keys, separated by the separator
6940
 * @param mixed  $default   the value to be returned if not found
6941
 * @param string $separator the separator substring
6942
 *
6943
 * @return mixed the found value or $default
6944
 */
6945
function api_array_sub_value($array, $path, $default = null, $separator = '/')
6946
{
6947
    $pos = strpos($path, $separator);
6948
    if (false === $pos) {
6949
        if (is_array($array) && array_key_exists($path, $array)) {
6950
            return $array[$path];
6951
        }
6952
6953
        return $default;
6954
    }
6955
    $key = substr($path, 0, $pos);
6956
    if (is_array($array) && array_key_exists($key, $array)) {
6957
        $newArray = $array[$key];
6958
    } else {
6959
        return $default;
6960
    }
6961
    if (is_array($newArray)) {
6962
        $newPath = substr($path, $pos + 1);
6963
6964
        return api_array_sub_value($newArray, $newPath, $default);
6965
    }
6966
6967
    return $default;
6968
}
6969
6970
/**
6971
 * Returns supported image extensions in the portal.
6972
 *
6973
 * @param bool $supportVectors Whether vector images should also be accepted or not
6974
 *
6975
 * @return array Supported image extensions in the portal
6976
 */
6977
function api_get_supported_image_extensions($supportVectors = true)
6978
{
6979
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
6980
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
6981
    if ($supportVectors) {
6982
        array_push($supportedImageExtensions, 'svg');
6983
    }
6984
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
6985
        array_push($supportedImageExtensions, 'webp');
6986
    }
6987
6988
    return $supportedImageExtensions;
6989
}
6990
6991
/**
6992
 * This setting changes the registration status for the campus.
6993
 *
6994
 * @author Patrick Cool <[email protected]>, Ghent University
6995
 *
6996
 * @version August 2006
6997
 *
6998
 * @param bool $listCampus Whether we authorize
6999
 *
7000
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
7001
 */
7002
function api_register_campus($listCampus = true)
7003
{
7004
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
7005
7006
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
7007
    Database::query($sql);
7008
7009
    if (!$listCampus) {
7010
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
7011
        Database::query($sql);
7012
    }
7013
}
7014
7015
/**
7016
 * Check whether the user type should be exclude.
7017
 * Such as invited or anonymous users.
7018
 *
7019
 * @param bool $checkDB Optional. Whether check the user status
7020
 * @param int  $userId  Options. The user id
7021
 *
7022
 * @return bool
7023
 */
7024
function api_is_excluded_user_type($checkDB = false, $userId = 0)
7025
{
7026
    if ($checkDB) {
7027
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
7028
7029
        if (0 == $userId) {
7030
            return true;
7031
        }
7032
7033
        $userInfo = api_get_user_info($userId);
7034
7035
        switch ($userInfo['status']) {
7036
            case INVITEE:
7037
            case ANONYMOUS:
7038
                return true;
7039
            default:
7040
                return false;
7041
        }
7042
    }
7043
7044
    $isInvited = api_is_invitee();
7045
    $isAnonymous = api_is_anonymous();
7046
7047
    if ($isInvited || $isAnonymous) {
7048
        return true;
7049
    }
7050
7051
    return false;
7052
}
7053
7054
/**
7055
 * Get the user status to ignore in reports.
7056
 *
7057
 * @param string $format Optional. The result type (array or string)
7058
 *
7059
 * @return array|string
7060
 */
7061
function api_get_users_status_ignored_in_reports($format = 'array')
7062
{
7063
    $excludedTypes = [
7064
        INVITEE,
7065
        ANONYMOUS,
7066
    ];
7067
7068
    if ('string' == $format) {
7069
        return implode(', ', $excludedTypes);
7070
    }
7071
7072
    return $excludedTypes;
7073
}
7074
7075
/**
7076
 * Set the Site Use Cookie Warning for 1 year.
7077
 */
7078
function api_set_site_use_cookie_warning_cookie()
7079
{
7080
    setcookie('ChamiloUsesCookies', 'ok', time() + 31556926);
7081
}
7082
7083
/**
7084
 * Return true if the Site Use Cookie Warning Cookie warning exists.
7085
 *
7086
 * @return bool
7087
 */
7088
function api_site_use_cookie_warning_cookie_exist()
7089
{
7090
    return isset($_COOKIE['ChamiloUsesCookies']);
7091
}
7092
7093
/**
7094
 * Given a number of seconds, format the time to show hours, minutes and seconds.
7095
 *
7096
 * @param int    $time         The time in seconds
7097
 * @param string $originFormat Optional. PHP o JS
7098
 *
7099
 * @return string (00h00'00")
7100
 */
7101
function api_format_time($time, $originFormat = 'php')
7102
{
7103
    $h = get_lang('h');
7104
    $hours = $time / 3600;
7105
    $mins = ($time % 3600) / 60;
7106
    $secs = ($time % 60);
7107
7108
    if ($time < 0) {
7109
        $hours = 0;
7110
        $mins = 0;
7111
        $secs = 0;
7112
    }
7113
7114
    if ('js' === $originFormat) {
7115
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
7116
    } else {
7117
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
7118
    }
7119
7120
    return $formattedTime;
7121
}
7122
7123
function api_set_noreply_and_from_address_to_mailer(
7124
    TemplatedEmail $email,
7125
    array $sender,
7126
    array $replyToAddress = []
7127
): void {
7128
    $validator = Container::getLegacyHelper()->getValidator();
7129
    $emailConstraint = new Assert\Email();
7130
7131
    $noReplyAddress = api_get_setting('noreply_email_address');
7132
    $avoidReplyToAddress = false;
7133
7134
    if (!empty($noReplyAddress)) {
7135
        // $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
7136
    }
7137
7138
    // Default values
7139
    $notification = new Notification();
7140
    $defaultSenderName = $notification->getDefaultPlatformSenderName();
7141
    $defaultSenderEmail = $notification->getDefaultPlatformSenderEmail();
7142
7143
    // If the parameter is set don't use the admin.
7144
    $senderName = !empty($sender['name']) ? $sender['name'] : $defaultSenderName;
7145
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $defaultSenderEmail;
7146
7147
    // Send errors to the platform admin
7148
    $adminEmail = api_get_setting('admin.administrator_email');
7149
7150
    $adminEmailValidation = $validator->validate($adminEmail, $emailConstraint);
7151
7152
    if (!empty($adminEmail) && 0 === $adminEmailValidation->count()) {
7153
        $email
7154
            ->getHeaders()
7155
            ->addIdHeader('Errors-To', $adminEmail)
7156
        ;
7157
    }
7158
7159
    if (!$avoidReplyToAddress && !empty($replyToAddress)) {
7160
        $replyToEmailValidation = $validator->validate($replyToAddress['mail'], $emailConstraint);
7161
7162
        if (0 === $replyToEmailValidation->count()) {
7163
            $email->addReplyTo(new Address($replyToAddress['mail'], $replyToAddress['name']));
7164
        }
7165
    }
7166
7167
    if ('true' === api_get_setting('mail.smtp_unique_sender')) {
7168
        $senderName = $defaultSenderName;
7169
        $senderEmail = $defaultSenderEmail;
7170
7171
        $email->sender(new Address($senderEmail, $senderName));
7172
    }
7173
7174
    if ($senderEmail) {
7175
        $email->from(new Address($senderEmail, $senderName));
7176
    }
7177
}
7178
7179
/**
7180
 * Sends an email
7181
 * Sender name and email can be specified, if not specified
7182
 * name and email of the platform admin are used.
7183
 *
7184
 * @param string    name of recipient
7185
 * @param string    email of recipient
7186
 * @param string    email subject
7187
 * @param string    email body
7188
 * @param string    sender name
7189
 * @param string    sender e-mail
7190
 * @param array     extra headers in form $headers = array($name => $value) to allow parsing
7191
 * @param array     data file (path and filename)
7192
 * @param bool      True for attaching a embedded file inside content html (optional)
7193
 * @param array     Additional parameters
7194
 *
7195
 * @return bool true if mail was sent
7196
 */
7197
function api_mail_html(
7198
    $recipientName,
7199
    $recipientEmail,
7200
    $subject,
7201
    $body,
7202
    $senderName = '',
7203
    $senderEmail = '',
7204
    $extra_headers = [],
7205
    $data_file = [],
7206
    $embeddedImage = false,
7207
    $additionalParameters = [],
7208
    string $sendErrorTo = null
7209
) {
7210
    $mailHelper = Container::$container->get(MailHelper::class);
7211
7212
    return $mailHelper->send(
7213
        $recipientName,
7214
        $recipientEmail,
7215
        $subject,
7216
        $body,
7217
        $senderName,
7218
        $senderEmail,
7219
        $extra_headers,
7220
        $data_file,
7221
        $embeddedImage,
7222
        $additionalParameters,
7223
        $sendErrorTo
7224
    );
7225
}
7226
7227
/**
7228
 * @param int  $tool       Possible values: GroupManager::GROUP_TOOL_*
7229
 * @param bool $showHeader
7230
 */
7231
function api_protect_course_group($tool, $showHeader = true)
7232
{
7233
    $groupId = api_get_group_id();
7234
    if (!empty($groupId)) {
7235
        if (api_is_platform_admin()) {
7236
            return true;
7237
        }
7238
7239
        if (api_is_allowed_to_edit(false, true, true)) {
7240
            return true;
7241
        }
7242
7243
        $userId = api_get_user_id();
7244
        $sessionId = api_get_session_id();
7245
        if (!empty($sessionId)) {
7246
            if (api_is_coach($sessionId, api_get_course_int_id())) {
7247
                return true;
7248
            }
7249
7250
            if (api_is_drh()) {
7251
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
7252
                    return true;
7253
                }
7254
            }
7255
        }
7256
7257
        $group = api_get_group_entity($groupId);
7258
7259
        // Group doesn't exists
7260
        if (null === $group) {
7261
            api_not_allowed($showHeader);
7262
        }
7263
7264
        // Check group access
7265
        $allow = GroupManager::userHasAccess(
7266
            $userId,
7267
            $group,
7268
            $tool
7269
        );
7270
7271
        if (!$allow) {
7272
            api_not_allowed($showHeader);
7273
        }
7274
    }
7275
7276
    return false;
7277
}
7278
7279
/**
7280
 * Check if a date is in a date range.
7281
 *
7282
 * @param datetime $startDate
7283
 * @param datetime $endDate
7284
 * @param datetime $currentDate
7285
 *
7286
 * @return bool true if date is in rage, false otherwise
7287
 */
7288
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
7289
{
7290
    $startDate = strtotime(api_get_local_time($startDate));
7291
    $endDate = strtotime(api_get_local_time($endDate));
7292
    $currentDate = strtotime(api_get_local_time($currentDate));
7293
7294
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
7295
        return true;
7296
    }
7297
7298
    return false;
7299
}
7300
7301
/**
7302
 * Eliminate the duplicates of a multidimensional array by sending the key.
7303
 *
7304
 * @param array $array multidimensional array
7305
 * @param int   $key   key to find to compare
7306
 *
7307
 * @return array
7308
 */
7309
function api_unique_multidim_array($array, $key)
7310
{
7311
    $temp_array = [];
7312
    $i = 0;
7313
    $key_array = [];
7314
7315
    foreach ($array as $val) {
7316
        if (!in_array($val[$key], $key_array)) {
7317
            $key_array[$i] = $val[$key];
7318
            $temp_array[$i] = $val;
7319
        }
7320
        $i++;
7321
    }
7322
7323
    return $temp_array;
7324
}
7325
7326
/**
7327
 * Limit the access to Session Admins when the limit_session_admin_role
7328
 * configuration variable is set to true.
7329
 */
7330
function api_protect_limit_for_session_admin()
7331
{
7332
    $limitAdmin = api_get_setting('limit_session_admin_role');
7333
    if (api_is_session_admin() && 'true' === $limitAdmin) {
7334
        api_not_allowed(true);
7335
    }
7336
}
7337
7338
/**
7339
 * Limits that a session admin has access to list users.
7340
 * When limit_session_admin_list_users configuration variable is set to true.
7341
 */
7342
function api_protect_session_admin_list_users()
7343
{
7344
    $limitAdmin = ('true' === api_get_setting('session.limit_session_admin_list_users'));
7345
7346
    if (api_is_session_admin() && true === $limitAdmin) {
7347
        api_not_allowed(true);
7348
    }
7349
}
7350
7351
/**
7352
 * @return bool
7353
 */
7354
function api_is_student_view_active(): bool
7355
{
7356
    $studentView = Session::read('studentview');
7357
7358
    return 'studentview' === $studentView;
7359
}
7360
7361
/**
7362
 * Converts string value to float value.
7363
 *
7364
 * 3.141516 => 3.141516
7365
 * 3,141516 => 3.141516
7366
 *
7367
 * @todo WIP
7368
 *
7369
 * @param string $number
7370
 *
7371
 * @return float
7372
 */
7373
function api_float_val($number)
7374
{
7375
    return (float) str_replace(',', '.', trim($number));
7376
}
7377
7378
/**
7379
 * Converts float values
7380
 * Example if $decimals = 2.
7381
 *
7382
 * 3.141516 => 3.14
7383
 * 3,141516 => 3,14
7384
 *
7385
 * @param string $number            number in iso code
7386
 * @param int    $decimals
7387
 * @param string $decimalSeparator
7388
 * @param string $thousandSeparator
7389
 *
7390
 * @return bool|string
7391
 */
7392
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
7393
{
7394
    $number = api_float_val($number);
7395
7396
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
7397
}
7398
7399
/**
7400
 * Set location url with a exit break by default.
7401
 *
7402
 * @param string $url
7403
 * @param bool   $exit
7404
 */
7405
function api_location($url, $exit = true)
7406
{
7407
    header('Location: '.$url);
7408
7409
    if ($exit) {
7410
        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...
7411
    }
7412
}
7413
7414
/**
7415
 * @param string $from
7416
 * @param string $to
7417
 *
7418
 * @return string
7419
 */
7420
function api_get_relative_path($from, $to)
7421
{
7422
    // some compatibility fixes for Windows paths
7423
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
7424
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
7425
    $from = str_replace('\\', '/', $from);
7426
    $to = str_replace('\\', '/', $to);
7427
7428
    $from = explode('/', $from);
7429
    $to = explode('/', $to);
7430
    $relPath = $to;
7431
7432
    foreach ($from as $depth => $dir) {
7433
        // find first non-matching dir
7434
        if ($dir === $to[$depth]) {
7435
            // ignore this directory
7436
            array_shift($relPath);
7437
        } else {
7438
            // get number of remaining dirs to $from
7439
            $remaining = count($from) - $depth;
7440
            if ($remaining > 1) {
7441
                // add traversals up to first matching dir
7442
                $padLength = (count($relPath) + $remaining - 1) * -1;
7443
                $relPath = array_pad($relPath, $padLength, '..');
7444
                break;
7445
            } else {
7446
                $relPath[0] = './'.$relPath[0];
7447
            }
7448
        }
7449
    }
7450
7451
    return implode('/', $relPath);
7452
}
7453
7454
/**
7455
 * @param string $template
7456
 *
7457
 * @return string
7458
 */
7459
function api_find_template($template)
7460
{
7461
    return Template::findTemplateFilePath($template);
7462
}
7463
7464
/**
7465
 * @return array
7466
 */
7467
function api_get_language_list_for_flag()
7468
{
7469
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
7470
    $sql = "SELECT english_name, isocode FROM $table
7471
            ORDER BY original_name ASC";
7472
    static $languages = [];
7473
    if (empty($languages)) {
7474
        $result = Database::query($sql);
7475
        while ($row = Database::fetch_array($result)) {
7476
            $languages[$row['english_name']] = $row['isocode'];
7477
        }
7478
        $languages['english'] = 'gb';
7479
    }
7480
7481
    return $languages;
7482
}
7483
7484
function api_create_zip(string $name): ZipStream
7485
{
7486
    $zipStreamOptions = new Archive();
7487
    $zipStreamOptions->setSendHttpHeaders(true);
7488
    $zipStreamOptions->setContentDisposition('attachment');
7489
    $zipStreamOptions->setContentType('application/x-zip');
7490
7491
    return new ZipStream($name, $zipStreamOptions);
7492
}
7493
7494
function api_get_language_translate_html(): string
7495
{
7496
    $translate = 'true' === api_get_setting('editor.translate_html');
7497
7498
    if (!$translate) {
7499
        return '';
7500
    }
7501
7502
    /*$languageList = api_get_languages();
7503
    $hideAll = '';
7504
    foreach ($languageList as $isocode => $name) {
7505
        $hideAll .= '
7506
        $(".mce-translatehtml").hide();
7507
        $("span:lang('.$isocode.')").filter(
7508
            function(e, val) {
7509
                // Only find the spans if they have set the lang
7510
                if ($(this).attr("lang") == null) {
7511
                    return false;
7512
                }
7513
                // Ignore ckeditor classes
7514
                return !this.className.match(/cke(.*)/);
7515
        }).hide();'."\n";
7516
    }*/
7517
7518
    $userInfo = api_get_user_info();
7519
    if (!empty($userInfo['language'])) {
7520
        $isoCode = $userInfo['language'];
7521
7522
        return '
7523
            $(function() {
7524
                $(".mce-translatehtml").hide();
7525
                var defaultLanguageFromUser = "'.$isoCode.'";
7526
                $("span:lang('.$isoCode.')").show();
7527
            });
7528
        ';
7529
    }
7530
7531
    return '';
7532
}
7533
7534
function api_protect_webservices()
7535
{
7536
    if (api_get_configuration_value('disable_webservices')) {
7537
        echo "Webservices are disabled. \n";
7538
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
7539
        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...
7540
    }
7541
}
7542
7543
/**
7544
 * Checks if a set of roles have a specific permission.
7545
 *
7546
 * @param string $permissionSlug The slug of the permission to check.
7547
 * @param array  $roles          An array of role codes to check against.
7548
 * @return bool True if any of the roles have the permission, false otherwise.
7549
 */
7550
function api_get_permission(string $permissionSlug, array $roles): bool
7551
{
7552
    $permissionService = Container::$container->get(PermissionServiceHelper::class);
7553
7554
    return $permissionService->hasPermission($permissionSlug, $roles);
7555
}
7556
7557
/**
7558
 * Calculate the percentage of change between two numbers.
7559
 *
7560
 * @param int $newValue
7561
 * @param int $oldValue
7562
 * @return string
7563
 */
7564
function api_calculate_increment_percent(int $newValue, int $oldValue): string
7565
{
7566
    if ($oldValue <= 0) {
7567
        $result = " - ";
7568
    } else {
7569
        $result = ' '.round(100 * (($newValue / $oldValue) - 1), 2).' %';
7570
    }
7571
    return $result;
7572
}
7573