Passed
Push — master ( ed1ef5...2f2d8f )
by Angel Fernando Quiroz
14:29 queued 06:36
created

api_get_path()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 96
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 48
c 1
b 0
f 0
nc 16
nop 2
dl 0
loc 96
rs 8.8234

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\CoreBundle\ServiceHelper\PermissionServiceHelper;
16
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
17
use Chamilo\CourseBundle\Entity\CGroup;
18
use Chamilo\CourseBundle\Entity\CLp;
19
use ChamiloSession as Session;
20
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Mime\Address;
23
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
use Symfony\Component\Validator\Constraints as Assert;
26
use ZipStream\Option\Archive;
27
use ZipStream\ZipStream;
28
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
29
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
30
31
/**
32
 * This is a code library for Chamilo.
33
 * It is included by default in every Chamilo file (through including the global.inc.php)
34
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
35
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
36
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
37
 */
38
39
// PHP version requirement.
40
define('REQUIRED_PHP_VERSION', '8.2');
41
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
42
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
43
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
44
45
// USER STATUS CONSTANTS
46
/** global status of a user: student */
47
define('STUDENT', 5);
48
/** global status of a user: course manager */
49
define('COURSEMANAGER', 1);
50
/** global status of a user: session admin */
51
define('SESSIONADMIN', 3);
52
/** global status of a user: human resources manager */
53
define('DRH', 4);
54
/** global status of a user: anonymous visitor */
55
define('ANONYMOUS', 6);
56
/** global status of a user: low security, necessary for inserting data from
57
 * the teacher through HTMLPurifier */
