Completed
Push — master ( d9adeb...430d2c )
by
unknown
01:15 queued 37s
created

api_format_course_array()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 58
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 40
nc 5
nop 1
dl 0
loc 58
rs 8.9688
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

2079
            $url .= '&origin=' . /** @scrutinizer ignore-type */ $origin;
Loading history...
2080
        }
2081
    }
2082
2083
    return $url;
2084
}
2085
2086
/**
2087
 * Get if we visited a gradebook page.
2088
 *
2089
 * @return bool
2090
 */
2091
function api_is_in_gradebook()
2092
{
2093
    return Session::read('in_gradebook', false);
2094
}
2095
2096
/**
2097
 * Set that we are in a page inside a gradebook.
2098
 */
2099
function api_set_in_gradebook()
2100
{
2101
    Session::write('in_gradebook', true);
2102
}
2103
2104
/**
2105
 * Remove gradebook session.
2106
 */
2107
function api_remove_in_gradebook()
2108
{
2109
    Session::erase('in_gradebook');
2110
}
2111
2112
/**
2113
 * Returns the current course info array see api_format_course_array()
2114
 * If the course_code is given, the returned array gives info about that
2115
 * particular course, if none given it gets the course info from the session.
2116
 *
2117
 * @param string $courseCode
2118
 *
2119
 * @return array
2120
 */
2121
function api_get_course_info($courseCode = null)
2122
{
2123
    if (!empty($courseCode)) {
2124
        $course = Container::getCourseRepository()->findOneByCode($courseCode);
2125
2126
        return api_format_course_array($course);
2127
    }
2128
2129
    $course = Session::read('_course');
2130
    if ('-1' == $course) {
2131
        $course = [];
2132
    }
2133
2134
    if (empty($course) && isset($_REQUEST['cid'])) {
2135
        $course = api_get_course_info_by_id((int) $_REQUEST['cid']);
2136
    }
2137
2138
    return $course;
2139
}
2140
2141
/**
2142
 * @param int $courseId
2143
 */
2144
function api_get_course_entity($courseId = 0): ?Course
2145
{
2146
    if (empty($courseId)) {
2147
        $courseId = api_get_course_int_id();
2148
    }
2149
2150
    if (empty($courseId)) {
2151
        return null;
2152
    }
2153
2154
    return Container::getCourseRepository()->find($courseId);
2155
}
2156
2157
/**
2158
 * @param int $id
2159
 */
2160
function api_get_session_entity($id = 0): ?SessionEntity
2161
{
2162
    if (empty($id)) {
2163
        $id = api_get_session_id();
2164
    }
2165
2166
    if (empty($id)) {
2167
        return null;
2168
    }
2169
2170
    return Container::getSessionRepository()->find($id);
2171
}
2172
2173
/**
2174
 * @param int $id
2175
 */
2176
function api_get_group_entity($id = 0): ?CGroup
2177
{
2178
    if (empty($id)) {
2179
        $id = api_get_group_id();
2180
    }
2181
2182
    return Container::getGroupRepository()->find($id);
2183
}
2184
2185
/**
2186
 * @param int $id
2187
 */
2188
function api_get_url_entity($id = 0): ?AccessUrl
2189
{
2190
    if (empty($id)) {
2191
        $id = api_get_current_access_url_id();
2192
    }
2193
2194
    return Container::getAccessUrlRepository()->find($id);
2195
}
2196
2197
/**
2198
 * Returns the current course info array.
2199
2200
 * Now if the course_code is given, the returned array gives info about that
2201
 * particular course, not specially the current one.
2202
 *
2203
 * @param int $id Numeric ID of the course
2204
 *
2205
 * @return array The course info as an array formatted by api_format_course_array, including category.title
2206
 */
2207
function api_get_course_info_by_id(?int $id = 0)
2208
{
2209
    if (empty($id)) {
2210
        $course = Session::read('_course', []);
2211
2212
        return $course;
2213
    }
2214
2215
    $course = Container::getCourseRepository()->find($id);
2216
    if (empty($course)) {
2217
        return [];
2218
    }
2219
2220
    return api_format_course_array($course);
2221
}
2222
2223
/**
2224
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2225
 * to switch from 'code' to 'id' in the array.
2226
 *
2227
 * @return array
2228
 *
2229
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2230
 */
2231
function api_format_course_array(Course $course = null)
2232
{
2233
    if (empty($course)) {
2234
        return [];
2235
    }
2236
2237
    $courseData = [];
2238
    $courseData['id'] = $courseData['real_id'] = $course->getId();
2239
2240
    // Added
2241
    $courseData['code'] = $courseData['sysCode'] = $course->getCode();
2242
    $courseData['name'] = $courseData['title'] = $course->getTitle(); // 'name' only used for backwards compatibility - should be removed in the long run
2243
    $courseData['official_code'] = $courseData['visual_code'] = $course->getVisualCode();
2244
    $courseData['creation_date'] = $course->getCreationDate()->format('Y-m-d H:i:s');
2245
    $courseData['titular'] = $course->getTutorName();
2246
    $courseData['language'] = $courseData['course_language'] = $course->getCourseLanguage();
2247
    $courseData['extLink']['url'] = $courseData['department_url'] = $course->getDepartmentUrl();
2248
    $courseData['extLink']['name'] = $courseData['department_name'] = $course->getDepartmentName();
2249
2250
    $courseData['visibility'] = $course->getVisibility();
2251
    $courseData['subscribe_allowed'] = $courseData['subscribe'] = $course->getSubscribe();
2252
    $courseData['unsubscribe'] = $course->getUnsubscribe();
2253
    $courseData['activate_legal'] = $course->getActivateLegal();
2254
    $courseData['legal'] = $course->getLegal();
2255
    $courseData['show_score'] = $course->getShowScore(); //used in the work tool
2256
    $courseData['video_url'] = $course->getVideoUrl();
2257
    $courseData['sticky'] = (int) $course->isSticky();
2258
2259
    $coursePath = '/course/';
2260
    $webCourseHome = $coursePath.$courseData['real_id'].'/home';
2261
2262
    // Course password
2263
    $courseData['registration_code'] = $course->getRegistrationCode();
2264
    $courseData['disk_quota'] = $course->getDiskQuota();
2265
    $courseData['course_public_url'] = $webCourseHome;
2266
    $courseData['about_url'] = $coursePath.$courseData['real_id'].'/about';
2267
    $courseData['add_teachers_to_sessions_courses'] = $course->isAddTeachersToSessionsCourses();
2268
2269
    $image = Display::getMdiIcon(
2270
        ObjectIcon::COURSE,
2271
        'ch-tool-icon',
2272
        null,
2273
        ICON_SIZE_BIG
2274
    );
2275
2276
    $illustration = Container::getIllustrationRepository()->getIllustrationUrl($course);
2277
    if (!empty($illustration)) {
2278
        $image = $illustration;
2279
    }
2280
2281
    $courseData['course_image'] = $image.'?filter=course_picture_small';
2282
    $courseData['course_image_large'] = $image.'?filter=course_picture_medium';
2283
2284
    if ('true' === api_get_setting('course.show_course_duration') && null !== $course->getDuration()) {
2285
        $courseData['duration'] = $course->getDuration();
2286
    }
2287
2288
    return $courseData;
2289
}
2290
2291
/**
2292
 * Returns a difficult to guess password.
2293
 */
2294
function api_generate_password(int $length = 8, $useRequirements = true): string
2295
{
2296
    if ($length < 2) {
2297
        $length = 2;
2298
    }
2299
2300
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2301
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2302
    $charactersSpecials = '!@#$%^&*()_+-=[]{}|;:,.<>?';
2303
    $minNumbers = 2;
2304
    $length = $length - $minNumbers;
2305
    $minLowerCase = round($length / 2);
2306
    $minUpperCase = $length - $minLowerCase;
2307
    $minSpecials = 1; // Default minimum special characters
2308
2309
    $password = '';
2310
    $passwordRequirements = $useRequirements ? Security::getPasswordRequirements() : [];
2311
2312
    $factory = new RandomLib\Factory();
2313
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2314
2315
    if (!empty($passwordRequirements)) {
2316
        $length = $passwordRequirements['min']['length'];
2317
        $minNumbers = $passwordRequirements['min']['numeric'];
2318
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2319
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2320
        $minSpecials = $passwordRequirements['min']['specials'];
2321
2322
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2323
        // Add the rest to fill the length requirement
2324
        if ($rest > 0) {
2325
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2326
        }
2327
    }
2328
2329
    // Min digits default 2
2330
    for ($i = 0; $i < $minNumbers; $i++) {
2331
        $password .= $generator->generateInt(2, 9);
2332
    }
2333
2334
    // Min lowercase
2335
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2336
2337
    // Min uppercase
2338
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2339
2340
    // Min special characters
2341
    $password .= $generator->generateString($minSpecials, $charactersSpecials);
2342
2343
    // Shuffle the password to ensure randomness
2344
    $password = str_shuffle($password);
2345
2346
    return $password;
2347
}
2348
2349
/**
2350
 * Checks a password to see wether it is OK to use.
2351
 *
2352
 * @param string $password
2353
 *
2354
 * @return bool if the password is acceptable, false otherwise
2355
 *              Notes about what a password "OK to use" is:
2356
 *              1. The password should be at least 5 characters long.
2357
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2358
 *              3. The password should contain at least 3 letters.
2359
 *              4. It should contain at least 2 digits.
2360
 *              Settings will change if the configuration value is set: password_requirements
2361
 */
