Passed
Pull Request — master (#5614)
by Angel Fernando Quiroz
07:16
created

api_get_visual_theme()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

Loading history...
1094
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1095
        $tpl->assign('hide_login_link', 1);
1096
1097
        //api_not_allowed(true, get_lang('Account inactive'));
1098
        // we were not in a course, return to home page
1099
        $msg = Display::return_message(
1100
            get_lang('Account inactive'),
1101
            'error',
1102
            false
1103
        );
1104
1105
        $msg .= '<p class="text-center">
1106
                 <a class="btn btn--plain" href="'.$homeUrl.'">'.get_lang('Back to Home Page.').'</a></p>';
1107
1108
        $tpl->assign('content', $msg);
1109
        $tpl->display_one_col_template();
1110
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

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

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

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