58
define('COURSEMANAGERLOWSECURITY', 10);
59
// Soft user status
60
define('PLATFORM_ADMIN', 11);
61
define('SESSION_COURSE_COACH', 12);
62
define('SESSION_GENERAL_COACH', 13);
63
define('COURSE_STUDENT', 14); //student subscribed in a course
64
define('SESSION_STUDENT', 15); //student subscribed in a session course
65
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
66
define('STUDENT_BOSS', 17); // student is boss
67
define('INVITEE', 20);
68
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
69
70
// COURSE VISIBILITY CONSTANTS
71
/** only visible for course admin */
72
define('COURSE_VISIBILITY_CLOSED', 0);
73
/** only visible for users registered in the course */
74
define('COURSE_VISIBILITY_REGISTERED', 1);
75
/** Open for all registered users on the platform */
76
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
77
/** Open for the whole world */
78
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
79
/** Invisible to all except admin */
80
define('COURSE_VISIBILITY_HIDDEN', 4);
81
82
define('COURSE_REQUEST_PENDING', 0);
83
define('COURSE_REQUEST_ACCEPTED', 1);
84
define('COURSE_REQUEST_REJECTED', 2);
85
define('DELETE_ACTION_ENABLED', false);
86
87
// EMAIL SENDING RECIPIENT CONSTANTS
88
define('SEND_EMAIL_EVERYONE', 1);
89
define('SEND_EMAIL_STUDENTS', 2);
90
define('SEND_EMAIL_TEACHERS', 3);
91
92
// SESSION VISIBILITY CONSTANTS
93
define('SESSION_VISIBLE_READ_ONLY', 1);
94
define('SESSION_VISIBLE', 2);
95
define('SESSION_INVISIBLE', 3); // not available
96
define('SESSION_AVAILABLE', 4);
97
98
define('SESSION_LINK_TARGET', '_self');
99
100
define('SUBSCRIBE_ALLOWED', 1);
101
define('SUBSCRIBE_NOT_ALLOWED', 0);
102
define('UNSUBSCRIBE_ALLOWED', 1);
103
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
104
105
// SURVEY VISIBILITY CONSTANTS
106
define('SURVEY_VISIBLE_TUTOR', 0);
107
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
108
define('SURVEY_VISIBLE_PUBLIC', 2);
109
110
// CONSTANTS defining all tools, using the english version
111
/* When you add a new tool you must add it into function api_get_tools_lists() too */
112
define('TOOL_DOCUMENT', 'document');
113
define('TOOL_LP_FINAL_ITEM', 'final_item');
114
define('TOOL_READOUT_TEXT', 'readout_text');
115
define('TOOL_THUMBNAIL', 'thumbnail');
116
define('TOOL_HOTPOTATOES', 'hotpotatoes');
117
define('TOOL_CALENDAR_EVENT', 'calendar_event');
118
define('TOOL_LINK', 'link');
119
define('TOOL_LINK_CATEGORY', 'link_category');
120
define('TOOL_COURSE_DESCRIPTION', 'course_description');
121
define('TOOL_SEARCH', 'search');
122
define('TOOL_LEARNPATH', 'learnpath');
123
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
124
define('TOOL_AGENDA', 'agenda');
125
define('TOOL_ANNOUNCEMENT', 'announcement');
126
define('TOOL_FORUM', 'forum');
127
define('TOOL_FORUM_CATEGORY', 'forum_category');
128
define('TOOL_FORUM_THREAD', 'forum_thread');
129
define('TOOL_FORUM_POST', 'forum_post');
130
define('TOOL_FORUM_ATTACH', 'forum_attachment');
131
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
132
define('TOOL_THREAD', 'thread');
133
define('TOOL_POST', 'post');
134
define('TOOL_DROPBOX', 'dropbox');
135
define('TOOL_QUIZ', 'quiz');
136
define('TOOL_TEST_CATEGORY', 'test_category');
137
define('TOOL_USER', 'user');
138
define('TOOL_GROUP', 'group');
139
define('TOOL_BLOGS', 'blog_management');
140
define('TOOL_CHAT', 'chat');
141
define('TOOL_STUDENTPUBLICATION', 'student_publication');
142
define('TOOL_TRACKING', 'tracking');
143
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
144
define('TOOL_COURSE_SETTING', 'course_setting');
145
define('TOOL_BACKUP', 'backup');
146
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
147
define('TOOL_RECYCLE_COURSE', 'recycle_course');
148
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
149
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
150
define('TOOL_UPLOAD', 'file_upload');
151
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
152
define('TOOL_SURVEY', 'survey');
153
//define('TOOL_WIKI', 'wiki');
154
define('TOOL_GLOSSARY', 'glossary');
155
define('TOOL_GRADEBOOK', 'gradebook');
156
define('TOOL_NOTEBOOK', 'notebook');
157
define('TOOL_ATTENDANCE', 'attendance');
158
define('TOOL_COURSE_PROGRESS', 'course_progress');
159
define('TOOL_PORTFOLIO', 'portfolio');
160
define('TOOL_PLAGIARISM', 'compilatio');
161
define('TOOL_XAPI', 'xapi');
162
163
// CONSTANTS defining Chamilo interface sections
164
define('SECTION_CAMPUS', 'mycampus');
165
define('SECTION_COURSES', 'mycourses');
166
define('SECTION_CATALOG', 'catalog');
167
define('SECTION_MYPROFILE', 'myprofile');
168
define('SECTION_MYAGENDA', 'myagenda');
169
define('SECTION_COURSE_ADMIN', 'course_admin');
170
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
171
define('SECTION_MYGRADEBOOK', 'mygradebook');
172
define('SECTION_TRACKING', 'session_my_space');
173
define('SECTION_SOCIAL', 'social-network');
174
define('SECTION_DASHBOARD', 'dashboard');
175
define('SECTION_REPORTS', 'reports');
176
define('SECTION_GLOBAL', 'global');
177
define('SECTION_INCLUDE', 'include');
178
define('SECTION_CUSTOMPAGE', 'custompage');
179
180
// CONSTANT name for local authentication source
181
define('PLATFORM_AUTH_SOURCE', 'platform');
182
define('CAS_AUTH_SOURCE', 'cas');
183
define('LDAP_AUTH_SOURCE', 'extldap');
184
185
// event logs types
186
define('LOG_COURSE_DELETE', 'course_deleted');
187
define('LOG_COURSE_CREATE', 'course_created');
188
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
189
190
// @todo replace 'soc_gr' with social_group
191
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
192
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
193
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
194
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
195
196
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
197
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
198
199
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
200
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
201
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
202
203
define('LOG_MESSAGE_DATA', 'message_data');
204
define('LOG_MESSAGE_DELETE', 'msg_deleted');
205
206
define('LOG_USER_DELETE', 'user_deleted');
207
define('LOG_USER_PREDELETE', 'user_predeleted');
208
define('LOG_USER_CREATE', 'user_created');
209
define('LOG_USER_UPDATE', 'user_updated');
210
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
211
define('LOG_USER_ENABLE', 'user_enable');
212
define('LOG_USER_DISABLE', 'user_disable');
213
define('LOG_USER_ANONYMIZE', 'user_anonymized');
214
define('LOG_USER_FIELD_CREATE', 'user_field_created');
215
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
216
define('LOG_SESSION_CREATE', 'session_created');
217
define('LOG_SESSION_DELETE', 'session_deleted');
218
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
219
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
220
define('LOG_SESSION_ADD_USER', 'session_add_user');
221
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
222
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
223
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
224
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
225
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
226
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
227
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
228
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
229
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
230
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
231
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
232
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
233
define('LOG_PROMOTION_CREATE', 'promotion_created');
234
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
235
define('LOG_CAREER_CREATE', 'career_created');
236
define('LOG_CAREER_DELETE', 'career_deleted');
237
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
238
//define('LOG_WIKI_ACCESS', 'wiki_page_view');
239
// All results from an exercise
240
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
241
// Logs only the one attempt
242
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
243
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
244
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
245
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
246
247
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
248
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
249
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
250
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
251
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
252
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
253
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
254
255
// Event logs data types (max 20 chars)
256
define('LOG_COURSE_CODE', 'course_code');
257
define('LOG_COURSE_ID', 'course_id');
258
define('LOG_USER_ID', 'user_id');
259
define('LOG_USER_OBJECT', 'user_object');
260
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
261
define('LOG_SESSION_ID', 'session_id');
262
263
define('LOG_QUESTION_ID', 'question_id');
264
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
265
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
266
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
267
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
268
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
269
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
270
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
271
define('LOG_CAREER_ID', 'career_id');
272
define('LOG_PROMOTION_ID', 'promotion_id');
273
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
274
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
275
define('LOG_GRADEBOOK_ID', 'gradebook_id');
276
//define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
277
define('LOG_EXERCISE_ID', 'exercise_id');
278
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
279
define('LOG_LP_ID', 'lp_id');
280
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
281
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
282
283
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
284
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
285
define('LOG_WORK_DATA', 'work_data_array');
286
287
define('LOG_MY_FOLDER_PATH', 'path');
288
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
289
290
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
291
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
292
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
293
294
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
295
296
define('LOG_QUESTION_CREATED', 'question_created');
297
define('LOG_QUESTION_UPDATED', 'question_updated');
298
define('LOG_QUESTION_DELETED', 'question_deleted');
299
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
300
301
define('LOG_SURVEY_ID', 'survey_id');
302
define('LOG_SURVEY_CREATED', 'survey_created');
303
define('LOG_SURVEY_DELETED', 'survey_deleted');
304
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
305
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
306
307
//used when login_is_email setting is true
308
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
309
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
310
311
// This constant is a result of Windows OS detection, it has a boolean value:
312
// true whether the server runs on Windows OS, false otherwise.
313
define('IS_WINDOWS_OS', api_is_windows_os());
314
315
// Patterns for processing paths. Examples.
316
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
317
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
318
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
319
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
320
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
321
// basic (leaf elements)
322
define('REL_CODE_PATH', 'REL_CODE_PATH');
323
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
324
define('REL_HOME_PATH', 'REL_HOME_PATH');
325
326
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
327
define('WEB_PATH', 'WEB_PATH');
328
define('SYS_PATH', 'SYS_PATH');
329
define('SYMFONY_SYS_PATH', 'SYMFONY_SYS_PATH');
330
331
define('REL_PATH', 'REL_PATH');
332
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
333
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
334
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
335
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
336
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
337
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
338
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
339
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
340
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
341
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
342
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
343
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
344
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
345
define('LIBRARY_PATH', 'LIBRARY_PATH');
346
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
347
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
348
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
349
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
350
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
351
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
352
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
353
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
354
355
// Relations type with Course manager
356
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
357
358
// Relations type with Human resources manager
359
define('COURSE_RELATION_TYPE_RRHH', 1);
360
361
// User image sizes
362
define('USER_IMAGE_SIZE_ORIGINAL', 1);
363
define('USER_IMAGE_SIZE_BIG', 2);
364
define('USER_IMAGE_SIZE_MEDIUM', 3);
365
define('USER_IMAGE_SIZE_SMALL', 4);
366
367
// Gradebook link constants
368
// Please do not change existing values, they are used in the database !
369
define('GRADEBOOK_ITEM_LIMIT', 1000);
370
371
define('LINK_EXERCISE', 1);
372
define('LINK_DROPBOX', 2);
373
define('LINK_STUDENTPUBLICATION', 3);
374
define('LINK_LEARNPATH', 4);
375
define('LINK_FORUM_THREAD', 5);
376
//define('LINK_WORK',6);
377
define('LINK_ATTENDANCE', 7);
378
define('LINK_SURVEY', 8);
379
define('LINK_HOTPOTATOES', 9);
380
define('LINK_PORTFOLIO', 10);
381
382
// Score display types constants
383
define('SCORE_DIV', 1); // X / Y
384
define('SCORE_PERCENT', 2); // XX %
385
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
386
define('SCORE_AVERAGE', 4); // XX %
387
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
388
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
389
define('SCORE_SIMPLE', 7); // X
390
define('SCORE_IGNORE_SPLIT', 8); //  ??
391
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
392
define('SCORE_CUSTOM', 10); // Good!
393
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
394
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
395
define('SCORE_ONLY_SCORE', 13); // X - Good!
396
define('SCORE_NUMERIC', 14);
397
398
define('SCORE_BOTH', 1);
399
define('SCORE_ONLY_DEFAULT', 2);
400
define('SCORE_ONLY_CUSTOM', 3);
401
402
// From display.lib.php
403
404
define('MAX_LENGTH_BREADCRUMB', 100);
405
define('ICON_SIZE_ATOM', 8);
406
define('ICON_SIZE_TINY', 16);
407
define('ICON_SIZE_SMALL', 22);
408
define('ICON_SIZE_MEDIUM', 32);
409
define('ICON_SIZE_LARGE', 48);
410
define('ICON_SIZE_BIG', 64);
411
define('ICON_SIZE_HUGE', 128);
412
define('SHOW_TEXT_NEAR_ICONS', false);
413
414
// Session catalog
415
define('CATALOG_COURSES', 0);
416
define('CATALOG_SESSIONS', 1);
417
define('CATALOG_COURSES_SESSIONS', 2);
418
419
// Hook type events, pre-process and post-process.
420
// All means to be executed for both hook event types
421
define('HOOK_EVENT_TYPE_PRE', 0);
422
define('HOOK_EVENT_TYPE_POST', 1);
423
define('HOOK_EVENT_TYPE_ALL', 10);
424
425
// Group permissions
426
define('GROUP_PERMISSION_OPEN', '1');
427
define('GROUP_PERMISSION_CLOSED', '2');
428
429
// Group user permissions
430
define('GROUP_USER_PERMISSION_ADMIN', 1); // the admin of a group
431
define('GROUP_USER_PERMISSION_READER', 2); // a normal user
432
define('GROUP_USER_PERMISSION_PENDING_INVITATION', 3); // When an admin/moderator invites a user
433
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', 4); // an user joins a group
434
define('GROUP_USER_PERMISSION_MODERATOR', 5); // a moderator
435
define('GROUP_USER_PERMISSION_ANONYMOUS', 6); // an anonymous user
436
define('GROUP_USER_PERMISSION_HRM', 7); // a human resources manager
437
438
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
439
define('GROUP_IMAGE_SIZE_BIG', 2);
440
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
441
define('GROUP_IMAGE_SIZE_SMALL', 4);
442
define('GROUP_TITLE_LENGTH', 50);
443
444
// Exercise
445
// @todo move into a class
446
define('ALL_ON_ONE_PAGE', 1);
447
define('ONE_PER_PAGE', 2);
448
449
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
450
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
451
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
452
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
453
454
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
455
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
456
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
457
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
458
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
459
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
460
define('RESULT_DISABLE_RANKING', 6);
461
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
462
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
463
define('RESULT_DISABLE_RADAR', 9);
464
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
465
466
define('EXERCISE_MAX_NAME_SIZE', 80);
467
468
// Question types (edit next array as well when adding values)
469
// @todo move into a class
470
define('UNIQUE_ANSWER', 1);
471
define('MULTIPLE_ANSWER', 2);
472
define('FILL_IN_BLANKS', 3);
473
define('MATCHING', 4);
474
define('FREE_ANSWER', 5);
475
define('HOT_SPOT', 6);
476
define('HOT_SPOT_ORDER', 7);
477
define('HOT_SPOT_DELINEATION', 8);
478
define('MULTIPLE_ANSWER_COMBINATION', 9);
479
define('UNIQUE_ANSWER_NO_OPTION', 10);
480
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
481
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
482
define('ORAL_EXPRESSION', 13);
483
define('GLOBAL_MULTIPLE_ANSWER', 14);
484
define('MEDIA_QUESTION', 15);
485
define('CALCULATED_ANSWER', 16);
486
define('UNIQUE_ANSWER_IMAGE', 17);
487
define('DRAGGABLE', 18);
488
define('MATCHING_DRAGGABLE', 19);
489
define('ANNOTATION', 20);
490
define('READING_COMPREHENSION', 21);
491
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
492
493
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
494
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
495
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
496
497
// Question selection type
498
define('EX_Q_SELECTION_ORDERED', 1);
499
define('EX_Q_SELECTION_RANDOM', 2);
500
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
501
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
502
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
503
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
504
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
505
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
506
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
507
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
508
509
// Used to save the skill_rel_item table
510
define('ITEM_TYPE_EXERCISE', 1);
511
define('ITEM_TYPE_HOTPOTATOES', 2);
512
define('ITEM_TYPE_LINK', 3);
513
define('ITEM_TYPE_LEARNPATH', 4);
514
define('ITEM_TYPE_GRADEBOOK', 5);
515
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
516
//define('ITEM_TYPE_FORUM', 7);
517
define('ITEM_TYPE_ATTENDANCE', 8);
518
define('ITEM_TYPE_SURVEY', 9);
519
define('ITEM_TYPE_FORUM_THREAD', 10);
520
define('ITEM_TYPE_PORTFOLIO', 11);
521
522
// Course description blocks.
523
define('ADD_BLOCK', 8);
524
525
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
526
define(
527
    'QUESTION_TYPES',
528
    UNIQUE_ANSWER.':'.
529
    MULTIPLE_ANSWER.':'.
530
    FILL_IN_BLANKS.':'.
531
    MATCHING.':'.
532
    FREE_ANSWER.':'.
533
    HOT_SPOT.':'.
534
    HOT_SPOT_ORDER.':'.
535
    HOT_SPOT_DELINEATION.':'.
536
    MULTIPLE_ANSWER_COMBINATION.':'.
537
    UNIQUE_ANSWER_NO_OPTION.':'.
538
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
539
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
540
    ORAL_EXPRESSION.':'.
541
    GLOBAL_MULTIPLE_ANSWER.':'.
542
    MEDIA_QUESTION.':'.
543
    CALCULATED_ANSWER.':'.
544
    UNIQUE_ANSWER_IMAGE.':'.
545
    DRAGGABLE.':'.
546
    MATCHING_DRAGGABLE.':'.
547
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
548
    ANNOTATION
549
);
550
551
//Some alias used in the QTI exports
552
define('MCUA', 1);
553
define('TF', 1);
554
define('MCMA', 2);
555
define('FIB', 3);
556
557
// Message
558
define('MESSAGE_STATUS_INVITATION_PENDING', 5);
559
define('MESSAGE_STATUS_INVITATION_ACCEPTED', 6);
560
define('MESSAGE_STATUS_INVITATION_DENIED', 7);
561
define('MESSAGE_STATUS_WALL', 8);
562
563
define('MESSAGE_STATUS_WALL_DELETE', 9);
564
define('MESSAGE_STATUS_WALL_POST', 10);
565
566
define('MESSAGE_STATUS_FORUM', 12);
567
define('MESSAGE_STATUS_PROMOTED', 13);
568
569
// Images
570
define('IMAGE_WALL_SMALL_SIZE', 200);
571
define('IMAGE_WALL_MEDIUM_SIZE', 500);
572
define('IMAGE_WALL_BIG_SIZE', 2000);
573
define('IMAGE_WALL_SMALL', 'small');
574
define('IMAGE_WALL_MEDIUM', 'medium');
575
define('IMAGE_WALL_BIG', 'big');
576
577
// Social PLUGIN PLACES
578
define('SOCIAL_LEFT_PLUGIN', 1);
579
define('SOCIAL_CENTER_PLUGIN', 2);
580
define('SOCIAL_RIGHT_PLUGIN', 3);
581
define('CUT_GROUP_NAME', 50);
582
583
/**
584
 * FormValidator Filter.
585
 */