2362
function api_check_password($password)
2363
{
2364
    $passwordRequirements = Security::getPasswordRequirements();
2365
2366
    $minLength = $passwordRequirements['min']['length'];
2367
    $minNumbers = $passwordRequirements['min']['numeric'];
2368
    // Optional
2369
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2370
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2371
2372
    $minLetters = $minLowerCase + $minUpperCase;
2373
    $passwordLength = api_strlen($password);
2374
2375
    $conditions = [
2376
        'min_length' => $passwordLength >= $minLength,
2377
    ];
2378
2379
    $digits = 0;
2380
    $lowerCase = 0;
2381
    $upperCase = 0;
2382
2383
    for ($i = 0; $i < $passwordLength; $i++) {
2384
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2385
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2386
            $upperCase++;
2387
        }
2388
2389
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2390
            $lowerCase++;
2391
        }
2392
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2393
            $digits++;
2394
        }
2395
    }
2396
2397
    // Min number of digits
2398
    $conditions['min_numeric'] = $digits >= $minNumbers;
2399
2400
    if (!empty($minUpperCase)) {
2401
        // Uppercase
2402
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2403
    }
2404
2405
    if (!empty($minLowerCase)) {
2406
        // Lowercase
2407
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2408
    }
2409
2410
    // Min letters
2411
    $letters = $upperCase + $lowerCase;
2412
    $conditions['min_letters'] = $letters >= $minLetters;
2413
2414
    $isPasswordOk = true;
2415
    foreach ($conditions as $condition) {
2416
        if (false === $condition) {
2417
            $isPasswordOk = false;
2418
            break;
2419
        }
2420
    }
2421
2422
    if (false === $isPasswordOk) {
2423
        $output = get_lang('The new password does not match the minimum security requirements').'<br />';
2424
        $output .= Security::getPasswordRequirementsToString($conditions);
2425
2426
        Display::addFlash(Display::return_message($output, 'warning', false));
2427
    }
2428
2429
    return $isPasswordOk;
2430
}
2431
2432
/**
2433
 * Gets the current Chamilo (not PHP/cookie) session ID.
2434
 *
2435
 * @return int O if no active session, the session ID otherwise
2436
 */
2437
function api_get_session_id()
2438
{
2439
    return (int) Session::read('sid', 0);
2440
}
2441
2442
/**
2443
 * Gets the current Chamilo (not social network) group ID.
2444
 *
2445
 * @return int O if no active session, the session ID otherwise
2446
 */
2447
function api_get_group_id()
2448
{
2449
    return Session::read('gid', 0);
2450
}
2451
2452
/**
2453
 * Gets the current or given session name.
2454
 *
2455
 * @param   int     Session ID (optional)
2456
 *
2457
 * @return string The session name, or null if not found
2458
 */
2459
function api_get_session_name($session_id = 0)
2460
{
2461
    if (empty($session_id)) {
2462
        $session_id = api_get_session_id();
2463
        if (empty($session_id)) {
2464
            return null;
2465
        }
2466
    }
2467
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2468
    $s = "SELECT title FROM $t WHERE id = ".(int) $session_id;
2469
    $r = Database::query($s);
2470
    $c = Database::num_rows($r);
2471
    if ($c > 0) {
2472
        //technically, there can be only one, but anyway we take the first
2473
        $rec = Database::fetch_array($r);
2474
2475
        return $rec['title'];
2476
    }
2477
2478
    return null;
2479
}
2480
2481
/**
2482
 * Gets the session info by id.
2483
 *
2484
 * @param int $id Session ID
2485
 *
2486
 * @return array information of the session
2487
 */
2488
function api_get_session_info($id)
2489
{
2490
    return SessionManager::fetch($id);
2491
}
2492
2493
/**
2494
 * Gets the session visibility by session id.
2495
 *
2496
 * @param int  $session_id
2497
 * @param int  $courseId
2498
 * @param bool $ignore_visibility_for_admins
2499
 *
2500
 * @return int
2501
 *             0 = session still available,
2502
 *             SESSION_VISIBLE_READ_ONLY = 1,
2503
 *             SESSION_VISIBLE = 2,
2504
 *             SESSION_INVISIBLE = 3
2505
 */
2506
function api_get_session_visibility(
2507
    $session_id,
2508
    $courseId = null,
2509
    $ignore_visibility_for_admins = true,
2510
    $userId = 0
2511
) {
2512
    if (api_is_platform_admin()) {
2513
        if ($ignore_visibility_for_admins) {
2514
            return SESSION_AVAILABLE;
2515
        }
2516
    }
2517
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2518
2519
    $now = time();
2520
    if (empty($session_id)) {
2521
        return 0; // Means that the session is still available.
2522
    }
2523
2524
    $session_id = (int) $session_id;
2525
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2526
2527
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2528
2529
    if (Database::num_rows($result) <= 0) {
2530
        return SESSION_INVISIBLE;
2531
    }
2532
2533
    $row = Database::fetch_assoc($result);
2534
    $visibility = $row['visibility'];
2535
2536
    // I don't care the session visibility.
2537
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2538
        // Session duration per student.
2539
        if (isset($row['duration']) && !empty($row['duration'])) {
2540
            $duration = $row['duration'] * 24 * 60 * 60;
2541
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2542
2543
            // If there is a session duration but there is no previous
2544
            // access by the user, then the session is still available
2545
            if (0 == count($courseAccess)) {
2546
                return SESSION_AVAILABLE;
2547
            }
2548
2549
            $currentTime = time();
2550
            $firstAccess = isset($courseAccess['login_course_date'])
2551
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2552
                : 0;
2553
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2554
            $userDuration = isset($userDurationData['duration'])
2555
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2556
                : 0;
2557
2558
            $totalDuration = $firstAccess + $duration + $userDuration;
2559
2560
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2561
        }
2562
2563
        return SESSION_AVAILABLE;
2564
    }
2565
2566
    // If start date was set.
2567
    if (!empty($row['access_start_date'])) {
2568
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2569
    } else {
2570
        // If there's no start date, assume it's available until the end date
2571
        $visibility = SESSION_AVAILABLE;
2572
    }
2573
2574
    // If the end date was set.
2575
    if (!empty($row['access_end_date'])) {
2576
        // Only if date_start said that it was ok
2577
        if (SESSION_AVAILABLE === $visibility) {
2578
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2579
                ? SESSION_AVAILABLE // Date still available
2580
                : $row['visibility']; // Session ends
2581
        }
2582
    }
2583
2584
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2585
    $isCoach = api_is_coach($session_id, $courseId);
2586
2587
    if ($isCoach) {
2588
        // Test start date.
2589
        if (!empty($row['coach_access_start_date'])) {
2590
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2591
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2592
        }
2593
2594
        // Test end date.
2595
        if (!empty($row['coach_access_end_date'])) {
2596
            if (SESSION_AVAILABLE === $visibility) {
2597
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2598
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2599
            }
2600
        }
2601
    }
2602
2603
    return $visibility;
2604
}
2605
2606
/**
2607
 * This function returns a (star) session icon if the session is not null and
2608
 * the user is not a student.
2609
 *
2610
 * @param int $sessionId
2611
 * @param int $statusId  User status id - if 5 (student), will return empty
2612
 *
2613
 * @return string Session icon
2614
 */
2615
function api_get_session_image($sessionId, User $user)
2616
{
2617
    $sessionId = (int) $sessionId;
2618
    $image = '';
2619
    if (!$user->hasRole('ROLE_STUDENT')) {
2620
        // Check whether is not a student
2621
        if ($sessionId > 0) {
2622
            $image = '&nbsp;&nbsp;'.Display::getMdiIcon(
2623
                ObjectIcon::STAR,
2624
                'ch-tool-icon',
2625
                'align:absmiddle;',
2626
                ICON_SIZE_SMALL,
2627
                get_lang('Session-specific resource')
2628
            );
2629
        }
2630
    }
2631
2632
    return $image;
2633
}
2634
2635
/**
2636
 * This function add an additional condition according to the session of the course.
2637
 *
2638
 * @param int    $session_id        session id
2639
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2640
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2641
 *                                  false for strict session condition
2642
 * @param string $session_field
2643
 *
2644
 * @return string condition of the session
2645
 */
2646
function api_get_session_condition(
2647
    $session_id,
2648
    $and = true,
2649
    $with_base_content = false,
2650
    $session_field = 'session_id'
2651
) {
2652
    $session_id = (int) $session_id;
2653
2654
    if (empty($session_field)) {
2655
        $session_field = 'session_id';
2656
    }
2657
    // Condition to show resources by session
2658
    $condition_add = $and ? ' AND ' : ' WHERE ';
2659
2660
    if ($with_base_content) {
2661
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2662
    } else {
2663
        if (empty($session_id)) {
2664
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2665
        } else {
2666
            $condition_session = $condition_add." $session_field = $session_id ";
2667
        }
2668
    }
2669
2670
    return $condition_session;
2671
}
2672
2673
/**
2674
 * Returns the value of a setting from the web-adjustable admin config settings.
2675
 *
2676
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2677
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2678
 * instead of
2679
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2680
 *
2681
 * @param string $variable The variable name
2682
 *
2683
 * @return string|array
2684
 */