586
define('NO_HTML', 1);
587
define('STUDENT_HTML', 2);
588
define('TEACHER_HTML', 3);
589
define('STUDENT_HTML_FULLPAGE', 4);
590
define('TEACHER_HTML_FULLPAGE', 5);
591
592
// Timeline
593
define('TIMELINE_STATUS_ACTIVE', '1');
594
define('TIMELINE_STATUS_INACTIVE', '2');
595
596
// Event email template class
597
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
598
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
599
600
// Course home
601
define('SHORTCUTS_HORIZONTAL', 0);
602
define('SHORTCUTS_VERTICAL', 1);
603
604
// Course copy
605
define('FILE_SKIP', 1);
606
define('FILE_RENAME', 2);
607
define('FILE_OVERWRITE', 3);
608
define('UTF8_CONVERT', false); //false by default
609
610
define('DOCUMENT', 'file');
611
define('FOLDER', 'folder');
612
613
define('RESOURCE_ASSET', 'asset');
614
define('RESOURCE_DOCUMENT', 'document');
615
define('RESOURCE_GLOSSARY', 'glossary');
616
define('RESOURCE_EVENT', 'calendar_event');
617
define('RESOURCE_LINK', 'link');
618
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
619
define('RESOURCE_LEARNPATH', 'learnpath');
620
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
621
define('RESOURCE_ANNOUNCEMENT', 'announcement');
622
define('RESOURCE_FORUM', 'forum');
623
define('RESOURCE_FORUMTOPIC', 'thread');
624
define('RESOURCE_FORUMPOST', 'post');
625
define('RESOURCE_QUIZ', 'quiz');
626
define('RESOURCE_TEST_CATEGORY', 'test_category');
627
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
628
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
629
define('RESOURCE_LINKCATEGORY', 'Link_Category');
630
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
631
define('RESOURCE_SCORM', 'Scorm');
632
define('RESOURCE_SURVEY', 'survey');
633
define('RESOURCE_SURVEYQUESTION', 'survey_question');
634
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
635
//define('RESOURCE_WIKI', 'wiki');
636
define('RESOURCE_THEMATIC', 'thematic');
637
define('RESOURCE_ATTENDANCE', 'attendance');
638
define('RESOURCE_WORK', 'work');
639
define('RESOURCE_SESSION_COURSE', 'session_course');
640
define('RESOURCE_GRADEBOOK', 'gradebook');
641
define('ADD_THEMATIC_PLAN', 6);
642
643
// Max online users to show per page (whoisonline)
644
define('MAX_ONLINE_USERS', 12);
645
646
define('TOOL_AUTHORING', 'toolauthoring');
647
define('TOOL_INTERACTION', 'toolinteraction');
648
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
649
define('TOOL_ADMIN', 'tooladmin');
650
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
651
define('TOOL_DRH', 'tool_drh');
652
define('TOOL_STUDENT_VIEW', 'toolstudentview');
653
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
654
655
// Search settings (from main/inc/lib/search/IndexableChunk.class.php )
656
// some constants to avoid serialize string keys on serialized data array
657
define('SE_COURSE_ID', 0);
658
define('SE_TOOL_ID', 1);
659
define('SE_DATA', 2);
660
define('SE_USER', 3);
661
662
// in some cases we need top differenciate xapian documents of the same tool
663
define('SE_DOCTYPE_EXERCISE_EXERCISE', 0);
664
define('SE_DOCTYPE_EXERCISE_QUESTION', 1);
665
666
// xapian prefixes
667
define('XAPIAN_PREFIX_COURSEID', 'C');
668
define('XAPIAN_PREFIX_TOOLID', 'O');
669
670
// User active field constants
671
define('USER_ACTIVE', 1);
672
define('USER_INACTIVE', 0);
673
define('USER_INACTIVE_AUTOMATIC', -1);
674
define('USER_SOFT_DELETED', -2);
675
676
/**
677
 * Returns a path to a certain resource within Chamilo.
678
 *
679
 * @param string $path A path which type is to be converted. Also, it may be a defined constant for a path.
680
 *
681
 * @return string the requested path or the converted path
682
 *
683
 * Notes about the current behaviour model:
684
 * 1. Windows back-slashes are converted to slashes in the result.
685
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
686
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
687
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
688
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
689
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
690
 * It has not been identified as needed yet.
691
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
692
 *
693
 * Vchamilo changes : allow using an alternate configuration
694
 * to get vchamilo  instance paths
695
 */