2685
function api_get_setting($variable, $isArray = false, $key = null)
2686
{
2687
    $settingsManager = Container::getSettingsManager();
2688
    if (empty($settingsManager)) {
2689
        return '';
2690
    }
2691
    $variable = trim($variable);
2692
2693
    switch ($variable) {
2694
        case 'server_type':
2695
            $test = ['dev', 'test'];
2696
            $environment = Container::getEnvironment();
2697
            if (in_array($environment, $test)) {
2698
                return 'test';
2699
            }
2700
2701
            return 'prod';
2702
        case 'stylesheets':
2703
            $variable = 'platform.theme';
2704
        // deprecated settings
2705
        // no break
2706
        case 'openid_authentication':
2707
        case 'service_ppt2lp':
2708
        case 'formLogin_hide_unhide_label':
2709
            return false;
2710
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
2711
        case 'tool_visible_by_default_at_creation':
2712
            $values = $settingsManager->getSetting($variable);
2713
            $newResult = [];
2714
            foreach ($values as $parameter) {
2715
                $newResult[$parameter] = 'true';
2716
            }
2717
2718
            return $newResult;
2719
            break;
2720
        default:
2721
            $settingValue = $settingsManager->getSetting($variable, true);
2722
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2723
                // Check if the value is a valid JSON string
2724
                $decodedValue = json_decode($settingValue, true);
2725
2726
                // If it's a valid JSON string and the result is an array, return it
2727
                if (is_array($decodedValue)) {
2728
                    return $decodedValue;
2729
                }
2730
2731
                // If it's not an array, continue with the normal flow
2732
                // Optional: If you need to evaluate the value using eval
2733
                $strArrayValue = rtrim($settingValue, ';');
2734
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2735
                if (is_array($value)) {
2736
                    return $value;
2737
                }
2738
            }
2739
2740
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2741
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2742
                return $settingValue[$variable][$key];
2743
            }
2744
2745
            return $settingValue;
2746
            break;
2747
    }
2748
}
2749
2750
/**
2751
 * @param string $variable
2752
 * @param string $option
2753
 *
2754
 * @return bool
2755
 */
2756
function api_get_setting_in_list($variable, $option)
2757
{
2758
    $value = api_get_setting($variable);
2759
2760
    return in_array($option, $value);
2761
}
2762
2763
/**
2764
 * @param string $plugin
2765
 * @param string $variable
2766
 *
2767
 * @return string
2768
 */
2769
function api_get_plugin_setting($plugin, $variable)
2770
{
2771
    $variableName = $plugin.'_'.$variable;
2772
    //$result = api_get_setting($variableName);
2773
    $params = [
2774
        'category = ? AND subkey = ? AND variable = ?' => [
2775
            'Plugins',
2776
            $plugin,
2777
            $variableName,
2778
        ],
2779
    ];
2780
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2781
    $result = Database::select(
2782
        'selected_value',
2783
        $table,
2784
        ['where' => $params],
2785
        'one'
2786
    );
2787
    if ($result) {
2788
        $value = $result['selected_value'];
2789
        $serializedValue = @unserialize($result['selected_value'], []);
2790
        if (false !== $serializedValue) {
2791
            $value = $serializedValue;
2792
        }
2793
2794
        return $value;
2795
    }
2796
2797
    return null;
2798
    /// Old code
2799
2800
    $variableName = $plugin.'_'.$variable;
0 ignored issues
show
Unused Code introduced by
$variableName = $plugin . '_' . $variable is not reachable.

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

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

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

    return false;
}

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

Loading history...
2801
    $result = api_get_setting($variableName);
2802
2803
    if (isset($result[$plugin])) {
2804
        $value = $result[$plugin];
2805
2806
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2807
2808
        if (false !== $unserialized) {
2809
            $value = $unserialized;
2810
        }
2811
2812
        return $value;
2813
    }
2814
2815
    return null;
2816
}
2817
2818
/**
2819
 * Returns the value of a setting from the web-adjustable admin config settings.
2820
 */
2821
function api_get_settings_params($params)
2822
{
2823
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2824
2825
    return Database::select('*', $table, ['where' => $params]);
2826
}
2827
2828
/**
2829
 * @param array $params example: [id = ? => '1']
2830
 *
2831
 * @return array
2832
 */
2833
function api_get_settings_params_simple($params)
2834
{
2835
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2836
2837
    return Database::select('*', $table, ['where' => $params], 'one');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::select(...re' => $params), 'one') also could return the type integer which is incompatible with the documented return type array.
Loading history...
2838
}
2839
2840
/**
2841
 * Returns the value of a setting from the web-adjustable admin config settings.
2842
 */
2843
function api_delete_settings_params($params)
2844
{
2845
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2846
2847
    return Database::delete($table, $params);
2848
}
2849
2850
/**
2851
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2852
 *
2853
 * @return string Escaped version of $_SERVER['PHP_SELF']
2854
 */
2855
function api_get_self()
2856
{
2857
    return htmlentities($_SERVER['PHP_SELF']);
2858
}
2859
2860
/**
2861
 * Checks whether current user is a platform administrator.
2862
 *
2863
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2864
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2865
 *
2866
 * @return bool true if the user has platform admin rights,
2867
 *              false otherwise
2868
 *
2869
 * @see usermanager::is_admin(user_id) for a user-id specific function
2870
 */
2871
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2872
{
2873
    $currentUser = api_get_current_user();
2874
2875
    if (null === $currentUser) {
2876
        return false;
2877
    }
2878
2879
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2880
2881
    if ($isAdmin) {
2882
        return true;
2883
    }
2884
2885
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2886
        return true;
2887
    }
2888
2889
    if ($allowDrh && $currentUser->hasRole('ROLE_RRHH')) {
2890
        return true;
2891
    }
2892
2893
    return false;
2894
}
2895
2896
/**
2897
 * Checks whether the user given as user id is in the admin table.
2898
 *
2899
 * @param int $user_id If none provided, will use current user
2900
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2901
 *
2902
 * @return bool True if the user is admin, false otherwise
2903
 */
2904
function api_is_platform_admin_by_id($user_id = null, $url = null)
2905
{
2906
    $user_id = (int) $user_id;
2907
    if (empty($user_id)) {
2908
        $user_id = api_get_user_id();
2909
    }
2910
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2911
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2912
    $res = Database::query($sql);
2913
    $is_admin = 1 === Database::num_rows($res);
2914
    if (!$is_admin || !isset($url)) {
2915
        return $is_admin;
2916
    }
2917
    // We get here only if $url is set
2918
    $url = (int) $url;
2919
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2920
    $sql = "SELECT * FROM $url_user_table
2921
            WHERE access_url_id = $url AND user_id = $user_id";
2922
    $res = Database::query($sql);
2923
2924
    return 1 === Database::num_rows($res);
2925
}
2926
2927
/**
2928
 * Checks whether current user is allowed to create courses.
2929
 *
2930
 * @return bool true if the user has course creation rights,
2931
 *              false otherwise
2932
 */
2933
function api_is_allowed_to_create_course()
2934
{
2935
    if (api_is_platform_admin()) {
2936
        return true;
2937
    }
2938
2939
    // Teachers can only create courses
2940
    if (api_is_teacher()) {
2941
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2942
            return true;
2943
        } else {
2944
            return false;
2945
        }
2946
    }
2947
2948
    return Session::read('is_allowedCreateCourse');
2949
}
2950
2951
/**
2952
 * Checks whether the current user is a course administrator.
2953
 *
2954
 * @return bool True if current user is a course administrator
2955
 */
2956
function api_is_course_admin()
2957
{
2958
    if (api_is_platform_admin()) {
2959
        return true;
2960
    }
2961
2962
    $user = api_get_current_user();
2963
    if ($user) {
2964
        if (
2965
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2966
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2967
        ) {
2968
            return true;
2969
        }
2970
    }
2971
2972
    return false;
2973
}
2974
2975
/**
2976
 * Checks whether the current user is a course coach
2977
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2978
 *
2979
 * @return bool True if current user is a course coach
2980
 */
2981
function api_is_session_general_coach()
2982
{
2983
    return Session::read('is_session_general_coach');
2984
}
2985
2986
/**
2987
 * Checks whether the current user is a course tutor
2988
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2989
 *
2990
 * @return bool True if current user is a course tutor
2991
 */
2992
function api_is_course_tutor()
2993
{
2994
    return Session::read('is_courseTutor');
2995
}
2996
2997
/**
2998
 * @param int $user_id
2999
 * @param int $courseId
3000
 * @param int $session_id
3001
 *
3002
 * @return bool
3003
 */
3004
function api_is_course_session_coach($user_id, $courseId, $session_id)
3005
{
3006
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3007
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3008
3009
    $user_id = (int) $user_id;
3010
    $session_id = (int) $session_id;
3011
    $courseId = (int) $courseId;
3012
3013
    $sql = "SELECT DISTINCT session.id
3014
            FROM $session_table
3015
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3016
            ON session.id = session_rc_ru.session_id
3017
            WHERE
3018
                session_rc_ru.user_id = '".$user_id."'  AND
3019
                session_rc_ru.c_id = '$courseId' AND
3020
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3021
                session_rc_ru.session_id = '$session_id'";
3022
    $result = Database::query($sql);
3023
3024
    return Database::num_rows($result) > 0;
3025
}
3026
3027
/**
3028
 * Checks whether the current user is a course or session coach.
3029
 *
3030
 * @param int $session_id
3031
 * @param int $courseId
3032
 * @param bool  Check whether we are in student view and, if we are, return false
3033
 * @param int $userId
3034
 *
3035
 * @return bool True if current user is a course or session coach
3036
 */
3037
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3038
{
3039
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3040
3041
    if (!empty($session_id)) {
3042
        $session_id = (int) $session_id;
3043
    } else {
3044
        $session_id = api_get_session_id();
3045
    }
3046
3047
    // The student preview was on
3048
    if ($check_student_view && api_is_student_view_active()) {
3049
        return false;
3050
    }
3051
3052
    if (!empty($courseId)) {
3053
        $courseId = (int) $courseId;
3054
    } else {
3055
        $courseId = api_get_course_int_id();
3056
    }
3057
3058
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3059
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3060
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3061
    $sessionIsCoach = [];
3062
3063
    if (!empty($courseId)) {
3064
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3065
                FROM $session_table s
3066
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3067
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3068
                WHERE
3069
                    session_rc_ru.c_id = '$courseId' AND
3070
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3071
                    session_rc_ru.session_id = '$session_id'";
3072
        $result = Database::query($sql);
3073
        $sessionIsCoach = Database::store_result($result);
3074
    }
3075
3076
    if (!empty($session_id)) {
3077
        $sql = "SELECT DISTINCT s.id
3078
                FROM $session_table AS s
3079
                INNER JOIN $tblSessionRelUser sru
3080
                ON s.id = sru.session_id
3081
                WHERE
3082
                    sru.user_id = $userId AND
3083
                    s.id = $session_id AND
3084
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3085
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3086
        $result = Database::query($sql);
3087
        if (!empty($sessionIsCoach)) {
3088
            $sessionIsCoach = array_merge(
3089
                $sessionIsCoach,
3090
                Database::store_result($result)
3091
            );
3092
        } else {
3093
            $sessionIsCoach = Database::store_result($result);
3094
        }
3095
    }
3096
3097
    return count($sessionIsCoach) > 0;
3098
}
3099
3100
function api_user_has_role(string $role, ?User $user = null): bool
3101
{
3102
    if (null === $user) {
3103
        $user = api_get_current_user();
3104
    }
3105
3106
    if (null === $user) {
3107
        return false;
3108
    }
3109
3110
    return $user->hasRole($role);
3111
}
3112
3113
function api_is_allowed_in_course(): bool
3114
{
3115
    if (api_is_platform_admin()) {
3116
        return true;
3117
    }
3118
3119
    $user = api_get_current_user();
3120
    if ($user instanceof User) {
3121
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3122
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3123
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3124
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3125
        ) {
3126
            return true;
3127
        }
3128
    }
3129
3130
    return false;
3131
}
3132
3133
/**
3134
 * Checks whether current user is a student boss.
3135
 */
3136
function api_is_student_boss(?User $user = null): bool
3137
{
3138
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3139
}
3140
3141
/**
3142
 * Checks whether the current user is a session administrator.
3143
 *
3144
 * @return bool True if current user is a course administrator
3145
 */
3146
function api_is_session_admin(?User $user = null)
3147
{
3148
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3149
}
3150
3151
/**
3152
 * Checks whether the current user is a human resources manager.
3153
 *
3154
 * @return bool True if current user is a human resources manager
3155
 */
3156
function api_is_drh()
3157
{
3158
    return api_user_has_role('ROLE_RRHH');
3159
}
3160
3161
/**
3162
 * Checks whether the current user is a student.
3163
 *
3164
 * @return bool True if current user is a human resources manager
3165
 */
3166
function api_is_student()
3167
{
3168
    return api_user_has_role('ROLE_STUDENT');
3169
}
3170
3171
/**
3172
 * Checks whether the current user has the status 'teacher'.
3173
 *
3174
 * @return bool True if current user is a human resources manager
3175
 */
3176
function api_is_teacher()
3177
{
3178
    return api_user_has_role('ROLE_TEACHER');
3179
}
3180
3181
/**
3182
 * Checks whether the current user is a invited user.
3183
 *
3184
 * @return bool
3185
 */
3186
function api_is_invitee()
3187
{
3188
    return api_user_has_role('ROLE_INVITEE');
3189
}
3190
3191
/**
3192
 * This function checks whether a session is assigned into a category.
3193
 *
3194
 * @param int       - session id
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
3195
 * @param string    - category name
3196
 *
3197
 * @return bool - true if is found, otherwise false
3198
 */
3199
function api_is_session_in_category($session_id, $category_name)
3200
{
3201
    $session_id = (int) $session_id;
3202
    $category_name = Database::escape_string($category_name);
3203
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3204
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3205
3206
    $sql = "SELECT 1
3207
            FROM $tbl_session
3208
            WHERE $session_id IN (
3209
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3210
                WHERE
3211
                  s.session_category_id = sc.id AND
3212
                  sc.name LIKE '%$category_name'
3213
            )";
3214
    $rs = Database::query($sql);
3215
3216
    if (Database::num_rows($rs) > 0) {
3217
        return true;
3218
    }
3219
3220
    return false;
3221
}
3222
3223
/**
3224
 * Displays options for switching between student view and course manager view.
3225
 *
3226
 * Changes in version 1.2 (Patrick Cool)
3227
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3228
 * is changed explicitly
3229
 *
3230
 * Changes in version 1.1 (Patrick Cool)
3231
 * student view now works correctly in subfolders of the document tool
3232
 * student view works correctly in the new links tool
3233
 *
3234
 * Example code for using this in your tools:
3235
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3236
 * //   display_tool_view_option($isStudentView);
3237
 * //}
3238
 * //and in later sections, use api_is_allowed_to_edit()
3239
 *
3240
 * @author Roan Embrechts
3241
 * @author Patrick Cool
3242
 * @author Julio Montoya, changes added in Chamilo
3243
 *
3244
 * @version 1.2
3245
 *
3246
 * @todo rewrite code so it is easier to understand
3247
 */
3248
function api_display_tool_view_option()
3249
{
3250
    if ('true' != api_get_setting('student_view_enabled')) {
3251
        return '';
3252
    }
3253
3254
    $sourceurl = '';
3255
    $is_framed = false;
3256
    // Exceptions apply for all multi-frames pages
3257
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3258
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3259
        return '';
3260
    }
3261
3262
    // Uncomment to remove student view link from document view page
3263
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3264
        if (empty($_GET['lp_id'])) {
3265
            return '';
3266
        }
3267
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3268
        $sourceurl = str_replace(
3269
            'lp/lp_header.php',
3270
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3271
            $sourceurl
3272
        );
3273
        //showinframes doesn't handle student view anyway...
3274
        //return '';
3275
        $is_framed = true;
3276
    }
3277
3278
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3279
    if (!$is_framed) {
3280
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3281
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3282
        } else {
3283
            $sourceurl = $_SERVER['REQUEST_URI'];
3284
        }
3285
    }
3286
3287
    $output_string = '';
3288
    if (!empty($_SESSION['studentview'])) {
3289
        if ('studentview' == $_SESSION['studentview']) {
3290
            // We have to remove the isStudentView=true from the $sourceurl
3291
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3292
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3293
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3294
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3295
        } elseif ('teacherview' == $_SESSION['studentview']) {
3296
            // Switching to teacherview
3297
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3298
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3299
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3300
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3301
        }
3302
    } else {
3303
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3304
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3305
    }
3306
    $output_string = Security::remove_XSS($output_string);
3307
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3308
3309
    return $html;
3310
}
3311
3312
/**
3313
 * Function that removes the need to directly use is_courseAdmin global in
3314
 * tool scripts. It returns true or false depending on the user's rights in
3315
 * this particular course.
3316
 * Optionally checking for tutor and coach roles here allows us to use the
3317
 * student_view feature altogether with these roles as well.
3318
 *
3319
 * @param bool  Whether to check if the user has the tutor role
3320
 * @param bool  Whether to check if the user has the coach role
3321
 * @param bool  Whether to check if the user has the session coach role
3322
 * @param bool  check the student view or not
3323
 *
3324
 * @author Roan Embrechts
3325
 * @author Patrick Cool
3326
 * @author Julio Montoya
3327
 *
3328
 * @version 1.1, February 2004
3329
 *
3330
 * @return bool true: the user has the rights to edit, false: he does not
3331
 */
3332
function api_is_allowed_to_edit(
3333
    $tutor = false,
3334
    $coach = false,
3335
    $session_coach = false,
3336
    $check_student_view = true
3337
) {
3338
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3339
    // Admins can edit anything.
3340
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3341
        //The student preview was on
3342
        if ($check_student_view && api_is_student_view_active()) {
3343
            return false;
3344
        }
3345
3346
        return true;
3347
    }
3348
3349
    $sessionId = api_get_session_id();
3350
3351
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3352
        $efv = new ExtraFieldValue('course');
3353
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3354
            api_get_course_int_id(),
3355
            'session_courses_read_only_mode'
3356
        );
3357
3358
        if (!empty($lockExrafieldField['value'])) {
3359
            return false;
3360
        }
3361
    }
3362
3363
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3364
    $session_visibility = api_get_session_visibility($sessionId);
3365
    $is_courseAdmin = api_is_course_admin();
3366
3367
    if (!$is_courseAdmin && $tutor) {
3368
        // If we also want to check if the user is a tutor...
3369
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3370
    }