696
function api_get_path($path = '', $configuration = [])
697
{
698
    global $paths;
699
700
    // get proper configuration data if exists
701
    global $_configuration;
702
703
    $emptyConfigurationParam = false;
704
    if (empty($configuration)) {
705
        $configuration = (array) $_configuration;
706
        $emptyConfigurationParam = true;
707
    }
708
709
    $root_sys = Container::getProjectDir();
710
    $root_web = '';
711
    if (isset(Container::$container)) {
712
        $root_web = Container::$container->get('router')->generate(
713
            'index',
714
            [],
715
            UrlGeneratorInterface::ABSOLUTE_URL
716
        );
717
    }
718
719
    /*if (api_get_multiple_access_url()) {
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
        // deprecated settings
2703
        // no break
2704
        case 'openid_authentication':
2705
        case 'service_ppt2lp':
2706
        case 'formLogin_hide_unhide_label':
2707
            return false;
2708
            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...
2709
        case 'tool_visible_by_default_at_creation':
2710
            $values = $settingsManager->getSetting($variable);
2711
            $newResult = [];
2712
            foreach ($values as $parameter) {
2713
                $newResult[$parameter] = 'true';
2714
            }
2715
2716
            return $newResult;
2717
            break;
2718
        default:
2719
            $settingValue = $settingsManager->getSetting($variable, true);
2720
            if (is_string($settingValue) && $isArray && !empty($settingValue)) {
2721
                // Check if the value is a valid JSON string
2722
                $decodedValue = json_decode($settingValue, true);
2723
2724
                // If it's a valid JSON string and the result is an array, return it
2725
                if (is_array($decodedValue)) {
2726
                    return $decodedValue;
2727
                }
2728
2729
                // If it's not an array, continue with the normal flow
2730
                // Optional: If you need to evaluate the value using eval
2731
                $strArrayValue = rtrim($settingValue, ';');
2732
                $value = eval("return $strArrayValue;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2733
                if (is_array($value)) {
2734
                    return $value;
2735
                }
2736
            }
2737
2738
            // If the value is not a JSON array or wasn't returned previously, continue with the normal flow
2739
            if (!empty($key) && isset($settingValue[$variable][$key])) {
2740
                return $settingValue[$variable][$key];
2741
            }
2742
2743
            return $settingValue;
2744
            break;
2745
    }
2746
}
2747
2748
/**
2749
 * @param string $variable
2750
 * @param string $option
2751
 *
2752
 * @return bool
2753
 */
2754
function api_get_setting_in_list($variable, $option)
2755
{
2756
    $value = api_get_setting($variable);
2757
2758
    return in_array($option, $value);
2759
}
2760
2761
/**
2762
 * @param string $plugin
2763
 * @param string $variable
2764
 *
2765
 * @return string
2766
 */
2767
function api_get_plugin_setting($plugin, $variable)
2768
{
2769
    $variableName = $plugin.'_'.$variable;
2770
    //$result = api_get_setting($variableName);
2771
    $params = [
2772
        'category = ? AND subkey = ? AND variable = ?' => [
2773
            'Plugins',
2774
            $plugin,
2775
            $variableName,
2776
        ],
2777
    ];
2778
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2779
    $result = Database::select(
2780
        'selected_value',
2781
        $table,
2782
        ['where' => $params],
2783
        'one'
2784
    );
2785
    if ($result) {
2786
        $value = $result['selected_value'];
2787
        $serializedValue = @unserialize($result['selected_value'], []);
2788
        if (false !== $serializedValue) {
2789
            $value = $serializedValue;
2790
        }
2791
2792
        return $value;
2793
    }
2794
2795
    return null;
2796
    /// Old code
2797
2798
    $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...
2799
    $result = api_get_setting($variableName);
2800
2801
    if (isset($result[$plugin])) {
2802
        $value = $result[$plugin];
2803
2804
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
2805
2806
        if (false !== $unserialized) {
2807
            $value = $unserialized;
2808
        }
2809
2810
        return $value;
2811
    }
2812
2813
    return null;
2814
}
2815
2816
/**
2817
 * Returns the value of a setting from the web-adjustable admin config settings.
2818
 */
2819
function api_get_settings_params($params)
2820
{
2821
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2822
2823
    return Database::select('*', $table, ['where' => $params]);
2824
}
2825
2826
/**
2827
 * @param array $params example: [id = ? => '1']
2828
 *
2829
 * @return array
2830
 */
2831
function api_get_settings_params_simple($params)
2832
{
2833
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2834
2835
    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...
2836
}
2837
2838
/**
2839
 * Returns the value of a setting from the web-adjustable admin config settings.
2840
 */
2841
function api_delete_settings_params($params)
2842
{
2843
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS);
2844
2845
    return Database::delete($table, $params);
2846
}
2847
2848
/**
2849
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
2850
 *
2851
 * @return string Escaped version of $_SERVER['PHP_SELF']
2852
 */
2853
function api_get_self()
2854
{
2855
    return htmlentities($_SERVER['PHP_SELF']);
2856
}
2857
2858
/**
2859
 * Checks whether current user is a platform administrator.
2860
 *
2861
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
2862
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
2863
 *
2864
 * @return bool true if the user has platform admin rights,
2865
 *              false otherwise
2866
 *
2867
 * @see usermanager::is_admin(user_id) for a user-id specific function
2868
 */
2869
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
2870
{
2871
    $currentUser = api_get_current_user();
2872
2873
    if (null === $currentUser) {
2874
        return false;
2875
    }
2876
2877
    $isAdmin = $currentUser->hasRole('ROLE_ADMIN') || $currentUser->hasRole('ROLE_SUPER_ADMIN');
2878
2879
    if ($isAdmin) {
2880
        return true;
2881
    }
2882
2883
    if ($allowSessionAdmins && $currentUser->hasRole('ROLE_SESSION_MANAGER')) {
2884
        return true;
2885
    }
2886
2887
    if ($allowDrh && $currentUser->hasRole('ROLE_HR')) {
2888
        return true;
2889
    }
2890
2891
    return false;
2892
}
2893
2894
/**
2895
 * Checks whether the user given as user id is in the admin table.
2896
 *
2897
 * @param int $user_id If none provided, will use current user
2898
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
2899
 *
2900
 * @return bool True if the user is admin, false otherwise
2901
 */
2902
function api_is_platform_admin_by_id($user_id = null, $url = null)
2903
{
2904
    $user_id = (int) $user_id;
2905
    if (empty($user_id)) {
2906
        $user_id = api_get_user_id();
2907
    }
2908
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
2909
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
2910
    $res = Database::query($sql);
2911
    $is_admin = 1 === Database::num_rows($res);
2912
    if (!$is_admin || !isset($url)) {
2913
        return $is_admin;
2914
    }
2915
    // We get here only if $url is set
2916
    $url = (int) $url;
2917
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2918
    $sql = "SELECT * FROM $url_user_table
2919
            WHERE access_url_id = $url AND user_id = $user_id";
2920
    $res = Database::query($sql);
2921
2922
    return 1 === Database::num_rows($res);
2923
}
2924
2925
/**
2926
 * Checks whether current user is allowed to create courses.
2927
 *
2928
 * @return bool true if the user has course creation rights,
2929
 *              false otherwise
2930
 */
2931
function api_is_allowed_to_create_course()
2932
{
2933
    if (api_is_platform_admin()) {
2934
        return true;
2935
    }
2936
2937
    // Teachers can only create courses
2938
    if (api_is_teacher()) {
2939
        if ('true' === api_get_setting('allow_users_to_create_courses')) {
2940
            return true;
2941
        } else {
2942
            return false;
2943
        }
2944
    }
2945
2946
    return Session::read('is_allowedCreateCourse');
2947
}
2948
2949
/**
2950
 * Checks whether the current user is a course administrator.
2951
 *
2952
 * @return bool True if current user is a course administrator
2953
 */
2954
function api_is_course_admin()
2955
{
2956
    if (api_is_platform_admin()) {
2957
        return true;
2958
    }
2959
2960
    $user = api_get_current_user();
2961
    if ($user) {
2962
        if (
2963
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
2964
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
2965
        ) {
2966
            return true;
2967
        }
2968
    }
2969
2970
    return false;
2971
}
2972
2973
/**
2974
 * Checks whether the current user is a course coach
2975
 * Based on the presence of user in session_rel_user.relation_type (as session general coach, value 3).
2976
 *
2977
 * @return bool True if current user is a course coach
2978
 */
2979
function api_is_session_general_coach()
2980
{
2981
    return Session::read('is_session_general_coach');
2982
}
2983
2984
/**
2985
 * Checks whether the current user is a course tutor
2986
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
2987
 *
2988
 * @return bool True if current user is a course tutor
2989
 */
2990
function api_is_course_tutor()
2991
{
2992
    return Session::read('is_courseTutor');
2993
}
2994
2995
/**
2996
 * @param int $user_id
2997
 * @param int $courseId
2998
 * @param int $session_id
2999
 *
3000
 * @return bool
3001
 */
3002
function api_is_course_session_coach($user_id, $courseId, $session_id)
3003
{
3004
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3005
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3006
3007
    $user_id = (int) $user_id;
3008
    $session_id = (int) $session_id;
3009
    $courseId = (int) $courseId;
3010
3011
    $sql = "SELECT DISTINCT session.id
3012
            FROM $session_table
3013
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3014
            ON session.id = session_rc_ru.session_id
3015
            WHERE
3016
                session_rc_ru.user_id = '".$user_id."'  AND
3017
                session_rc_ru.c_id = '$courseId' AND
3018
                session_rc_ru.status = ".SessionEntity::COURSE_COACH." AND
3019
                session_rc_ru.session_id = '$session_id'";
3020
    $result = Database::query($sql);
3021
3022
    return Database::num_rows($result) > 0;
3023
}
3024
3025
/**
3026
 * Checks whether the current user is a course or session coach.
3027
 *
3028
 * @param int $session_id
3029
 * @param int $courseId
3030
 * @param bool  Check whether we are in student view and, if we are, return false
3031
 * @param int $userId
3032
 *
3033
 * @return bool True if current user is a course or session coach
3034
 */