3371
3372
    if (!$is_courseAdmin && $coach) {
3373
        // If we also want to check if the user is a coach...';
3374
        // Check if session visibility is read only for coaches.
3375
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3376
            $is_allowed_coach_to_edit = false;
3377
        }
3378
3379
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3380
            // Check if coach is allowed to edit a course.
3381
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3382
        }
3383
    }
3384
3385
    if (!$is_courseAdmin && $session_coach) {
3386
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3387
    }
3388
3389
    // Check if the student_view is enabled, and if so, if it is activated.
3390
    if ('true' === api_get_setting('student_view_enabled')) {
3391
        $studentView = api_is_student_view_active();
3392
        if (!empty($sessionId)) {
3393
            // Check if session visibility is read only for coaches.
3394
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3395
                $is_allowed_coach_to_edit = false;
3396
            }
3397
3398
            $is_allowed = false;
3399
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3400
                // Check if coach is allowed to edit a course.
3401
                $is_allowed = $is_allowed_coach_to_edit;
3402
            }
3403
            if ($check_student_view) {
3404
                $is_allowed = $is_allowed && false === $studentView;
3405
            }
3406
        } else {
3407
            $is_allowed = $is_courseAdmin;
3408
            if ($check_student_view) {
3409
                $is_allowed = $is_courseAdmin && false === $studentView;
3410
            }
3411
        }
3412
3413
        return $is_allowed;
3414
    } else {
3415
        return $is_courseAdmin;
3416
    }
3417
}
3418
3419
/**
3420
 * Returns true if user is a course coach of at least one course in session.
3421
 *
3422
 * @param int $sessionId
3423
 *
3424
 * @return bool
3425
 */
3426
function api_is_coach_of_course_in_session($sessionId)
3427
{
3428
    if (api_is_platform_admin()) {
3429
        return true;
3430
    }
3431
3432
    $userId = api_get_user_id();
3433
    $courseList = UserManager::get_courses_list_by_session(
3434
        $userId,
3435
        $sessionId
3436
    );
3437
3438
    // Session visibility.
3439
    $visibility = api_get_session_visibility(
3440
        $sessionId,
3441
        null,
3442
        false
3443
    );
3444
3445
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3446
        // Course Coach session visibility.
3447
        $blockedCourseCount = 0;
3448
        $closedVisibilityList = [
3449
            COURSE_VISIBILITY_CLOSED,
3450
            COURSE_VISIBILITY_HIDDEN,
3451
        ];
3452
3453
        foreach ($courseList as $course) {
3454
            // Checking session visibility
3455
            $sessionCourseVisibility = api_get_session_visibility(
3456
                $sessionId,
3457
                $course['real_id']
3458
            );
3459
3460
            $courseIsVisible = !in_array(
3461
                $course['visibility'],
3462
                $closedVisibilityList
3463
            );
3464
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3465
                $blockedCourseCount++;
3466
            }
3467
        }
3468
3469
        // If all courses are blocked then no show in the list.
3470
        if ($blockedCourseCount === count($courseList)) {
3471
            $visibility = SESSION_INVISIBLE;
3472
        } else {
3473
            $visibility = SESSION_VISIBLE;
3474
        }
3475
    }
3476
3477
    switch ($visibility) {
3478
        case SESSION_VISIBLE_READ_ONLY:
3479
        case SESSION_VISIBLE:
3480
        case SESSION_AVAILABLE:
3481
            return true;
3482
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

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

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

6410
    /** @scrutinizer ignore-call */ 
6411
    $hierarchy = Container::$container->getParameter('security.role_hierarchy.roles');

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...
6411
    $roles = [];
6412
    array_walk_recursive($hierarchy, function ($role) use (&$roles) {
6413
        $roles[$role] = $role;
6414
    });
6415
6416
    return $roles;
6417
}
6418
6419
function api_get_user_roles(): array
6420
{
6421
    $roles = [
6422
        'ROLE_TEACHER',
6423
        'ROLE_STUDENT',
6424
        'ROLE_RRHH',
6425
        'ROLE_SESSION_MANAGER',
6426
        'ROLE_STUDENT_BOSS',
6427
        'ROLE_INVITEE',
6428
        'ROLE_USER',
6429
    ];
6430
6431
    return array_combine($roles, $roles);
6432
}
6433
6434
/**
6435
 * @param string $file
6436
 *
6437
 * @return string
6438
 */
6439
function api_get_js_simple($file)
6440
{
6441
    return '<script type="text/javascript" src="'.$file.'"></script>'."\n";
6442
}
6443
6444
/**
6445
 * Modify default memory_limit and max_execution_time limits
6446
 * Needed when processing long tasks.
6447
 */
6448
function api_set_more_memory_and_time_limits()
6449
{
6450
    if (function_exists('ini_set')) {
6451
        api_set_memory_limit('256M');
6452
        ini_set('max_execution_time', 1800);
6453
    }
6454
}
6455
6456
/**
6457
 * Tries to set memory limit, if authorized and new limit is higher than current.
6458
 *
6459
 * @param string $mem New memory limit
6460
 *
6461
 * @return bool True on success, false on failure or current is higher than suggested
6462
 * @assert (null) === false
6463
 * @assert (-1) === false
6464
 * @assert (0) === true
6465
 * @assert ('1G') === true
6466
 */
6467
function api_set_memory_limit($mem)
6468
{
6469
    //if ini_set() not available, this function is useless
6470
    if (!function_exists('ini_set') || is_null($mem) || -1 == $mem) {
6471
        return false;
6472
    }
6473
6474
    $memory_limit = ini_get('memory_limit');
6475
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
6476
        ini_set('memory_limit', $mem);
6477
6478
        return true;
6479
    }
6480
6481
    return false;
6482
}
6483
6484
/**
6485
 * Gets memory limit in bytes.
6486
 *
6487
 * @param string The memory size (128M, 1G, 1000K, etc)
6488
 *
6489
 * @return int
6490
 * @assert (null) === false
6491
 * @assert ('1t')  === 1099511627776
6492
 * @assert ('1g')  === 1073741824
6493
 * @assert ('1m')  === 1048576
6494
 * @assert ('100k') === 102400
6495
 */
6496
function api_get_bytes_memory_limit($mem)
6497
{
6498
    $size = strtolower(substr($mem, -1));
6499
6500
    switch ($size) {
6501
        case 't':
6502
            $mem = (int) substr($mem, -1) * 1024 * 1024 * 1024 * 1024;
6503
            break;
6504
        case 'g':
6505
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024 * 1024;
6506
            break;
6507
        case 'm':
6508
            $mem = (int) substr($mem, 0, -1) * 1024 * 1024;
6509
            break;
6510
        case 'k':
6511
            $mem = (int) substr($mem, 0, -1) * 1024;
6512
            break;
6513
        default:
6514
            // we assume it's integer only
6515
            $mem = (int) $mem;
6516
            break;
6517
    }
6518
6519
    return $mem;
6520
}
6521
6522
/**
6523
 * Finds all the information about a user from username instead of user id.
6524
 *
6525
 * @param string $officialCode
6526
 *
6527
 * @return array $user_info user_id, lastname, firstname, username, email, ...
6528
 *
6529
 * @author Yannick Warnier <[email protected]>
6530
 */
6531
function api_get_user_info_from_official_code($officialCode)
6532
{
6533
    if (empty($officialCode)) {
6534
        return false;
6535
    }
6536
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
6537
            WHERE official_code ='".Database::escape_string($officialCode)."'";
6538
    $result = Database::query($sql);
6539
    if (Database::num_rows($result) > 0) {
6540
        $result_array = Database::fetch_array($result);
6541
6542
        return _api_format_user($result_array);
6543
    }
6544
6545
    return false;
6546
}
6547
6548
/**
6549
 * @param string $usernameInputId
6550
 * @param string $passwordInputId
6551
 *
6552
 * @return string|null
6553
 */
6554
function api_get_password_checker_js($usernameInputId, $passwordInputId)
6555
{
6556
    $checkPass = api_get_setting('allow_strength_pass_checker');
6557
    $useStrengthPassChecker = 'true' === $checkPass;
6558
6559
    if (false === $useStrengthPassChecker) {
6560
        return null;
6561
    }
6562
6563
    $minRequirements = Security::getPasswordRequirements()['min'];
6564
6565
    $options = [
6566
        'rules' => [],
6567
    ];
6568
6569
    if ($minRequirements['length'] > 0) {
6570
        $options['rules'][] = [
6571
            'minChar' => $minRequirements['length'],
6572
            'pattern' => '.',
6573
            'helpText' => sprintf(
6574
                get_lang('Minimum %s characters in total'),
6575
                $minRequirements['length']
6576
            ),
6577
        ];
6578
    }
6579
6580
    if ($minRequirements['lowercase'] > 0) {
6581
        $options['rules'][] = [
6582
            'minChar' => $minRequirements['lowercase'],
6583
            'pattern' => '[a-z]',
6584
            'helpText' => sprintf(
6585
                get_lang('Minimum %s lowercase characters'),
6586
                $minRequirements['lowercase']
6587
            ),
6588
        ];
6589
    }
6590
6591
    if ($minRequirements['uppercase'] > 0) {
6592
        $options['rules'][] = [
6593
            'minChar' => $minRequirements['uppercase'],
6594
            'pattern' => '[A-Z]',
6595
            'helpText' => sprintf(
6596
                get_lang('Minimum %s uppercase characters'),
6597
                $minRequirements['uppercase']
6598
            ),
6599
        ];
6600
    }
6601
6602
    if ($minRequirements['numeric'] > 0) {
6603
        $options['rules'][] = [
6604
            'minChar' => $minRequirements['numeric'],
6605
            'pattern' => '[0-9]',
6606
            'helpText' => sprintf(
6607
                get_lang('Minimum %s numerical (0-9) characters'),
6608
                $minRequirements['numeric']
6609
            ),
6610
        ];
6611
    }
6612
6613
    if ($minRequirements['specials'] > 0) {
6614
        $options['rules'][] = [
6615
            'minChar' => $minRequirements['specials'],
6616
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
6617
            'helpText' => sprintf(
6618
                get_lang('Minimum %s special characters'),
6619
                $minRequirements['specials']
6620
            ),
6621
        ];
6622
    }
6623
6624
    $js = api_get_js('password-checker/password-checker.js');
6625
    $js .= "<script>
6626
    $(function() {
6627
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
6628
    });
6629
    </script>";
6630
6631
    return $js;
6632
}
6633
6634
/**
6635
 * create an user extra field called 'captcha_blocked_until_date'.
6636
 *
6637
 * @param string $username
6638
 *
6639
 * @return bool
6640
 */