3035
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3036
{
3037
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3038
3039
    if (!empty($session_id)) {
3040
        $session_id = (int) $session_id;
3041
    } else {
3042
        $session_id = api_get_session_id();
3043
    }
3044
3045
    // The student preview was on
3046
    if ($check_student_view && api_is_student_view_active()) {
3047
        return false;
3048
    }
3049
3050
    if (!empty($courseId)) {
3051
        $courseId = (int) $courseId;
3052
    } else {
3053
        $courseId = api_get_course_int_id();
3054
    }
3055
3056
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3057
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3058
    $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3059
    $sessionIsCoach = [];
3060
3061
    if (!empty($courseId)) {
3062
        $sql = "SELECT DISTINCT s.id, title, access_start_date, access_end_date
3063
                FROM $session_table s
3064
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3065
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3066
                WHERE
3067
                    session_rc_ru.c_id = '$courseId' AND
3068
                    session_rc_ru.status =".SessionEntity::COURSE_COACH." AND
3069
                    session_rc_ru.session_id = '$session_id'";
3070
        $result = Database::query($sql);
3071
        $sessionIsCoach = Database::store_result($result);
3072
    }
3073
3074
    if (!empty($session_id)) {
3075
        $sql = "SELECT DISTINCT s.id
3076
                FROM $session_table AS s
3077
                INNER JOIN $tblSessionRelUser sru
3078
                ON s.id = sru.session_id
3079
                WHERE
3080
                    sru.user_id = $userId AND
3081
                    s.id = $session_id AND
3082
                    sru.relation_type = ".SessionEntity::GENERAL_COACH."
3083
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3084
        $result = Database::query($sql);
3085
        if (!empty($sessionIsCoach)) {
3086
            $sessionIsCoach = array_merge(
3087
                $sessionIsCoach,
3088
                Database::store_result($result)
3089
            );
3090
        } else {
3091
            $sessionIsCoach = Database::store_result($result);
3092
        }
3093
    }
3094
3095
    return count($sessionIsCoach) > 0;
3096
}
3097
3098
function api_user_has_role(string $role, ?User $user = null): bool
3099
{
3100
    if (null === $user) {
3101
        $user = api_get_current_user();
3102
    }
3103
3104
    if (null === $user) {
3105
        return false;
3106
    }
3107
3108
    return $user->hasRole($role);
3109
}
3110
3111
function api_is_allowed_in_course(): bool
3112
{
3113
    if (api_is_platform_admin()) {
3114
        return true;
3115
    }
3116
3117
    $user = api_get_current_user();
3118
    if ($user instanceof User) {
3119
        if ($user->hasRole('ROLE_CURRENT_COURSE_SESSION_STUDENT') ||
3120
            $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') ||
3121
            $user->hasRole('ROLE_CURRENT_COURSE_STUDENT') ||
3122
            $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')
3123
        ) {
3124
            return true;
3125
        }
3126
    }
3127
3128
    return false;
3129
}
3130
3131
/**
3132
 * Checks whether current user is a student boss.
3133
 */
3134
function api_is_student_boss(?User $user = null): bool
3135
{
3136
    return api_user_has_role('ROLE_STUDENT_BOSS', $user);
3137
}
3138
3139
/**
3140
 * Checks whether the current user is a session administrator.
3141
 *
3142
 * @return bool True if current user is a course administrator
3143
 */
3144
function api_is_session_admin(?User $user = null)
3145
{
3146
    return api_user_has_role('ROLE_SESSION_MANAGER', $user);
3147
}
3148
3149
/**
3150
 * Checks whether the current user is a human resources manager.
3151
 *
3152
 * @return bool True if current user is a human resources manager
3153
 */
3154
function api_is_drh()
3155
{
3156
    return api_user_has_role('ROLE_HR');
3157
}
3158
3159
/**
3160
 * Checks whether the current user is a student.
3161
 *
3162
 * @return bool True if current user is a human resources manager
3163
 */
3164
function api_is_student()
3165
{
3166
    return api_user_has_role('ROLE_STUDENT');
3167
}
3168
3169
/**
3170
 * Checks whether the current user has the status 'teacher'.
3171
 *
3172
 * @return bool True if current user is a human resources manager
3173
 */
3174
function api_is_teacher()
3175
{
3176
    return api_user_has_role('ROLE_TEACHER');
3177
}
3178
3179
/**
3180
 * Checks whether the current user is a invited user.
3181
 *
3182
 * @return bool
3183
 */
3184
function api_is_invitee()
3185
{
3186
    return api_user_has_role('ROLE_INVITEE');
3187
}
3188
3189
/**
3190
 * This function checks whether a session is assigned into a category.
3191
 *
3192
 * @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...
3193
 * @param string    - category name
3194
 *
3195
 * @return bool - true if is found, otherwise false
3196
 */
3197
function api_is_session_in_category($session_id, $category_name)
3198
{
3199
    $session_id = (int) $session_id;
3200
    $category_name = Database::escape_string($category_name);
3201
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3202
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3203
3204
    $sql = "SELECT 1
3205
            FROM $tbl_session
3206
            WHERE $session_id IN (
3207
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3208
                WHERE
3209
                  s.session_category_id = sc.id AND
3210
                  sc.name LIKE '%$category_name'
3211
            )";
3212
    $rs = Database::query($sql);
3213
3214
    if (Database::num_rows($rs) > 0) {
3215
        return true;
3216
    }
3217
3218
    return false;
3219
}
3220
3221
/**
3222
 * Displays options for switching between student view and course manager view.
3223
 *
3224
 * Changes in version 1.2 (Patrick Cool)
3225
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3226
 * is changed explicitly
3227
 *
3228
 * Changes in version 1.1 (Patrick Cool)
3229
 * student view now works correctly in subfolders of the document tool
3230
 * student view works correctly in the new links tool
3231
 *
3232
 * Example code for using this in your tools:
3233
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3234
 * //   display_tool_view_option($isStudentView);
3235
 * //}
3236
 * //and in later sections, use api_is_allowed_to_edit()
3237
 *
3238
 * @author Roan Embrechts
3239
 * @author Patrick Cool
3240
 * @author Julio Montoya, changes added in Chamilo
3241
 *
3242
 * @version 1.2
3243
 *
3244
 * @todo rewrite code so it is easier to understand
3245
 */
3246
function api_display_tool_view_option()
3247
{
3248
    if ('true' != api_get_setting('student_view_enabled')) {
3249
        return '';
3250
    }
3251
3252
    $sourceurl = '';
3253
    $is_framed = false;
3254
    // Exceptions apply for all multi-frames pages
3255
    if (false !== strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php')) {
3256
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3257
        return '';
3258
    }
3259
3260
    // Uncomment to remove student view link from document view page
3261
    if (false !== strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php')) {
3262
        if (empty($_GET['lp_id'])) {
3263
            return '';
3264
        }
3265
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3266
        $sourceurl = str_replace(
3267
            'lp/lp_header.php',
3268
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.('studentview' == $_SESSION['studentview'] ? 'false' : 'true'),
3269
            $sourceurl
3270
        );
3271
        //showinframes doesn't handle student view anyway...
3272
        //return '';
3273
        $is_framed = true;
3274
    }
3275
3276
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3277
    if (!$is_framed) {
3278
        if (false === strpos($_SERVER['REQUEST_URI'], '?')) {
3279
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3280
        } else {
3281
            $sourceurl = $_SERVER['REQUEST_URI'];
3282
        }
3283
    }
3284
3285
    $output_string = '';
3286
    if (!empty($_SESSION['studentview'])) {
3287
        if ('studentview' == $_SESSION['studentview']) {
3288
            // We have to remove the isStudentView=true from the $sourceurl
3289
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3290
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3291
            $output_string .= '<a class="btn btn--primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3292
                Display::getMdiIcon('eye').' '.get_lang('Switch to teacher view').'</a>';
3293
        } elseif ('teacherview' == $_SESSION['studentview']) {
3294
            // Switching to teacherview
3295
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3296
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3297
            $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3298
                Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3299
        }
3300
    } else {
3301
        $output_string .= '<a class="btn btn--plain btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3302
            Display::getMdiIcon('eye').' '.get_lang('Switch to student view').'</a>';
3303
    }
3304
    $output_string = Security::remove_XSS($output_string);
3305
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3306
3307
    return $html;
3308
}
3309
3310
/**
3311
 * Function that removes the need to directly use is_courseAdmin global in
3312
 * tool scripts. It returns true or false depending on the user's rights in
3313
 * this particular course.
3314
 * Optionally checking for tutor and coach roles here allows us to use the
3315
 * student_view feature altogether with these roles as well.
3316
 *
3317
 * @param bool  Whether to check if the user has the tutor role
3318
 * @param bool  Whether to check if the user has the coach role
3319
 * @param bool  Whether to check if the user has the session coach role
3320
 * @param bool  check the student view or not
3321
 *
3322
 * @author Roan Embrechts
3323
 * @author Patrick Cool
3324
 * @author Julio Montoya
3325
 *
3326
 * @version 1.1, February 2004
3327
 *
3328
 * @return bool true: the user has the rights to edit, false: he does not
3329
 */
3330
function api_is_allowed_to_edit(
3331
    $tutor = false,
3332
    $coach = false,
3333
    $session_coach = false,
3334
    $check_student_view = true
3335
) {
3336
    $allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
3337
    // Admins can edit anything.
3338
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3339
        //The student preview was on
3340
        if ($check_student_view && api_is_student_view_active()) {
3341
            return false;
3342
        }
3343
3344
        return true;
3345
    }
3346
3347
    $sessionId = api_get_session_id();
3348
3349
    if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
3350
        $efv = new ExtraFieldValue('course');
3351
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3352
            api_get_course_int_id(),
3353
            'session_courses_read_only_mode'
3354
        );
3355
3356
        if (!empty($lockExrafieldField['value'])) {
3357
            return false;
3358
        }
3359
    }
3360
3361
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3362
    $session_visibility = api_get_session_visibility($sessionId);
3363
    $is_courseAdmin = api_is_course_admin();
3364
3365
    if (!$is_courseAdmin && $tutor) {
3366
        // If we also want to check if the user is a tutor...
3367
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3368
    }
3369
3370
    if (!$is_courseAdmin && $coach) {
3371
        // If we also want to check if the user is a coach...';
3372
        // Check if session visibility is read only for coaches.
3373
        if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3374
            $is_allowed_coach_to_edit = false;
3375
        }
3376
3377
        if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3378
            // Check if coach is allowed to edit a course.
3379
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3380
        }
3381
    }
3382
3383
    if (!$is_courseAdmin && $session_coach) {
3384
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3385
    }
3386
3387
    // Check if the student_view is enabled, and if so, if it is activated.
3388
    if ('true' === api_get_setting('student_view_enabled')) {
3389
        $studentView = api_is_student_view_active();
3390
        if (!empty($sessionId)) {
3391
            // Check if session visibility is read only for coaches.
3392
            if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
3393
                $is_allowed_coach_to_edit = false;
3394
            }
3395
3396
            $is_allowed = false;
3397
            if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
3398
                // Check if coach is allowed to edit a course.
3399
                $is_allowed = $is_allowed_coach_to_edit;
3400
            }
3401
            if ($check_student_view) {
3402
                $is_allowed = $is_allowed && false === $studentView;
3403
            }
3404
        } else {
3405
            $is_allowed = $is_courseAdmin;
3406
            if ($check_student_view) {
3407
                $is_allowed = $is_courseAdmin && false === $studentView;
3408
            }
3409
        }
3410
3411
        return $is_allowed;
3412
    } else {
3413
        return $is_courseAdmin;
3414
    }
3415
}
3416
3417
/**
3418
 * Returns true if user is a course coach of at least one course in session.
3419
 *
3420
 * @param int $sessionId
3421
 *
3422
 * @return bool
3423
 */
3424
function api_is_coach_of_course_in_session($sessionId)
3425
{
3426
    if (api_is_platform_admin()) {
3427
        return true;
3428
    }
3429
3430
    $userId = api_get_user_id();
3431
    $courseList = UserManager::get_courses_list_by_session(
3432
        $userId,
3433
        $sessionId
3434
    );
3435
3436
    // Session visibility.
3437
    $visibility = api_get_session_visibility(
3438
        $sessionId,
3439
        null,
3440
        false
3441
    );
3442
3443
    if (SESSION_VISIBLE != $visibility && !empty($courseList)) {
3444
        // Course Coach session visibility.
3445
        $blockedCourseCount = 0;
3446
        $closedVisibilityList = [
3447
            COURSE_VISIBILITY_CLOSED,
3448
            COURSE_VISIBILITY_HIDDEN,
3449
        ];
3450
3451
        foreach ($courseList as $course) {
3452
            // Checking session visibility
3453
            $sessionCourseVisibility = api_get_session_visibility(
3454
                $sessionId,
3455
                $course['real_id']
3456
            );
3457
3458
            $courseIsVisible = !in_array(
3459
                $course['visibility'],
3460
                $closedVisibilityList
3461
            );
3462
            if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3463
                $blockedCourseCount++;
3464
            }
3465
        }
3466
3467
        // If all courses are blocked then no show in the list.
3468
        if ($blockedCourseCount === count($courseList)) {
3469
            $visibility = SESSION_INVISIBLE;
3470
        } else {
3471
            $visibility = SESSION_VISIBLE;
3472
        }
3473
    }
3474
3475
    switch ($visibility) {
3476
        case SESSION_VISIBLE_READ_ONLY:
3477
        case SESSION_VISIBLE:
3478
        case SESSION_AVAILABLE:
3479
            return true;
3480
            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...
3481
        case SESSION_INVISIBLE:
3482
            return false;
3483
    }
3484
3485
    return false;
3486
}
3487
3488
/**
3489
 * Checks if a student can edit contents in a session depending
3490
 * on the session visibility.
3491
 *
3492
 * @param bool $tutor Whether to check if the user has the tutor role
3493
 * @param bool $coach Whether to check if the user has the coach role
3494
 *
3495
 * @return bool true: the user has the rights to edit, false: he does not
3496
 */