6641
function api_block_account_captcha($username)
6642
{
6643
    $userInfo = api_get_user_info_from_username($username);
6644
    if (empty($userInfo)) {
6645
        return false;
6646
    }
6647
    $minutesToBlock = api_get_setting('captcha_time_to_block');
6648
    $time = time() + $minutesToBlock * 60;
6649
    UserManager::update_extra_field_value(
6650
        $userInfo['user_id'],
6651
        'captcha_blocked_until_date',
6652
        api_get_utc_datetime($time)
6653
    );
6654
6655
    return true;
6656
}
6657
6658
/**
6659
 * @param string $username
6660
 *
6661
 * @return bool
6662
 */
6663
function api_clean_account_captcha($username)
6664
{
6665
    $userInfo = api_get_user_info_from_username($username);
6666
    if (empty($userInfo)) {
6667
        return false;
6668
    }
6669
    Session::erase('loginFailedCount');
6670
    UserManager::update_extra_field_value(
6671
        $userInfo['user_id'],
6672
        'captcha_blocked_until_date',
6673
        null
6674
    );
6675
6676
    return true;
6677
}
6678
6679
/**
6680
 * @param string $username
6681
 *
6682
 * @return bool
6683
 */
6684
function api_get_user_blocked_by_captcha($username)
6685
{
6686
    $userInfo = api_get_user_info_from_username($username);
6687
    if (empty($userInfo)) {
6688
        return false;
6689
    }
6690
    $data = UserManager::get_extra_user_data_by_field(
6691
        $userInfo['user_id'],
6692
        'captcha_blocked_until_date'
6693
    );
6694
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
6695
        return $data['captcha_blocked_until_date'];
6696
    }
6697
6698
    return false;
6699
}
6700
6701
/**
6702
 * If true, the drh can access all content (courses, users) inside a session.
6703
 *
6704
 * @return bool
6705
 */
6706
function api_drh_can_access_all_session_content()
6707
{
6708
    return 'true' === api_get_setting('drh_can_access_all_session_content');
6709
}
6710
6711
/**
6712
 * Checks if user can login as another user.
6713
 *
6714
 * @param int $loginAsUserId the user id to log in
6715
 * @param int $userId        my user id
6716
 *
6717
 * @return bool
6718
 */
6719
function api_can_login_as($loginAsUserId, $userId = null)
6720
{
6721
    $loginAsUserId = (int) $loginAsUserId;
6722
6723
    if (empty($loginAsUserId)) {
6724
        return false;
6725
    }
6726
6727
    if (empty($userId)) {
6728
        $userId = api_get_user_id();
6729
    }
6730
6731
    if ($loginAsUserId == $userId) {
6732
        return false;
6733
    }
6734
6735
    // Check if the user to login is an admin
6736
    if (api_is_platform_admin_by_id($loginAsUserId)) {
6737
        // Only super admins can login to admin accounts
6738
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
6739
            return false;
6740
        }
6741
    }
6742
6743
    $userInfo = api_get_user_info($loginAsUserId);
6744
6745
    $isDrh = function () use ($loginAsUserId) {
6746
        if (api_is_drh()) {
6747
            if (api_drh_can_access_all_session_content()) {
6748
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
6749
                    'drh_all',
6750
                    api_get_user_id()
6751
                );
6752
                $userList = [];
6753
                if (is_array($users)) {
6754
                    foreach ($users as $user) {
6755
                        $userList[] = $user['id'];
6756
                    }
6757
                }
6758
                if (in_array($loginAsUserId, $userList)) {
6759
                    return true;
6760
                }
6761
            } else {
6762
                if (api_is_drh() &&
6763
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
6764
                ) {
6765
                    return true;
6766
                }
6767
            }
6768
        }
6769
6770
        return false;
6771
    };
6772
6773
    $loginAsStatusForSessionAdmins = [STUDENT];
6774
6775
    if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
6776
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
6777
    }
6778
6779
    return api_is_platform_admin() ||
6780
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
6781
        $isDrh();
6782
}
6783
6784
/**
6785
 * Return true on https install.
6786
 *
6787
 * @return bool
6788
 */
6789
function api_is_https()
6790
{
6791
    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...
6792
        'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] || !empty(api_get_configuration_value('force_https_forwarded_proto'))
6793
    ) {
6794
        $isSecured = true;
6795
    } else {
6796
        if (!empty($_SERVER['HTTPS']) && 'off' != $_SERVER['HTTPS']) {
6797
            $isSecured = true;
6798
        } else {
6799
            $isSecured = false;
6800
            // last chance
6801
            if (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) {
6802
                $isSecured = true;
6803
            }
6804
        }
6805
    }
6806
6807
    return $isSecured;
6808
}
6809
6810
/**
6811
 * Return protocol (http or https).
6812
 *
6813
 * @return string
6814
 */
6815
function api_get_protocol()
6816
{
6817
    return api_is_https() ? 'https' : 'http';
6818
}
6819
6820
/**
6821
 * Get origin.
6822
 *
6823
 * @param string
6824
 *
6825
 * @return string
6826
 */
6827
function api_get_origin()
6828
{
6829
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
6830
}
6831
6832
/**
6833
 * Warns an user that the portal reach certain limit.
6834
 *
6835
 * @param string $limitName
6836
 */
6837
function api_warn_hosting_contact($limitName)
6838
{
6839
    $hostingParams = api_get_configuration_value(1);
6840
    $email = null;
6841
6842
    if (!empty($hostingParams)) {
6843
        if (isset($hostingParams['hosting_contact_mail'])) {
6844
            $email = $hostingParams['hosting_contact_mail'];
6845
        }
6846
    }
6847
6848
    if (!empty($email)) {
6849
        $subject = get_lang('Hosting warning reached');
6850
        $body = get_lang('Portal name').': '.api_get_path(WEB_PATH)." \n ";
6851
        $body .= get_lang('Portal\'s limit type').': '.$limitName." \n ";
6852
        if (isset($hostingParams[$limitName])) {
6853
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
6854
        }
6855
        api_mail_html(null, $email, $subject, $body);
6856
    }
6857
}
6858
6859
/**
6860
 * Gets value of a variable from config/configuration.php
6861
 * Variables that are not set in the configuration.php file but set elsewhere:
6862
 * - virtual_css_theme_folder (vchamilo plugin)
6863
 * - access_url (global.inc.php)
6864
 * - apc/apc_prefix (global.inc.php).
6865
 *
6866
 * @param string $variable
6867
 *
6868
 * @return bool|mixed
6869
 */
6870
function api_get_configuration_value($variable)
6871
{
6872
    global $_configuration;
6873
    // Check the current url id, id = 1 by default
6874
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
6875
6876
    $variable = trim($variable);
6877
6878
    // Check if variable exists
6879
    if (isset($_configuration[$variable])) {
6880
        if (is_array($_configuration[$variable])) {
6881
            // Check if it exists for the sub portal
6882
            if (array_key_exists($urlId, $_configuration[$variable])) {
6883
                return $_configuration[$variable][$urlId];
6884
            } else {
6885
                // Try to found element with id = 1 (master portal)
6886
                if (array_key_exists(1, $_configuration[$variable])) {
6887
                    return $_configuration[$variable][1];
6888
                }
6889
            }
6890
        }
6891
6892
        return $_configuration[$variable];
6893
    }
6894
6895
    return false;
6896
}
6897
6898
/**
6899
 * Retreives and returns a value in a hierarchical configuration array
6900
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
6901
 *
6902
 * @param string $path      the successive array keys, separated by the separator
6903
 * @param mixed  $default   value to be returned if not found, null by default
6904
 * @param string $separator '/' by default
6905
 * @param array  $array     the active configuration array by default
6906
 *
6907
 * @return mixed the found value or $default
6908
 */