3497
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3498
{
3499
    if (api_is_allowed_to_edit($tutor, $coach)) {
3500
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3501
        return true;
3502
    } else {
3503
        $sessionId = api_get_session_id();
3504
3505
        if (0 == $sessionId) {
3506
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3507
            return true;
3508
        } else {
3509
            // I'm in a session and I'm a student
3510
            // Get the session visibility
3511
            $session_visibility = api_get_session_visibility($sessionId);
3512
            // if 5 the session is still available
3513
            switch ($session_visibility) {
3514
                case SESSION_VISIBLE_READ_ONLY: // 1
3515
                    return false;
3516
                case SESSION_VISIBLE:           // 2
3517
                    return true;
3518
                case SESSION_INVISIBLE:         // 3
3519
                    return false;
3520
                case SESSION_AVAILABLE:         //5
3521
                    return true;
3522
            }
3523
        }
3524
    }
3525
3526
    return false;
3527
}
3528
3529
/**
3530
 * Current user is anon?
3531
 *
3532
 * @return bool true if this user is anonymous, false otherwise
3533
 */
3534
function api_is_anonymous()
3535
{
3536
    return !Container::getAuthorizationChecker()->isGranted('IS_AUTHENTICATED');
3537
}
3538
3539
/**
3540
 * Displays message "You are not allowed here..." and exits the entire script.
3541
 *
3542
 * @param bool $print_headers Whether to print headers (default = false -> does not print them)
3543
 * @param string $message
3544
 * @param int $responseCode
3545
 *
3546
 * @throws Exception
3547
 */
3548
function api_not_allowed(
3549
    $print_headers = false,
3550
    $message = null,
3551
    $responseCode = 0
3552
): never {
3553
    throw new NotAllowedException($message ?: 'You are not allowed', null, $responseCode);
3554
}
3555
3556
/**
3557
 * @param string $languageIsoCode
3558
 *
3559
 * @return string
3560
 */
3561
function languageToCountryIsoCode($languageIsoCode)
3562
{
3563
    $allow = ('true' === api_get_setting('language.language_flags_by_country'));
3564
3565
    // @todo save in DB
3566
    switch ($languageIsoCode) {
3567
        case 'ar':
3568
            $country = 'ae';
3569
            break;
3570
        case 'bs':
3571
            $country = 'ba';
3572
            break;
3573
        case 'ca':
3574
            $country = 'es';
3575
            if ($allow) {
3576
                $country = 'catalan';
3577
            }
3578
            break;
3579
        case 'cs':
3580
            $country = 'cz';
3581
            break;
3582
        case 'da':
3583
            $country = 'dk';
3584
            break;
3585
        case 'el':
3586
            $country = 'ae';
3587
            break;
3588
        case 'en':
3589
            $country = 'gb';
3590
            break;
3591
        case 'eu': // Euskera
3592
            $country = 'es';
3593
            if ($allow) {
3594
                $country = 'basque';
3595
            }
3596
            break;
3597
        case 'gl': // galego
3598
            $country = 'es';
3599
            if ($allow) {
3600
                $country = 'galician';
3601
            }
3602
            break;
3603
        case 'he':
3604
            $country = 'il';
3605
            break;
3606
        case 'ja':
3607
            $country = 'jp';
3608
            break;
3609
        case 'ka':
3610
            $country = 'ge';
3611
            break;
3612
        case 'ko':
3613
            $country = 'kr';
3614
            break;
3615
        case 'ms':
3616
            $country = 'my';
3617
            break;
3618
        case 'pt-BR':
3619
            $country = 'br';
3620
            break;
3621
        case 'qu':
3622
            $country = 'pe';
3623
            break;
3624
        case 'sl':
3625
            $country = 'si';
3626
            break;
3627
        case 'sv':
3628
            $country = 'se';
3629
            break;
3630
        case 'uk': // Ukraine
3631
            $country = 'ua';
3632
            break;
3633
        case 'zh-TW':
3634
        case 'zh':
3635
            $country = 'cn';
3636
            break;
3637
        default:
3638
            $country = $languageIsoCode;
3639
            break;
3640
    }
3641
    $country = strtolower($country);
3642
3643
    return $country;
3644
}
3645
3646
/**
3647
 * Returns a list of all the languages that are made available by the admin.
3648
 *
3649
 * @return array An array with all languages. Structure of the array is
3650
 *               array['name'] = An array with the name of every language
3651
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
3652
 */
3653
function api_get_languages()
3654
{
3655
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3656
    $sql = "SELECT * FROM $table WHERE available='1'
3657
            ORDER BY original_name ASC";
3658
    $result = Database::query($sql);
3659
    $languages = [];
3660
    while ($row = Database::fetch_assoc($result)) {
3661
        $languages[$row['isocode']] = $row['original_name'];
3662
    }
3663
3664
    return $languages;
3665
}
3666
3667
/**
3668
 * Returns the id (the database id) of a language.
3669
 *
3670
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
3671
 *
3672
 * @return int id of the language
3673
 */
3674
function api_get_language_id($language)
3675
{
3676
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
3677
    if (empty($language)) {
3678
        return null;
3679
    }
3680
3681
    // We check the language by iscocode
3682
    $langInfo = api_get_language_from_iso($language);
3683
    if (null !== $langInfo && !empty($langInfo->getId())) {
3684
        return $langInfo->getId();
3685
    }
3686
3687
    $language = Database::escape_string($language);
3688
    $sql = "SELECT id FROM $tbl_language
3689
            WHERE english_name = '$language' LIMIT 1";
3690
    $result = Database::query($sql);
3691
    $row = Database::fetch_array($result);
3692
3693
    return $row['id'];
3694
}
3695
3696
/**
3697
 * Get the language information by its id.
3698
 *
3699
 * @param int $languageId
3700
 *
3701
 * @throws Exception
3702
 *
3703
 * @return array
3704
 */
3705
function api_get_language_info($languageId)
3706
{
3707
    if (empty($languageId)) {
3708
        return [];
3709
    }
3710
3711
    $language = Database::getManager()->find(Language::class, $languageId);
3712
3713
    if (!$language) {
3714
        return [];
3715
    }
3716
3717
    return [
3718
        'id' => $language->getId(),
3719
        'original_name' => $language->getOriginalName(),
3720
        'english_name' => $language->getEnglishName(),
3721
        'isocode' => $language->getIsocode(),
3722
        'available' => $language->getAvailable(),
3723
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
3724
    ];
3725
}
3726
3727
/**
3728
 * @param string $code
3729
 *
3730
 * @return Language
3731
 */
3732
function api_get_language_from_iso($code)
3733
{
3734
    $em = Database::getManager();
3735
3736
    return $em->getRepository(Language::class)->findOneBy(['isocode' => $code]);
3737
}
3738
3739
/**
3740
 * Shortcut to ThemeHelper::getVisualTheme()
3741
 */
3742
function api_get_visual_theme(): string
3743
{
3744
    $themeHelper = Container::$container->get(ThemeHelper::class);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

3744
    /** @scrutinizer ignore-call */ 
3745
    $themeHelper = Container::$container->get(ThemeHelper::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

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