6909
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
6910
{
6911
    $pos = strpos($path, $separator);
6912
    if (false === $pos) {
6913
        if (is_null($array)) {
6914
            return api_get_configuration_value($path);
6915
        }
6916
        if (is_array($array) && array_key_exists($path, $array)) {
6917
            return $array[$path];
6918
        }
6919
6920
        return $default;
6921
    }
6922
    $key = substr($path, 0, $pos);
6923
    if (is_null($array)) {
6924
        $newArray = api_get_configuration_value($key);
6925
    } elseif (is_array($array) && array_key_exists($key, $array)) {
6926
        $newArray = $array[$key];
6927
    } else {
6928
        return $default;
6929
    }
6930
    if (is_array($newArray)) {
6931
        $newPath = substr($path, $pos + 1);
6932
6933
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
6934
    }
6935
6936
    return $default;
6937
}
6938
6939
/**
6940
 * Retrieves and returns a value in a hierarchical configuration array
6941
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
6942
 *
6943
 * @param array  $array     the recursive array that contains the value to be returned (or not)
6944
 * @param string $path      the successive array keys, separated by the separator
6945
 * @param mixed  $default   the value to be returned if not found
6946
 * @param string $separator the separator substring
6947
 *
6948
 * @return mixed the found value or $default
6949
 */
6950
function api_array_sub_value($array, $path, $default = null, $separator = '/')
6951
{
6952
    $pos = strpos($path, $separator);
6953
    if (false === $pos) {
6954
        if (is_array($array) && array_key_exists($path, $array)) {
6955
            return $array[$path];
6956
        }
6957
6958
        return $default;
6959
    }
6960
    $key = substr($path, 0, $pos);
6961
    if (is_array($array) && array_key_exists($key, $array)) {
6962
        $newArray = $array[$key];
6963
    } else {
6964
        return $default;
6965
    }
6966
    if (is_array($newArray)) {
6967
        $newPath = substr($path, $pos + 1);
6968
6969
        return api_array_sub_value($newArray, $newPath, $default);
6970
    }
6971
6972
    return $default;
6973
}
6974
6975
/**
6976
 * Returns supported image extensions in the portal.
6977
 *
6978
 * @param bool $supportVectors Whether vector images should also be accepted or not
6979
 *
6980
 * @return array Supported image extensions in the portal
6981
 */
6982
function api_get_supported_image_extensions($supportVectors = true)
6983
{
6984
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
6985
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
6986
    if ($supportVectors) {
6987
        array_push($supportedImageExtensions, 'svg');
6988
    }
6989
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
6990
        array_push($supportedImageExtensions, 'webp');
6991
    }
6992
6993
    return $supportedImageExtensions;
6994
}
6995
6996
/**
6997
 * This setting changes the registration status for the campus.
6998
 *
6999
 * @author Patrick Cool <[email protected]>, Ghent University
7000
 *
7001
 * @version August 2006
7002
 *
7003
 * @param bool $listCampus Whether we authorize
7004
 *
7005
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
7006
 */
7007
function api_register_campus($listCampus = true)
7008
{
7009
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS);
7010
7011
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
7012
    Database::query($sql);
7013
7014
    if (!$listCampus) {
7015
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
7016
        Database::query($sql);
7017
    }
7018
}
7019
7020
/**
7021
 * Check whether the user type should be exclude.
7022
 * Such as invited or anonymous users.
7023
 *
7024
 * @param bool $checkDB Optional. Whether check the user status
7025
 * @param int  $userId  Options. The user id
7026
 *
7027
 * @return bool
7028
 */
7029
function api_is_excluded_user_type($checkDB = false, $userId = 0)
7030
{
7031
    if ($checkDB) {
7032
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
7033
7034
        if (0 == $userId) {
7035
            return true;
7036
        }
7037
7038
        $userInfo = api_get_user_info($userId);
7039
7040
        switch ($userInfo['status']) {
7041
            case INVITEE:
7042
            case ANONYMOUS:
7043
                return true;
7044
            default:
7045
                return false;
7046
        }
7047
    }
7048
7049
    $isInvited = api_is_invitee();
7050
    $isAnonymous = api_is_anonymous();
7051
7052
    if ($isInvited || $isAnonymous) {
7053
        return true;
7054
    }
7055
7056
    return false;
7057
}
7058
7059
/**
7060
 * Get the user status to ignore in reports.
7061
 *
7062
 * @param string $format Optional. The result type (array or string)
7063
 *
7064
 * @return array|string
7065
 */
7066
function api_get_users_status_ignored_in_reports($format = 'array')
7067
{
7068
    $excludedTypes = [
7069
        INVITEE,
7070
        ANONYMOUS,
7071
    ];
7072
7073
    if ('string' == $format) {
7074
        return implode(', ', $excludedTypes);
7075
    }
7076
7077
    return $excludedTypes;
7078
}
7079
7080
/**
7081
 * Set the Site Use Cookie Warning for 1 year.
7082
 */
7083
function api_set_site_use_cookie_warning_cookie()
7084
{
7085
    setcookie('ChamiloUsesCookies', 'ok', time() + 31556926);
7086
}
7087
7088
/**
7089
 * Return true if the Site Use Cookie Warning Cookie warning exists.
7090
 *
7091
 * @return bool
7092
 */
7093
function api_site_use_cookie_warning_cookie_exist()
7094
{
7095
    return isset($_COOKIE['ChamiloUsesCookies']);
7096
}
7097
7098
/**
7099
 * Given a number of seconds, format the time to show hours, minutes and seconds.
7100
 *
7101
 * @param int    $time         The time in seconds
7102
 * @param string $originFormat Optional. PHP o JS
7103
 *
7104
 * @return string (00h00'00")
7105
 */
7106
function api_format_time($time, $originFormat = 'php')
7107
{
7108
    $h = get_lang('h');
7109
    $hours = $time / 3600;
7110
    $mins = ($time % 3600) / 60;
7111
    $secs = ($time % 60);
7112
7113
    if ($time < 0) {
7114
        $hours = 0;
7115
        $mins = 0;
7116
        $secs = 0;
7117
    }
7118
7119
    if ('js' === $originFormat) {
7120
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
7121
    } else {
7122
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
7123
    }
7124
7125
    return $formattedTime;
7126
}
7127
7128
function api_set_noreply_and_from_address_to_mailer(
7129
    TemplatedEmail $email,
7130
    array $sender,
7131
    array $replyToAddress = []
7132
): void {
7133
    $validator = Container::getLegacyHelper()->getValidator();
7134
    $emailConstraint = new Assert\Email();
7135
7136
    $noReplyAddress = api_get_setting('noreply_email_address');
7137
    $avoidReplyToAddress = false;
7138
7139
    if (!empty($noReplyAddress)) {
7140
        // $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
7141
    }
7142
7143
    // Default values
7144
    $notification = new Notification();
7145
    $defaultSenderName = $notification->getDefaultPlatformSenderName();
7146
    $defaultSenderEmail = $notification->getDefaultPlatformSenderEmail();
7147
7148
    // If the parameter is set don't use the admin.
7149
    $senderName = !empty($sender['name']) ? $sender['name'] : $defaultSenderName;
7150
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $defaultSenderEmail;
7151
7152
    // Send errors to the platform admin
7153
    $adminEmail = api_get_setting('admin.administrator_email');
7154
7155
    $adminEmailValidation = $validator->validate($adminEmail, $emailConstraint);
7156
7157
    if (!empty($adminEmail) && 0 === $adminEmailValidation->count()) {
7158
        $email
7159
            ->getHeaders()
7160
            ->addIdHeader('Errors-To', $adminEmail)
7161
        ;
7162
    }
7163
7164
    if (!$avoidReplyToAddress && !empty($replyToAddress)) {
7165
        $replyToEmailValidation = $validator->validate($replyToAddress['mail'], $emailConstraint);
7166
7167
        if (0 === $replyToEmailValidation->count()) {
7168
            $email->addReplyTo(new Address($replyToAddress['mail'], $replyToAddress['name']));
7169
        }
7170
    }
7171
7172
    if ('true' === api_get_setting('mail.smtp_unique_sender')) {
7173
        $senderName = $defaultSenderName;
7174
        $senderEmail = $defaultSenderEmail;
7175
7176
        $email->sender(new Address($senderEmail, $senderName));
7177
    }
7178
7179
    if ($senderEmail) {
7180
        $email->from(new Address($senderEmail, $senderName));
7181
    }
7182
}
7183
7184
/**
7185
 * Sends an email
7186
 * Sender name and email can be specified, if not specified
7187
 * name and email of the platform admin are used.
7188
 *
7189
 * @param string    name of recipient
7190
 * @param string    email of recipient
7191
 * @param string    email subject
7192
 * @param string    email body
7193
 * @param string    sender name
7194
 * @param string    sender e-mail
7195
 * @param array     extra headers in form $headers = array($name => $value) to allow parsing
7196
 * @param array     data file (path and filename)
7197
 * @param bool      True for attaching a embedded file inside content html (optional)
7198
 * @param array     Additional parameters
7199
 *
7200
 * @return bool true if mail was sent
7201
 */
7202
function api_mail_html(
7203
    $recipientName,
7204
    $recipientEmail,
7205
    $subject,
7206
    $body,
7207
    $senderName = '',
7208
    $senderEmail = '',
7209
    $extra_headers = [],
7210
    $data_file = [],
7211
    $embeddedImage = false,
7212
    $additionalParameters = [],
7213
    string $sendErrorTo = null
7214
) {
7215
    $mailHelper = Container::$container->get(MailHelper::class);
7216
7217
    return $mailHelper->send(
7218
        $recipientName,
7219
        $recipientEmail,
7220
        $subject,
7221
        $body,
7222
        $senderName,
7223
        $senderEmail,
7224
        $extra_headers,
7225
        $data_file,
7226
        $embeddedImage,
7227
        $additionalParameters,
7228
        $sendErrorTo
7229
    );
7230
}
7231
7232
/**
7233
 * @param int  $tool       Possible values: GroupManager::GROUP_TOOL_*
7234
 * @param bool $showHeader
7235
 */
7236
function api_protect_course_group($tool, $showHeader = true)
7237
{
7238
    $groupId = api_get_group_id();
7239
    if (!empty($groupId)) {
7240
        if (api_is_platform_admin()) {
7241
            return true;
7242
        }
7243
7244
        if (api_is_allowed_to_edit(false, true, true)) {
7245
            return true;
7246
        }
7247
7248
        $userId = api_get_user_id();
7249
        $sessionId = api_get_session_id();
7250
        if (!empty($sessionId)) {
7251
            if (api_is_coach($sessionId, api_get_course_int_id())) {
7252
                return true;
7253
            }
7254
7255
            if (api_is_drh()) {
7256
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
7257
                    return true;
7258
                }
7259
            }
7260
        }
7261
7262
        $group = api_get_group_entity($groupId);
7263
7264
        // Group doesn't exists
7265
        if (null === $group) {
7266
            api_not_allowed($showHeader);
7267
        }
7268
7269
        // Check group access
7270
        $allow = GroupManager::userHasAccess(
7271
            $userId,
7272
            $group,
7273
            $tool
7274
        );
7275
7276
        if (!$allow) {
7277
            api_not_allowed($showHeader);
7278
        }
7279
    }
7280
7281
    return false;
7282
}
7283
7284
/**
7285
 * Check if a date is in a date range.
7286
 *
7287
 * @param datetime $startDate
7288
 * @param datetime $endDate
7289
 * @param datetime $currentDate
7290
 *
7291
 * @return bool true if date is in rage, false otherwise
7292
 */
7293
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
7294
{
7295
    $startDate = strtotime(api_get_local_time($startDate));
7296
    $endDate = strtotime(api_get_local_time($endDate));
7297
    $currentDate = strtotime(api_get_local_time($currentDate));
7298
7299
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
7300
        return true;
7301
    }
7302
7303
    return false;
7304
}
7305
7306
/**
7307
 * Eliminate the duplicates of a multidimensional array by sending the key.
7308
 *
7309
 * @param array $array multidimensional array
7310
 * @param int   $key   key to find to compare
7311
 *
7312
 * @return array
7313
 */
7314
function api_unique_multidim_array($array, $key)
7315
{
7316
    $temp_array = [];
7317
    $i = 0;
7318
    $key_array = [];
7319
7320
    foreach ($array as $val) {
7321
        if (!in_array($val[$key], $key_array)) {
7322
            $key_array[$i] = $val[$key];
7323
            $temp_array[$i] = $val;
7324
        }
7325
        $i++;
7326
    }
7327
7328
    return $temp_array;
7329
}
7330
7331
/**
7332
 * Limit the access to Session Admins when the limit_session_admin_role
7333
 * configuration variable is set to true.
7334
 */
7335
function api_protect_limit_for_session_admin()
7336
{
7337
    $limitAdmin = api_get_setting('limit_session_admin_role');
7338
    if (api_is_session_admin() && 'true' === $limitAdmin) {
7339
        api_not_allowed(true);
7340
    }
7341
}
7342
7343
/**
7344
 * Limits that a session admin has access to list users.
7345
 * When limit_session_admin_list_users configuration variable is set to true.
7346
 */
7347
function api_protect_session_admin_list_users()
7348
{
7349
    $limitAdmin = ('true' === api_get_setting('session.limit_session_admin_list_users'));
7350
7351
    if (api_is_session_admin() && true === $limitAdmin) {
7352
        api_not_allowed(true);
7353
    }
7354
}
7355
7356
/**
7357
 * @return bool
7358
 */
7359
function api_is_student_view_active(): bool
7360
{
7361
    $studentView = Session::read('studentview');
7362
7363
    return 'studentview' === $studentView;
7364
}
7365
7366
/**
7367
 * Converts string value to float value.
7368
 *
7369
 * 3.141516 => 3.141516
7370
 * 3,141516 => 3.141516
7371
 *
7372
 * @todo WIP
7373
 *
7374
 * @param string $number
7375
 *
7376
 * @return float
7377
 */
7378
function api_float_val($number)
7379
{
7380
    return (float) str_replace(',', '.', trim($number));
7381
}
7382
7383
/**
7384
 * Converts float values
7385
 * Example if $decimals = 2.
7386
 *
7387
 * 3.141516 => 3.14
7388
 * 3,141516 => 3,14
7389
 *
7390
 * @param string $number            number in iso code
7391
 * @param int    $decimals
7392
 * @param string $decimalSeparator
7393
 * @param string $thousandSeparator
7394
 *
7395
 * @return bool|string
7396
 */
7397
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
7398
{
7399
    $number = api_float_val($number);
7400
7401
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
7402
}
7403
7404
/**
7405
 * Set location url with a exit break by default.
7406
 *
7407
 * @param string $url
7408
 * @param bool   $exit
7409
 */
7410
function api_location($url, $exit = true)
7411
{
7412
    header('Location: '.$url);
7413
7414
    if ($exit) {
7415
        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...
7416
    }
7417
}
7418
7419
/**
7420
 * @param string $from
7421
 * @param string $to
7422
 *
7423
 * @return string
7424
 */
7425
function api_get_relative_path($from, $to)
7426
{
7427
    // some compatibility fixes for Windows paths
7428
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
7429
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
7430
    $from = str_replace('\\', '/', $from);
7431
    $to = str_replace('\\', '/', $to);
7432
7433
    $from = explode('/', $from);
7434
    $to = explode('/', $to);
7435
    $relPath = $to;
7436
7437
    foreach ($from as $depth => $dir) {
7438
        // find first non-matching dir
7439
        if ($dir === $to[$depth]) {
7440
            // ignore this directory
7441
            array_shift($relPath);
7442
        } else {
7443
            // get number of remaining dirs to $from
7444
            $remaining = count($from) - $depth;
7445
            if ($remaining > 1) {
7446
                // add traversals up to first matching dir
7447
                $padLength = (count($relPath) + $remaining - 1) * -1;
7448
                $relPath = array_pad($relPath, $padLength, '..');
7449
                break;
7450
            } else {
7451
                $relPath[0] = './'.$relPath[0];
7452
            }
7453
        }
7454
    }
7455
7456
    return implode('/', $relPath);
7457
}
7458
7459
/**
7460
 * @param string $template
7461
 *
7462
 * @return string
7463
 */
7464
function api_find_template($template)
7465
{
7466
    return Template::findTemplateFilePath($template);
7467
}
7468
7469
/**
7470
 * @return array
7471
 */
7472
function api_get_language_list_for_flag()
7473
{
7474
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
7475
    $sql = "SELECT english_name, isocode FROM $table
7476
            ORDER BY original_name ASC";
7477
    static $languages = [];
7478
    if (empty($languages)) {
7479
        $result = Database::query($sql);
7480
        while ($row = Database::fetch_array($result)) {
7481
            $languages[$row['english_name']] = $row['isocode'];
7482
        }
7483
        $languages['english'] = 'gb';
7484
    }
7485
7486
    return $languages;
7487
}
7488
7489
function api_create_zip(string $name): ZipStream
7490
{
7491
    $zipStreamOptions = new Archive();
7492
    $zipStreamOptions->setSendHttpHeaders(true);
7493
    $zipStreamOptions->setContentDisposition('attachment');
7494
    $zipStreamOptions->setContentType('application/x-zip');
7495
7496
    return new ZipStream($name, $zipStreamOptions);
7497
}
7498
7499
function api_get_language_translate_html(): string
7500
{
7501
    $translate = 'true' === api_get_setting('editor.translate_html');
7502
7503
    if (!$translate) {
7504
        return '';
7505
    }
7506
7507
    /*$languageList = api_get_languages();
7508
    $hideAll = '';
7509
    foreach ($languageList as $isocode => $name) {
7510
        $hideAll .= '
7511
        $(".mce-translatehtml").hide();
7512
        $("span:lang('.$isocode.')").filter(
7513
            function(e, val) {
7514
                // Only find the spans if they have set the lang
7515
                if ($(this).attr("lang") == null) {
7516
                    return false;
7517
                }
7518
                // Ignore ckeditor classes
7519
                return !this.className.match(/cke(.*)/);
7520
        }).hide();'."\n";
7521
    }*/
7522
7523
    $userInfo = api_get_user_info();
7524
    if (!empty($userInfo['language'])) {
7525
        $isoCode = $userInfo['language'];
7526
7527
        return '
7528
            $(function() {
7529
                $(".mce-translatehtml").hide();
7530
                var defaultLanguageFromUser = "'.$isoCode.'";
7531
                $("span:lang('.$isoCode.')").show();
7532
            });
7533
        ';
7534
    }
7535
7536
    return '';
7537
}
7538
7539
function api_protect_webservices()
7540
{
7541
    if (api_get_configuration_value('disable_webservices')) {
7542
        echo "Webservices are disabled. \n";
7543
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
7544
        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...
7545
    }
7546
}
7547