Passed
Push — 1.11.x ( efc14f...a62b75 )
by
unknown
11:29
created

main/inc/lib/api.lib.php (1 issue)

Code
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\SettingsCurrent;
6
use Chamilo\CourseBundle\Entity\CItemProperty;
7
use Chamilo\UserBundle\Entity\User;
8
use ChamiloSession as Session;
9
use PHPMailer\PHPMailer\PHPMailer;
10
use Symfony\Component\Finder\Finder;
11
12
/**
13
 * This is a code library for Chamilo.
14
 * It is included by default in every Chamilo file (through including the global.inc.php)
15
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
16
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
17
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
18
 */
19
20
// PHP version requirement.
21
define('REQUIRED_PHP_VERSION', '7.1');
22
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
23
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
24
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
25
26
// USER STATUS CONSTANTS
27
/** global status of a user: student */
28
define('STUDENT', 5);
29
/** global status of a user: course manager */
30
define('COURSEMANAGER', 1);
31
/** global status of a user: session admin */
32
define('SESSIONADMIN', 3);
33
/** global status of a user: human ressource manager */
34
define('DRH', 4);
35
/** global status of a user: human ressource manager */
36
define('ANONYMOUS', 6);
37
/** global status of a user: low security, necessary for inserting data from
38
 * the teacher through HTMLPurifier */
39
define('COURSEMANAGERLOWSECURITY', 10);
40
// Soft user status
41
define('PLATFORM_ADMIN', 11);
42
define('SESSION_COURSE_COACH', 12);
43
define('SESSION_GENERAL_COACH', 13);
44
define('COURSE_STUDENT', 14); //student subscribed in a course
45
define('SESSION_STUDENT', 15); //student subscribed in a session course
46
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
47
define('STUDENT_BOSS', 17); // student is boss
48
define('INVITEE', 20);
49
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
50
51
// Table of status
52
$_status_list[COURSEMANAGER] = 'teacher'; // 1
53
$_status_list[SESSIONADMIN] = 'session_admin'; // 3
54
$_status_list[DRH] = 'drh'; // 4
55
$_status_list[STUDENT] = 'user'; // 5
56
$_status_list[ANONYMOUS] = 'anonymous'; // 6
57
$_status_list[INVITEE] = 'invited'; // 20
58
59
// COURSE VISIBILITY CONSTANTS
60
/** only visible for course admin */
61
define('COURSE_VISIBILITY_CLOSED', 0);
62
/** only visible for users registered in the course */
63
define('COURSE_VISIBILITY_REGISTERED', 1);
64
/** Open for all registered users on the platform */
65
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
66
/** Open for the whole world */
67
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
68
/** Invisible to all except admin */
69
define('COURSE_VISIBILITY_HIDDEN', 4);
70
71
define('COURSE_REQUEST_PENDING', 0);
72
define('COURSE_REQUEST_ACCEPTED', 1);
73
define('COURSE_REQUEST_REJECTED', 2);
74
define('DELETE_ACTION_ENABLED', false);
75
76
// EMAIL SENDING RECIPIENT CONSTANTS
77
define('SEND_EMAIL_EVERYONE', 1);
78
define('SEND_EMAIL_STUDENTS', 2);
79
define('SEND_EMAIL_TEACHERS', 3);
80
81
// SESSION VISIBILITY CONSTANTS
82
define('SESSION_VISIBLE_READ_ONLY', 1);
83
define('SESSION_VISIBLE', 2);
84
define('SESSION_INVISIBLE', 3); // not available
85
define('SESSION_AVAILABLE', 4);
86
87
define('SESSION_LINK_TARGET', '_self');
88
89
define('SUBSCRIBE_ALLOWED', 1);
90
define('SUBSCRIBE_NOT_ALLOWED', 0);
91
define('UNSUBSCRIBE_ALLOWED', 1);
92
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
93
94
// SURVEY VISIBILITY CONSTANTS
95
define('SURVEY_VISIBLE_TUTOR', 0);
96
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
97
define('SURVEY_VISIBLE_PUBLIC', 2);
98
99
// CONSTANTS defining all tools, using the english version
100
/* When you add a new tool you must add it into function api_get_tools_lists() too */
101
define('TOOL_DOCUMENT', 'document');
102
define('TOOL_LP_FINAL_ITEM', 'final_item');
103
define('TOOL_READOUT_TEXT', 'readout_text');
104
define('TOOL_THUMBNAIL', 'thumbnail');
105
define('TOOL_HOTPOTATOES', 'hotpotatoes');
106
define('TOOL_CALENDAR_EVENT', 'calendar_event');
107
define('TOOL_LINK', 'link');
108
define('TOOL_LINK_CATEGORY', 'link_category');
109
define('TOOL_COURSE_DESCRIPTION', 'course_description');
110
define('TOOL_SEARCH', 'search');
111
define('TOOL_LEARNPATH', 'learnpath');
112
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
113
define('TOOL_AGENDA', 'agenda');
114
define('TOOL_ANNOUNCEMENT', 'announcement');
115
define('TOOL_FORUM', 'forum');
116
define('TOOL_FORUM_CATEGORY', 'forum_category');
117
define('TOOL_FORUM_THREAD', 'forum_thread');
118
define('TOOL_FORUM_POST', 'forum_post');
119
define('TOOL_FORUM_ATTACH', 'forum_attachment');
120
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
121
define('TOOL_THREAD', 'thread');
122
define('TOOL_POST', 'post');
123
define('TOOL_DROPBOX', 'dropbox');
124
define('TOOL_QUIZ', 'quiz');
125
define('TOOL_TEST_CATEGORY', 'test_category');
126
define('TOOL_USER', 'user');
127
define('TOOL_GROUP', 'group');
128
define('TOOL_BLOGS', 'blog_management');
129
define('TOOL_CHAT', 'chat');
130
define('TOOL_STUDENTPUBLICATION', 'student_publication');
131
define('TOOL_TRACKING', 'tracking');
132
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
133
define('TOOL_COURSE_SETTING', 'course_setting');
134
define('TOOL_BACKUP', 'backup');
135
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
136
define('TOOL_RECYCLE_COURSE', 'recycle_course');
137
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
138
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
139
define('TOOL_UPLOAD', 'file_upload');
140
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
141
define('TOOL_SURVEY', 'survey');
142
define('TOOL_WIKI', 'wiki');
143
define('TOOL_GLOSSARY', 'glossary');
144
define('TOOL_GRADEBOOK', 'gradebook');
145
define('TOOL_NOTEBOOK', 'notebook');
146
define('TOOL_ATTENDANCE', 'attendance');
147
define('TOOL_COURSE_PROGRESS', 'course_progress');
148
define('TOOL_PORTFOLIO', 'portfolio');
149
define('TOOL_PLAGIARISM', 'compilatio');
150
define('TOOL_XAPI', 'xapi');
151
152
// CONSTANTS defining Chamilo interface sections
153
define('SECTION_CAMPUS', 'mycampus');
154
define('SECTION_COURSES', 'mycourses');
155
define('SECTION_CATALOG', 'catalog');
156
define('SECTION_MYPROFILE', 'myprofile');
157
define('SECTION_MYAGENDA', 'myagenda');
158
define('SECTION_COURSE_ADMIN', 'course_admin');
159
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
160
define('SECTION_MYGRADEBOOK', 'mygradebook');
161
define('SECTION_TRACKING', 'session_my_space');
162
define('SECTION_SOCIAL', 'social-network');
163
define('SECTION_DASHBOARD', 'dashboard');
164
define('SECTION_REPORTS', 'reports');
165
define('SECTION_GLOBAL', 'global');
166
define('SECTION_INCLUDE', 'include');
167
define('SECTION_CUSTOMPAGE', 'custompage');
168
169
// CONSTANT name for local authentication source
170
define('PLATFORM_AUTH_SOURCE', 'platform');
171
define('CAS_AUTH_SOURCE', 'cas');
172
define('LDAP_AUTH_SOURCE', 'extldap');
173
174
// CONSTANT defining the default HotPotatoes files directory
175
define('DIR_HOTPOTATOES', '/HotPotatoes_files');
176
177
// event logs types
178
define('LOG_COURSE_DELETE', 'course_deleted');
179
define('LOG_COURSE_CREATE', 'course_created');
180
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
181
182
// @todo replace 'soc_gr' with social_group
183
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
184
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
185
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
186
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
187
188
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
189
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
190
191
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
192
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
193
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
194
195
define('LOG_USER_DELETE', 'user_deleted');
196
define('LOG_USER_CREATE', 'user_created');
197
define('LOG_USER_UPDATE', 'user_updated');
198
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
199
define('LOG_USER_ENABLE', 'user_enable');
200
define('LOG_USER_DISABLE', 'user_disable');
201
define('LOG_USER_ANONYMIZE', 'user_anonymized');
202
define('LOG_USER_FIELD_CREATE', 'user_field_created');
203
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
204
define('LOG_SESSION_CREATE', 'session_created');
205
define('LOG_SESSION_DELETE', 'session_deleted');
206
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
207
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
208
define('LOG_SESSION_ADD_USER', 'session_add_user');
209
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
210
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
211
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
212
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
213
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
214
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
215
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
216
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
217
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
218
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
219
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
220
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
221
define('LOG_PROMOTION_CREATE', 'promotion_created');
222
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
223
define('LOG_CAREER_CREATE', 'career_created');
224
define('LOG_CAREER_DELETE', 'career_deleted');
225
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
226
define('LOG_WIKI_ACCESS', 'wiki_page_view');
227
// All results from an exercise
228
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
229
// Logs only the one attempt
230
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
231
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
232
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
233
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
234
235
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
236
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
237
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
238
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
239
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
240
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
241
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
242
243
// Event logs data types (max 20 chars)
244
define('LOG_COURSE_CODE', 'course_code');
245
define('LOG_COURSE_ID', 'course_id');
246
define('LOG_USER_ID', 'user_id');
247
define('LOG_USER_OBJECT', 'user_object');
248
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
249
define('LOG_SESSION_ID', 'session_id');
250
251
define('LOG_QUESTION_ID', 'question_id');
252
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
253
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
254
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
255
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
256
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
257
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
258
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
259
define('LOG_CAREER_ID', 'career_id');
260
define('LOG_PROMOTION_ID', 'promotion_id');
261
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
262
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
263
define('LOG_GRADEBOOK_ID', 'gradebook_id');
264
define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
265
define('LOG_EXERCISE_ID', 'exercise_id');
266
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
267
define('LOG_LP_ID', 'lp_id');
268
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
269
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
270
271
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
272
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
273
define('LOG_WORK_DATA', 'work_data_array');
274
275
define('LOG_MY_FOLDER_PATH', 'path');
276
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
277
278
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
279
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
280
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
281
282
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
283
284
define('LOG_QUESTION_CREATED', 'question_created');
285
define('LOG_QUESTION_UPDATED', 'question_updated');
286
define('LOG_QUESTION_DELETED', 'question_deleted');
287
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
288
289
define('LOG_SURVEY_ID', 'survey_id');
290
define('LOG_SURVEY_CREATED', 'survey_created');
291
define('LOG_SURVEY_DELETED', 'survey_deleted');
292
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
293
294
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.\$-]/');
295
296
//used when login_is_email setting is true
297
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
298
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
299
300
// This constant is a result of Windows OS detection, it has a boolean value:
301
// true whether the server runs on Windows OS, false otherwise.
302
define('IS_WINDOWS_OS', api_is_windows_os());
303
304
// Checks for installed optional php-extensions.
305
// intl extension (from PECL), it is installed by default as of PHP 5.3.0.
306
define('INTL_INSTALLED', function_exists('intl_get_error_code'));
307
// iconv extension, for PHP5 on Windows it is installed by default.
308
define('ICONV_INSTALLED', function_exists('iconv'));
309
define('MBSTRING_INSTALLED', function_exists('mb_strlen')); // mbstring extension.
310
311
// Patterns for processing paths. Examples.
312
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
313
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
314
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
315
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
316
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
317
// basic (leaf elements)
318
define('REL_CODE_PATH', 'REL_CODE_PATH');
319
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
320
define('REL_HOME_PATH', 'REL_HOME_PATH');
321
322
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
323
define('WEB_PATH', 'WEB_PATH');
324
define('WEB_APP_PATH', 'WEB_APP_PATH');
325
define('SYS_PATH', 'SYS_PATH');
326
define('SYS_APP_PATH', 'SYS_APP_PATH');
327
define('SYS_UPLOAD_PATH', 'SYS_UPLOAD_PATH');
328
define('WEB_UPLOAD_PATH', 'WEB_UPLOAD_PATH');
329
330
define('REL_PATH', 'REL_PATH');
331
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
332
define('SYS_COURSE_PATH', 'SYS_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('SYS_INC_PATH', 'SYS_INC_PATH');
346
define('LIBRARY_PATH', 'LIBRARY_PATH');
347
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
348
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
349
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
350
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
351
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
352
define('WEB_TEMPLATE_PATH', 'WEB_TEMPLATE_PATH');
353
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
354
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
355
define('SYS_HOME_PATH', 'SYS_HOME_PATH');
356
define('WEB_HOME_PATH', 'WEB_HOME_PATH');
357
define('WEB_FONTS_PATH', 'WEB_FONTS_PATH');
358
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
359
360
define('SYS_DEFAULT_COURSE_DOCUMENT_PATH', 'SYS_DEFAULT_COURSE_DOCUMENT_PATH');
361
define('REL_DEFAULT_COURSE_DOCUMENT_PATH', 'REL_DEFAULT_COURSE_DOCUMENT_PATH');
362
define('WEB_DEFAULT_COURSE_DOCUMENT_PATH', 'WEB_DEFAULT_COURSE_DOCUMENT_PATH');
363
364
// Relations type with Course manager
365
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
366
define('SESSION_RELATION_TYPE_COURSE_MANAGER', 1);
367
368
// Relations type with Human resources manager
369
define('COURSE_RELATION_TYPE_RRHH', 1);
370
define('SESSION_RELATION_TYPE_RRHH', 1);
371
372
//User image sizes
373
define('USER_IMAGE_SIZE_ORIGINAL', 1);
374
define('USER_IMAGE_SIZE_BIG', 2);
375
define('USER_IMAGE_SIZE_MEDIUM', 3);
376
define('USER_IMAGE_SIZE_SMALL', 4);
377
378
// Relation type between users
379
define('USER_UNKNOWN', 0);
380
define('USER_RELATION_TYPE_UNKNOWN', 1);
381
define('USER_RELATION_TYPE_PARENT', 2); // should be deprecated is useless
382
define('USER_RELATION_TYPE_FRIEND', 3);
383
define('USER_RELATION_TYPE_GOODFRIEND', 4); // should be deprecated is useless
384
define('USER_RELATION_TYPE_ENEMY', 5); // should be deprecated is useless
385
define('USER_RELATION_TYPE_DELETED', 6);
386
define('USER_RELATION_TYPE_RRHH', 7);
387
define('USER_RELATION_TYPE_BOSS', 8);
388
define('USER_RELATION_TYPE_HRM_REQUEST', 9);
389
390
// Gradebook link constants
391
// Please do not change existing values, they are used in the database !
392
define('GRADEBOOK_ITEM_LIMIT', 1000);
393
394
define('LINK_EXERCISE', 1);
395
define('LINK_DROPBOX', 2);
396
define('LINK_STUDENTPUBLICATION', 3);
397
define('LINK_LEARNPATH', 4);
398
define('LINK_FORUM_THREAD', 5);
399
//define('LINK_WORK',6);
400
define('LINK_ATTENDANCE', 7);
401
define('LINK_SURVEY', 8);
402
define('LINK_HOTPOTATOES', 9);
403
define('LINK_PORTFOLIO', 10);
404
405
// Score display types constants
406
define('SCORE_DIV', 1); // X / Y
407
define('SCORE_PERCENT', 2); // XX %
408
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
409
define('SCORE_AVERAGE', 4); // XX %
410
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
411
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
412
define('SCORE_SIMPLE', 7); // X
413
define('SCORE_IGNORE_SPLIT', 8); //  ??
414
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
415
define('SCORE_CUSTOM', 10); // Good!
416
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
417
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
418
define('SCORE_ONLY_SCORE', 13); // X - Good!
419
define('SCORE_NUMERIC', 14);
420
421
define('SCORE_BOTH', 1);
422
define('SCORE_ONLY_DEFAULT', 2);
423
define('SCORE_ONLY_CUSTOM', 3);
424
425
// From display.lib.php
426
427
define('MAX_LENGTH_BREADCRUMB', 100);
428
define('ICON_SIZE_ATOM', 8);
429
define('ICON_SIZE_TINY', 16);
430
define('ICON_SIZE_SMALL', 22);
431
define('ICON_SIZE_MEDIUM', 32);
432
define('ICON_SIZE_LARGE', 48);
433
define('ICON_SIZE_BIG', 64);
434
define('ICON_SIZE_HUGE', 128);
435
define('SHOW_TEXT_NEAR_ICONS', false);
436
437
// Session catalog
438
define('CATALOG_COURSES', 0);
439
define('CATALOG_SESSIONS', 1);
440
define('CATALOG_COURSES_SESSIONS', 2);
441
442
// Hook type events, pre-process and post-process.
443
// All means to be executed for both hook event types
444
define('HOOK_EVENT_TYPE_PRE', 0);
445
define('HOOK_EVENT_TYPE_POST', 1);
446
define('HOOK_EVENT_TYPE_ALL', 10);
447
448
define('CAREER_STATUS_ACTIVE', 1);
449
define('CAREER_STATUS_INACTIVE', 0);
450
451
define('PROMOTION_STATUS_ACTIVE', 1);
452
define('PROMOTION_STATUS_INACTIVE', 0);
453
454
// Group permissions
455
define('GROUP_PERMISSION_OPEN', '1');
456
define('GROUP_PERMISSION_CLOSED', '2');
457
458
// Group user permissions
459
define('GROUP_USER_PERMISSION_ADMIN', '1'); // the admin of a group
460
define('GROUP_USER_PERMISSION_READER', '2'); // a normal user
461
define('GROUP_USER_PERMISSION_PENDING_INVITATION', '3'); // When an admin/moderator invites a user
462
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', '4'); // an user joins a group
463
define('GROUP_USER_PERMISSION_MODERATOR', '5'); // a moderator
464
define('GROUP_USER_PERMISSION_ANONYMOUS', '6'); // an anonymous user
465
define('GROUP_USER_PERMISSION_HRM', '7'); // a human resources manager
466
467
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
468
define('GROUP_IMAGE_SIZE_BIG', 2);
469
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
470
define('GROUP_IMAGE_SIZE_SMALL', 4);
471
define('GROUP_TITLE_LENGTH', 50);
472
473
// Exercise
474
// @todo move into a class
475
define('ALL_ON_ONE_PAGE', 1);
476
define('ONE_PER_PAGE', 2);
477
478
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
479
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
480
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
481
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
482
483
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
484
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
485
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
486
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
487
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
488
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
489
define('RESULT_DISABLE_RANKING', 6);
490
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
491
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
492
define('RESULT_DISABLE_RADAR', 9);
493
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
494
495
define('EXERCISE_MAX_NAME_SIZE', 80);
496
497
// Question types (edit next array as well when adding values)
498
// @todo move into a class
499
define('UNIQUE_ANSWER', 1);
500
define('MULTIPLE_ANSWER', 2);
501
define('FILL_IN_BLANKS', 3);
502
define('MATCHING', 4);
503
define('FREE_ANSWER', 5);
504
define('HOT_SPOT', 6);
505
define('HOT_SPOT_ORDER', 7);
506
define('HOT_SPOT_DELINEATION', 8);
507
define('MULTIPLE_ANSWER_COMBINATION', 9);
508
define('UNIQUE_ANSWER_NO_OPTION', 10);
509
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
510
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
511
define('ORAL_EXPRESSION', 13);
512
define('GLOBAL_MULTIPLE_ANSWER', 14);
513
define('MEDIA_QUESTION', 15);
514
define('CALCULATED_ANSWER', 16);
515
define('UNIQUE_ANSWER_IMAGE', 17);
516
define('DRAGGABLE', 18);
517
define('MATCHING_DRAGGABLE', 19);
518
define('ANNOTATION', 20);
519
define('READING_COMPREHENSION', 21);
520
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
521
define('UPLOAD_ANSWER', 23);
522
523
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
524
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
525
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
526
527
// Question selection type
528
define('EX_Q_SELECTION_ORDERED', 1);
529
define('EX_Q_SELECTION_RANDOM', 2);
530
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
531
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
532
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
533
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
534
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
535
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
536
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
537
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
538
539
// Used to save the skill_rel_item table
540
define('ITEM_TYPE_EXERCISE', 1);
541
define('ITEM_TYPE_HOTPOTATOES', 2);
542
define('ITEM_TYPE_LINK', 3);
543
define('ITEM_TYPE_LEARNPATH', 4);
544
define('ITEM_TYPE_GRADEBOOK', 5);
545
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
546
//define('ITEM_TYPE_FORUM', 7);
547
define('ITEM_TYPE_ATTENDANCE', 8);
548
define('ITEM_TYPE_SURVEY', 9);
549
define('ITEM_TYPE_FORUM_THREAD', 10);
550
define('ITEM_TYPE_PORTFOLIO', 11);
551
552
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
553
define(
554
    'QUESTION_TYPES',
555
    UNIQUE_ANSWER.':'.
556
    MULTIPLE_ANSWER.':'.
557
    FILL_IN_BLANKS.':'.
558
    MATCHING.':'.
559
    FREE_ANSWER.':'.
560
    HOT_SPOT.':'.
561
    HOT_SPOT_ORDER.':'.
562
    HOT_SPOT_DELINEATION.':'.
563
    MULTIPLE_ANSWER_COMBINATION.':'.
564
    UNIQUE_ANSWER_NO_OPTION.':'.
565
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
566
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
567
    ORAL_EXPRESSION.':'.
568
    GLOBAL_MULTIPLE_ANSWER.':'.
569
    MEDIA_QUESTION.':'.
570
    CALCULATED_ANSWER.':'.
571
    UNIQUE_ANSWER_IMAGE.':'.
572
    DRAGGABLE.':'.
573
    MATCHING_DRAGGABLE.':'.
574
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
575
    ANNOTATION
576
);
577
578
//Some alias used in the QTI exports
579
define('MCUA', 1);
580
define('TF', 1);
581
define('MCMA', 2);
582
define('FIB', 3);
583
584
// Skills
585
define('SKILL_TYPE_REQUIREMENT', 'required');
586
define('SKILL_TYPE_ACQUIRED', 'acquired');
587
define('SKILL_TYPE_BOTH', 'both');
588
589
// Message
590
define('MESSAGE_STATUS_NEW', '0');
591
define('MESSAGE_STATUS_UNREAD', '1');
592
//2 ??
593
define('MESSAGE_STATUS_DELETED', '3');
594
define('MESSAGE_STATUS_OUTBOX', '4');
595
define('MESSAGE_STATUS_INVITATION_PENDING', '5');
596
define('MESSAGE_STATUS_INVITATION_ACCEPTED', '6');
597
define('MESSAGE_STATUS_INVITATION_DENIED', '7');
598
define('MESSAGE_STATUS_WALL', '8');
599
define('MESSAGE_STATUS_WALL_DELETE', '9');
600
define('MESSAGE_STATUS_WALL_POST', '10');
601
define('MESSAGE_STATUS_CONVERSATION', '11');
602
define('MESSAGE_STATUS_FORUM', '12');
603
define('MESSAGE_STATUS_PROMOTED', '13');
604
605
// Images
606
define('IMAGE_WALL_SMALL_SIZE', 200);
607
define('IMAGE_WALL_MEDIUM_SIZE', 500);
608
define('IMAGE_WALL_BIG_SIZE', 2000);
609
define('IMAGE_WALL_SMALL', 'small');
610
define('IMAGE_WALL_MEDIUM', 'medium');
611
define('IMAGE_WALL_BIG', 'big');
612
613
// Social PLUGIN PLACES
614
define('SOCIAL_LEFT_PLUGIN', 1);
615
define('SOCIAL_CENTER_PLUGIN', 2);
616
define('SOCIAL_RIGHT_PLUGIN', 3);
617
define('CUT_GROUP_NAME', 50);
618
619
/**
620
 * FormValidator Filter.
621
 */
622
define('NO_HTML', 1);
623
define('STUDENT_HTML', 2);
624
define('TEACHER_HTML', 3);
625
define('STUDENT_HTML_FULLPAGE', 4);
626
define('TEACHER_HTML_FULLPAGE', 5);
627
628
// Timeline
629
define('TIMELINE_STATUS_ACTIVE', '1');
630
define('TIMELINE_STATUS_INACTIVE', '2');
631
632
// Event email template class
633
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
634
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
635
636
// Course home
637
define('SHORTCUTS_HORIZONTAL', 0);
638
define('SHORTCUTS_VERTICAL', 1);
639
640
// Image class
641
define('IMAGE_PROCESSOR', 'gd'); // 'imagick' or 'gd' strings
642
643
// Course copy
644
define('FILE_SKIP', 1);
645
define('FILE_RENAME', 2);
646
define('FILE_OVERWRITE', 3);
647
define('UTF8_CONVERT', false); //false by default
648
649
define('DOCUMENT', 'file');
650
define('FOLDER', 'folder');
651
652
define('RESOURCE_ASSET', 'asset');
653
define('RESOURCE_DOCUMENT', 'document');
654
define('RESOURCE_GLOSSARY', 'glossary');
655
define('RESOURCE_EVENT', 'calendar_event');
656
define('RESOURCE_LINK', 'link');
657
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
658
define('RESOURCE_LEARNPATH', 'learnpath');
659
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
660
define('RESOURCE_ANNOUNCEMENT', 'announcement');
661
define('RESOURCE_FORUM', 'forum');
662
define('RESOURCE_FORUMTOPIC', 'thread');
663
define('RESOURCE_FORUMPOST', 'post');
664
define('RESOURCE_QUIZ', 'quiz');
665
define('RESOURCE_TEST_CATEGORY', 'test_category');
666
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
667
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
668
define('RESOURCE_LINKCATEGORY', 'Link_Category');
669
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
670
define('RESOURCE_SCORM', 'Scorm');
671
define('RESOURCE_SURVEY', 'survey');
672
define('RESOURCE_SURVEYQUESTION', 'survey_question');
673
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
674
define('RESOURCE_WIKI', 'wiki');
675
define('RESOURCE_THEMATIC', 'thematic');
676
define('RESOURCE_ATTENDANCE', 'attendance');
677
define('RESOURCE_WORK', 'work');
678
define('RESOURCE_SESSION_COURSE', 'session_course');
679
define('RESOURCE_GRADEBOOK', 'gradebook');
680
define('ADD_THEMATIC_PLAN', 6);
681
682
// Max online users to show per page (whoisonline)
683
define('MAX_ONLINE_USERS', 12);
684
685
// Number of characters maximum to show in preview of course blog posts
686
define('BLOG_MAX_PREVIEW_CHARS', 800);
687
// HTML string to replace with a 'Read more...' link
688
define('BLOG_PAGE_BREAK', '<div style="page-break-after: always"><span style="display: none;">&nbsp;</span></div>');
689
690
// Make sure the CHAMILO_LOAD_WYSIWYG constant is defined
691
// To remove CKeditor libs from HTML, set this constant to true before loading
692
if (!defined('CHAMILO_LOAD_WYSIWYG')) {
693
    define('CHAMILO_LOAD_WYSIWYG', true);
694
}
695
696
/* Constants for course home */
697
define('TOOL_PUBLIC', 'Public');
698
define('TOOL_PUBLIC_BUT_HIDDEN', 'PublicButHide');
699
define('TOOL_COURSE_ADMIN', 'courseAdmin');
700
define('TOOL_PLATFORM_ADMIN', 'platformAdmin');
701
define('TOOL_AUTHORING', 'toolauthoring');
702
define('TOOL_INTERACTION', 'toolinteraction');
703
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
704
define('TOOL_ADMIN', 'tooladmin');
705
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
706
define('TOOL_DRH', 'tool_drh');
707
define('TOOL_STUDENT_VIEW', 'toolstudentview');
708
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
709
710
/**
711
 * Inclusion of internationalization libraries.
712
 */
713
require_once __DIR__.'/internationalization.lib.php';
714
715
/**
716
 * Returns a path to a certain resource within the Chamilo area, specifyed through a parameter.
717
 * Also, this function provides conversion between path types, in this case the input path points inside the Chamilo area too.
718
 *
719
 * See $_configuration['course_folder'] in the configuration.php to alter the WEB_COURSE_PATH and SYS_COURSE_PATH parameters.
720
721
 *
722
 * @param string $path (optional)   A path which type is to be converted. Also, it may be a defined constant for a path.
723
 *                     This parameter has meaning when $type parameter has one of the following values: TO_WEB, TO_SYS, TO_REL. Otherwise it is ignored.
724
 *
725
 * @return string the requested path or the converted path
726
 *
727
 * Notes about the current behaviour model:
728
 * 1. Windows back-slashes are converted to slashes in the result.
729
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
730
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
731
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
732
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
733
 * no a mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
734
 * It has not been identified as needed yet.
735
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
736
 *
737
 * For examples go to: *
738
 * See main/admin/system_status.php?section=paths
739
 *
740
 * Vchamilo changes : allow using an alternate configuration
741
 * to get vchamilo  instance paths
742
 */
743
function api_get_path($path = '', $configuration = [])
744
{
745
    global $paths;
746
747
    // get proper configuration data if exists
748
    global $_configuration;
749
750
    $emptyConfigurationParam = false;
751
    if (empty($configuration)) {
752
        $configuration = (array) $_configuration;
753
        $emptyConfigurationParam = true;
754
    }
755
756
    $course_folder = 'courses/';
757
    static $root_web = '';
758
    $root_sys = null;
759
    if ($_configuration) {
760
        $root_sys = $_configuration['root_sys'];
761
    }
762
763
    // If no $root_web has been set so far *and* no custom config has been passed to the function
764
    // then re-use the previously-calculated (run-specific) $root_web and skip this complex calculation
765
    if (empty($root_web) || $emptyConfigurationParam === false || empty($configuration)) {
766
        // Resolve master hostname.
767
        if (!empty($configuration) && array_key_exists('root_web', $configuration)) {
768
            $root_web = $configuration['root_web'];
769
        } else {
770
            $root_web = '';
771
            // Try guess it from server.
772
            if (defined('SYSTEM_INSTALLATION') && SYSTEM_INSTALLATION) {
773
                if (($pos = strpos(($requested_page_rel = api_get_self()), 'main/install')) !== false) {
774
                    $root_rel = substr($requested_page_rel, 0, $pos);
775
                    // See http://www.mediawiki.org/wiki/Manual:$wgServer
776
                    $server_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
777
                    $server_name =
778
                        isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME']
779
                            : (isset($_SERVER['HOSTNAME']) ? $_SERVER['HOSTNAME']
780
                            : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
781
                                : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR']
782
                                    : 'localhost')));
783
                    if (isset($_SERVER['SERVER_PORT']) &&
784
                        !strpos($server_name, ':') &&
785
                        (($server_protocol == 'http' && $_SERVER['SERVER_PORT'] != 80) ||
786
                        ($server_protocol == 'https' && $_SERVER['SERVER_PORT'] != 443))
787
                    ) {
788
                        $server_name .= ":".$_SERVER['SERVER_PORT'];
789
                    }
790
                    $root_web = $server_protocol.'://'.$server_name.$root_rel;
791
                    $root_sys = str_replace('\\', '/', realpath(__DIR__.'/../../../')).'/';
792
                }
793
                // Here we give up, so we don't touch anything.
794
            }
795
        }
796
    }
797
798
    if (isset($configuration['multiple_access_urls']) &&
799
        $configuration['multiple_access_urls']
800
    ) {
801
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the api.lib.php.
802
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
803
            // We look into the DB the function api_get_access_url
804
            $urlInfo = api_get_access_url($configuration['access_url']);
805
            // Avoid default value
806
            $defaultValues = ['http://localhost/', 'https://localhost/'];
807
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
808
                $root_web = $urlInfo['active'] == 1 ? $urlInfo['url'] : $configuration['root_web'];
809
            }
810
        }
811
    }
812
813
    $paths = [];
814
    // Initialise cache with default values.
815
    if (!array_key_exists($root_web, $paths)) {
816
        $paths[$root_web] = [
817
            WEB_PATH => '',
818
            SYS_PATH => '',
819
            REL_PATH => '',
820
            WEB_COURSE_PATH => '',
821
            SYS_COURSE_PATH => '',
822
            REL_COURSE_PATH => '',
823
            WEB_CODE_PATH => 'main/',
824
            SYS_CODE_PATH => 'main/',
825
            REL_CODE_PATH => '/main/',
826
            SYS_LANG_PATH => 'lang/',
827
            WEB_IMG_PATH => 'img/',
828
            WEB_CSS_PATH => 'web/css/',
829
            SYS_CSS_PATH => 'app/Resources/public/css/',
830
            SYS_PLUGIN_PATH => 'plugin/',
831
            WEB_PLUGIN_PATH => 'plugin/',
832
            WEB_PLUGIN_ASSET_PATH => 'public/plugins/',
833
            SYS_ARCHIVE_PATH => 'app/cache/',
834
            WEB_ARCHIVE_PATH => 'app/cache/',
835
            SYS_HOME_PATH => 'app/home/',
836
            WEB_HOME_PATH => 'app/home/',
837
            REL_HOME_PATH => 'app/home/',
838
            SYS_APP_PATH => 'app/',
839
            WEB_APP_PATH => 'app/',
840
            SYS_UPLOAD_PATH => 'app/upload/',
841
            SYS_INC_PATH => 'inc/',
842
            CONFIGURATION_PATH => 'app/config/',
843
            LIBRARY_PATH => 'inc/lib/',
844
            WEB_LIBRARY_PATH => 'inc/lib/',
845
            WEB_LIBRARY_JS_PATH => 'inc/lib/javascript/',
846
            WEB_AJAX_PATH => 'inc/ajax/',
847
            SYS_TEST_PATH => 'tests/',
848
            WEB_TEMPLATE_PATH => 'template/',
849
            SYS_TEMPLATE_PATH => 'template/',
850
            WEB_UPLOAD_PATH => 'app/upload/',
851
            WEB_PUBLIC_PATH => 'web/',
852
            SYS_PUBLIC_PATH => 'web/',
853
            WEB_FONTS_PATH => 'fonts/',
854
            SYS_FONTS_PATH => 'fonts/',
855
        ];
856
    }
857
858
    $isInitialized = [];
859
    $course_folder = isset($configuration['course_folder']) ? $configuration['course_folder'] : $course_folder;
860
    $root_rel = isset($configuration['url_append']) ? $configuration['url_append'] : '';
861
    if (!empty($root_rel)) {
862
        // Adds "/" to the root_rel
863
        $hasSlash = substr($configuration['url_append'], 0, 1);
864
        if ($hasSlash !== '/') {
865
            $root_rel = '/'.$root_rel;
866
        }
867
    }
868
869
    // Web server base and system server base.
870
    if (!array_key_exists($root_web, $isInitialized)) {
871
        // process absolute global roots
872
        if (!empty($configuration)) {
873
            $code_folder = 'main';
874
        } else {
875
            $code_folder = $paths[$root_web][REL_CODE_PATH];
876
        }
877
878
        // Support for the installation process.
879
        // Developers might use the function api_get_path() directly or indirectly (this is difficult to be traced), at the moment when
880
        // configuration has not been created yet. This is why this function should be upgraded to return correct results in this case.
881
882
        // Dealing with trailing slashes.
883
        $slashed_root_web = api_add_trailing_slash($root_web);
884
        $root_sys = api_add_trailing_slash($root_sys);
885
        $root_rel = api_add_trailing_slash($root_rel);
886
        $code_folder = api_add_trailing_slash($code_folder);
887
        $course_folder = api_add_trailing_slash($course_folder);
888
889
        // Initialization of a table that contains common-purpose paths.
890
        $paths[$root_web][REL_PATH] = $root_rel;
891
        $paths[$root_web][REL_COURSE_PATH] = $root_rel.$course_folder;
892
        $paths[$root_web][REL_CODE_PATH] = $root_rel.$code_folder;
893
        $paths[$root_web][REL_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][REL_PATH].'main/default_course_document/';
894
895
        $paths[$root_web][WEB_PATH] = $slashed_root_web;
896
        $paths[$root_web][WEB_CODE_PATH] = $paths[$root_web][WEB_PATH].$code_folder;
897
        $paths[$root_web][WEB_COURSE_PATH] = $paths[$root_web][WEB_PATH].$course_folder;
898
        $paths[$root_web][WEB_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][WEB_CODE_PATH].'default_course_document/';
899
        $paths[$root_web][WEB_APP_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_APP_PATH];
900
        $paths[$root_web][WEB_PLUGIN_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_PATH];
901
        $paths[$root_web][WEB_PLUGIN_ASSET_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_ASSET_PATH];
902
        $paths[$root_web][WEB_ARCHIVE_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_ARCHIVE_PATH];
903
904
        $paths[$root_web][WEB_CSS_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_CSS_PATH];
905
        $paths[$root_web][WEB_IMG_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_IMG_PATH];
906
        $paths[$root_web][WEB_LIBRARY_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_PATH];
907
        $paths[$root_web][WEB_LIBRARY_JS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_JS_PATH];
908
        $paths[$root_web][WEB_AJAX_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_AJAX_PATH];
909
        $paths[$root_web][WEB_FONTS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_FONTS_PATH];
910
        $paths[$root_web][WEB_TEMPLATE_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_TEMPLATE_PATH];
911
        $paths[$root_web][WEB_UPLOAD_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_UPLOAD_PATH];
912
        $paths[$root_web][WEB_PUBLIC_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PUBLIC_PATH];
913
        $paths[$root_web][WEB_HOME_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][REL_HOME_PATH];
914
915
        $paths[$root_web][SYS_PATH] = $root_sys;
916
        $paths[$root_web][SYS_CODE_PATH] = $root_sys.$code_folder;
917
        $paths[$root_web][SYS_TEST_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_TEST_PATH];
918
        $paths[$root_web][SYS_TEMPLATE_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_TEMPLATE_PATH];
919
        $paths[$root_web][SYS_PUBLIC_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PUBLIC_PATH];
920
        $paths[$root_web][SYS_CSS_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_CSS_PATH];
921
        $paths[$root_web][SYS_FONTS_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_FONTS_PATH];
922
        $paths[$root_web][SYS_ARCHIVE_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_ARCHIVE_PATH];
923
        $paths[$root_web][SYS_APP_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_APP_PATH];
924
        $paths[$root_web][SYS_COURSE_PATH] = $paths[$root_web][SYS_APP_PATH].$course_folder;
925
        $paths[$root_web][SYS_UPLOAD_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_UPLOAD_PATH];
926
        $paths[$root_web][SYS_LANG_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_LANG_PATH];
927
        $paths[$root_web][SYS_HOME_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_HOME_PATH];
928
        $paths[$root_web][SYS_PLUGIN_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PLUGIN_PATH];
929
        $paths[$root_web][SYS_INC_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_INC_PATH];
930
931
        $paths[$root_web][LIBRARY_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][LIBRARY_PATH];
932
        $paths[$root_web][CONFIGURATION_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][CONFIGURATION_PATH];
933
934
        global $virtualChamilo;
935
        if (!empty($virtualChamilo)) {
936
            $paths[$root_web][SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
937
            $paths[$root_web][SYS_HOME_PATH] = api_add_trailing_slash($virtualChamilo[SYS_HOME_PATH]);
938
            $paths[$root_web][SYS_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_COURSE_PATH]);
939
            $paths[$root_web][SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
940
941
            $paths[$root_web][WEB_HOME_PATH] = api_add_trailing_slash($virtualChamilo[WEB_HOME_PATH]);
942
            $paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
943
            $paths[$root_web][WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
944
            //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
945
946
            // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
947
948
            // RewriteEngine On
949
            // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
950
951
            //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
952
            //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
953
            //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
954
        }
955
956
        $isInitialized[$root_web] = true;
957
    }
958
959
    $path = trim($path);
960
961
    // Retrieving a common-purpose path.
962
    if (isset($paths[$root_web][$path])) {
963
        return $paths[$root_web][$path];
964
    }
965
966
    // Second purification.
967
968
    // Replacing Windows back slashes.
969
    $path = str_replace('\\', '/', $path);
970
    // Query strings sometimes mighth wrongly appear in non-URLs.
971
    // Let us check remove them from all types of paths.
972
    if (($pos = strpos($path, '?')) !== false) {
973
        $path = substr($path, 0, $pos);
974
    }
975
976
    // Detection of the input path type. Conversion to semi-absolute type ( /chamilo/main/inc/.... ).
977
978
    if (preg_match(VALID_WEB_PATH, $path)) {
979
        // A special case: When a URL points to the document download script directly, without
980
        // mod-rewrite translation, we have to translate it into an "ordinary" web path.
981
        // For example:
982
        // http://localhost/chamilo/main/document/download.php?doc_url=/image.png&cDir=/
983
        // becomes
984
        // http://localhost/chamilo/courses/TEST/document/image.png
985
        // TEST is a course directory name, so called "system course code".
986
        if (strpos($path, 'download.php') !== false) { // Fast detection first.
987
            $path = urldecode($path);
988
            if (preg_match('/(.*)main\/document\/download.php\?doc_url=\/(.*)&cDir=\/(.*)?/', $path, $matches)) {
989
                $sys_course_code =
990
                    isset($_SESSION['_course']['sysCode'])  // User is inside a course?
991
                        ? $_SESSION['_course']['sysCode']   // Yes, then use course's directory name.
992
                        : '{SYS_COURSE_CODE}'; // No, then use a fake code, it may be processed later.
993
                $path = $matches[1].'courses/'.$sys_course_code.'/document/'.str_replace('//', '/', $matches[3].'/'.$matches[2]);
994
            }
995
        }
996
        // Replacement of the present web server base with a slash '/'.
997
        $path = preg_replace(VALID_WEB_SERVER_BASE, '/', $path);
998
    }
999
1000
    // Path now is semi-absolute. It is convenient at this moment repeated slashes to be removed.
1001
    $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
1002
1003
    return $path;
1004
}
1005
1006
/**
1007
 * Gets a modified version of the path for the CDN, if defined in
1008
 * configuration.php.
1009
 *
1010
 * @param string $web_path The path of the resource without CDN
1011
 *
1012
 * @return string The path of the resource converted to CDN
1013
 *
1014
 * @author Yannick Warnier <[email protected]>
1015
 */
1016
function api_get_cdn_path($web_path)
1017
{
1018
    global $_configuration;
1019
    if (!empty($_configuration['cdn_enable'])) {
1020
        $web_root = api_get_path(WEB_PATH);
1021
        $ext = substr($web_path, strrpos($web_path, '.'));
1022
        if (isset($ext[2])) { // faster version of strlen to check if len>2
1023
            // Check for CDN definitions
1024
            if (!empty($ext)) {
1025
                foreach ($_configuration['cdn'] as $host => $exts) {
1026
                    if (in_array($ext, $exts)) {
1027
                        //Use host as defined in $_configuration['cdn'], without
1028
                        // trailing slash
1029
                        return str_replace($web_root, $host.'/', $web_path);
1030
                    }
1031
                }
1032
            }
1033
        }
1034
    }
1035
1036
    return $web_path;
1037
}
1038
1039
/**
1040
 * @return bool Return true if CAS authentification is activated
1041
 */
1042
function api_is_cas_activated()
1043
{
1044
    return api_get_setting('cas_activate') == "true";
1045
}
1046
1047
/**
1048
 * @return bool Return true if LDAP authentification is activated
1049
 */
1050
function api_is_ldap_activated()
1051
{
1052
    global $extAuthSource;
1053
1054
    return is_array($extAuthSource[LDAP_AUTH_SOURCE]);
1055
}
1056
1057
/**
1058
 * @return bool Return true if Facebook authentification is activated
1059
 */
1060
function api_is_facebook_auth_activated()
1061
{
1062
    global $_configuration;
1063
1064
    return isset($_configuration['facebook_auth']) && $_configuration['facebook_auth'] == 1;
1065
}
1066
1067
/**
1068
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
1069
 *
1070
 * @param string $path the input path
1071
 *
1072
 * @return string returns the modified path
1073
 */
1074
function api_add_trailing_slash($path)
1075
{
1076
    return substr($path, -1) == '/' ? $path : $path.'/';
1077
}
1078
1079
/**
1080
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
1081
 *
1082
 * @param string $path the input path
1083
 *
1084
 * @return string returns the modified path
1085
 */
1086
function api_remove_trailing_slash($path)
1087
{
1088
    return substr($path, -1) == '/' ? substr($path, 0, -1) : $path;
1089
}
1090
1091
/**
1092
 * Checks the RFC 3986 syntax of a given URL.
1093
 *
1094
 * @param string $url      the URL to be checked
1095
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
1096
 *
1097
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
1098
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
1099
 *
1100
 * @see http://drupal.org
1101
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
1102
 * @see http://bugs.php.net/51192
1103
 */
1104
function api_valid_url($url, $absolute = false)
1105
{
1106
    if ($absolute) {
1107
        if (preg_match("
1108
            /^                                                      # Start at the beginning of the text
1109
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
1110
            (?:                                                     # Userinfo (optional) which is typically
1111
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
1112
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
1113
            )?
1114
            (?:
1115
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
1116
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
1117
            )
1118
            (?::[0-9]+)?                                            # Server port number (optional)
1119
            (?:[\/|\?]
1120
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
1121
            *)?
1122
            $/xi", $url)) {
1123
            return $url;
1124
        }
1125
1126
        return false;
1127
    } else {
1128
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
1129
    }
1130
}
1131
1132
/**
1133
 * Checks whether a given string looks roughly like an email address.
1134
 *
1135
 * @param string $address the e-mail address to be checked
1136
 *
1137
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
1138
 */
1139
function api_valid_email($address)
1140
{
1141
    return filter_var($address, FILTER_VALIDATE_EMAIL);
1142
}
1143
1144
/* PROTECTION FUNCTIONS
1145
   Use these functions to protect your scripts. */
1146
1147
/**
1148
 * Function used to protect a course script.
1149
 * The function blocks access when
1150
 * - there is no $_SESSION["_course"] defined; or
1151
 * - $is_allowed_in_course is set to false (this depends on the course
1152
 * visibility and user status).
1153
 *
1154
 * This is only the first proposal, test and improve!
1155
 *
1156
 * @param bool Option to print headers when displaying error message. Default: false
1157
 * @param bool whether session admins should be allowed or not
1158
 * @param bool $checkTool check if tool is available for users (user, group)
1159
 *
1160
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
1161
 *
1162
 * @todo replace global variable
1163
 *
1164
 * @author Roan Embrechts
1165
 */
1166
function api_protect_course_script($print_headers = false, $allow_session_admins = false, $checkTool = '')
1167
{
1168
    $course_info = api_get_course_info();
1169
    if (empty($course_info)) {
1170
        api_not_allowed($print_headers);
1171
1172
        return false;
1173
    }
1174
1175
    if (api_is_drh()) {
1176
        return true;
1177
    }
1178
1179
    // Session admin has access to course
1180
    $sessionAccess = api_get_configuration_value('session_admins_access_all_content');
1181
    if ($sessionAccess) {
1182
        $allow_session_admins = true;
1183
    }
1184
1185
    if (api_is_platform_admin($allow_session_admins)) {
1186
        return true;
1187
    }
1188
1189
    $isAllowedInCourse = api_is_allowed_in_course();
1190
1191
    $is_visible = false;
1192
    if (isset($course_info) && isset($course_info['visibility'])) {
1193
        switch ($course_info['visibility']) {
1194
            default:
1195
            case COURSE_VISIBILITY_CLOSED:
1196
                // Completely closed: the course is only accessible to the teachers. - 0
1197
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1198
                    $is_visible = true;
1199
                }
1200
                break;
1201
            case COURSE_VISIBILITY_REGISTERED:
1202
                // Private - access authorized to course members only - 1
1203
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1204
                    $is_visible = true;
1205
                }
1206
                break;
1207
            case COURSE_VISIBILITY_OPEN_PLATFORM:
1208
                // Open - access allowed for users registered on the platform - 2
1209
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1210
                    $is_visible = true;
1211
                }
1212
                break;
1213
            case COURSE_VISIBILITY_OPEN_WORLD:
1214
                //Open - access allowed for the whole world - 3
1215
                $is_visible = true;
1216
                break;
1217
            case COURSE_VISIBILITY_HIDDEN:
1218
                //Completely closed: the course is only accessible to the teachers. - 0
1219
                if (api_is_platform_admin()) {
1220
                    $is_visible = true;
1221
                }
1222
                break;
1223
        }
1224
1225
        // If password is set and user is not registered to the course then the course is not visible.
1226
        if (false == $isAllowedInCourse &&
1227
            isset($course_info['registration_code']) && !empty($course_info['registration_code'])
1228
        ) {
1229
            $is_visible = false;
1230
        }
1231
    }
1232
1233
    if (!empty($checkTool)) {
1234
        if (!api_is_allowed_to_edit(true, true, true)) {
1235
            $toolInfo = api_get_tool_information_by_name($checkTool);
1236
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && $toolInfo['visibility'] == 0) {
1237
                api_not_allowed(true);
1238
1239
                return false;
1240
            }
1241
        }
1242
    }
1243
1244
    // Check session visibility
1245
    $session_id = api_get_session_id();
1246
1247
    if (!empty($session_id)) {
1248
        // $isAllowedInCourse was set in local.inc.php
1249
        if (!$isAllowedInCourse) {
1250
            $is_visible = false;
1251
        }
1252
1253
        // Check if course is inside session.
1254
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
1255
            $is_visible = false;
1256
        }
1257
    }
1258
1259
    if (!$is_visible) {
1260
        api_not_allowed($print_headers);
1261
1262
        return false;
1263
    }
1264
1265
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
1266
        $plugin = Positioning::create();
1267
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1268
        if ('true' === $block) {
1269
            $currentPath = $_SERVER['PHP_SELF'];
1270
            // Allowed only this course paths.
1271
            $paths = [
1272
                '/plugin/positioning/start.php',
1273
                '/plugin/positioning/start_student.php',
1274
                '/main/course_home/course_home.php',
1275
                '/main/exercise/overview.php',
1276
            ];
1277
1278
            if (!in_array($currentPath, $paths, true)) {
1279
                // Check if entering an exercise.
1280
                global $current_course_tool;
1281
                if ('quiz' !== $current_course_tool) {
1282
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1283
                    if ($initialData && isset($initialData['exercise_id'])) {
1284
                        $results = Event::getExerciseResultsByUser(
1285
                            api_get_user_id(),
1286
                            $initialData['exercise_id'],
1287
                            $course_info['real_id'],
1288
                            $session_id
1289
                        );
1290
                        if (empty($results)) {
1291
                            api_not_allowed($print_headers);
1292
1293
                            return false;
1294
                        }
1295
                    }
1296
                }
1297
            }
1298
        }
1299
    }
1300
1301
    api_block_inactive_user();
1302
1303
    return true;
1304
}
1305
1306
/**
1307
 * Function used to protect an admin script.
1308
 *
1309
 * The function blocks access when the user has no platform admin rights
1310
 * with an error message printed on default output
1311
 *
1312
 * @param bool Whether to allow session admins as well
1313
 * @param bool Whether to allow HR directors as well
1314
 * @param string An optional message (already passed through get_lang)
1315
 *
1316
 * @return bool True if user is allowed, false otherwise.
1317
 *              The function also outputs an error message in case not allowed
1318
 *
1319
 * @author Roan Embrechts (original author)
1320
 */
1321
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null)
1322
{
1323
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1324
        api_not_allowed(true, $message);
1325
1326
        return false;
1327
    }
1328
1329
    api_block_inactive_user();
1330
1331
    return true;
1332
}
1333
1334
/**
1335
 * Blocks inactive users with a currently active session from accessing more pages "live".
1336
 *
1337
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1338
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1339
 */
1340
function api_block_inactive_user()
1341
{
1342
    $data = true;
1343
    if (api_get_configuration_value('security_block_inactive_users_immediately') != 1) {
1344
        return $data;
1345
    }
1346
1347
    $userId = api_get_user_id();
1348
    $homeUrl = api_get_path(WEB_PATH);
1349
    if ($userId == 0) {
1350
        return $data;
1351
    }
1352
1353
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1354
            WHERE id = $userId";
1355
1356
    $result = Database::query($sql);
1357
    if (Database::num_rows($result) > 0) {
1358
        $result_array = Database::fetch_array($result);
1359
        $data = (bool) $result_array['active'];
1360
    }
1361
    if ($data == false) {
1362
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1363
        $tpl->assign('hide_login_link', 1);
1364
1365
        //api_not_allowed(true, get_lang('AccountInactive'));
1366
        // we were not in a course, return to home page
1367
        $msg = Display::return_message(
1368
            get_lang('AccountInactive'),
1369
            'error',
1370
            false
1371
        );
1372
1373
        $msg .= '<p class="text-center">
1374
                 <a class="btn btn-default" href="'.$homeUrl.'">'.get_lang('BackHome').'</a></p>';
1375
1376
        if (api_is_anonymous()) {
1377
            $form = api_get_not_allowed_login_form();
1378
            $msg .= '<div class="well">';
1379
            $msg .= $form->returnForm();
1380
            $msg .= '</div>';
1381
        }
1382
1383
        $tpl->assign('content', $msg);
1384
        $tpl->display_one_col_template();
1385
        exit;
1386
    }
1387
1388
    return $data;
1389
}
1390
1391
/**
1392
 * Function used to protect a teacher script.
1393
 * The function blocks access when the user has no teacher rights.
1394
 *
1395
 * @return bool True if the current user can access the script, false otherwise
1396
 *
1397
 * @author Yoselyn Castillo
1398
 */
1399
function api_protect_teacher_script()
1400
{
1401
    if (!api_is_allowed_to_edit()) {
1402
        api_not_allowed(true);
1403
1404
        return false;
1405
    }
1406
1407
    return true;
1408
}
1409
1410
/**
1411
 * Function used to prevent anonymous users from accessing a script.
1412
 *
1413
 * @param bool|true $printHeaders
1414
 *
1415
 * @author Roan Embrechts
1416
 *
1417
 * @return bool
1418
 */
1419
function api_block_anonymous_users($printHeaders = true)
1420
{
1421
    $user = api_get_user_info();
1422
    if (!(isset($user['user_id']) && $user['user_id']) || api_is_anonymous($user['user_id'], true)) {
1423
        api_not_allowed($printHeaders);
1424
1425
        return false;
1426
    }
1427
    api_block_inactive_user();
1428
1429
    return true;
1430
}
1431
1432
/**
1433
 * Returns a rough evaluation of the browser's name and version based on very
1434
 * simple regexp.
1435
 *
1436
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1437
 */
1438
function api_get_navigator()
1439
{
1440
    $navigator = 'Unknown';
1441
    $version = 0;
1442
1443
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1444
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1445
    }
1446
1447
    if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false) {
1448
        $navigator = 'Opera';
1449
        list(, $version) = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1450
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== false) {
1451
        $navigator = 'Edge';
1452
        list(, $version) = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1453
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
1454
        $navigator = 'Internet Explorer';
1455
        list(, $version) = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1456
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false) {
1457
        $navigator = 'Chrome';
1458
        list(, $version) = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1459
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false) {
1460
        $navigator = 'Safari';
1461
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Version/') !== false) {
1462
            // If this Safari does have the "Version/" string in its user agent
1463
            // then use that as a version indicator rather than what's after
1464
            // "Safari/" which is rather a "build number" or something
1465
            list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1466
        } else {
1467
            list(, $version) = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1468
        }
1469
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== false) {
1470
        $navigator = 'Firefox';
1471
        list(, $version) = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1472
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape') !== false) {
1473
        $navigator = 'Netscape';
1474
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/') !== false) {
1475
            list(, $version) = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1476
        } else {
1477
            list(, $version) = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1478
        }
1479
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false) {
1480
        $navigator = 'Konqueror';
1481
        list(, $version) = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1482
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit') !== false) {
1483
        $navigator = 'AppleWebKit';
1484
        list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1485
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false) {
1486
        $navigator = 'Mozilla';
1487
        list(, $version) = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1488
    }
1489
1490
    // Now cut extra stuff around (mostly *after*) the version number
1491
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1492
1493
    if (strpos($version, '.') === false) {
1494
        $version = number_format(doubleval($version), 1);
1495
    }
1496
1497
    return ['name' => $navigator, 'version' => $version];
1498
}
1499
1500
/**
1501
 * @return true if user self registration is allowed, false otherwise
1502
 */
1503
function api_is_self_registration_allowed()
1504
{
1505
    return isset($GLOBALS['allowSelfReg']) ? $GLOBALS['allowSelfReg'] : false;
1506
}
1507
1508
/**
1509
 * This function returns the id of the user which is stored in the $_user array.
1510
 *
1511
 * example: The function can be used to check if a user is logged in
1512
 *          if (api_get_user_id())
1513
 *
1514
 * @return int the id of the current user, 0 if is empty
1515
 */
1516
function api_get_user_id()
1517
{
1518
    $userInfo = Session::read('_user');
1519
    if ($userInfo && isset($userInfo['user_id'])) {
1520
        return (int) $userInfo['user_id'];
1521
    }
1522
1523
    return 0;
1524
}
1525
1526
/**
1527
 * Gets the list of courses a specific user is subscribed to.
1528
 *
1529
 * @param int       User ID
1530
 * @param bool $fetch_session Whether to get session courses or not - NOT YET IMPLEMENTED
1531
 *
1532
 * @return array Array of courses in the form [0]=>('code'=>xxx,'db'=>xxx,'dir'=>xxx,'status'=>d)
1533
 *
1534
 * @deprecated use CourseManager::get_courses_list_by_user_id()
1535
 */
1536
function api_get_user_courses($userId, $fetch_session = true)
1537
{
1538
    // Get out if not integer
1539
    if ($userId != strval(intval($userId))) {
1540
        return [];
1541
    }
1542
1543
    $t_course = Database::get_main_table(TABLE_MAIN_COURSE);
1544
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1545
1546
    $sql = "SELECT cc.id as real_id, cc.code code, cc.directory dir, cu.status status
1547
            FROM $t_course cc, $t_course_user cu
1548
            WHERE
1549
                cc.id = cu.c_id AND
1550
                cu.user_id = $userId AND
1551
                cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH;
1552
    $result = Database::query($sql);
1553
    if ($result === false) {
1554
        return [];
1555
    }
1556
1557
    $courses = [];
1558
    while ($row = Database::fetch_array($result)) {
1559
        // we only need the database name of the course
1560
        $courses[] = $row;
1561
    }
1562
1563
    return $courses;
1564
}
1565
1566
/**
1567
 * Formats user information into a standard array
1568
 * This function should be only used inside api_get_user_info().
1569
 *
1570
 * @param array Non-standard user array
1571
 * @param bool $add_password
1572
 * @param bool $loadAvatars  turn off to improve performance
1573
 *
1574
 * @return array Standard user array
1575
 */
1576
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1577
{
1578
    $result = [];
1579
1580
    $result['firstname'] = null;
1581
    $result['lastname'] = null;
1582
1583
    if (isset($user['firstname']) && isset($user['lastname'])) { // with only lowercase
1584
        $result['firstname'] = $user['firstname'];
1585
        $result['lastname'] = $user['lastname'];
1586
    } elseif (isset($user['firstName']) && isset($user['lastName'])) { // with uppercase letters
1587
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1588
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1589
    }
1590
1591
    if (isset($user['email'])) {
1592
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1593
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1594
    } else {
1595
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1596
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1597
    }
1598
1599
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1600
    $result['complete_name_with_username'] = $result['complete_name'];
1601
1602
    if (!empty($user['username']) && !api_get_configuration_value('hide_username_with_complete_name')) {
1603
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1604
    }
1605
1606
    $showEmail = api_get_setting('show_email_addresses') === 'true';
1607
    if (!empty($user['email'])) {
1608
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1609
        if ($showEmail) {
1610
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1611
        }
1612
    } else {
1613
        $result['complete_name_with_email'] = $result['complete_name'];
1614
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1615
    }
1616
1617
    // Kept for historical reasons
1618
    $result['firstName'] = $result['firstname'];
1619
    $result['lastName'] = $result['lastname'];
1620
1621
    $attributes = [
1622
        'phone',
1623
        'address',
1624
        'picture_uri',
1625
        'official_code',
1626
        'status',
1627
        'active',
1628
        'auth_source',
1629
        'username',
1630
        'theme',
1631
        'language',
1632
        'creator_id',
1633
        'registration_date',
1634
        'hr_dept_id',
1635
        'expiration_date',
1636
        'last_login',
1637
        'user_is_online',
1638
    ];
1639
1640
    if (api_get_setting('extended_profile') === 'true') {
1641
        $attributes[] = 'competences';
1642
        $attributes[] = 'diplomas';
1643
        $attributes[] = 'teach';
1644
        $attributes[] = 'openarea';
1645
    }
1646
1647
    foreach ($attributes as $attribute) {
1648
        $result[$attribute] = isset($user[$attribute]) ? $user[$attribute] : null;
1649
    }
1650
1651
    $user_id = (int) $user['user_id'];
1652
    // Maintain the user_id index for backwards compatibility
1653
    $result['user_id'] = $result['id'] = $user_id;
1654
1655
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1656
    $result['has_certificates'] = 0;
1657
    if (!empty($hasCertificates)) {
1658
        $result['has_certificates'] = 1;
1659
    }
1660
1661
    $result['icon_status'] = '';
1662
    $result['icon_status_medium'] = '';
1663
1664
    $result['is_admin'] = UserManager::is_admin($user_id);
1665
1666
    // Getting user avatar.
1667
    if ($loadAvatars) {
1668
        $result['avatar'] = '';
1669
        $result['avatar_no_query'] = '';
1670
        $result['avatar_small'] = '';
1671
        $result['avatar_medium'] = '';
1672
1673
        if (!isset($user['avatar'])) {
1674
            $originalFile = UserManager::getUserPicture(
1675
                $user_id,
1676
                USER_IMAGE_SIZE_ORIGINAL,
1677
                null,
1678
                $result
1679
            );
1680
            $result['avatar'] = $originalFile;
1681
            $avatarString = explode('?', $result['avatar']);
1682
            $result['avatar_no_query'] = reset($avatarString);
1683
        } else {
1684
            $result['avatar'] = $user['avatar'];
1685
            $avatarString = explode('?', $user['avatar']);
1686
            $result['avatar_no_query'] = reset($avatarString);
1687
        }
1688
1689
        if (!isset($user['avatar_small'])) {
1690
            $smallFile = UserManager::getUserPicture(
1691
                $user_id,
1692
                USER_IMAGE_SIZE_SMALL,
1693
                null,
1694
                $result
1695
            );
1696
            $result['avatar_small'] = $smallFile;
1697
        } else {
1698
            $result['avatar_small'] = $user['avatar_small'];
1699
        }
1700
1701
        if (!isset($user['avatar_medium'])) {
1702
            $mediumFile = UserManager::getUserPicture(
1703
                $user_id,
1704
                USER_IMAGE_SIZE_MEDIUM,
1705
                null,
1706
                $result
1707
            );
1708
            $result['avatar_medium'] = $mediumFile;
1709
        } else {
1710
            $result['avatar_medium'] = $user['avatar_medium'];
1711
        }
1712
1713
        $urlImg = api_get_path(WEB_IMG_PATH);
1714
        $iconStatus = '';
1715
        $iconStatusMedium = '';
1716
        $label = '';
1717
        switch ($result['status']) {
1718
            case STUDENT:
1719
                if ($result['has_certificates']) {
1720
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1721
                    $label = get_lang('Graduated');
1722
                } else {
1723
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1724
                    $label = get_lang('Student');
1725
                }
1726
                break;
1727
            case COURSEMANAGER:
1728
                if ($result['is_admin']) {
1729
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1730
                    $label = get_lang('Admin');
1731
                } else {
1732
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1733
                    $label = get_lang('Teacher');
1734
                }
1735
                break;
1736
            case STUDENT_BOSS:
1737
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1738
                $label = get_lang('StudentBoss');
1739
                break;
1740
        }
1741
1742
        if (!empty($iconStatus)) {
1743
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1744
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1745
        }
1746
1747
        $result['icon_status'] = $iconStatus;
1748
        $result['icon_status_label'] = $label;
1749
        $result['icon_status_medium'] = $iconStatusMedium;
1750
    }
1751
1752
    if (isset($user['user_is_online'])) {
1753
        $result['user_is_online'] = $user['user_is_online'] == true ? 1 : 0;
1754
    }
1755
    if (isset($user['user_is_online_in_chat'])) {
1756
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1757
    }
1758
1759
    if ($add_password) {
1760
        $result['password'] = $user['password'];
1761
    }
1762
1763
    if (isset($result['profile_completed'])) {
1764
        $result['profile_completed'] = $user['profile_completed'];
1765
    }
1766
1767
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1768
1769
    // Send message link
1770
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$user_id;
1771
    $result['complete_name_with_message_link'] = Display::url(
1772
        $result['complete_name_with_username'],
1773
        $sendMessage,
1774
        ['class' => 'ajax']
1775
    );
1776
1777
    if (isset($user['extra'])) {
1778
        $result['extra'] = $user['extra'];
1779
    }
1780
1781
    return $result;
1782
}
1783
1784
/**
1785
 * Finds all the information about a user.
1786
 * If no parameter is passed you find all the information about the current user.
1787
 *
1788
 * @param int  $user_id
1789
 * @param bool $checkIfUserOnline
1790
 * @param bool $showPassword
1791
 * @param bool $loadExtraData
1792
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1793
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1794
 * @param bool $updateCache              update apc cache if exists
1795
 *
1796
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1797
 *
1798
 * @author Patrick Cool <[email protected]>
1799
 * @author Julio Montoya
1800
 *
1801
 * @version 21 September 2004
1802
 */
1803
function api_get_user_info(
1804
    $user_id = 0,
1805
    $checkIfUserOnline = false,
1806
    $showPassword = false,
1807
    $loadExtraData = false,
1808
    $loadOnlyVisibleExtraData = false,
1809
    $loadAvatars = true,
1810
    $updateCache = false
1811
) {
1812
    $apcVar = null;
1813
    $user = false;
1814
    $cacheAvailable = api_get_configuration_value('apc');
1815
1816
    if (empty($user_id)) {
1817
        $userFromSession = Session::read('_user');
1818
1819
        if (isset($userFromSession)) {
1820
            if ($cacheAvailable === true &&
1821
                (
1822
                    empty($userFromSession['is_anonymous']) &&
1823
                    (isset($userFromSession['status']) && $userFromSession['status'] != ANONYMOUS)
1824
                )
1825
            ) {
1826
                $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userFromSession['user_id'];
1827
                if (apcu_exists($apcVar)) {
1828
                    if ($updateCache) {
1829
                        apcu_store($apcVar, $userFromSession, 60);
1830
                    }
1831
                    $user = apcu_fetch($apcVar);
1832
                } else {
1833
                    $user = _api_format_user(
1834
                        $userFromSession,
1835
                        $showPassword,
1836
                        $loadAvatars
1837
                    );
1838
                    apcu_store($apcVar, $user, 60);
1839
                }
1840
            } else {
1841
                $user = _api_format_user(
1842
                    $userFromSession,
1843
                    $showPassword,
1844
                    $loadAvatars
1845
                );
1846
            }
1847
1848
            return $user;
1849
        }
1850
1851
        return false;
1852
    }
1853
1854
    // Make sure user_id is safe
1855
    $user_id = (int) $user_id;
1856
1857
    // Re-use user information if not stale and already stored in APCu
1858
    if ($cacheAvailable === true) {
1859
        $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1860
        if (apcu_exists($apcVar) && $updateCache == false && $checkIfUserOnline == false) {
1861
            $user = apcu_fetch($apcVar);
1862
1863
            return $user;
1864
        }
1865
    }
1866
1867
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1868
            WHERE id = $user_id";
1869
    $result = Database::query($sql);
1870
    if (Database::num_rows($result) > 0) {
1871
        $result_array = Database::fetch_array($result);
1872
        $result_array['user_is_online_in_chat'] = 0;
1873
        if ($checkIfUserOnline) {
1874
            $use_status_in_platform = user_is_online($user_id);
1875
            $result_array['user_is_online'] = $use_status_in_platform;
1876
            $user_online_in_chat = 0;
1877
            if ($use_status_in_platform) {
1878
                $user_status = UserManager::get_extra_user_data_by_field(
1879
                    $user_id,
1880
                    'user_chat_status',
1881
                    false,
1882
                    true
1883
                );
1884
                if ((int) $user_status['user_chat_status'] == 1) {
1885
                    $user_online_in_chat = 1;
1886
                }
1887
            }
1888
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1889
        }
1890
1891
        if ($loadExtraData) {
1892
            $fieldValue = new ExtraFieldValue('user');
1893
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1894
                $user_id,
1895
                $loadOnlyVisibleExtraData
1896
            );
1897
        }
1898
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1899
    }
1900
1901
    if ($cacheAvailable === true) {
1902
        apcu_store($apcVar, $user, 60);
1903
    }
1904
1905
    return $user;
1906
}
1907
1908
/**
1909
 * @param int $userId
1910
 *
1911
 * @return User
1912
 */
1913
function api_get_user_entity($userId)
1914
{
1915
    $userId = (int) $userId;
1916
    $repo = UserManager::getRepository();
1917
1918
    /** @var User $user */
1919
    $user = $repo->find($userId);
1920
1921
    return $user;
1922
}
1923
1924
/**
1925
 * Finds all the information about a user from username instead of user id.
1926
 *
1927
 * @param string $username
1928
 *
1929
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1930
 *
1931
 * @author Yannick Warnier <[email protected]>
1932
 */
1933
function api_get_user_info_from_username($username)
1934
{
1935
    if (empty($username)) {
1936
        return false;
1937
    }
1938
    $username = trim($username);
1939
1940
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1941
            WHERE username='".Database::escape_string($username)."'";
1942
    $result = Database::query($sql);
1943
    if (Database::num_rows($result) > 0) {
1944
        $resultArray = Database::fetch_array($result);
1945
1946
        return _api_format_user($resultArray);
1947
    }
1948
1949
    return false;
1950
}
1951
1952
/**
1953
 * Get first user with an email.
1954
 *
1955
 * @param string $email
1956
 *
1957
 * @return array|bool
1958
 */
1959
function api_get_user_info_from_email($email = '')
1960
{
1961
    if (empty($email)) {
1962
        return false;
1963
    }
1964
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1965
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
1966
    $result = Database::query($sql);
1967
    if (Database::num_rows($result) > 0) {
1968
        $resultArray = Database::fetch_array($result);
1969
1970
        return _api_format_user($resultArray);
1971
    }
1972
1973
    return false;
1974
}
1975
1976
/**
1977
 * @return string
1978
 */
1979
function api_get_course_id()
1980
{
1981
    return Session::read('_cid', null);
1982
}
1983
1984
/**
1985
 * Returns the current course id (integer).
1986
 *
1987
 * @param string $code Optional course code
1988
 *
1989
 * @return int
1990
 */
1991
function api_get_course_int_id($code = null)
1992
{
1993
    if (!empty($code)) {
1994
        $code = Database::escape_string($code);
1995
        $row = Database::select(
1996
            'id',
1997
            Database::get_main_table(TABLE_MAIN_COURSE),
1998
            ['where' => ['code = ?' => [$code]]],
1999
            'first'
2000
        );
2001
2002
        if (is_array($row) && isset($row['id'])) {
2003
            return $row['id'];
2004
        } else {
2005
            return false;
2006
        }
2007
    }
2008
2009
    return Session::read('_real_cid', 0);
2010
}
2011
2012
/**
2013
 * Returns the current course directory.
2014
 *
2015
 * This function relies on api_get_course_info()
2016
 *
2017
 * @param string    The course code - optional (takes it from session if not given)
2018
 *
2019
 * @return string The directory where the course is located inside the Chamilo "courses" directory
2020
 *
2021
 * @author Yannick Warnier <[email protected]>
2022
 */
2023
function api_get_course_path($course_code = null)
2024
{
2025
    $info = !empty($course_code) ? api_get_course_info($course_code) : api_get_course_info();
2026
2027
    return $info['path'];
2028
}
2029
2030
/**
2031
 * Gets a course setting from the current course_setting table. Try always using integer values.
2032
 *
2033
 * @param string $settingName The name of the setting we want from the table
2034
 * @param array  $courseInfo
2035
 * @param bool   $force       force checking the value in the database
2036
 *
2037
 * @return mixed The value of that setting in that table. Return -1 if not found.
2038
 */
2039
function api_get_course_setting($settingName, $courseInfo = [], $force = false)
2040
{
2041
    if (empty($courseInfo)) {
2042
        $courseInfo = api_get_course_info();
2043
    }
2044
2045
    if (empty($courseInfo) || empty($settingName)) {
2046
        return -1;
2047
    }
2048
2049
    $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
2050
2051
    if (empty($courseId)) {
2052
        return -1;
2053
    }
2054
2055
    static $courseSettingInfo = [];
2056
2057
    if ($force) {
2058
        $courseSettingInfo = [];
2059
    }
2060
2061
    if (!isset($courseSettingInfo[$courseId])) {
2062
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
2063
        $settingName = Database::escape_string($settingName);
2064
2065
        $sql = "SELECT variable, value FROM $table
2066
                WHERE c_id = $courseId ";
2067
        $res = Database::query($sql);
2068
        if (Database::num_rows($res) > 0) {
2069
            $result = Database::store_result($res, 'ASSOC');
2070
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
2071
2072
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
2073
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
2074
                if (!is_null($value)) {
2075
                    $result = explode(',', $value);
2076
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
2077
                }
2078
            }
2079
        }
2080
    }
2081
2082
    if (isset($courseSettingInfo[$courseId]) && array_key_exists($settingName, $courseSettingInfo[$courseId])) {
2083
        return $courseSettingInfo[$courseId][$settingName];
2084
    }
2085
2086
    return -1;
2087
}
2088
2089
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
2090
{
2091
    $value = api_get_course_setting($settingName, $courseInfo, true);
2092
2093
    if (-1 === $value) {
2094
        // Check global settings
2095
        $value = api_get_plugin_setting($plugin, $settingName);
2096
        if ($value === 'true') {
2097
            return 1;
2098
        }
2099
        if ($value === 'false') {
2100
            return 0;
2101
        }
2102
        if (null === $value) {
2103
            return -1;
2104
        }
2105
    }
2106
2107
    return $value;
2108
}
2109
2110
/**
2111
 * Gets an anonymous user ID.
2112
 *
2113
 * For some tools that need tracking, like the learnpath tool, it is necessary
2114
 * to have a usable user-id to enable some kind of tracking, even if not
2115
 * perfect. An anonymous ID is taken from the users table by looking for a
2116
 * status of "6" (anonymous).
2117
 *
2118
 * @return int User ID of the anonymous user, or O if no anonymous user found
2119
 */
2120
function api_get_anonymous_id()
2121
{
2122
    // Find if another anon is connected now
2123
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2124
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
2125
    $ip = Database::escape_string(api_get_real_ip());
2126
    $max = (int) api_get_configuration_value('max_anonymous_users');
2127
    if ($max >= 2) {
2128
        $sql = "SELECT * FROM $table as TEL
2129
                JOIN $tableU as U
2130
                ON U.user_id = TEL.login_user_id
2131
                WHERE TEL.user_ip = '$ip'
2132
                    AND U.status = ".ANONYMOUS."
2133
                    AND U.user_id != 2 ";
2134
2135
        $result = Database::query($sql);
2136
        if (empty(Database::num_rows($result))) {
2137
            $login = uniqid('anon_');
2138
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['registration_date ASC']);
2139
            if (count($anonList) >= $max) {
2140
                foreach ($anonList as $userToDelete) {
2141
                    UserManager::delete_user($userToDelete['user_id']);
2142
                    break;
2143
                }
2144
            }
2145
            // Return the user ID
2146
            return UserManager::create_user(
2147
                $login,
2148
                'anon',
2149
                ANONYMOUS,
2150
                ' anonymous@localhost',
2151
                $login,
2152
                $login
2153
            );
2154
        } else {
2155
            $row = Database::fetch_array($result, 'ASSOC');
2156
2157
            return $row['user_id'];
2158
        }
2159
    }
2160
2161
    $table = Database::get_main_table(TABLE_MAIN_USER);
2162
    $sql = "SELECT user_id
2163
            FROM $table
2164
            WHERE status = ".ANONYMOUS." ";
2165
    $res = Database::query($sql);
2166
    if (Database::num_rows($res) > 0) {
2167
        $row = Database::fetch_array($res, 'ASSOC');
2168
2169
        return $row['user_id'];
2170
    }
2171
2172
    // No anonymous user was found.
2173
    return 0;
2174
}
2175
2176
/**
2177
 * @param string $courseCode
2178
 * @param int    $sessionId
2179
 * @param int    $groupId
2180
 *
2181
 * @return string
2182
 */
2183
function api_get_cidreq_params($courseCode, $sessionId = 0, $groupId = 0)
2184
{
2185
    $courseCode = !empty($courseCode) ? htmlspecialchars($courseCode) : '';
2186
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2187
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2188
2189
    $url = 'cidReq='.$courseCode;
2190
    $url .= '&id_session='.$sessionId;
2191
    $url .= '&gidReq='.$groupId;
2192
2193
    return $url;
2194
}
2195
2196
/**
2197
 * Returns the current course url part including session, group, and gradebook params.
2198
 *
2199
 * @param bool   $addSessionId
2200
 * @param bool   $addGroupId
2201
 * @param string $origin
2202
 *
2203
 * @return string Course & session references to add to a URL
2204
 */
2205
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2206
{
2207
    $courseCode = api_get_course_id();
2208
    $url = empty($courseCode) ? '' : 'cidReq='.urlencode(htmlspecialchars($courseCode));
2209
    $origin = empty($origin) ? api_get_origin() : urlencode(Security::remove_XSS($origin));
2210
2211
    if ($addSessionId) {
2212
        if (!empty($url)) {
2213
            $url .= api_get_session_id() == 0 ? '&id_session=0' : '&id_session='.api_get_session_id();
2214
        }
2215
    }
2216
2217
    if ($addGroupId) {
2218
        if (!empty($url)) {
2219
            $url .= api_get_group_id() == 0 ? '&gidReq=0' : '&gidReq='.api_get_group_id();
2220
        }
2221
    }
2222
2223
    if (!empty($url)) {
2224
        $url .= '&gradebook='.intval(api_is_in_gradebook());
2225
        $url .= '&origin='.$origin;
2226
    }
2227
2228
    return $url;
2229
}
2230
2231
/**
2232
 * Get if we visited a gradebook page.
2233
 *
2234
 * @return bool
2235
 */
2236
function api_is_in_gradebook()
2237
{
2238
    return Session::read('in_gradebook', false);
2239
}
2240
2241
/**
2242
 * Set that we are in a page inside a gradebook.
2243
 */
2244
function api_set_in_gradebook()
2245
{
2246
    Session::write('in_gradebook', true);
2247
}
2248
2249
/**
2250
 * Remove gradebook session.
2251
 */
2252
function api_remove_in_gradebook()
2253
{
2254
    Session::erase('in_gradebook');
2255
}
2256
2257
/**
2258
 * Returns the current course info array see api_format_course_array()
2259
 * If the course_code is given, the returned array gives info about that
2260
 * particular course, if none given it gets the course info from the session.
2261
 *
2262
 * @param string $course_code
2263
 *
2264
 * @return array
2265
 */
2266
function api_get_course_info($course_code = null)
2267
{
2268
    if (!empty($course_code)) {
2269
        $course_code = Database::escape_string($course_code);
2270
        $courseId = api_get_course_int_id($course_code);
2271
2272
        if (empty($courseId)) {
2273
            return [];
2274
        }
2275
2276
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2277
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2278
        $sql = "SELECT
2279
                    course.*,
2280
                    course_category.code faCode,
2281
                    course_category.name faName
2282
                FROM $course_table
2283
                LEFT JOIN $course_cat_table
2284
                ON course.category_code = course_category.code
2285
                WHERE course.id = $courseId";
2286
        $result = Database::query($sql);
2287
        $courseInfo = [];
2288
        if (Database::num_rows($result) > 0) {
2289
            $data = Database::fetch_array($result);
2290
            $courseInfo = api_format_course_array($data);
2291
        }
2292
2293
        return $courseInfo;
2294
    }
2295
2296
    global $_course;
2297
    if ($_course == '-1') {
2298
        $_course = [];
2299
    }
2300
2301
    return $_course;
2302
}
2303
2304
/**
2305
 * @param int $courseId
2306
 *
2307
 * @return \Chamilo\CoreBundle\Entity\Course
2308
 */
2309
function api_get_course_entity($courseId = 0)
2310
{
2311
    if (empty($courseId)) {
2312
        $courseId = api_get_course_int_id();
2313
    }
2314
2315
    return Database::getManager()->getRepository('ChamiloCoreBundle:Course')->find($courseId);
2316
}
2317
2318
function api_get_group_entity($id = 0)
2319
{
2320
    if (empty($id)) {
2321
        $id = api_get_group_id();
2322
    }
2323
2324
    return Database::getManager()->getRepository('ChamiloCourseBundle:CGroupInfo')->find($id);
2325
}
2326
2327
/**
2328
 * @param int $id
2329
 *
2330
 * @return \Chamilo\CoreBundle\Entity\Session
2331
 */
2332
function api_get_session_entity($id = 0)
2333
{
2334
    if (empty($id)) {
2335
        $id = api_get_session_id();
2336
    }
2337
2338
    return Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($id);
2339
}
2340
2341
/**
2342
 * Returns the current course info array.
2343
2344
 * Now if the course_code is given, the returned array gives info about that
2345
 * particular course, not specially the current one.
2346
 *
2347
 * @param int $id Numeric ID of the course
2348
 *
2349
 * @return array The course info as an array formatted by api_format_course_array, including category.name
2350
 */
2351
function api_get_course_info_by_id($id = null)
2352
{
2353
    if (!empty($id)) {
2354
        $id = (int) $id;
2355
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2356
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2357
        $sql = "SELECT
2358
                    course.*,
2359
                    course_category.code faCode,
2360
                    course_category.name faName
2361
                FROM $course_table
2362
                LEFT JOIN $course_cat_table
2363
                ON course.category_code = course_category.code
2364
                WHERE course.id = $id";
2365
        $result = Database::query($sql);
2366
        $_course = [];
2367
        if (Database::num_rows($result) > 0) {
2368
            $row = Database::fetch_array($result);
2369
            $_course = api_format_course_array($row);
2370
        }
2371
2372
        return $_course;
2373
    }
2374
2375
    global $_course;
2376
    if ($_course == '-1') {
2377
        $_course = [];
2378
    }
2379
2380
    return $_course;
2381
}
2382
2383
/**
2384
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2385
 * to switch from 'code' to 'id' in the array. This is a legacy feature and is
2386
 * now possibly causing massive confusion as a new "id" field has been added to
2387
 * the course table in 1.9.0.
2388
 *
2389
 * @param $course_data
2390
 *
2391
 * @return array
2392
 *
2393
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2394
 */
2395
function api_format_course_array($course_data)
2396
{
2397
    if (empty($course_data)) {
2398
        return [];
2399
    }
2400
2401
    $_course = [];
2402
    $_course['id'] = $course_data['code'];
2403
    $_course['real_id'] = $course_data['id'];
2404
2405
    // Added
2406
    $_course['code'] = $course_data['code'];
2407
    $_course['name'] = $course_data['title'];
2408
    $_course['title'] = $course_data['title'];
2409
    $_course['official_code'] = $course_data['visual_code'];
2410
    $_course['visual_code'] = $course_data['visual_code'];
2411
    $_course['sysCode'] = $course_data['code'];
2412
    $_course['path'] = $course_data['directory']; // Use as key in path.
2413
    $_course['directory'] = $course_data['directory'];
2414
    $_course['creation_date'] = $course_data['creation_date'];
2415
    $_course['titular'] = $course_data['tutor_name'];
2416
    $_course['tutor_name'] = $course_data['tutor_name'];
2417
    $_course['language'] = $course_data['course_language'];
2418
    $_course['extLink']['url'] = $course_data['department_url'];
2419
    $_course['extLink']['name'] = $course_data['department_name'];
2420
    $_course['categoryCode'] = $course_data['faCode'];
2421
    $_course['category_code'] = $course_data['faCode'];
2422
    $_course['categoryName'] = $course_data['faName'];
2423
    $_course['visibility'] = $course_data['visibility'];
2424
    $_course['subscribe_allowed'] = $course_data['subscribe'];
2425
    $_course['subscribe'] = $course_data['subscribe'];
2426
    $_course['unsubscribe'] = $course_data['unsubscribe'];
2427
    $_course['course_language'] = $course_data['course_language'];
2428
    $_course['activate_legal'] = isset($course_data['activate_legal']) ? $course_data['activate_legal'] : false;
2429
    $_course['legal'] = $course_data['legal'];
2430
    $_course['show_score'] = $course_data['show_score']; //used in the work tool
2431
    $_course['department_name'] = $course_data['department_name'];
2432
    $_course['department_url'] = $course_data['department_url'];
2433
2434
    $courseSys = api_get_path(SYS_COURSE_PATH).$course_data['directory'];
2435
    $webCourseHome = api_get_path(WEB_COURSE_PATH).$course_data['directory'];
2436
2437
    // Course password
2438
    $_course['registration_code'] = !empty($course_data['registration_code']) ? sha1($course_data['registration_code']) : null;
2439
    $_course['disk_quota'] = $course_data['disk_quota'];
2440
    $_course['course_public_url'] = $webCourseHome.'/index.php';
2441
    $_course['course_sys_path'] = $courseSys.'/';
2442
2443
    if (array_key_exists('add_teachers_to_sessions_courses', $course_data)) {
2444
        $_course['add_teachers_to_sessions_courses'] = $course_data['add_teachers_to_sessions_courses'];
2445
    }
2446
2447
    // Course image
2448
    $_course['course_image_source'] = '';
2449
    if (file_exists($courseSys.'/course-pic85x85.png')) {
2450
        $url_image = $webCourseHome.'/course-pic85x85.png';
2451
        $_course['course_image_source'] = $courseSys.'/course-pic85x85.png';
2452
    } else {
2453
        $url_image = Display::return_icon(
2454
            'course.png',
2455
            null,
2456
            null,
2457
            ICON_SIZE_LARGE,
2458
            null,
2459
            true,
2460
            true
2461
        );
2462
    }
2463
    $_course['course_image'] = $url_image;
2464
2465
    // Course large image
2466
    $_course['course_image_large_source'] = '';
2467
    if (file_exists($courseSys.'/course-pic.png')) {
2468
        $url_image = $webCourseHome.'/course-pic.png';
2469
        $_course['course_image_large_source'] = $courseSys.'/course-pic.png';
2470
    } else {
2471
        $url_image = Display::return_icon(
2472
            'session_default.png',
2473
            null,
2474
            null,
2475
            null,
2476
            null,
2477
            true,
2478
            true
2479
        );
2480
    }
2481
2482
    $_course['course_image_large'] = $url_image;
2483
2484
    return $_course;
2485
}
2486
2487
/**
2488
 * Returns a difficult to guess password.
2489
 *
2490
 * @param int $length the length of the password
2491
 *
2492
 * @return string the generated password
2493
 */
2494
function api_generate_password($length = 8)
2495
{
2496
    if ($length < 2) {
2497
        $length = 2;
2498
    }
2499
2500
    $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz';
2501
    $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
2502
    $minNumbers = 2;
2503
    $length = $length - $minNumbers;
2504
    $minLowerCase = round($length / 2);
2505
    $minUpperCase = $length - $minLowerCase;
2506
2507
    $password = '';
2508
    $passwordRequirements = api_get_configuration_value('password_requirements');
2509
2510
    $factory = new RandomLib\Factory();
2511
    $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));
2512
2513
    if (!empty($passwordRequirements)) {
2514
        $length = $passwordRequirements['min']['length'];
2515
        $minNumbers = $passwordRequirements['min']['numeric'];
2516
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2517
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2518
2519
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase;
2520
        // Add the rest to fill the length requirement
2521
        if ($rest > 0) {
2522
            $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase);
2523
        }
2524
    }
2525
2526
    // Min digits default 2
2527
    for ($i = 0; $i < $minNumbers; $i++) {
2528
        $password .= $generator->generateInt(2, 9);
2529
    }
2530
2531
    // Min lowercase
2532
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2533
2534
    // Min uppercase
2535
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2536
    $password = str_shuffle($password);
2537
2538
    return $password;
2539
}
2540
2541
/**
2542
 * Checks a password to see wether it is OK to use.
2543
 *
2544
 * @param string $password
2545
 *
2546
 * @return bool if the password is acceptable, false otherwise
2547
 *              Notes about what a password "OK to use" is:
2548
 *              1. The password should be at least 5 characters long.
2549
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2550
 *              3. The password should contain at least 3 letters.
2551
 *              4. It should contain at least 2 digits.
2552
 *              Settings will change if the configuration value is set: password_requirements
2553
 */
2554
function api_check_password($password)
2555
{
2556
    $passwordRequirements = Security::getPasswordRequirements();
2557
2558
    $minLength = $passwordRequirements['min']['length'];
2559
    $minNumbers = $passwordRequirements['min']['numeric'];
2560
    // Optional
2561
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2562
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2563
2564
    $minLetters = $minLowerCase + $minUpperCase;
2565
    $passwordLength = api_strlen($password);
2566
2567
    $conditions = [
2568
        'min_length' => $passwordLength >= $minLength,
2569
    ];
2570
2571
    $digits = 0;
2572
    $lowerCase = 0;
2573
    $upperCase = 0;
2574
2575
    for ($i = 0; $i < $passwordLength; $i++) {
2576
        $currentCharacterCode = api_ord(api_substr($password, $i, 1));
2577
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2578
            $upperCase++;
2579
        }
2580
2581
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2582
            $lowerCase++;
2583
        }
2584
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2585
            $digits++;
2586
        }
2587
    }
2588
2589
    // Min number of digits
2590
    $conditions['min_numeric'] = $digits >= $minNumbers;
2591
2592
    if (!empty($minUpperCase)) {
2593
        // Uppercase
2594
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2595
    }
2596
2597
    if (!empty($minLowerCase)) {
2598
        // Lowercase
2599
        $conditions['min_lowercase'] = $upperCase >= $minLowerCase;
2600
    }
2601
2602
    // Min letters
2603
    $letters = $upperCase + $lowerCase;
2604
    $conditions['min_letters'] = $letters >= $minLetters;
2605
2606
    $isPasswordOk = true;
2607
    foreach ($conditions as $condition) {
2608
        if ($condition === false) {
2609
            $isPasswordOk = false;
2610
            break;
2611
        }
2612
    }
2613
2614
    if ($isPasswordOk === false) {
2615
        $output = get_lang('NewPasswordRequirementsNotMatched').'<br />';
2616
        $output .= Security::getPasswordRequirementsToString($conditions);
2617
2618
        Display::addFlash(Display::return_message($output, 'warning', false));
2619
    }
2620
2621
    return $isPasswordOk;
2622
}
2623
2624
/**
2625
 * Clears the user ID from the session if it was the anonymous user. Generally
2626
 * used on out-of-tools pages to remove a user ID that could otherwise be used
2627
 * in the wrong context.
2628
 * This function is to be used in conjunction with the api_set_anonymous()
2629
 * function to simulate the user existence in case of an anonymous visit.
2630
 *
2631
 * @param bool      database check switch - passed to api_is_anonymous()
2632
 *
2633
 * @return bool true if succesfully unregistered, false if not anonymous
2634
 */
2635
function api_clear_anonymous($db_check = false)
2636
{
2637
    global $_user;
2638
    if (isset($_user['user_id']) && api_is_anonymous($_user['user_id'], $db_check)) {
2639
        unset($_user['user_id']);
2640
        Session::erase('_uid');
2641
2642
        return true;
2643
    }
2644
2645
    return false;
2646
}
2647
2648
/**
2649
 * Returns the status string corresponding to the status code.
2650
 *
2651
 * @author Noel Dieschburg
2652
 *
2653
 * @param int $status_code The integer status code (usually in the form of a constant)
2654
 *
2655
 * @return string
2656
 */
2657
function get_status_from_code($status_code)
2658
{
2659
    switch ($status_code) {
2660
        case STUDENT:
2661
            return get_lang('Student', '');
2662
        case COURSEMANAGER:
2663
            return get_lang('Teacher', '');
2664
        case SESSIONADMIN:
2665
            return get_lang('SessionsAdmin', '');
2666
        case DRH:
2667
            return get_lang('Drh', '');
2668
        case ANONYMOUS:
2669
            return get_lang('Anonymous', '');
2670
        case PLATFORM_ADMIN:
2671
            return get_lang('Administrator', '');
2672
        case SESSION_COURSE_COACH:
2673
            return get_lang('SessionCourseCoach', '');
2674
        case SESSION_GENERAL_COACH:
2675
            return get_lang('SessionGeneralCoach', '');
2676
        case COURSE_TUTOR:
2677
            return get_lang('CourseAssistant', '');
2678
        case STUDENT_BOSS:
2679
            return get_lang('StudentBoss', '');
2680
        case INVITEE:
2681
            return get_lang('Invitee', '');
2682
    }
2683
2684
    return '';
2685
}
2686
2687
/**
2688
 * Sets the current user as anonymous if it hasn't been identified yet. This
2689
 * function should be used inside a tool only. The function api_clear_anonymous()
2690
 * acts in the opposite direction by clearing the anonymous user's data every
2691
 * time we get on a course homepage or on a neutral page (index, admin, my space).
2692
 *
2693
 * @return bool true if set user as anonymous, false if user was already logged in or anonymous id could not be found
2694
 */
2695
function api_set_anonymous()
2696
{
2697
    global $_user;
2698
2699
    if (!empty($_user['user_id'])) {
2700
        return false;
2701
    }
2702
2703
    $user_id = api_get_anonymous_id();
2704
    if ($user_id == 0) {
2705
        return false;
2706
    }
2707
2708
    if (isset($_user['is_anonymous'])) {
2709
        return false;
2710
    }
2711
2712
    Session::erase('_user');
2713
    $_user['user_id'] = $user_id;
2714
    $_user['is_anonymous'] = true;
2715
    $GLOBALS['_user'] = $_user;
2716
    Session::write('_user', $_user);
2717
2718
    return true;
2719
}
2720
2721
/**
2722
 * Gets the current Chamilo (not PHP/cookie) session ID.
2723
 *
2724
 * @return int O if no active session, the session ID otherwise
2725
 */
2726
function api_get_session_id()
2727
{
2728
    return (int) Session::read('id_session', 0);
2729
}
2730
2731
/**
2732
 * Gets the current Chamilo (not social network) group ID.
2733
 *
2734
 * @return int O if no active group, the group id otherwise
2735
 */
2736
function api_get_group_id()
2737
{
2738
    return (int) Session::read('_gid', 0);
2739
}
2740
2741
/**
2742
 * Gets the current or given session name.
2743
 *
2744
 * @param   int     Session ID (optional)
2745
 *
2746
 * @return string The session name, or null if not found
2747
 */
2748
function api_get_session_name($session_id = 0)
2749
{
2750
    if (empty($session_id)) {
2751
        $session_id = api_get_session_id();
2752
        if (empty($session_id)) {
2753
            return null;
2754
        }
2755
    }
2756
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2757
    $s = "SELECT name FROM $t WHERE id = ".(int) $session_id;
2758
    $r = Database::query($s);
2759
    $c = Database::num_rows($r);
2760
    if ($c > 0) {
2761
        //technically, there can be only one, but anyway we take the first
2762
        $rec = Database::fetch_array($r);
2763
2764
        return $rec['name'];
2765
    }
2766
2767
    return null;
2768
}
2769
2770
/**
2771
 * Gets the session info by id.
2772
 *
2773
 * @param int $id Session ID
2774
 *
2775
 * @return array information of the session
2776
 */
2777
function api_get_session_info($id)
2778
{
2779
    return SessionManager::fetch($id);
2780
}
2781
2782
/**
2783
 * Gets the session visibility by session id.
2784
 *
2785
 * @param int  $session_id
2786
 * @param int  $courseId
2787
 * @param bool $ignore_visibility_for_admins
2788
 * @param int  $userId
2789
 *
2790
 * @return int
2791
 *             0 = session still available,
2792
 *             SESSION_VISIBLE_READ_ONLY = 1,
2793
 *             SESSION_VISIBLE = 2,
2794
 *             SESSION_INVISIBLE = 3
2795
 */
2796
function api_get_session_visibility(
2797
    $session_id,
2798
    $courseId = null,
2799
    $ignore_visibility_for_admins = true,
2800
    $userId = 0
2801
) {
2802
    if (api_is_platform_admin()) {
2803
        if ($ignore_visibility_for_admins) {
2804
            return SESSION_AVAILABLE;
2805
        }
2806
    }
2807
2808
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2809
2810
    $now = time();
2811
    if (empty($session_id)) {
2812
        return 0; // Means that the session is still available.
2813
    }
2814
2815
    $session_id = (int) $session_id;
2816
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2817
2818
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2819
2820
    if (Database::num_rows($result) <= 0) {
2821
        return SESSION_INVISIBLE;
2822
    }
2823
2824
    $row = Database::fetch_array($result, 'ASSOC');
2825
    $visibility = $row['visibility'];
2826
2827
    // I don't care the session visibility.
2828
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2829
        // Session duration per student.
2830
        if (isset($row['duration']) && !empty($row['duration'])) {
2831
            $duration = $row['duration'] * 24 * 60 * 60;
2832
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2833
2834
            // If there is a session duration but there is no previous
2835
            // access by the user, then the session is still available
2836
            if (0 == count($courseAccess)) {
2837
                return SESSION_AVAILABLE;
2838
            }
2839
2840
            $currentTime = time();
2841
            $firstAccess = isset($courseAccess['login_course_date'])
2842
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2843
                : 0;
2844
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2845
            $userDuration = isset($userDurationData['duration'])
2846
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2847
                : 0;
2848
2849
            $totalDuration = $firstAccess + $duration + $userDuration;
2850
2851
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : SESSION_VISIBLE_READ_ONLY;
2852
        }
2853
2854
        return SESSION_AVAILABLE;
2855
    }
2856
2857
    // If start date was set.
2858
    if (!empty($row['access_start_date'])) {
2859
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2860
    }
2861
2862
    // If the end date was set.
2863
    if (!empty($row['access_end_date'])) {
2864
        // Only if date_start said that it was ok
2865
        if ($visibility === SESSION_AVAILABLE) {
2866
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2867
                ? SESSION_AVAILABLE // Date still available
2868
                : $row['visibility']; // Session ends
2869
        }
2870
    }
2871
2872
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2873
    $isCoach = api_is_coach($session_id, $courseId, $userId);
2874
2875
    if ($isCoach) {
2876
        // Test start date.
2877
        if (!empty($row['coach_access_start_date'])) {
2878
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2879
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2880
        }
2881
2882
        // Test end date.
2883
        if (!empty($row['coach_access_end_date'])) {
2884
            if ($visibility === SESSION_AVAILABLE) {
2885
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2886
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2887
            }
2888
        }
2889
    }
2890
2891
    return $visibility;
2892
}
2893
2894
/**
2895
 * This function returns a (star) session icon if the session is not null and
2896
 * the user is not a student.
2897
 *
2898
 * @param int $sessionId
2899
 * @param int $statusId  User status id - if 5 (student), will return empty
2900
 *
2901
 * @return string Session icon
2902
 */
2903
function api_get_session_image($sessionId, $statusId)
2904
{
2905
    $sessionId = (int) $sessionId;
2906
    $image = '';
2907
    if ($statusId != STUDENT) {
2908
        // Check whether is not a student
2909
        if ($sessionId > 0) {
2910
            $image = '&nbsp;&nbsp;'.Display::return_icon(
2911
                'star.png',
2912
                get_lang('SessionSpecificResource'),
2913
                ['align' => 'absmiddle'],
2914
                ICON_SIZE_SMALL
2915
            );
2916
        }
2917
    }
2918
2919
    return $image;
2920
}
2921
2922
/**
2923
 * This function add an additional condition according to the session of the course.
2924
 *
2925
 * @param int    $session_id        session id
2926
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
2927
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
2928
 *                                  false for strict session condition
2929
 * @param string $session_field
2930
 *
2931
 * @return string condition of the session
2932
 */
2933
function api_get_session_condition(
2934
    $session_id,
2935
    $and = true,
2936
    $with_base_content = false,
2937
    $session_field = 'session_id'
2938
) {
2939
    $session_id = (int) $session_id;
2940
2941
    if (empty($session_field)) {
2942
        $session_field = 'session_id';
2943
    }
2944
    // Condition to show resources by session
2945
    $condition_add = $and ? ' AND ' : ' WHERE ';
2946
2947
    if ($with_base_content) {
2948
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
2949
    } else {
2950
        if (empty($session_id)) {
2951
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
2952
        } else {
2953
            $condition_session = $condition_add." $session_field = $session_id ";
2954
        }
2955
    }
2956
2957
    return $condition_session;
2958
}
2959
2960
/**
2961
 * Returns the value of a setting from the web-adjustable admin config settings.
2962
 *
2963
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
2964
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
2965
 * instead of
2966
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
2967
 *
2968
 * @param string $variable The variable name
2969
 * @param string $key      The subkey (sub-variable) if any. Defaults to NULL
2970
 *
2971
 * @return string
2972
 *
2973
 * @author René Haentjens
2974
 * @author Bart Mollet
2975
 */
2976
function api_get_setting($variable, $key = null)
2977
{
2978
    global $_setting;
2979
    if ($variable == 'header_extra_content') {
2980
        $filename = api_get_home_path().'header_extra_content.txt';
2981
        if (file_exists($filename)) {
2982
            $value = file_get_contents($filename);
2983
2984
            return $value;
2985
        } else {
2986
            return '';
2987
        }
2988
    }
2989
    if ($variable == 'footer_extra_content') {
2990
        $filename = api_get_home_path().'footer_extra_content.txt';
2991
        if (file_exists($filename)) {
2992
            $value = file_get_contents($filename);
2993
2994
            return $value;
2995
        } else {
2996
            return '';
2997
        }
2998
    }
2999
    $value = null;
3000
    if (is_null($key)) {
3001
        $value = ((isset($_setting[$variable]) && $_setting[$variable] != '') ? $_setting[$variable] : null);
3002
    } else {
3003
        if (isset($_setting[$variable][$key])) {
3004
            $value = $_setting[$variable][$key];
3005
        }
3006
    }
3007
3008
    return $value;
3009
}
3010
3011
/**
3012
 * @param string $plugin
3013
 * @param string $variable
3014
 *
3015
 * @return string
3016
 */
3017
function api_get_plugin_setting($plugin, $variable)
3018
{
3019
    $variableName = $plugin.'_'.$variable;
3020
    $result = api_get_setting($variableName);
3021
3022
    if (isset($result[$plugin])) {
3023
        $value = $result[$plugin];
3024
        $unSerialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
3025
3026
        if (false !== $unSerialized) {
3027
            $value = $unSerialized;
3028
        }
3029
3030
        return $value;
3031
    }
3032
3033
    return null;
3034
}
3035
3036
/**
3037
 * Returns the value of a setting from the web-adjustable admin config settings.
3038
 */
3039
function api_get_settings_params($params)
3040
{
3041
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3042
3043
    return Database::select('*', $table, ['where' => $params]);
3044
}
3045
3046
/**
3047
 * @param array $params example: [id = ? => '1']
3048
 *
3049
 * @return array
3050
 */
3051
function api_get_settings_params_simple($params)
3052
{
3053
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3054
3055
    return Database::select('*', $table, ['where' => $params], 'one');
3056
}
3057
3058
/**
3059
 * Returns the value of a setting from the web-adjustable admin config settings.
3060
 */
3061
function api_delete_settings_params($params)
3062
{
3063
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3064
    $result = Database::delete($table, $params);
3065
3066
    return $result;
3067
}
3068
3069
/**
3070
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
3071
 *
3072
 * @return string Escaped version of $_SERVER['PHP_SELF']
3073
 */
3074
function api_get_self()
3075
{
3076
    return htmlentities($_SERVER['PHP_SELF']);
3077
}
3078
3079
/* USER PERMISSIONS */
3080
3081
/**
3082
 * Checks whether current user is a platform administrator.
3083
 *
3084
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
3085
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
3086
 *
3087
 * @return bool true if the user has platform admin rights,
3088
 *              false otherwise
3089
 *
3090
 * @see usermanager::is_admin(user_id) for a user-id specific function
3091
 */
3092
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
3093
{
3094
    $isAdmin = Session::read('is_platformAdmin');
3095
    if ($isAdmin) {
3096
        return true;
3097
    }
3098
    $user = api_get_user_info();
3099
3100
    return
3101
        isset($user['status']) &&
3102
        (
3103
            ($allowSessionAdmins && $user['status'] == SESSIONADMIN) ||
3104
            ($allowDrh && $user['status'] == DRH)
3105
        );
3106
}
3107
3108
/**
3109
 * Checks whether the user given as user id is in the admin table.
3110
 *
3111
 * @param int $user_id If none provided, will use current user
3112
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
3113
 *
3114
 * @return bool True if the user is admin, false otherwise
3115
 */
3116
function api_is_platform_admin_by_id($user_id = null, $url = null)
3117
{
3118
    $user_id = (int) $user_id;
3119
    if (empty($user_id)) {
3120
        $user_id = api_get_user_id();
3121
    }
3122
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3123
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3124
    $res = Database::query($sql);
3125
    $is_admin = Database::num_rows($res) === 1;
3126
    if (!$is_admin || !isset($url)) {
3127
        return $is_admin;
3128
    }
3129
    // We get here only if $url is set
3130
    $url = (int) $url;
3131
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3132
    $sql = "SELECT * FROM $url_user_table
3133
            WHERE access_url_id = $url AND user_id = $user_id";
3134
    $res = Database::query($sql);
3135
    $result = Database::num_rows($res) === 1;
3136
3137
    return $result;
3138
}
3139
3140
/**
3141
 * Returns the user's numeric status ID from the users table.
3142
 *
3143
 * @param int $user_id If none provided, will use current user
3144
 *
3145
 * @return int User's status (1 for teacher, 5 for student, etc)
3146
 */
3147
function api_get_user_status($user_id = null)
3148
{
3149
    $user_id = (int) $user_id;
3150
    if (empty($user_id)) {
3151
        $user_id = api_get_user_id();
3152
    }
3153
    $table = Database::get_main_table(TABLE_MAIN_USER);
3154
    $sql = "SELECT status FROM $table WHERE user_id = $user_id ";
3155
    $result = Database::query($sql);
3156
    $status = null;
3157
    if (Database::num_rows($result)) {
3158
        $row = Database::fetch_array($result);
3159
        $status = $row['status'];
3160
    }
3161
3162
    return $status;
3163
}
3164
3165
/**
3166
 * Checks whether current user is allowed to create courses.
3167
 *
3168
 * @return bool true if the user has course creation rights,
3169
 *              false otherwise
3170
 */
3171
function api_is_allowed_to_create_course()
3172
{
3173
    if (api_is_platform_admin()) {
3174
        return true;
3175
    }
3176
3177
    // Teachers can only create courses
3178
    if (api_is_teacher()) {
3179
        if (api_get_setting('allow_users_to_create_courses') === 'true') {
3180
            return true;
3181
        } else {
3182
            return false;
3183
        }
3184
    }
3185
3186
    return Session::read('is_allowedCreateCourse');
3187
}
3188
3189
/**
3190
 * Checks whether the current user is a course administrator.
3191
 *
3192
 * @return bool True if current user is a course administrator
3193
 */
3194
function api_is_course_admin()
3195
{
3196
    if (api_is_platform_admin()) {
3197
        return true;
3198
    }
3199
3200
    return Session::read('is_courseAdmin');
3201
}
3202
3203
/**
3204
 * Checks whether the current user is a course coach
3205
 * Based on the presence of user in session.id_coach (session general coach).
3206
 *
3207
 * @return bool True if current user is a course coach
3208
 */
3209
function api_is_session_general_coach()
3210
{
3211
    return Session::read('is_session_general_coach');
3212
}
3213
3214
/**
3215
 * Checks whether the current user is a course tutor
3216
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
3217
 *
3218
 * @return bool True if current user is a course tutor
3219
 */
3220
function api_is_course_tutor()
3221
{
3222
    return Session::read('is_courseTutor');
3223
}
3224
3225
/**
3226
 * @param int $user_id
3227
 * @param int $courseId
3228
 * @param int $session_id
3229
 *
3230
 * @return bool
3231
 */
3232
function api_is_course_session_coach($user_id, $courseId, $session_id)
3233
{
3234
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3235
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3236
3237
    $user_id = (int) $user_id;
3238
    $session_id = (int) $session_id;
3239
    $courseId = (int) $courseId;
3240
3241
    $sql = "SELECT DISTINCT session.id
3242
            FROM $session_table
3243
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3244
            ON session.id = session_rc_ru.session_id
3245
            WHERE
3246
                session_rc_ru.user_id = '".$user_id."'  AND
3247
                session_rc_ru.c_id = '$courseId' AND
3248
                session_rc_ru.status = 2 AND
3249
                session_rc_ru.session_id = '$session_id'";
3250
    $result = Database::query($sql);
3251
3252
    return Database::num_rows($result) > 0;
3253
}
3254
3255
/**
3256
 * Checks whether the current user is a course or session coach.
3257
 *
3258
 * @param int $session_id
3259
 * @param int $courseId
3260
 * @param bool  Check whether we are in student view and, if we are, return false
3261
 * @param int $userId
3262
 *
3263
 * @return bool True if current user is a course or session coach
3264
 */
3265
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3266
{
3267
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3268
3269
    if (!empty($session_id)) {
3270
        $session_id = (int) $session_id;
3271
    } else {
3272
        $session_id = api_get_session_id();
3273
    }
3274
3275
    // The student preview was on
3276
    if ($check_student_view && api_is_student_view_active()) {
3277
        return false;
3278
    }
3279
3280
    if (!empty($courseId)) {
3281
        $courseId = (int) $courseId;
3282
    } else {
3283
        $courseId = api_get_course_int_id();
3284
    }
3285
3286
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3287
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3288
    $sessionIsCoach = [];
3289
3290
    if (!empty($courseId)) {
3291
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3292
                FROM $session_table s
3293
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3294
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3295
                WHERE
3296
                    session_rc_ru.c_id = '$courseId' AND
3297
                    session_rc_ru.status = 2 AND
3298
                    session_rc_ru.session_id = '$session_id'";
3299
        $result = Database::query($sql);
3300
        $sessionIsCoach = Database::store_result($result);
3301
    }
3302
3303
    if (!empty($session_id)) {
3304
        $sql = "SELECT DISTINCT id, name, access_start_date, access_end_date
3305
                FROM $session_table
3306
                WHERE session.id_coach = $userId AND id = $session_id
3307
                ORDER BY access_start_date, access_end_date, name";
3308
        $result = Database::query($sql);
3309
        if (!empty($sessionIsCoach)) {
3310
            $sessionIsCoach = array_merge(
3311
                $sessionIsCoach,
3312
                Database::store_result($result)
3313
            );
3314
        } else {
3315
            $sessionIsCoach = Database::store_result($result);
3316
        }
3317
    }
3318
3319
    return count($sessionIsCoach) > 0;
3320
}
3321
3322
/**
3323
 * Checks whether the current user is a session administrator.
3324
 *
3325
 * @return bool True if current user is a course administrator
3326
 */
3327
function api_is_session_admin()
3328
{
3329
    $user = api_get_user_info();
3330
3331
    return isset($user['status']) && $user['status'] == SESSIONADMIN;
3332
}
3333
3334
/**
3335
 * Checks whether the current user is a human resources manager.
3336
 *
3337
 * @return bool True if current user is a human resources manager
3338
 */
3339
function api_is_drh()
3340
{
3341
    $user = api_get_user_info();
3342
3343
    return isset($user['status']) && $user['status'] == DRH;
3344
}
3345
3346
/**
3347
 * Checks whether the current user is a student.
3348
 *
3349
 * @return bool True if current user is a human resources manager
3350
 */
3351
function api_is_student()
3352
{
3353
    $user = api_get_user_info();
3354
3355
    return isset($user['status']) && $user['status'] == STUDENT;
3356
}
3357
3358
/**
3359
 * Checks whether the current user has the status 'teacher'.
3360
 *
3361
 * @return bool True if current user is a human resources manager
3362
 */
3363
function api_is_teacher()
3364
{
3365
    $user = api_get_user_info();
3366
3367
    return isset($user['status']) && $user['status'] == COURSEMANAGER;
3368
}
3369
3370
/**
3371
 * Checks whether the current user is a invited user.
3372
 *
3373
 * @return bool
3374
 */
3375
function api_is_invitee()
3376
{
3377
    $user = api_get_user_info();
3378
3379
    return isset($user['status']) && $user['status'] == INVITEE;
3380
}
3381
3382
/**
3383
 * This function checks whether a session is assigned into a category.
3384
 *
3385
 * @param int       - session id
3386
 * @param string    - category name
3387
 *
3388
 * @return bool - true if is found, otherwise false
3389
 */
3390
function api_is_session_in_category($session_id, $category_name)
3391
{
3392
    $session_id = (int) $session_id;
3393
    $category_name = Database::escape_string($category_name);
3394
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3395
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3396
3397
    $sql = "SELECT 1
3398
            FROM $tbl_session
3399
            WHERE $session_id IN (
3400
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3401
                WHERE
3402
                  s.session_category_id = sc.id AND
3403
                  sc.name LIKE '%$category_name'
3404
            )";
3405
    $rs = Database::query($sql);
3406
3407
    if (Database::num_rows($rs) > 0) {
3408
        return true;
3409
    } else {
3410
        return false;
3411
    }
3412
}
3413
3414
/**
3415
 * Displays the title of a tool.
3416
 * Normal use: parameter is a string:
3417
 * api_display_tool_title("My Tool").
3418
 *
3419
 * Optionally, there can be a subtitle below
3420
 * the normal title, and / or a supra title above the normal title.
3421
 *
3422
 * e.g. supra title:
3423
 * group
3424
 * GROUP PROPERTIES
3425
 *
3426
 * e.g. subtitle:
3427
 * AGENDA
3428
 * calender & events tool
3429
 *
3430
 * @author Hugues Peeters <[email protected]>
3431
 *
3432
 * @param mixed $title_element - it could either be a string or an array
3433
 *                             containing 'supraTitle', 'mainTitle',
3434
 *                             'subTitle'
3435
 */
3436
function api_display_tool_title($title_element)
3437
{
3438
    if (is_string($title_element)) {
3439
        $tit = $title_element;
3440
        unset($title_element);
3441
        $title_element = [];
3442
        $title_element['mainTitle'] = $tit;
3443
    }
3444
    echo '<h3>';
3445
    if (!empty($title_element['supraTitle'])) {
3446
        echo '<small>'.$title_element['supraTitle'].'</small><br />';
3447
    }
3448
    if (!empty($title_element['mainTitle'])) {
3449
        echo $title_element['mainTitle'];
3450
    }
3451
    if (!empty($title_element['subTitle'])) {
3452
        echo '<br /><small>'.$title_element['subTitle'].'</small>';
3453
    }
3454
    echo '</h3>';
3455
}
3456
3457
/**
3458
 * Displays options for switching between student view and course manager view.
3459
 *
3460
 * Changes in version 1.2 (Patrick Cool)
3461
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3462
 * is changed explicitly
3463
 *
3464
 * Changes in version 1.1 (Patrick Cool)
3465
 * student view now works correctly in subfolders of the document tool
3466
 * student view works correctly in the new links tool
3467
 *
3468
 * Example code for using this in your tools:
3469
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3470
 * //   display_tool_view_option($isStudentView);
3471
 * //}
3472
 * //and in later sections, use api_is_allowed_to_edit()
3473
 *
3474
 * @author Roan Embrechts
3475
 * @author Patrick Cool
3476
 * @author Julio Montoya, changes added in Chamilo
3477
 *
3478
 * @version 1.2
3479
 *
3480
 * @todo rewrite code so it is easier to understand
3481
 */
3482
function api_display_tool_view_option()
3483
{
3484
    if (api_get_setting('student_view_enabled') != 'true') {
3485
        return '';
3486
    }
3487
3488
    $sourceurl = '';
3489
    $is_framed = false;
3490
    // Exceptions apply for all multi-frames pages
3491
    if (strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php') !== false) {
3492
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3493
        return '';
3494
    }
3495
3496
    // Uncomment to remove student view link from document view page
3497
    if (strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php') !== false) {
3498
        if (empty($_GET['lp_id'])) {
3499
            return '';
3500
        }
3501
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3502
        $sourceurl = str_replace(
3503
            'lp/lp_header.php',
3504
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.($_SESSION['studentview'] == 'studentview' ? 'false' : 'true'),
3505
            $sourceurl
3506
        );
3507
        //showinframes doesn't handle student view anyway...
3508
        //return '';
3509
        $is_framed = true;
3510
    }
3511
3512
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3513
    if (!$is_framed) {
3514
        if (strpos($_SERVER['REQUEST_URI'], '?') === false) {
3515
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3516
        } else {
3517
            $sourceurl = $_SERVER['REQUEST_URI'];
3518
        }
3519
    }
3520
3521
    $output_string = '';
3522
    if (!empty($_SESSION['studentview'])) {
3523
        if ($_SESSION['studentview'] == 'studentview') {
3524
            // We have to remove the isStudentView=true from the $sourceurl
3525
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3526
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3527
            $output_string .= '<a class="btn btn-primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3528
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToTeacherView').'</a>';
3529
        } elseif ($_SESSION['studentview'] == 'teacherview') {
3530
            // Switching to teacherview
3531
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3532
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3533
            $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3534
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3535
        }
3536
    } else {
3537
        $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3538
            Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3539
    }
3540
    $output_string = Security::remove_XSS($output_string);
3541
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3542
3543
    return $html;
3544
}
3545
3546
// TODO: This is for the permission section.
3547
/**
3548
 * Function that removes the need to directly use is_courseAdmin global in
3549
 * tool scripts. It returns true or false depending on the user's rights in
3550
 * this particular course.
3551
 * Optionally checking for tutor and coach roles here allows us to use the
3552
 * student_view feature altogether with these roles as well.
3553
 *
3554
 * @param bool  Whether to check if the user has the tutor role
3555
 * @param bool  Whether to check if the user has the coach role
3556
 * @param bool  Whether to check if the user has the session coach role
3557
 * @param bool  check the student view or not
3558
 *
3559
 * @author Roan Embrechts
3560
 * @author Patrick Cool
3561
 * @author Julio Montoya
3562
 *
3563
 * @version 1.1, February 2004
3564
 *
3565
 * @return bool true: the user has the rights to edit, false: he does not
3566
 */
3567
function api_is_allowed_to_edit(
3568
    $tutor = false,
3569
    $coach = false,
3570
    $session_coach = false,
3571
    $check_student_view = true
3572
) {
3573
    $allowSessionAdminEdit = api_get_configuration_value('session_admins_edit_courses_content') === true;
3574
3575
    // Admins can edit anything.
3576
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3577
        //The student preview was on
3578
        if ($check_student_view && api_is_student_view_active()) {
3579
            return false;
3580
        }
3581
3582
        return true;
3583
    }
3584
3585
    $sessionId = api_get_session_id();
3586
3587
    if ($sessionId && api_get_configuration_value('session_courses_read_only_mode')) {
3588
        $efv = new ExtraFieldValue('course');
3589
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3590
            api_get_course_int_id(),
3591
            'session_courses_read_only_mode'
3592
        );
3593
3594
        if (!empty($lockExrafieldField['value'])) {
3595
            return false;
3596
        }
3597
    }
3598
3599
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3600
    $session_visibility = api_get_session_visibility($sessionId);
3601
    $is_courseAdmin = api_is_course_admin();
3602
3603
    if (!$is_courseAdmin && $tutor) {
3604
        // If we also want to check if the user is a tutor...
3605
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3606
    }
3607
3608
    if (!$is_courseAdmin && $coach) {
3609
        // If we also want to check if the user is a coach...';
3610
        // Check if session visibility is read only for coaches.
3611
        if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3612
            $is_allowed_coach_to_edit = false;
3613
        }
3614
3615
        if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3616
            // Check if coach is allowed to edit a course.
3617
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3618
        }
3619
    }
3620
3621
    if (!$is_courseAdmin && $session_coach) {
3622
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3623
    }
3624
3625
    // Check if the student_view is enabled, and if so, if it is activated.
3626
    if (api_get_setting('student_view_enabled') == 'true') {
3627
        if (!empty($sessionId)) {
3628
            // Check if session visibility is read only for coaches.
3629
            if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3630
                $is_allowed_coach_to_edit = false;
3631
            }
3632
3633
            if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3634
                // Check if coach is allowed to edit a course.
3635
                $is_allowed = $is_allowed_coach_to_edit;
3636
            } else {
3637
                $is_allowed = false;
3638
            }
3639
            if ($check_student_view) {
3640
                $is_allowed = $is_allowed && $_SESSION['studentview'] != 'studentview';
3641
            }
3642
        } else {
3643
            if ($check_student_view) {
3644
                $is_allowed = $is_courseAdmin && $_SESSION['studentview'] != 'studentview';
3645
            } else {
3646
                $is_allowed = $is_courseAdmin;
3647
            }
3648
        }
3649
3650
        return $is_allowed;
3651
    } else {
3652
        return $is_courseAdmin;
3653
    }
3654
}
3655
3656
/**
3657
 * Returns true if user is a course coach of at least one course in session.
3658
 *
3659
 * @param int $sessionId
3660
 *
3661
 * @return bool
3662
 */
3663
function api_is_coach_of_course_in_session($sessionId)
3664
{
3665
    if (api_is_platform_admin()) {
3666
        return true;
3667
    }
3668
3669
    $userId = api_get_user_id();
3670
    $courseList = UserManager::get_courses_list_by_session(
3671
        $userId,
3672
        $sessionId
3673
    );
3674
3675
    // Session visibility.
3676
    $visibility = api_get_session_visibility(
3677
        $sessionId,
3678
        null,
3679
        false
3680
    );
3681
3682
    if ($visibility != SESSION_VISIBLE && !empty($courseList)) {
3683
        // Course Coach session visibility.
3684
        $blockedCourseCount = 0;
3685
        $closedVisibilityList = [
3686
            COURSE_VISIBILITY_CLOSED,
3687
            COURSE_VISIBILITY_HIDDEN,
3688
        ];
3689
3690
        foreach ($courseList as $course) {
3691
            // Checking session visibility
3692
            $sessionCourseVisibility = api_get_session_visibility(
3693
                $sessionId,
3694
                $course['real_id']
3695
            );
3696
3697
            $courseIsVisible = !in_array(
3698
                $course['visibility'],
3699
                $closedVisibilityList
3700
            );
3701
            if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3702
                $blockedCourseCount++;
3703
            }
3704
        }
3705
3706
        // If all courses are blocked then no show in the list.
3707
        if ($blockedCourseCount === count($courseList)) {
3708
            $visibility = SESSION_INVISIBLE;
3709
        } else {
3710
            $visibility = SESSION_VISIBLE;
3711
        }
3712
    }
3713
3714
    switch ($visibility) {
3715
        case SESSION_VISIBLE_READ_ONLY:
3716
        case SESSION_VISIBLE:
3717
        case SESSION_AVAILABLE:
3718
            return true;
3719
            break;
3720
        case SESSION_INVISIBLE:
3721
            return false;
3722
    }
3723
3724
    return false;
3725
}
3726
3727
/**
3728
 * Checks if a student can edit contents in a session depending
3729
 * on the session visibility.
3730
 *
3731
 * @param bool $tutor Whether to check if the user has the tutor role
3732
 * @param bool $coach Whether to check if the user has the coach role
3733
 *
3734
 * @return bool true: the user has the rights to edit, false: he does not
3735
 */
3736
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3737
{
3738
    if (api_is_allowed_to_edit($tutor, $coach)) {
3739
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3740
        return true;
3741
    } else {
3742
        $sessionId = api_get_session_id();
3743
3744
        if (0 == $sessionId) {
3745
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3746
            return true;
3747
        } else {
3748
            // I'm in a session and I'm a student
3749
            // Get the session visibility
3750
            $session_visibility = api_get_session_visibility($sessionId);
3751
            // if 5 the session is still available
3752
            switch ($session_visibility) {
3753
                case SESSION_VISIBLE_READ_ONLY: // 1
3754
                    return false;
3755
                case SESSION_VISIBLE:           // 2
3756
                    return true;
3757
                case SESSION_INVISIBLE:         // 3
3758
                    return false;
3759
                case SESSION_AVAILABLE:         //5
3760
                    return true;
3761
            }
3762
        }
3763
    }
3764
3765
    return false;
3766
}
3767
3768
/**
3769
 * Checks whether the user is allowed in a specific tool for a specific action.
3770
 *
3771
 * @param string $tool   the tool we are checking if the user has a certain permission
3772
 * @param string $action the action we are checking (add, edit, delete, move, visibility)
3773
 *
3774
 * @return bool
3775
 *
3776
 * @author Patrick Cool <[email protected]>, Ghent University
3777
 * @author Julio Montoya
3778
 *
3779
 * @version 1.0
3780
 */
3781
function api_is_allowed($tool, $action, $task_id = 0)
3782
{
3783
    $_user = api_get_user_info();
3784
    $_course = api_get_course_info();
3785
3786
    if (api_is_course_admin()) {
3787
        return true;
3788
    }
3789
3790
    if (is_array($_course) and count($_course) > 0) {
3791
        require_once __DIR__.'/../../permissions/permissions_functions.inc.php';
3792
3793
        // Getting the permissions of this user.
3794
        if ($task_id == 0) {
3795
            $user_permissions = get_permissions('user', $_user['user_id']);
3796
            $_SESSION['total_permissions'][$_course['code']] = $user_permissions;
3797
        }
3798
3799
        // Getting the permissions of the task.
3800
        if ($task_id != 0) {
3801
            $task_permissions = get_permissions('task', $task_id);
3802
            $_SESSION['total_permissions'][$_course['code']] = $task_permissions;
3803
        }
3804
        //print_r($_SESSION['total_permissions']);
3805
3806
        // Getting the permissions of the groups of the user
3807
        //$groups_of_user = GroupManager::get_group_ids($_course['db_name'], $_user['user_id']);
3808
3809
        //foreach($groups_of_user as $group)
3810
        //   $this_group_permissions = get_permissions('group', $group);
3811
3812
        // Getting the permissions of the courseroles of the user
3813
        $user_courserole_permissions = get_roles_permissions('user', $_user['user_id']);
3814
3815
        // Getting the permissions of the platformroles of the user
3816
        //$user_platformrole_permissions = get_roles_permissions('user', $_user['user_id'], ', platform');
3817
3818
        // Getting the permissions of the roles of the groups of the user
3819
        //foreach($groups_of_user as $group)
3820
        //    $this_group_courserole_permissions = get_roles_permissions('group', $group);
3821
3822
        // Getting the permissions of the platformroles of the groups of the user
3823
        //foreach($groups_of_user as $group)
3824
        //    $this_group_platformrole_permissions = get_roles_permissions('group', $group, 'platform');
3825
    }
3826
3827
    // If the permissions are limited, we have to map the extended ones to the limited ones.
3828
    if (api_get_setting('permissions') == 'limited') {
3829
        if ($action == 'Visibility') {
3830
            $action = 'Edit';
3831
        }
3832
        if ($action == 'Move') {
3833
            $action = 'Edit';
3834
        }
3835
    }
3836
3837
    // The session that contains all the permissions already exists for this course
3838
    // so there is no need to requery everything.
3839
    //my_print_r($_SESSION['total_permissions'][$_course['code']][$tool]);
3840
    if (is_array($_SESSION['total_permissions'][$_course['code']][$tool])) {
3841
        if (in_array($action, $_SESSION['total_permissions'][$_course['code']][$tool])) {
3842
            return true;
3843
        } else {
3844
            return false;
3845
        }
3846
    }
3847
3848
    return false;
3849
}
3850
3851
/**
3852
 * Tells whether this user is an anonymous user.
3853
 *
3854
 * @param int  $user_id  User ID (optional, will take session ID if not provided)
3855
 * @param bool $db_check Whether to check in the database (true) or simply in
3856
 *                       the session (false) to see if the current user is the anonymous user
3857
 *
3858
 * @return bool true if this user is anonymous, false otherwise
3859
 */
3860
function api_is_anonymous($user_id = null, $db_check = false)
3861
{
3862
    if (!isset($user_id)) {
3863
        $user_id = api_get_user_id();
3864
    }
3865
3866
    if ($db_check) {
3867
        $info = api_get_user_info($user_id);
3868
        if (false === $info || $info['status'] == ANONYMOUS) {
3869
            return true;
3870
        }
3871
    }
3872
3873
    $_user = api_get_user_info();
3874
3875
    if (isset($_user['status']) && $_user['status'] == ANONYMOUS) {
3876
        //if ($_user['user_id'] == 0) {
3877
        // In some cases, api_set_anonymous doesn't seem to be triggered in local.inc.php. Make sure it is.
3878
        // Occurs in agenda for admin links - YW
3879
        global $use_anonymous;
3880
        if (isset($use_anonymous) && $use_anonymous) {
3881
            api_set_anonymous();
3882
        }
3883
3884
        return true;
3885
    }
3886
3887
    return (isset($_user['is_anonymous']) && $_user['is_anonymous'] === true) || $_user === false;
3888
}
3889
3890
/**
3891
 * Displays message "You are not allowed here..." and exits the entire script.
3892
 *
3893
 * @param bool   $print_headers Whether or not to print headers (default = false -> does not print them)
3894
 * @param string $message
3895
 * @param int    $responseCode
3896
 */
3897
function api_not_allowed(
3898
    $print_headers = false,
3899
    $message = null,
3900
    $responseCode = 0
3901
) {
3902
    if (api_get_setting('sso_authentication') === 'true') {
3903
        global $osso;
3904
        if ($osso) {
3905
            $osso->logout();
3906
        }
3907
    }
3908
    $home_url = api_get_path(WEB_PATH);
3909
    $user_id = api_get_user_id();
3910
    $course = api_get_course_id();
3911
3912
    global $this_section;
3913
3914
    if (CustomPages::enabled() && !isset($user_id)) {
3915
        if (empty($user_id)) {
3916
            // Why the CustomPages::enabled() need to be to set the request_uri
3917
            $_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
3918
        }
3919
        CustomPages::display(CustomPages::INDEX_UNLOGGED);
3920
    }
3921
3922
    $origin = api_get_origin();
3923
3924
    $msg = null;
3925
    if (isset($message)) {
3926
        $msg = $message;
3927
    } else {
3928
        $msg = Display::return_message(
3929
            get_lang('NotAllowedClickBack').'
3930
            <script>function goBack(){window.history.back();}</script>',
3931
            'error',
3932
            false
3933
        );
3934
        $msg .= '<p class="text-center">
3935
             <a onclick="goBack();" class="btn btn-default" href="'.$home_url.'">'.get_lang('GoBack').'</a>
3936
             </p>';
3937
    }
3938
3939
    $msg = Display::div($msg, ['align' => 'center']);
3940
3941
    $show_headers = 0;
3942
    if ($print_headers && $origin != 'learnpath') {
3943
        $show_headers = 1;
3944
    }
3945
3946
    $hideBreadCrumb = false;
3947
    if (api_get_configuration_value('hide_breadcrumb_if_not_allowed')) {
3948
        $hideBreadCrumb = true;
3949
    }
3950
3951
    $tpl = new Template(null, $show_headers, $show_headers, $hideBreadCrumb, true, false, true, $responseCode);
3952
    $tpl->assign('hide_login_link', 1);
3953
    $tpl->assign('content', $msg);
3954
3955
    if (($user_id != 0 && !api_is_anonymous()) &&
3956
        (!isset($course) || $course == -1) &&
3957
        empty($_GET['cidReq'])
3958
    ) {
3959
        // if the access is not authorized and there is some login information
3960
        // but the cidReq is not found, assume we are missing course data and send the user
3961
        // to the user_portal
3962
        $tpl->display_one_col_template();
3963
        exit;
3964
    }
3965
3966
    $tplPlugin = new AppPlugin();
3967
    $loginTopRegionContent = $tplPlugin->load_region('login_top', $tpl, true);
3968
    $loginBottomRegionContent = $tplPlugin->load_region('login_bottom', $tpl, true);
3969
3970
    if (!empty($_SERVER['REQUEST_URI']) &&
3971
        (
3972
            !empty($_GET['cidReq']) ||
3973
            $this_section == SECTION_MYPROFILE ||
3974
            $this_section == SECTION_PLATFORM_ADMIN
3975
        )
3976
    ) {
3977
        $courseCode = api_get_course_id();
3978
        // Only display form and return to the previous URL if there was a course ID included
3979
        if ($user_id != 0 && !api_is_anonymous()) {
3980
            //if there is a user ID, then the user is not allowed but the session is still there. Say so and exit
3981
            $tpl->assign('content', $msg);
3982
            $tpl->display_one_col_template();
3983
            exit;
3984
        }
3985
3986
        if (!is_null($courseCode)) {
3987
            api_set_firstpage_parameter($courseCode);
3988
        }
3989
3990
        // If the user has no user ID, then his session has expired
3991
        $form = api_get_not_allowed_login_form();
3992
3993
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (above)
3994
        $content = Display::return_message(get_lang('NotAllowed'), 'error', false);
3995
3996
        if (!empty($courseCode)) {
3997
            $content .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
3998
        }
3999
4000
        if (api_is_cas_activated()) {
4001
            $content .= Display::return_message(sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")), '', false);
4002
            $content .= Display::div(
4003
                Template::displayCASLoginButton(),
4004
                ['align' => 'center']
4005
            );
4006
            $content .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4007
            $content .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4008
            $content .= "<div style='display:none;'>";
4009
        }
4010
        $content .= PHP_EOL.$loginTopRegionContent;
4011
        $content .= '<div class="well">';
4012
        $content .= $form->returnForm();
4013
        $content .= '</div>';
4014
        $content .= PHP_EOL.$loginBottomRegionContent;
4015
        if (api_is_cas_activated()) {
4016
            $content .= "</div>";
4017
        }
4018
4019
        if (!empty($courseCode)) {
4020
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4021
                get_lang('ReturnToCourseHomepage').'</a></p>';
4022
        } else {
4023
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4024
                get_lang('BackHome').'</a></p>';
4025
        }
4026
4027
        $tpl->setLoginBodyClass();
4028
        $tpl->assign('content', $content);
4029
        $tpl->display_one_col_template();
4030
        exit;
4031
    }
4032
4033
    if ($user_id != 0 && !api_is_anonymous()) {
4034
        $tpl->display_one_col_template();
4035
        exit;
4036
    }
4037
4038
    $msg = null;
4039
    // The session is over and we were not in a course,
4040
    // or we try to get directly to a private course without being logged
4041
    $courseId = api_get_course_int_id();
4042
    if (!empty($courseId)) {
4043
        api_set_firstpage_parameter(api_get_course_id());
4044
        $tpl->setLoginBodyClass();
4045
4046
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (bellow)
4047
        $msg = Display::return_message(get_lang('NotAllowed'), 'error', false);
4048
        $msg .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
4049
        $casEnabled = api_is_cas_activated();
4050
        if ($casEnabled) {
4051
            $msg .= Display::return_message(
4052
                sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")),
4053
                '',
4054
                false
4055
            );
4056
            $msg .= Display::div(
4057
                Template::displayCASLoginButton(),
4058
                ['align' => 'center']
4059
            );
4060
            $msg .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4061
            $msg .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4062
            $msg .= "<div style='display:none;'>";
4063
        }
4064
        $form = api_get_not_allowed_login_form();
4065
        $msg .= PHP_EOL.$loginTopRegionContent;
4066
        $msg .= '<div class="well">';
4067
        $msg .= $form->returnForm();
4068
        $msg .= '</div>';
4069
        $msg .= PHP_EOL.$loginBottomRegionContent;
4070
        if ($casEnabled) {
4071
            $msg .= "</div>";
4072
        }
4073
    } else {
4074
        // we were not in a course, return to home page
4075
        $msg = Display::return_message(
4076
            get_lang('NotAllowed'),
4077
            'error',
4078
            false
4079
        );
4080
4081
        $msg .= '<p class="text-center">
4082
                 <a class="btn btn-default" href="'.$home_url.'">'.get_lang('BackHome').'</a>
4083
                 </p>';
4084
4085
        if (!empty($message)) {
4086
            $msg = $message;
4087
        }
4088
4089
        if (api_is_anonymous()) {
4090
            $form = api_get_not_allowed_login_form();
4091
            $msg .= PHP_EOL.$loginTopRegionContent;
4092
            $msg .= '<div class="well">';
4093
            $msg .= $form->returnForm();
4094
            $msg .= '</div>';
4095
            $msg .= PHP_EOL.$loginBottomRegionContent;
4096
        }
4097
    }
4098
4099
    $tpl->assign('content', $msg);
4100
    $tpl->display_one_col_template();
4101
    exit;
4102
}
4103
4104
/**
4105
 * @return FormValidator
4106
 */
4107
function api_get_not_allowed_login_form()
4108
{
4109
    $action = api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']);
4110
    $action = str_replace('&amp;', '&', $action);
4111
    Session::write('redirect_after_not_allow_page', $action);
4112
    $action .= '&redirect_after_not_allow_page=1';
4113
4114
    $form = new FormValidator(
4115
        'formLogin',
4116
        'post',
4117
        $action,
4118
        null,
4119
        ['class' => 'form-stacked']
4120
    );
4121
    $params = [
4122
        'placeholder' => get_lang('UserName'),
4123
        'class' => 'col-md-3',
4124
    ];
4125
    if (api_browser_support('autocapitalize')) {
4126
        $params['autocapitalize'] = 'none';
4127
    }
4128
4129
    $form->addElement(
4130
        'text',
4131
        'login',
4132
        null,
4133
        $params
4134
    );
4135
    $form->addElement(
4136
        'password',
4137
        'password',
4138
        null,
4139
        ['placeholder' => get_lang('Password'), 'class' => 'col-md-3']
4140
    ); //new
4141
    $form->addButtonNext(get_lang('LoginEnter'), 'submitAuth');
4142
4143
    return $form;
4144
}
4145
4146
/**
4147
 * Gets a UNIX timestamp from a database (MySQL) datetime format string.
4148
 *
4149
 * @param string $last_post_datetime standard output date in a sql query
4150
 *
4151
 * @return int timestamp
4152
 *
4153
 * @author Toon Van Hoecke <[email protected]>
4154
 *
4155
 * @version October 2003
4156
 * @desc convert sql date to unix timestamp
4157
 */
4158
function convert_sql_date($last_post_datetime)
4159
{
4160
    list($last_post_date, $last_post_time) = explode(' ', $last_post_datetime);
4161
    list($year, $month, $day) = explode('-', $last_post_date);
4162
    list($hour, $min, $sec) = explode(':', $last_post_time);
4163
4164
    return mktime((int) $hour, (int) $min, (int) $sec, (int) $month, (int) $day, (int) $year);
4165
}
4166
4167
/**
4168
 * Gets item visibility from the item_property table.
4169
 *
4170
 * Getting the visibility is done by getting the last updated visibility entry,
4171
 * using the largest session ID found if session 0 and another was found (meaning
4172
 * the only one that is actually from the session, in case there are results from
4173
 * session 0 *AND* session n).
4174
 *
4175
 * @param array  $_course  Course properties array (result of api_get_course_info())
4176
 * @param string $tool     Tool (learnpath, document, etc)
4177
 * @param int    $id       The item ID in the given tool
4178
 * @param int    $session  The session ID (optional)
4179
 * @param int    $user_id
4180
 * @param string $type
4181
 * @param string $group_id
4182
 *
4183
 * @return int -1 on error, 0 if invisible, 1 if visible
4184
 */
4185
function api_get_item_visibility(
4186
    $_course,
4187
    $tool,
4188
    $id,
4189
    $session = 0,
4190
    $user_id = null,
4191
    $type = null,
4192
    $group_id = null
4193
) {
4194
    if (!is_array($_course) || count($_course) == 0 || empty($tool) || empty($id)) {
4195
        return -1;
4196
    }
4197
4198
    $tool = Database::escape_string($tool);
4199
    $id = (int) $id;
4200
    $session = (int) $session;
4201
    $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
4202
    $course_id = (int) $_course['real_id'];
4203
4204
    $userCondition = '';
4205
    if (!empty($user_id)) {
4206
        $user_id = (int) $user_id;
4207
        $userCondition = " AND to_user_id = $user_id ";
4208
    }
4209
4210
    $typeCondition = '';
4211
    if (!empty($type)) {
4212
        $type = Database::escape_string($type);
4213
        $typeCondition = " AND lastedit_type = '$type' ";
4214
    }
4215
4216
    $groupCondition = '';
4217
    if (!empty($group_id)) {
4218
        $group_id = (int) $group_id;
4219
        $groupCondition = " AND to_group_id = '$group_id' ";
4220
    }
4221
4222
    $sql = "SELECT visibility
4223
            FROM $TABLE_ITEMPROPERTY
4224
            WHERE
4225
                c_id = $course_id AND
4226
                tool = '$tool' AND
4227
                ref = $id AND
4228
                (session_id = $session OR session_id = 0 OR session_id IS NULL)
4229
                $userCondition $typeCondition $groupCondition
4230
            ORDER BY session_id DESC, lastedit_date DESC
4231
            LIMIT 1";
4232
4233
    $res = Database::query($sql);
4234
    if ($res === false || Database::num_rows($res) == 0) {
4235
        return -1;
4236
    }
4237
    $row = Database::fetch_array($res);
4238
4239
    return (int) $row['visibility'];
4240
}
4241
4242
/**
4243
 * Delete a row in the c_item_property table.
4244
 *
4245
 * @param array  $courseInfo
4246
 * @param string $tool
4247
 * @param int    $itemId
4248
 * @param int    $userId
4249
 * @param int    $groupId    group.iid
4250
 * @param int    $sessionId
4251
 *
4252
 * @return false|null
4253
 */
4254
function api_item_property_delete(
4255
    $courseInfo,
4256
    $tool,
4257
    $itemId,
4258
    $userId,
4259
    $groupId = 0,
4260
    $sessionId = 0
4261
) {
4262
    if (empty($courseInfo)) {
4263
        return false;
4264
    }
4265
4266
    $courseId = (int) $courseInfo['real_id'];
4267
4268
    if (empty($courseId) || empty($tool) || empty($itemId)) {
4269
        return false;
4270
    }
4271
4272
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4273
    $tool = Database::escape_string($tool);
4274
    $itemId = intval($itemId);
4275
    $userId = intval($userId);
4276
    $groupId = intval($groupId);
4277
    $sessionId = intval($sessionId);
4278
4279
    $groupCondition = " AND to_group_id = $groupId ";
4280
    if (empty($groupId)) {
4281
        $groupCondition = " AND (to_group_id is NULL OR to_group_id = 0) ";
4282
    }
4283
4284
    $userCondition = " AND to_user_id = $userId ";
4285
    if (empty($userId)) {
4286
        $userCondition = " AND (to_user_id is NULL OR to_user_id = 0) ";
4287
    }
4288
    $sessionCondition = api_get_session_condition($sessionId, true, false, 'session_id');
4289
    $sql = "DELETE FROM $table
4290
            WHERE
4291
                c_id = $courseId AND
4292
                tool  = '$tool' AND
4293
                ref = $itemId
4294
                $sessionCondition
4295
                $userCondition
4296
                $groupCondition
4297
            ";
4298
4299
    Database::query($sql);
4300
}
4301
4302
/**
4303
 * Updates or adds item properties to the Item_propetry table
4304
 * Tool and lastedit_type are language independant strings (langvars->get_lang!).
4305
 *
4306
 * @param array  $_course        array with course properties
4307
 * @param string $tool           tool id, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4308
 * @param int    $item_id        id of the item itself, linked to key of every tool ('id', ...)
4309
 * @param string $last_edit_type add or update action
4310
 *                               (1) message to be translated (in trad4all) : e.g. DocumentAdded, DocumentUpdated;
4311
 *                               (2) "delete"
4312
 *                               (3) "visible"
4313
 *                               (4) "invisible"
4314
 * @param int    $user_id        id of the editing/adding user
4315
 * @param array  $groupInfo      must include group.iid/group.od
4316
 * @param int    $to_user_id     id of the intended user (always has priority over $to_group_id !), only relevant for $type (1)
4317
 * @param string $start_visible  0000-00-00 00:00:00 format
4318
 * @param string $end_visible    0000-00-00 00:00:00 format
4319
 * @param int    $session_id     The session ID, if any, otherwise will default to 0
4320
 *
4321
 * @return bool false if update fails
4322
 *
4323
 * @author Toon Van Hoecke <[email protected]>, Ghent University
4324
 *
4325
 * @version January 2005
4326
 * @desc update the item_properties table (if entry not exists, insert) of the course
4327
 */
4328
function api_item_property_update(
4329
    $_course,
4330
    $tool,
4331
    $item_id,
4332
    $last_edit_type,
4333
    $user_id,
4334
    $groupInfo = [],
4335
    $to_user_id = null,
4336
    $start_visible = '',
4337
    $end_visible = '',
4338
    $session_id = 0
4339
) {
4340
    if (empty($_course)) {
4341
        return false;
4342
    }
4343
4344
    $course_id = $_course['real_id'];
4345
4346
    if (empty($course_id)) {
4347
        return false;
4348
    }
4349
4350
    $to_group_id = 0;
4351
    if (!empty($groupInfo) && isset($groupInfo['iid'])) {
4352
        $to_group_id = (int) $groupInfo['iid'];
4353
    }
4354
4355
    $em = Database::getManager();
4356
4357
    // Definition of variables.
4358
    $tool = Database::escape_string($tool);
4359
    $item_id = (int) $item_id;
4360
    $lastEditTypeNoFilter = $last_edit_type;
4361
    $last_edit_type = Database::escape_string($last_edit_type);
4362
    $user_id = (int) $user_id;
4363
4364
    $startVisible = "NULL";
4365
    if (!empty($start_visible)) {
4366
        $start_visible = Database::escape_string($start_visible);
4367
        $startVisible = "'$start_visible'";
4368
    }
4369
4370
    $endVisible = "NULL";
4371
    if (!empty($end_visible)) {
4372
        $end_visible = Database::escape_string($end_visible);
4373
        $endVisible = "'$end_visible'";
4374
    }
4375
4376
    $to_filter = '';
4377
    $time = api_get_utc_datetime();
4378
4379
    if (!empty($session_id)) {
4380
        $session_id = (int) $session_id;
4381
    } else {
4382
        $session_id = api_get_session_id();
4383
    }
4384
4385
    // Definition of tables.
4386
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4387
4388
    if ($to_user_id <= 0) {
4389
        $to_user_id = null; // No to_user_id set
4390
    }
4391
4392
    if (!is_null($to_user_id)) {
4393
        // $to_user_id has more priority than $to_group_id
4394
        $to_user_id = (int) $to_user_id;
4395
        $to_field = 'to_user_id';
4396
        $to_value = $to_user_id;
4397
    } else {
4398
        // $to_user_id is not set.
4399
        $to_field = 'to_group_id';
4400
        $to_value = $to_group_id;
4401
    }
4402
4403
    $toValueCondition = empty($to_value) ? 'NULL' : "'$to_value'";
4404
    // Set filters for $to_user_id and $to_group_id, with priority for $to_user_id
4405
    $condition_session = " AND session_id = $session_id ";
4406
    if (empty($session_id)) {
4407
        $condition_session = ' AND (session_id = 0 OR session_id IS NULL) ';
4408
    }
4409
4410
    $filter = " c_id = $course_id AND tool = '$tool' AND ref = $item_id $condition_session ";
4411
4412
    // Check whether $to_user_id and $to_group_id are passed in the function call.
4413
    // If both are not passed (both are null) then it is a message for everybody and $to_group_id should be 0 !
4414
    if (is_null($to_user_id) && is_null($to_group_id)) {
4415
        $to_group_id = 0;
4416
    }
4417
4418
    if (!is_null($to_user_id)) {
4419
        // Set filter to intended user.
4420
        $to_filter = " AND to_user_id = $to_user_id $condition_session";
4421
    } else {
4422
        // Set filter to intended group.
4423
        if (($to_group_id != 0) && $to_group_id == strval(intval($to_group_id))) {
4424
            $to_filter = " AND to_group_id = $to_group_id $condition_session";
4425
        }
4426
    }
4427
4428
    // Adding filter if set.
4429
    $filter .= $to_filter;
4430
4431
    // Update if possible
4432
    $set_type = '';
4433
4434
    switch ($lastEditTypeNoFilter) {
4435
        case 'delete':
4436
            // delete = make item only visible for the platform admin.
4437
            $visibility = '2';
4438
            if (!empty($session_id)) {
4439
                // Check whether session id already exist into item_properties for updating visibility or add it.
4440
                $sql = "SELECT session_id FROM $tableItemProperty
4441
                        WHERE
4442
                            c_id = $course_id AND
4443
                            tool = '$tool' AND
4444
                            ref = $item_id AND
4445
                            session_id = $session_id";
4446
                $rs = Database::query($sql);
4447
                if (Database::num_rows($rs) > 0) {
4448
                    $sql = "UPDATE $tableItemProperty
4449
                            SET lastedit_type       = '".str_replace('_', '', ucwords($tool))."Deleted',
4450
                                lastedit_date       = '$time',
4451
                                lastedit_user_id    = $user_id,
4452
                                visibility          = $visibility,
4453
                                session_id          = $session_id $set_type
4454
                            WHERE $filter";
4455
                    $result = Database::query($sql);
4456
                } else {
4457
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
4458
                            VALUES ($course_id, '$tool',$item_id, '$time', $user_id, '$time', '$last_edit_type',$user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4459
                    $result = Database::query($sql);
4460
                    $id = Database::insert_id();
4461
                    if ($id) {
4462
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4463
                        Database::query($sql);
4464
                    }
4465
                }
4466
            } else {
4467
                $sql = "UPDATE $tableItemProperty
4468
                        SET
4469
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Deleted',
4470
                            lastedit_date='$time',
4471
                            lastedit_user_id = $user_id,
4472
                            visibility = $visibility $set_type
4473
                        WHERE $filter";
4474
                $result = Database::query($sql);
4475
            }
4476
            break;
4477
        case 'visible': // Change item to visible.
4478
            $visibility = '1';
4479
            if (!empty($session_id)) {
4480
                // Check whether session id already exist into item_properties for updating visibility or add it.
4481
                $sql = "SELECT session_id FROM $tableItemProperty
4482
                        WHERE
4483
                            c_id = $course_id AND
4484
                            tool = '$tool' AND
4485
                            ref = $item_id AND
4486
                            session_id = $session_id";
4487
                $rs = Database::query($sql);
4488
                if (Database::num_rows($rs) > 0) {
4489
                    $sql = "UPDATE $tableItemProperty
4490
                            SET
4491
                                lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4492
                                lastedit_date='$time',
4493
                                lastedit_user_id = $user_id,
4494
                                visibility = $visibility,
4495
                                session_id = $session_id $set_type
4496
                            WHERE $filter";
4497
                    $result = Database::query($sql);
4498
                } else {
4499
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
4500
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4501
                    $result = Database::query($sql);
4502
                    $id = Database::insert_id();
4503
                    if ($id) {
4504
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4505
                        Database::query($sql);
4506
                    }
4507
                }
4508
            } else {
4509
                $sql = "UPDATE $tableItemProperty
4510
                        SET
4511
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4512
                            lastedit_date='$time',
4513
                            lastedit_user_id = $user_id,
4514
                            visibility = $visibility $set_type
4515
                        WHERE $filter";
4516
                $result = Database::query($sql);
4517
            }
4518
            break;
4519
        case 'invisible': // Change item to invisible.
4520
            $visibility = '0';
4521
            if (!empty($session_id)) {
4522
                // Check whether session id already exist into item_properties for updating visibility or add it
4523
                $sql = "SELECT session_id FROM $tableItemProperty
4524
                        WHERE
4525
                            c_id = $course_id AND
4526
                            tool = '$tool' AND
4527
                            ref = $item_id AND
4528
                            session_id = $session_id";
4529
                $rs = Database::query($sql);
4530
                if (Database::num_rows($rs) > 0) {
4531
                    $sql = "UPDATE $tableItemProperty
4532
                            SET
4533
                                lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4534
                                lastedit_date = '$time',
4535
                                lastedit_user_id = $user_id,
4536
                                visibility = $visibility,
4537
                                session_id = $session_id $set_type
4538
                            WHERE $filter";
4539
                    $result = Database::query($sql);
4540
                } else {
4541
                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id,$to_field, visibility, start_visible, end_visible, session_id)
4542
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4543
                    $result = Database::query($sql);
4544
                    $id = Database::insert_id();
4545
                    if ($id) {
4546
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4547
                        Database::query($sql);
4548
                    }
4549
                }
4550
            } else {
4551
                $sql = "UPDATE $tableItemProperty
4552
                        SET
4553
                            lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4554
                            lastedit_date = '$time',
4555
                            lastedit_user_id = $user_id,
4556
                            visibility = $visibility $set_type
4557
                        WHERE $filter";
4558
                $result = Database::query($sql);
4559
            }
4560
            break;
4561
        default: // The item will be added or updated.
4562
            $set_type = ", lastedit_type = '$last_edit_type' ";
4563
            $visibility = '1';
4564
            //$filter .= $to_filter; already added
4565
            $sql = "UPDATE $tableItemProperty
4566
                    SET
4567
                      lastedit_date = '$time',
4568
                      lastedit_user_id = $user_id $set_type
4569
                    WHERE $filter";
4570
            $result = Database::query($sql);
4571
    }
4572
4573
    // Insert if no entries are found (can only happen in case of $last_edit_type switch is 'default').
4574
    if ($result == false || Database::affected_rows($result) == 0) {
4575
        $objCourse = $em->find('ChamiloCoreBundle:Course', intval($course_id));
4576
        $objTime = new DateTime('now', new DateTimeZone('UTC'));
4577
        $objUser = api_get_user_entity($user_id);
4578
        if (empty($objUser)) {
4579
            // Use anonymous
4580
            $user_id = api_get_anonymous_id();
4581
            $objUser = api_get_user_entity($user_id);
4582
        }
4583
4584
        $objGroup = null;
4585
        if (!empty($to_group_id)) {
4586
            $objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', $to_group_id);
4587
        }
4588
4589
        $objToUser = api_get_user_entity($to_user_id);
4590
        $objSession = $em->find('ChamiloCoreBundle:Session', intval($session_id));
4591
4592
        $startVisibleDate = !empty($start_visible) ? new DateTime($start_visible, new DateTimeZone('UTC')) : null;
4593
        $endVisibleDate = !empty($endVisibleDate) ? new DateTime($endVisibleDate, new DateTimeZone('UTC')) : null;
4594
4595
        $cItemProperty = new CItemProperty($objCourse);
4596
        $cItemProperty
4597
            ->setTool($tool)
4598
            ->setRef($item_id)
4599
            ->setInsertDate($objTime)
4600
            ->setInsertUser($objUser)
4601
            ->setLasteditDate($objTime)
4602
            ->setLasteditType($last_edit_type)
4603
            ->setGroup($objGroup)
4604
            ->setToUser($objToUser)
4605
            ->setVisibility($visibility)
4606
            ->setStartVisible($startVisibleDate)
4607
            ->setEndVisible($endVisibleDate)
4608
            ->setSession($objSession);
4609
4610
        $em->persist($cItemProperty);
4611
        $em->flush();
4612
4613
        $id = $cItemProperty->getIid();
4614
4615
        if ($id) {
4616
            $cItemProperty->setId($id);
4617
            $em->merge($cItemProperty);
4618
            $em->flush();
4619
4620
            return false;
4621
        }
4622
    }
4623
4624
    return true;
4625
}
4626
4627
/**
4628
 * Gets item property by tool.
4629
 *
4630
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4631
 * @param string $course_code
4632
 * @param int    $session_id
4633
 *
4634
 * @return array All fields from c_item_property (all rows found) or empty array
4635
 */
4636
function api_get_item_property_by_tool($tool, $course_code, $session_id = null)
4637
{
4638
    $course_info = api_get_course_info($course_code);
4639
    $tool = Database::escape_string($tool);
4640
4641
    // Definition of tables.
4642
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4643
    $session_id = (int) $session_id;
4644
    $session_condition = ' AND session_id = '.$session_id;
4645
    if (empty($session_id)) {
4646
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4647
    }
4648
    $course_id = $course_info['real_id'];
4649
4650
    $sql = "SELECT * FROM $item_property_table
4651
            WHERE
4652
                c_id = $course_id AND
4653
                tool = '$tool'
4654
                $session_condition ";
4655
    $rs = Database::query($sql);
4656
    $list = [];
4657
    if (Database::num_rows($rs) > 0) {
4658
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4659
            $list[] = $row;
4660
        }
4661
    }
4662
4663
    return $list;
4664
}
4665
4666
/**
4667
 * Gets item property by tool and user.
4668
 *
4669
 * @param int $userId
4670
 * @param int $tool
4671
 * @param int $courseId
4672
 * @param int $session_id
4673
 *
4674
 * @return array
4675
 */
4676
function api_get_item_property_list_by_tool_by_user(
4677
    $userId,
4678
    $tool,
4679
    $courseId,
4680
    $session_id = 0
4681
) {
4682
    $userId = intval($userId);
4683
    $tool = Database::escape_string($tool);
4684
    $session_id = intval($session_id);
4685
    $courseId = intval($courseId);
4686
4687
    // Definition of tables.
4688
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4689
    $session_condition = ' AND session_id = '.$session_id;
4690
    if (empty($session_id)) {
4691
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4692
    }
4693
    $sql = "SELECT * FROM $item_property_table
4694
            WHERE
4695
                insert_user_id = $userId AND
4696
                c_id = $courseId AND
4697
                tool = '$tool'
4698
                $session_condition ";
4699
4700
    $rs = Database::query($sql);
4701
    $list = [];
4702
    if (Database::num_rows($rs) > 0) {
4703
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4704
            $list[] = $row;
4705
        }
4706
    }
4707
4708
    return $list;
4709
}
4710
4711
/**
4712
 * Gets item property id from tool of a course.
4713
 *
4714
 * @param string $course_code course code
4715
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4716
 * @param int    $ref         id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4717
 * @param int    $sessionId   Session ID (optional)
4718
 *
4719
 * @return int
4720
 */
4721
function api_get_item_property_id($course_code, $tool, $ref, $sessionId = 0)
4722
{
4723
    $course_info = api_get_course_info($course_code);
4724
    $tool = Database::escape_string($tool);
4725
    $ref = (int) $ref;
4726
4727
    // Definition of tables.
4728
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4729
    $course_id = $course_info['real_id'];
4730
    $sessionId = (int) $sessionId;
4731
    $sessionCondition = " AND session_id = $sessionId ";
4732
    if (empty($sessionId)) {
4733
        $sessionCondition = ' AND (session_id = 0 OR session_id IS NULL) ';
4734
    }
4735
    $sql = "SELECT id FROM $tableItemProperty
4736
            WHERE
4737
                c_id = $course_id AND
4738
                tool = '$tool' AND
4739
                ref = $ref
4740
                $sessionCondition";
4741
    $rs = Database::query($sql);
4742
    $item_property_id = '';
4743
    if (Database::num_rows($rs) > 0) {
4744
        $row = Database::fetch_array($rs);
4745
        $item_property_id = $row['id'];
4746
    }
4747
4748
    return $item_property_id;
4749
}
4750
4751
/**
4752
 * Inserts a record in the track_e_item_property table (No update).
4753
 *
4754
 * @param string $tool
4755
 * @param int    $ref
4756
 * @param string $title
4757
 * @param string $content
4758
 * @param int    $progress
4759
 *
4760
 * @return bool|int
4761
 */
4762
function api_track_item_property_update($tool, $ref, $title, $content, $progress)
4763
{
4764
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4765
    $course_id = api_get_course_int_id(); //numeric
4766
    $course_code = api_get_course_id(); //alphanumeric
4767
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4768
    if (!empty($item_property_id)) {
4769
        $sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
4770
                course_id           = '$course_id',
4771
                item_property_id    = '$item_property_id',
4772
                title               = '".Database::escape_string($title)."',
4773
                content             = '".Database::escape_string($content)."',
4774
                progress            = '".intval($progress)."',
4775
                lastedit_date       = '".api_get_utc_datetime()."',
4776
                lastedit_user_id    = '".api_get_user_id()."',
4777
                session_id          = '".api_get_session_id()."'";
4778
        $result = Database::query($sql);
4779
        $affected_rows = Database::affected_rows($result);
4780
4781
        return $affected_rows;
4782
    }
4783
4784
    return false;
4785
}
4786
4787
/**
4788
 * @param string $tool
4789
 * @param int    $ref
4790
 *
4791
 * @return array|resource
4792
 */
4793
function api_get_track_item_property_history($tool, $ref)
4794
{
4795
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4796
    $course_id = api_get_course_int_id(); //numeric
4797
    $course_code = api_get_course_id(); //alphanumeric
4798
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4799
    $sql = "SELECT * FROM $tbl_stats_item_property
4800
            WHERE item_property_id = $item_property_id AND course_id = $course_id
4801
            ORDER BY lastedit_date DESC";
4802
    $result = Database::query($sql);
4803
    if ($result === false or $result === null) {
4804
        $result = [];
4805
    } else {
4806
        $result = Database::store_result($result, 'ASSOC');
4807
    }
4808
4809
    return $result;
4810
}
4811
4812
/**
4813
 * Gets item property data from tool of a course id.
4814
 *
4815
 * @param int    $course_id
4816
 * @param string $tool       tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4817
 * @param int    $ref        id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4818
 * @param int    $session_id
4819
 * @param int    $groupId
4820
 *
4821
 * @return array with all fields from c_item_property, empty array if not found or false if course could not be found
4822
 */
4823
function api_get_item_property_info($course_id, $tool, $ref, $session_id = 0, $groupId = 0)
4824
{
4825
    $courseInfo = api_get_course_info_by_id($course_id);
4826
4827
    if (empty($courseInfo)) {
4828
        return false;
4829
    }
4830
4831
    $tool = Database::escape_string($tool);
4832
    $course_id = $courseInfo['real_id'];
4833
    $ref = (int) $ref;
4834
    $session_id = (int) $session_id;
4835
4836
    $sessionCondition = " session_id = $session_id";
4837
    if (empty($session_id)) {
4838
        $sessionCondition = ' (session_id = 0 OR session_id IS NULL) ';
4839
    }
4840
4841
    // Definition of tables.
4842
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4843
4844
    $sql = "SELECT * FROM $table
4845
            WHERE
4846
                c_id = $course_id AND
4847
                tool = '$tool' AND
4848
                ref = $ref AND
4849
                $sessionCondition ";
4850
4851
    if (!empty($groupId)) {
4852
        $groupId = (int) $groupId;
4853
        $sql .= " AND to_group_id = $groupId ";
4854
    }
4855
4856
    $rs = Database::query($sql);
4857
    $row = [];
4858
    if (Database::num_rows($rs) > 0) {
4859
        $row = Database::fetch_array($rs, 'ASSOC');
4860
    }
4861
4862
    return $row;
4863
}
4864
4865
/**
4866
 * Displays a combo box so the user can select his/her preferred language.
4867
 *
4868
 * @param string The desired name= value for the select
4869
 * @param bool Whether we use the JQuery Chozen library or not
4870
 * (in some cases, like the indexing language picker, it can alter the presentation)
4871
 *
4872
 * @return string
4873
 */
4874
function api_get_languages_combo($name = 'language')
4875
{
4876
    $ret = '';
4877
    $platformLanguage = api_get_setting('platformLanguage');
4878
4879
    // Retrieve a complete list of all the languages.
4880
    $language_list = api_get_languages();
4881
4882
    if (count($language_list['name']) < 2) {
4883
        return $ret;
4884
    }
4885
4886
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
4887
    if (isset($_SESSION['user_language_choice'])) {
4888
        $default = $_SESSION['user_language_choice'];
4889
    } else {
4890
        $default = $platformLanguage;
4891
    }
4892
4893
    $languages = $language_list['name'];
4894
    $folder = $language_list['folder'];
4895
4896
    $ret .= '<select name="'.$name.'" id="language_chosen" class="selectpicker form-control">';
4897
    foreach ($languages as $key => $value) {
4898
        if ($folder[$key] == $default) {
4899
            $selected = ' selected="selected"';
4900
        } else {
4901
            $selected = '';
4902
        }
4903
        $ret .= sprintf('<option value=%s" %s>%s</option>', $folder[$key], $selected, $value);
4904
    }
4905
    $ret .= '</select>';
4906
4907
    return $ret;
4908
}
4909
4910
/**
4911
 * Displays a form (drop down menu) so the user can select his/her preferred language.
4912
 * The form works with or without javascript.
4913
 *
4914
 * @param  bool Hide form if only one language available (defaults to false = show the box anyway)
4915
 * @param bool $showAsButton
4916
 *
4917
 * @return string|null Display the box directly
4918
 */
4919
function api_display_language_form($hide_if_no_choice = false, $showAsButton = false)
4920
{
4921
    // Retrieve a complete list of all the languages.
4922
    $language_list = api_get_languages();
4923
    if (count($language_list['name']) <= 1 && $hide_if_no_choice) {
4924
        return null; //don't show any form
4925
    }
4926
4927
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
4928
    if (isset($_SESSION['user_language_choice'])) {
4929
        $user_selected_language = $_SESSION['user_language_choice'];
4930
    }
4931
    if (empty($user_selected_language)) {
4932
        $user_selected_language = api_get_setting('platformLanguage');
4933
    }
4934
4935
    $currentLanguageId = api_get_language_id($user_selected_language);
4936
    $currentLanguageInfo = api_get_language_info($currentLanguageId);
4937
4938
    $countryCode = languageCodeToCountryIsoCodeForFlags($currentLanguageInfo['isocode']);
4939
    $url = api_get_self();
4940
    if ($showAsButton) {
4941
        $html = '<div class="btn-group">
4942
              <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
4943
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
4944
                '.$currentLanguageInfo['original_name'].'
4945
                <span class="caret">
4946
                </span>
4947
              </button>';
4948
    } else {
4949
        $html = '
4950
            <a href="'.$url.'" class="dropdown-toggle" data-toggle="dropdown" role="button">
4951
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
4952
                '.$currentLanguageInfo['original_name'].'
4953
                <span class="caret"></span>
4954
            </a>
4955
            ';
4956
    }
4957
4958
    $html .= '<ul class="dropdown-menu" role="menu">';
4959
    foreach ($language_list['all'] as $key => $data) {
4960
        $urlLink = $url.'?language='.$data['english_name'];
4961
        $html .= '<li><a href="'.$urlLink.'"><span class="flag-icon flag-icon-'.languageCodeToCountryIsoCodeForFlags($data['isocode']).'"></span> '.$data['original_name'].'</a></li>';
4962
    }
4963
    $html .= '</ul>';
4964
4965
    if ($showAsButton) {
4966
        $html .= '</div>';
4967
    }
4968
4969
    return $html;
4970
}
4971
4972
/**
4973
 * Return a country code based on a language in order to show a country flag.
4974
 * Note: Showing a "language" flag is arguably a bad idea, as several countries
4975
 * share languages and the right flag cannot be shown for all of them.
4976
 *
4977
 * @param string $languageIsoCode
4978
 *
4979
 * @return string
4980
 */
4981
function languageCodeToCountryIsoCodeForFlags($languageIsoCode)
4982
{
4983
    $allow = api_get_configuration_value('language_flags_by_country');
4984
4985
    // @todo save in DB
4986
    switch ($languageIsoCode) {
4987
        case 'ar':
4988
            $country = 'ae';
4989
            break;
4990
        case 'bs':
4991
            $country = 'ba';
4992
            break;
4993
        case 'ca':
4994
            $country = 'es';
4995
            if ($allow) {
4996
                $country = 'catalan';
4997
            }
4998
            break;
4999
        case 'cs':
5000
            $country = 'cz';
5001
            break;
5002
        case 'da':
5003
            $country = 'dk';
5004
            break;
5005
        case 'el':
5006
            $country = 'gr';
5007
            break;
5008
        case 'en':
5009
            $country = 'gb';
5010
            break;
5011
        case 'eu': // Euskera
5012
            $country = 'es';
5013
            if ($allow) {
5014
                $country = 'basque';
5015
            }
5016
            break;
5017
        case 'gl': // galego
5018
            $country = 'es';
5019
            if ($allow) {
5020
                $country = 'galician';
5021
            }
5022
            break;
5023
        case 'he':
5024
            $country = 'il';
5025
            break;
5026
        case 'ja':
5027
            $country = 'jp';
5028
            break;
5029
        case 'ka':
5030
            $country = 'ge';
5031
            break;
5032
        case 'ko':
5033
            $country = 'kr';
5034
            break;
5035
        case 'ms':
5036
            $country = 'my';
5037
            break;
5038
        case 'pt-BR':
5039
            $country = 'br';
5040
            break;
5041
        case 'qu':
5042
            $country = 'pe';
5043
            break;
5044
        case 'sl':
5045
            $country = 'si';
5046
            break;
5047
        case 'sv':
5048
            $country = 'se';
5049
            break;
5050
        case 'uk': // Ukraine
5051
            $country = 'ua';
5052
            break;
5053
        case 'zh-TW':
5054
        case 'zh':
5055
            $country = 'cn';
5056
            break;
5057
        default:
5058
            $country = $languageIsoCode;
5059
            break;
5060
    }
5061
    $country = strtolower($country);
5062
5063
    return $country;
5064
}
5065
5066
/**
5067
 * Returns a list of all the languages that are made available by the admin.
5068
 *
5069
 * @return array An array with all languages. Structure of the array is
5070
 *               array['name'] = An array with the name of every language
5071
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
5072
 */
5073
function api_get_languages()
5074
{
5075
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5076
    $sql = "SELECT * FROM $tbl_language WHERE available='1'
5077
            ORDER BY original_name ASC";
5078
    $result = Database::query($sql);
5079
    $language_list = [];
5080
    while ($row = Database::fetch_array($result)) {
5081
        $language_list['name'][] = $row['original_name'];
5082
        $language_list['folder'][] = $row['dokeos_folder'];
5083
        $language_list['all'][] = $row;
5084
    }
5085
5086
    return $language_list;
5087
}
5088
5089
/**
5090
 * Returns a list of all the languages that are made available by the admin.
5091
 *
5092
 * @return array
5093
 */
5094
function api_get_languages_to_array()
5095
{
5096
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5097
    $sql = "SELECT * FROM $tbl_language
5098
            WHERE available='1' ORDER BY original_name ASC";
5099
    $result = Database::query($sql);
5100
    $languages = [];
5101
    while ($row = Database::fetch_array($result)) {
5102
        $languages[$row['dokeos_folder']] = $row['original_name'];
5103
    }
5104
5105
    return $languages;
5106
}
5107
5108
/**
5109
 * Returns the id (the database id) of a language.
5110
 *
5111
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
5112
 *
5113
 * @return int id of the language
5114
 */
5115
function api_get_language_id($language)
5116
{
5117
    if (empty($language)) {
5118
        return null;
5119
    }
5120
5121
    static $staticResult = [];
5122
5123
    if (isset($staticResult[$language])) {
5124
        return $staticResult[$language];
5125
    } else {
5126
        $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5127
        $language = Database::escape_string($language);
5128
        $sql = "SELECT id FROM $table
5129
                WHERE dokeos_folder = '$language' LIMIT 1";
5130
        $result = Database::query($sql);
5131
        $row = Database::fetch_array($result);
5132
5133
        $staticResult[$language] = $row['id'];
5134
5135
        return $row['id'];
5136
    }
5137
}
5138
5139
/**
5140
 * Gets language of the requested type for the current user. Types are :
5141
 * user_profil_lang : profile language of current user
5142
 * user_select_lang : language selected by user at login
5143
 * course_lang : language of the current course
5144
 * platform_lang : default platform language.
5145
 *
5146
 * @param string $lang_type
5147
 *
5148
 * @return string
5149
 */
5150
function api_get_language_from_type($lang_type)
5151
{
5152
    $return = false;
5153
    switch ($lang_type) {
5154
        case 'platform_lang':
5155
            $temp_lang = api_get_setting('platformLanguage');
5156
            if (!empty($temp_lang)) {
5157
                $return = $temp_lang;
5158
            }
5159
            break;
5160
        case 'user_profil_lang':
5161
            $_user = api_get_user_info();
5162
            if (isset($_user['language']) && !empty($_user['language'])) {
5163
                $return = $_user['language'];
5164
            }
5165
            break;
5166
        case 'user_selected_lang':
5167
            if (isset($_SESSION['user_language_choice']) && !empty($_SESSION['user_language_choice'])) {
5168
                $return = $_SESSION['user_language_choice'];
5169
            }
5170
            break;
5171
        case 'course_lang':
5172
            global $_course;
5173
            $cidReq = null;
5174
            if (empty($_course)) {
5175
                // Code modified because the local.inc.php file it's declarated after this work
5176
                // causing the function api_get_course_info() returns a null value
5177
                $cidReq = isset($_GET["cidReq"]) ? Database::escape_string($_GET["cidReq"]) : null;
5178
                $cDir = (!empty($_GET['cDir']) ? $_GET['cDir'] : null);
5179
                if (empty($cidReq) && !empty($cDir)) {
5180
                    $c = CourseManager::getCourseCodeFromDirectory($cDir);
5181
                    if ($c) {
5182
                        $cidReq = $c;
5183
                    }
5184
                }
5185
            }
5186
            $_course = api_get_course_info($cidReq);
5187
            if (isset($_course['language']) && !empty($_course['language'])) {
5188
                $return = $_course['language'];
5189
                $showCourseInUserLanguage = api_get_course_setting('show_course_in_user_language');
5190
                if ($showCourseInUserLanguage == 1) {
5191
                    $userInfo = api_get_user_info();
5192
                    if (isset($userInfo['language'])) {
5193
                        $return = $userInfo['language'];
5194
                    }
5195
                }
5196
            }
5197
            break;
5198
        default:
5199
            $return = false;
5200
            break;
5201
    }
5202
5203
    return $return;
5204
}
5205
5206
/**
5207
 * Get the language information by its id.
5208
 *
5209
 * @param int $languageId
5210
 *
5211
 * @throws Exception
5212
 *
5213
 * @return array
5214
 */
5215
function api_get_language_info($languageId)
5216
{
5217
    if (empty($languageId)) {
5218
        return [];
5219
    }
5220
5221
    $language = Database::getManager()
5222
        ->find('ChamiloCoreBundle:Language', $languageId);
5223
5224
    if (!$language) {
5225
        return [];
5226
    }
5227
5228
    return [
5229
        'id' => $language->getId(),
5230
        'original_name' => $language->getOriginalName(),
5231
        'english_name' => $language->getEnglishName(),
5232
        'isocode' => $language->getIsocode(),
5233
        'dokeos_folder' => $language->getDokeosFolder(),
5234
        'available' => $language->getAvailable(),
5235
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
5236
    ];
5237
}
5238
5239
/**
5240
 * Returns the name of the visual (CSS) theme to be applied on the current page.
5241
 * The returned name depends on the platform, course or user -wide settings.
5242
 *
5243
 * @return string The visual theme's name, it is the name of a folder inside web/css/themes
5244
 */
5245
function api_get_visual_theme()
5246
{
5247
    static $visual_theme;
5248
5249
    $course_id = api_get_course_id();
5250
    $courseThemeAvailable = false;
5251
    // If called from CLI or from inside a course, it should be reloaded.
5252
    if ('cli' === PHP_SAPI) {
5253
        $visual_theme = null;
5254
    } elseif (!empty($course_id)) {
5255
        $courseThemeAvailable = api_get_setting('allow_course_theme') == 'true';
5256
        if ($courseThemeAvailable) {
5257
            $visual_theme = null;
5258
        }
5259
    }
5260
5261
    if (!isset($visual_theme)) {
5262
        $cacheAvailable = api_get_configuration_value('apc');
5263
        $userThemeAvailable = api_get_setting('user_selected_theme') == 'true';
5264
        // only use a shared cache if no user-based or course-based theme is allowed
5265
        $useCache = ($cacheAvailable && !$userThemeAvailable && !$courseThemeAvailable);
5266
        $apcVar = '';
5267
        if ($useCache) {
5268
            $apcVar = api_get_configuration_value('apc_prefix').'my_campus_visual_theme';
5269
            if (apcu_exists($apcVar)) {
5270
                return apcu_fetch($apcVar);
5271
            }
5272
        }
5273
5274
        $accessUrlId = api_get_current_access_url_id();
5275
        if ('cli' === PHP_SAPI) {
5276
            $accessUrlId = api_get_configuration_value('access_url');
5277
        }
5278
5279
        // Get style directly from DB
5280
        $styleFromDatabase = api_get_settings_params_simple(
5281
            [
5282
                'variable = ? AND access_url = ?' => [
5283
                    'stylesheets',
5284
                    $accessUrlId,
5285
                ],
5286
            ]
5287
        );
5288
        if ($styleFromDatabase) {
5289
            $platform_theme = $styleFromDatabase['selected_value'];
5290
        } else {
5291
            $platform_theme = api_get_setting('stylesheets');
5292
        }
5293
5294
        // Platform's theme.
5295
        $visual_theme = $platform_theme;
5296
        if ($userThemeAvailable) {
5297
            $user_info = api_get_user_info();
5298
            if (isset($user_info['theme'])) {
5299
                $user_theme = $user_info['theme'];
5300
5301
                if (!empty($user_theme)) {
5302
                    $visual_theme = $user_theme;
5303
                    // User's theme.
5304
                }
5305
            }
5306
        }
5307
5308
        if (!empty($course_id)) {
5309
            if ($courseThemeAvailable) {
5310
                $course_theme = api_get_course_setting('course_theme', api_get_course_info());
5311
5312
                if (!empty($course_theme) && $course_theme != -1) {
5313
                    if (!empty($course_theme)) {
5314
                        // Course's theme.
5315
                        $visual_theme = $course_theme;
5316
                    }
5317
                }
5318
5319
                $allow_lp_theme = api_get_course_setting('allow_learning_path_theme');
5320
                if ($allow_lp_theme == 1) {
5321
                    global $lp_theme_css, $lp_theme_config;
5322
                    // These variables come from the file lp_controller.php.
5323
                    if (!$lp_theme_config) {
5324
                        if (!empty($lp_theme_css)) {
5325
                            // LP's theme.
5326
                            $visual_theme = $lp_theme_css;
5327
                        }
5328
                    }
5329
                }
5330
            }
5331
        }
5332
5333
        if (empty($visual_theme)) {
5334
            $visual_theme = 'chamilo';
5335
        }
5336
5337
        global $lp_theme_log;
5338
        if ($lp_theme_log) {
5339
            $visual_theme = $platform_theme;
5340
        }
5341
        if ($useCache) {
5342
            apcu_store($apcVar, $visual_theme, 120);
5343
        }
5344
    }
5345
5346
    return $visual_theme;
5347
}
5348
5349
/**
5350
 * Returns a list of CSS themes currently available in the CSS folder
5351
 * The folder must have a default.css file.
5352
 *
5353
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
5354
 *
5355
 * @return array list of themes directories from the css folder
5356
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
5357
 */
5358
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
5359
{
5360
    // This configuration value is set by the vchamilo plugin
5361
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
5362
5363
    $readCssFolder = function ($dir) use ($virtualTheme) {
5364
        $finder = new Finder();
5365
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
5366
        $list = [];
5367
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
5368
        foreach ($themes as $theme) {
5369
            $folder = $theme->getFilename();
5370
            // A theme folder is consider if there's a default.css file
5371
            if (!file_exists($theme->getPathname().'/default.css')) {
5372
                continue;
5373
            }
5374
            $name = ucwords(str_replace('_', ' ', $folder));
5375
            if ($folder == $virtualTheme) {
5376
                continue;
5377
            }
5378
            $list[$folder] = $name;
5379
        }
5380
5381
        return $list;
5382
    };
5383
5384
    $dir = api_get_path(SYS_CSS_PATH).'themes/';
5385
    $list = $readCssFolder($dir);
5386
5387
    if (!empty($virtualTheme)) {
5388
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
5389
        if ($getOnlyThemeFromVirtualInstance) {
5390
            return $newList;
5391
        }
5392
        $list = $list + $newList;
5393
        asort($list);
5394
    }
5395
5396
    return $list;
5397
}
5398
5399
/**
5400
 * Find the largest sort value in a given user_course_category
5401
 * This function is used when we are moving a course to a different category
5402
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
5403
 *
5404
 * @author Patrick Cool <[email protected]>, Ghent University
5405
 *
5406
 * @param int $user_course_category the id of the user_course_category
5407
 * @param int $user_id
5408
 *
5409
 * @return int the value of the highest sort of the user_course_category
5410
 */
5411
function api_max_sort_value($user_course_category, $user_id)
5412
{
5413
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5414
    $sql = "SELECT max(sort) as max_sort FROM $tbl_course_user
5415
            WHERE
5416
                user_id='".intval($user_id)."' AND
5417
                relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
5418
                user_course_cat='".intval($user_course_category)."'";
5419
    $result_max = Database::query($sql);
5420
    if (Database::num_rows($result_max) == 1) {
5421
        $row_max = Database::fetch_array($result_max);
5422
5423
        return $row_max['max_sort'];
5424
    }
5425
5426
    return 0;
5427
}
5428
5429
/**
5430
 * Transforms a number of seconds in hh:mm:ss format.
5431
 *
5432
 * @author Julian Prud'homme
5433
 *
5434
 * @param int    $seconds      number of seconds
5435
 * @param string $space
5436
 * @param bool   $showSeconds
5437
 * @param bool   $roundMinutes
5438
 *
5439
 * @return string the formatted time
5440
 */
5441
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
5442
{
5443
    // $seconds = -1 means that we have wrong data in the db.
5444
    if ($seconds == -1) {
5445
        return
5446
            get_lang('Unknown').
5447
            Display::return_icon(
5448
                'info2.gif',
5449
                get_lang('WrongDatasForTimeSpentOnThePlatform'),
5450
                ['align' => 'absmiddle', 'hspace' => '3px']
5451
            );
5452
    }
5453
5454
    // How many hours ?
5455
    $hours = floor($seconds / 3600);
5456
5457
    // How many minutes ?
5458
    $min = floor(($seconds - ($hours * 3600)) / 60);
5459
5460
    if ($roundMinutes) {
5461
        if ($min >= 45) {
5462
            $min = 45;
5463
        }
5464
5465
        if ($min >= 30 && $min <= 44) {
5466
            $min = 30;
5467
        }
5468
5469
        if ($min >= 15 && $min <= 29) {
5470
            $min = 15;
5471
        }
5472
5473
        if ($min >= 0 && $min <= 14) {
5474
            $min = 0;
5475
        }
5476
    }
5477
5478
    // How many seconds
5479
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
5480
5481
    if ($hours < 10) {
5482
        $hours = "0$hours";
5483
    }
5484
5485
    if ($sec < 10) {
5486
        $sec = "0$sec";
5487
    }
5488
5489
    if ($min < 10) {
5490
        $min = "0$min";
5491
    }
5492
5493
    $seconds = '';
5494
    if ($showSeconds) {
5495
        $seconds = $space.$sec;
5496
    }
5497
5498
    return $hours.$space.$min.$seconds;
5499
}
5500
5501
/* FILE SYSTEM RELATED FUNCTIONS */
5502
5503
/**
5504
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5505
 * The return value is based on the platform administrator's setting
5506
 * "Administration > Configuration settings > Security > Permissions for new directories".
5507
 *
5508
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
5509
 */
5510
function api_get_permissions_for_new_directories()
5511
{
5512
    static $permissions;
5513
    if (!isset($permissions)) {
5514
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
5515
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
5516
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
5517
    }
5518
5519
    return $permissions;
5520
}
5521
5522
/**
5523
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5524
 * The return value is based on the platform administrator's setting
5525
 * "Administration > Configuration settings > Security > Permissions for new files".
5526
 *
5527
 * @return int returns the permissions in the format
5528
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
5529
 */
5530
function api_get_permissions_for_new_files()
5531
{
5532
    static $permissions;
5533
    if (!isset($permissions)) {
5534
        $permissions = trim(api_get_setting('permissions_for_new_files'));
5535
        // The default value 0666 is according to that in the platform
5536
        // administration panel after fresh system installation.
5537
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
5538
    }
5539
5540
    return $permissions;
5541
}
5542
5543
/**
5544
 * Deletes a file, or a folder and its contents.
5545
 *
5546
 * @author      Aidan Lister <[email protected]>
5547
 *
5548
 * @version     1.0.3
5549
 *
5550
 * @param string $dirname Directory to delete
5551
 * @param       bool     Deletes only the content or not
5552
 * @param bool $strict if one folder/file fails stop the loop
5553
 *
5554
 * @return bool Returns TRUE on success, FALSE on failure
5555
 *
5556
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
5557
 *
5558
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
5559
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
5560
 */
5561
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
5562
{
5563
    $res = true;
5564
    // A sanity check.
5565
    if (!file_exists($dirname)) {
5566
        return false;
5567
    }
5568
    $php_errormsg = '';
5569
    // Simple delete for a file.
5570
    if (is_file($dirname) || is_link($dirname)) {
5571
        $res = unlink($dirname);
5572
        if ($res === false) {
5573
            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);
5574
        }
5575
5576
        return $res;
5577
    }
5578
5579
    // Loop through the folder.
5580
    $dir = dir($dirname);
5581
    // A sanity check.
5582
    $is_object_dir = is_object($dir);
5583
    if ($is_object_dir) {
5584
        while (false !== $entry = $dir->read()) {
5585
            // Skip pointers.
5586
            if ($entry == '.' || $entry == '..') {
5587
                continue;
5588
            }
5589
5590
            // Recurse.
5591
            if ($strict) {
5592
                $result = rmdirr("$dirname/$entry");
5593
                if ($result == false) {
5594
                    $res = false;
5595
                    break;
5596
                }
5597
            } else {
5598
                rmdirr("$dirname/$entry");
5599
            }
5600
        }
5601
    }
5602
5603
    // Clean up.
5604
    if ($is_object_dir) {
5605
        $dir->close();
5606
    }
5607
5608
    if ($delete_only_content_in_folder == false) {
5609
        $res = rmdir($dirname);
5610
        if ($res === false) {
5611
            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);
5612
        }
5613
    }
5614
5615
    return $res;
5616
}
5617
5618
// TODO: This function is to be simplified. File access modes to be implemented.
5619
/**
5620
 * function adapted from a php.net comment
5621
 * copy recursively a folder.
5622
 *
5623
 * @param string $source       the source folder
5624
 * @param string $dest         the dest folder
5625
 * @param array  $exclude      an array of excluded file_name (without extension)
5626
 * @param array  $copied_files the returned array of copied files
5627
 */
5628
function copyr($source, $dest, $exclude = [], $copied_files = [])
5629
{
5630
    if (empty($dest)) {
5631
        return false;
5632
    }
5633
    // Simple copy for a file
5634
    if (is_file($source)) {
5635
        $path_info = pathinfo($source);
5636
        if (!in_array($path_info['filename'], $exclude)) {
5637
            copy($source, $dest);
5638
        }
5639
5640
        return true;
5641
    } elseif (!is_dir($source)) {
5642
        //then source is not a dir nor a file, return
5643
        return false;
5644
    }
5645
5646
    // Make destination directory.
5647
    if (!is_dir($dest)) {
5648
        mkdir($dest, api_get_permissions_for_new_directories());
5649
    }
5650
5651
    // Loop through the folder.
5652
    $dir = dir($source);
5653
    while (false !== $entry = $dir->read()) {
5654
        // Skip pointers
5655
        if ($entry == '.' || $entry == '..') {
5656
            continue;
5657
        }
5658
5659
        // Deep copy directories.
5660
        if ($dest !== "$source/$entry") {
5661
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
5662
        }
5663
    }
5664
    // Clean up.
5665
    $dir->close();
5666
5667
    return true;
5668
}
5669
5670
/**
5671
 * @param string $pathname
5672
 * @param string $base_path_document
5673
 * @param int    $session_id
5674
 * @param array
5675
 * @param string
5676
 *
5677
 * @return mixed True if directory already exists, false if a file already exists at
5678
 *               the destination and null if everything goes according to plan
5679
 *@todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
5680
 * Documentation header to be added here.
5681
 */
5682
function copy_folder_course_session(
5683
    $pathname,
5684
    $base_path_document,
5685
    $session_id,
5686
    $course_info,
5687
    $document,
5688
    $source_course_id,
5689
    $originalFolderNameList = [],
5690
    $originalBaseName = ''
5691
) {
5692
    // Check whether directory already exists.
5693
    if (empty($pathname) || is_dir($pathname)) {
5694
        return true;
5695
    }
5696
5697
    // Ensure that a file with the same name does not already exist.
5698
    if (is_file($pathname)) {
5699
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
5700
5701
        return false;
5702
    }
5703
5704
    //error_log('checking:');
5705
    //error_log(str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5706
    $baseNoDocument = str_replace('document', '', $originalBaseName);
5707
    $folderTitles = explode('/', $baseNoDocument);
5708
    $folderTitles = array_filter($folderTitles);
5709
5710
    //error_log($baseNoDocument);error_log(print_r($folderTitles, 1));
5711
5712
    $table = Database::get_course_table(TABLE_DOCUMENT);
5713
    $session_id = (int) $session_id;
5714
    $source_course_id = (int) $source_course_id;
5715
    $course_id = $course_info['real_id'];
5716
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5717
    $new_pathname = $base_path_document;
5718
5719
    $path = '';
5720
    foreach ($folders as $index => $folder) {
5721
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
5722
        $path .= DIRECTORY_SEPARATOR.$folder;
5723
5724
        if (!file_exists($new_pathname)) {
5725
            $path = Database::escape_string($path);
5726
            //error_log("path: $path");
5727
            $sql = "SELECT * FROM $table
5728
                    WHERE
5729
                        c_id = $source_course_id AND
5730
                        path = '$path' AND
5731
                        filetype = 'folder' AND
5732
                        session_id = '$session_id'";
5733
            $rs1 = Database::query($sql);
5734
            $num_rows = Database::num_rows($rs1);
5735
5736
            if (0 == $num_rows) {
5737
                mkdir($new_pathname, api_get_permissions_for_new_directories());
5738
                $title = basename($new_pathname);
5739
5740
                if (isset($folderTitles[$index + 1])) {
5741
                    $checkPath = $folderTitles[$index + 1];
5742
                    //error_log("check $checkPath");
5743
                    if (isset($originalFolderNameList[$checkPath])) {
5744
                        $title = $originalFolderNameList[$checkPath];
5745
                        //error_log('use this name: '.$title);
5746
                    }
5747
                }
5748
5749
                // Insert new folder with destination session_id.
5750
                $params = [
5751
                    'c_id' => $course_id,
5752
                    'path' => $path,
5753
                    'comment' => $document->comment,
5754
                    'title' => $title,
5755
                    'filetype' => 'folder',
5756
                    'size' => '0',
5757
                    'session_id' => $session_id,
5758
                ];
5759
5760
                //error_log("old $folder"); error_log("Add doc $title in $path");
5761
                $document_id = Database::insert($table, $params);
5762
                if ($document_id) {
5763
                    $sql = "UPDATE $table SET id = iid WHERE iid = $document_id";
5764
                    Database::query($sql);
5765
5766
                    api_item_property_update(
5767
                        $course_info,
5768
                        TOOL_DOCUMENT,
5769
                        $document_id,
5770
                        'FolderCreated',
5771
                        api_get_user_id(),
5772
                        0,
5773
                        0,
5774
                        null,
5775
                        null,
5776
                        $session_id
5777
                    );
5778
                }
5779
            }
5780
        }
5781
    }
5782
}
5783
5784
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
5785
/**
5786
 * @param string $path
5787
 */
5788
function api_chmod_R($path, $filemode)
5789
{
5790
    if (!is_dir($path)) {
5791
        return chmod($path, $filemode);
5792
    }
5793
5794
    $handler = opendir($path);
5795
    while ($file = readdir($handler)) {
5796
        if ($file != '.' && $file != '..') {
5797
            $fullpath = "$path/$file";
5798
            if (!is_dir($fullpath)) {
5799
                if (!chmod($fullpath, $filemode)) {
5800
                    return false;
5801
                }
5802
            } else {
5803
                if (!api_chmod_R($fullpath, $filemode)) {
5804
                    return false;
5805
                }
5806
            }
5807
        }
5808
    }
5809
5810
    closedir($handler);
5811
5812
    return chmod($path, $filemode);
5813
}
5814
5815
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
5816
/**
5817
 * Parse info file format. (e.g: file.info).
5818
 *
5819
 * Files should use an ini-like format to specify values.
5820
 * White-space generally doesn't matter, except inside values.
5821
 * e.g.
5822
 *
5823
 * @verbatim
5824
 *   key = value
5825
 *   key = "value"
5826
 *   key = 'value'
5827
 *   key = "multi-line
5828
 *
5829
 *   value"
5830
 *   key = 'multi-line
5831
 *
5832
 *   value'
5833
 *   key
5834
 *   =
5835
 *   'value'
5836
 * @endverbatim
5837
 *
5838
 * Arrays are created using a GET-like syntax:
5839
 *
5840
 * @verbatim
5841
 *   key[] = "numeric array"
5842
 *   key[index] = "associative array"
5843
 *   key[index][] = "nested numeric array"
5844
 *   key[index][index] = "nested associative array"
5845
 * @endverbatim
5846
 *
5847
 * PHP constants are substituted in, but only when used as the entire value:
5848
 *
5849
 * Comments should start with a semi-colon at the beginning of a line.
5850
 *
5851
 * This function is NOT for placing arbitrary module-specific settings. Use
5852
 * variable_get() and variable_set() for that.
5853
 *
5854
 * Information stored in the module.info file:
5855
 * - name: The real name of the module for display purposes.
5856
 * - description: A brief description of the module.
5857
 * - dependencies: An array of shortnames of other modules this module depends on.
5858
 * - package: The name of the package of modules this module belongs to.
5859
 *
5860
 * Example of .info file:
5861
 * <code>
5862
 * @verbatim
5863
 *   name = Forum
5864
 *   description = Enables threaded discussions about general topics.
5865
 *   dependencies[] = taxonomy
5866
 *   dependencies[] = comment
5867
 *   package = Core - optional
5868
 *   version = VERSION
5869
 * @endverbatim
5870
 * </code>
5871
 *
5872
 * @param string $filename
5873
 *                         The file we are parsing. Accepts file with relative or absolute path.
5874
 *
5875
 * @return
5876
 *   The info array
5877
 */
5878
function api_parse_info_file($filename)
5879
{
5880
    $info = [];
5881
5882
    if (!file_exists($filename)) {
5883
        return $info;
5884
    }
5885
5886
    $data = file_get_contents($filename);
5887
    if (preg_match_all('
5888
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
5889
        ((?:
5890
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
5891
          \[[^\[\]]*\]                  # unless they are balanced and not nested
5892
        )+?)
5893
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
5894
        (?:
5895
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
5896
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
5897
          ([^\r\n]*?)                   # Non-quoted string
5898
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
5899
        @msx', $data, $matches, PREG_SET_ORDER)) {
5900
        $key = $value1 = $value2 = $value3 = '';
5901
        foreach ($matches as $match) {
5902
            // Fetch the key and value string.
5903
            $i = 0;
5904
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
5905
                $$var = isset($match[++$i]) ? $match[$i] : '';
5906
            }
5907
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
5908
5909
            // Parse array syntax.
5910
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
5911
            $last = array_pop($keys);
5912
            $parent = &$info;
5913
5914
            // Create nested arrays.
5915
            foreach ($keys as $key) {
5916
                if ($key == '') {
5917
                    $key = count($parent);
5918
                }
5919
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
5920
                    $parent[$key] = [];
5921
                }
5922
                $parent = &$parent[$key];
5923
            }
5924
5925
            // Handle PHP constants.
5926
            if (defined($value)) {
5927
                $value = constant($value);
5928
            }
5929
5930
            // Insert actual value.
5931
            if ($last == '') {
5932
                $last = count($parent);
5933
            }
5934
            $parent[$last] = $value;
5935
        }
5936
    }
5937
5938
    return $info;
5939
}
5940
5941
/**
5942
 * Gets Chamilo version from the configuration files.
5943
 *
5944
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
5945
 */
5946
function api_get_version()
5947
{
5948
    return (string) api_get_configuration_value('system_version');
5949
}
5950
5951
/**
5952
 * Gets the software name (the name/brand of the Chamilo-based customized system).
5953
 *
5954
 * @return string
5955
 */
5956
function api_get_software_name()
5957
{
5958
    $name = api_get_configuration_value('software_name');
5959
    if (!empty($name)) {
5960
        return $name;
5961
    } else {
5962
        return 'Chamilo';
5963
    }
5964
}
5965
5966
/**
5967
 * Checks whether status given in parameter exists in the platform.
5968
 *
5969
 * @param mixed the status (can be either int either string)
5970
 *
5971
 * @return bool if the status exists, else returns false
5972
 */
5973
function api_status_exists($status_asked)
5974
{
5975
    global $_status_list;
5976
5977
    return in_array($status_asked, $_status_list) ? true : isset($_status_list[$status_asked]);
5978
}
5979
5980
/**
5981
 * Checks whether status given in parameter exists in the platform. The function
5982
 * returns the status ID or false if it does not exist, but given the fact there
5983
 * is no "0" status, the return value can be checked against
5984
 * if(api_status_key()) to know if it exists.
5985
 *
5986
 * @param   mixed   The status (can be either int or string)
5987
 *
5988
 * @return mixed Status ID if exists, false otherwise
5989
 */
5990
function api_status_key($status)
5991
{
5992
    global $_status_list;
5993
5994
    return isset($_status_list[$status]) ? $status : array_search($status, $_status_list);
5995
}
5996
5997
/**
5998
 * Gets the status langvars list.
5999
 *
6000
 * @return string[] the list of status with their translations
6001
 */
6002
function api_get_status_langvars()
6003
{
6004
    return [
6005
        COURSEMANAGER => get_lang('Teacher', ''),
6006
        SESSIONADMIN => get_lang('SessionsAdmin', ''),
6007
        DRH => get_lang('Drh', ''),
6008
        STUDENT => get_lang('Student', ''),
6009
        ANONYMOUS => get_lang('Anonymous', ''),
6010
        STUDENT_BOSS => get_lang('RoleStudentBoss', ''),
6011
        INVITEE => get_lang('Invited'),
6012
    ];
6013
}
6014
6015
/**
6016
 * The function that retrieves all the possible settings for a certain config setting.
6017
 *
6018
 * @author Patrick Cool <[email protected]>, Ghent University
6019
 */
6020
function api_get_settings_options($var)
6021
{
6022
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6023
    $var = Database::escape_string($var);
6024
    $sql = "SELECT * FROM $table_settings_options
6025
            WHERE variable = '$var'
6026
            ORDER BY id";
6027
    $result = Database::query($sql);
6028
    $settings_options_array = [];
6029
    while ($row = Database::fetch_array($result, 'ASSOC')) {
6030
        $settings_options_array[] = $row;
6031
    }
6032
6033
    return $settings_options_array;
6034
}
6035
6036
/**
6037
 * @param array $params
6038
 */
6039
function api_set_setting_option($params)
6040
{
6041
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6042
    if (empty($params['id'])) {
6043
        Database::insert($table, $params);
6044
    } else {
6045
        Database::update($table, $params, ['id = ? ' => $params['id']]);
6046
    }
6047
}
6048
6049
/**
6050
 * @param array $params
6051
 */
6052
function api_set_setting_simple($params)
6053
{
6054
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6055
    $url_id = api_get_current_access_url_id();
6056
6057
    if (empty($params['id'])) {
6058
        $params['access_url'] = $url_id;
6059
        Database::insert($table, $params);
6060
    } else {
6061
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
6062
    }
6063
}
6064
6065
/**
6066
 * @param int $id
6067
 */
6068
function api_delete_setting_option($id)
6069
{
6070
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6071
    if (!empty($id)) {
6072
        Database::delete($table, ['id = ? ' => $id]);
6073
    }
6074
}
6075
6076
/**
6077
 * Sets a platform configuration setting to a given value.
6078
 *
6079
 * @param string    The variable we want to update
6080
 * @param string    The value we want to record
6081
 * @param string    The sub-variable if any (in most cases, this will remain null)
6082
 * @param string    The category if any (in most cases, this will remain null)
6083
 * @param int       The access_url for which this parameter is valid
6084
 * @param string $cat
6085
 *
6086
 * @return bool|null
6087
 */
6088
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
6089
{
6090
    if (empty($var)) {
6091
        return false;
6092
    }
6093
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6094
    $var = Database::escape_string($var);
6095
    $value = Database::escape_string($value);
6096
    $access_url = (int) $access_url;
6097
    if (empty($access_url)) {
6098
        $access_url = 1;
6099
    }
6100
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
6101
    if (!empty($subvar)) {
6102
        $subvar = Database::escape_string($subvar);
6103
        $select .= " AND subkey = '$subvar'";
6104
    }
6105
    if (!empty($cat)) {
6106
        $cat = Database::escape_string($cat);
6107
        $select .= " AND category = '$cat'";
6108
    }
6109
    if ($access_url > 1) {
6110
        $select .= " AND access_url = $access_url";
6111
    } else {
6112
        $select .= " AND access_url = 1 ";
6113
    }
6114
6115
    $res = Database::query($select);
6116
    if (Database::num_rows($res) > 0) {
6117
        // Found item for this access_url.
6118
        $row = Database::fetch_array($res);
6119
        $sql = "UPDATE $t_settings SET selected_value = '$value'
6120
                WHERE id = ".$row['id'];
6121
        Database::query($sql);
6122
    } else {
6123
        // Item not found for this access_url, we have to check if it exist with access_url = 1
6124
        $select = "SELECT * FROM $t_settings
6125
                   WHERE variable = '$var' AND access_url = 1 ";
6126
        // Just in case
6127
        if ($access_url == 1) {
6128
            if (!empty($subvar)) {
6129
                $select .= " AND subkey = '$subvar'";
6130
            }
6131
            if (!empty($cat)) {
6132
                $select .= " AND category = '$cat'";
6133
            }
6134
            $res = Database::query($select);
6135
            if (Database::num_rows($res) > 0) {
6136
                // We have a setting for access_url 1, but none for the current one, so create one.
6137
                $row = Database::fetch_array($res);
6138
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
6139
                        VALUES
6140
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6141
                        "'".$row['type']."','".$row['category']."',".
6142
                        "'$value','".$row['title']."',".
6143
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6144
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
6145
                Database::query($insert);
6146
            } else {
6147
                // Such a setting does not exist.
6148
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
6149
            }
6150
        } else {
6151
            // Other access url.
6152
            if (!empty($subvar)) {
6153
                $select .= " AND subkey = '$subvar'";
6154
            }
6155
            if (!empty($cat)) {
6156
                $select .= " AND category = '$cat'";
6157
            }
6158
            $res = Database::query($select);
6159
6160
            if (Database::num_rows($res) > 0) {
6161
                // We have a setting for access_url 1, but none for the current one, so create one.
6162
                $row = Database::fetch_array($res);
6163
                if ($row['access_url_changeable'] == 1) {
6164
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
6165
                            ('".$row['variable']."',".
6166
                            (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6167
                            "'".$row['type']."','".$row['category']."',".
6168
                            "'$value','".$row['title']."',".
6169
                            "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
6170
                            (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6171
                            "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
6172
                    Database::query($insert);
6173
                }
6174
            } else { // Such a setting does not exist.
6175
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
6176
            }
6177
        }
6178
    }
6179
}
6180
6181
/**
6182
 * Sets a whole category of settings to one specific value.
6183
 *
6184
 * @param string    Category
6185
 * @param string    Value
6186
 * @param int       Access URL. Optional. Defaults to 1
6187
 * @param array     Optional array of filters on field type
6188
 * @param string $category
6189
 * @param string $value
6190
 *
6191
 * @return bool
6192
 */
6193
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
6194
{
6195
    if (empty($category)) {
6196
        return false;
6197
    }
6198
    $category = Database::escape_string($category);
6199
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6200
    $access_url = (int) $access_url;
6201
    if (empty($access_url)) {
6202
        $access_url = 1;
6203
    }
6204
    if (isset($value)) {
6205
        $value = Database::escape_string($value);
6206
        $sql = "UPDATE $t_s SET selected_value = '$value'
6207
                WHERE category = '$category' AND access_url = $access_url";
6208
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6209
            $sql .= " AND ( ";
6210
            $i = 0;
6211
            foreach ($fieldtype as $type) {
6212
                if ($i > 0) {
6213
                    $sql .= ' OR ';
6214
                }
6215
                $type = Database::escape_string($type);
6216
                $sql .= " type='".$type."' ";
6217
                $i++;
6218
            }
6219
            $sql .= ")";
6220
        }
6221
        $res = Database::query($sql);
6222
6223
        return $res !== false;
6224
    } else {
6225
        $sql = "UPDATE $t_s SET selected_value = NULL
6226
                WHERE category = '$category' AND access_url = $access_url";
6227
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6228
            $sql .= " AND ( ";
6229
            $i = 0;
6230
            foreach ($fieldtype as $type) {
6231
                if ($i > 0) {
6232
                    $sql .= ' OR ';
6233
                }
6234
                $type = Database::escape_string($type);
6235
                $sql .= " type='".$type."' ";
6236
                $i++;
6237
            }
6238
            $sql .= ")";
6239
        }
6240
        $res = Database::query($sql);
6241
6242
        return $res !== false;
6243
    }
6244
}
6245
6246
/**
6247
 * Gets all available access urls in an array (as in the database).
6248
 *
6249
 * @return array An array of database records
6250
 */
6251
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
6252
{
6253
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6254
    $from = (int) $from;
6255
    $to = (int) $to;
6256
    $order = Database::escape_string($order);
6257
    $direction = Database::escape_string($direction);
6258
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
6259
6260
    $sql = "SELECT id, url, description, active, created_by, tms
6261
            FROM $table
6262
            ORDER BY `$order` $direction
6263
            LIMIT $to OFFSET $from";
6264
    $res = Database::query($sql);
6265
6266
    return Database::store_result($res);
6267
}
6268
6269
/**
6270
 * Gets the access url info in an array.
6271
 *
6272
 * @param int  $id            Id of the access url
6273
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
6274
 *
6275
 * @return array All the info (url, description, active, created_by, tms)
6276
 *               from the access_url table
6277
 *
6278
 * @author Julio Montoya
6279
 */
6280
function api_get_access_url($id, $returnDefault = true)
6281
{
6282
    static $staticResult;
6283
    $id = (int) $id;
6284
6285
    if (isset($staticResult[$id])) {
6286
        $result = $staticResult[$id];
6287
    } else {
6288
        // Calling the Database:: library dont work this is handmade.
6289
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6290
        $sql = "SELECT url, description, active, created_by, tms
6291
                FROM $table_access_url WHERE id = '$id' ";
6292
        $res = Database::query($sql);
6293
        $result = @Database::fetch_array($res);
6294
        $staticResult[$id] = $result;
6295
    }
6296
6297
    // If the result url is 'http://localhost/' (the default) and the root_web
6298
    // (=current url) is different, and the $id is = 1 (which might mean
6299
    // api_get_current_access_url_id() returned 1 by default), then return the
6300
    // root_web setting instead of the current URL
6301
    // This is provided as an option to avoid breaking the storage of URL-specific
6302
    // homepages in home/localhost/
6303
    if ($id === 1 && $returnDefault === false) {
6304
        $currentUrl = api_get_current_access_url_id();
6305
        // only do this if we are on the main URL (=1), otherwise we could get
6306
        // information on another URL instead of the one asked as parameter
6307
        if ($currentUrl === 1) {
6308
            $rootWeb = api_get_path(WEB_PATH);
6309
            $default = 'http://localhost/';
6310
            if ($result['url'] === $default && $rootWeb != $default) {
6311
                $result['url'] = $rootWeb;
6312
            }
6313
        }
6314
    }
6315
6316
    return $result;
6317
}
6318
6319
/**
6320
 * Gets all the current settings for a specific access url.
6321
 *
6322
 * @param string    The category, if any, that we want to get
6323
 * @param string    Whether we want a simple list (display a category) or
6324
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
6325
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
6326
 *
6327
 * @return array Array of database results for the current settings of the current access URL
6328
 */
6329
function &api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
6330
{
6331
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6332
    $access_url = (int) $access_url;
6333
    $where_condition = '';
6334
    if ($url_changeable == 1) {
6335
        $where_condition = " AND access_url_changeable= '1' ";
6336
    }
6337
    if (empty($access_url) || $access_url == -1) {
6338
        $access_url = 1;
6339
    }
6340
    $sql = "SELECT * FROM $table
6341
            WHERE access_url = $access_url  $where_condition ";
6342
6343
    if (!empty($cat)) {
6344
        $cat = Database::escape_string($cat);
6345
        $sql .= " AND category='$cat' ";
6346
    }
6347
    if ($ordering == 'group') {
6348
        $sql .= " ORDER BY id ASC";
6349
    } else {
6350
        $sql .= " ORDER BY 1,2 ASC";
6351
    }
6352
    $result = Database::query($sql);
6353
    if ($result === null) {
6354
        return [];
6355
    }
6356
    $result = Database::store_result($result, 'ASSOC');
6357
6358
    return $result;
6359
}
6360
6361
/**
6362
 * @param string $value       The value we want to record
6363
 * @param string $variable    The variable name we want to insert
6364
 * @param string $subKey      The subkey for the variable we want to insert
6365
 * @param string $type        The type for the variable we want to insert
6366
 * @param string $category    The category for the variable we want to insert
6367
 * @param string $title       The title
6368
 * @param string $comment     The comment
6369
 * @param string $scope       The scope
6370
 * @param string $subKeyText  The subkey text
6371
 * @param int    $accessUrlId The access_url for which this parameter is valid
6372
 * @param int    $visibility  The changeability of this setting for non-master urls
6373
 *
6374
 * @return int The setting ID
6375
 */
6376
function api_add_setting(
6377
    $value,
6378
    $variable,
6379
    $subKey = '',
6380
    $type = 'textfield',
6381
    $category = '',
6382
    $title = '',
6383
    $comment = '',
6384
    $scope = '',
6385
    $subKeyText = '',
6386
    $accessUrlId = 1,
6387
    $visibility = 0
6388
) {
6389
    $em = Database::getManager();
6390
    $settingRepo = $em->getRepository('ChamiloCoreBundle:SettingsCurrent');
6391
    $accessUrlId = (int) $accessUrlId ?: 1;
6392
6393
    if (is_array($value)) {
6394
        $value = serialize($value);
6395
    } else {
6396
        $value = trim($value);
6397
    }
6398
6399
    $criteria = ['variable' => $variable, 'accessUrl' => $accessUrlId];
6400
6401
    if (!empty($subKey)) {
6402
        $criteria['subkey'] = $subKey;
6403
    }
6404
6405
    // Check if this variable doesn't exist already
6406
    /** @var SettingsCurrent $setting */
6407
    $setting = $settingRepo->findOneBy($criteria);
6408
6409
    if ($setting) {
6410
        $setting->setSelectedValue($value);
6411
6412
        $em->persist($setting);
6413
        $em->flush();
6414
6415
        return $setting->getId();
6416
    }
6417
6418
    // Item not found for this access_url, we have to check if the whole thing is missing
6419
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
6420
    $setting = new SettingsCurrent();
6421
    $setting
6422
        ->setVariable($variable)
6423
        ->setSelectedValue($value)
6424
        ->setType($type)
6425
        ->setCategory($category)
6426
        ->setSubkey($subKey)
6427
        ->setTitle($title)
6428
        ->setComment($comment)
6429
        ->setScope($scope)
6430
        ->setSubkeytext($subKeyText)
6431
        ->setAccessUrl($accessUrlId)
6432
        ->setAccessUrlChangeable($visibility);
6433
6434
    $em->persist($setting);
6435
    $em->flush();
6436
6437
    return $setting->getId();
6438
}
6439
6440
/**
6441
 * Checks wether a user can or can't view the contents of a course.
6442
 *
6443
 * @deprecated use CourseManager::is_user_subscribed_in_course
6444
 *
6445
 * @param int $userid User id or NULL to get it from $_SESSION
6446
 * @param int $cid    course id to check whether the user is allowed
6447
 *
6448
 * @return bool
6449
 */
6450
function api_is_course_visible_for_user($userid = null, $cid = null)
6451
{
6452
    if ($userid === null) {
6453
        $userid = api_get_user_id();
6454
    }
6455
    if (empty($userid) || strval(intval($userid)) != $userid) {
6456
        if (api_is_anonymous()) {
6457
            $userid = api_get_anonymous_id();
6458
        } else {
6459
            return false;
6460
        }
6461
    }
6462
    $cid = Database::escape_string($cid);
6463
6464
    $courseInfo = api_get_course_info($cid);
6465
    $courseId = $courseInfo['real_id'];
6466
    $is_platformAdmin = api_is_platform_admin();
6467
6468
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
6469
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
6470
6471
    $sql = "SELECT
6472
                $course_table.category_code,
6473
                $course_table.visibility,
6474
                $course_table.code,
6475
                $course_cat_table.code
6476
            FROM $course_table
6477
            LEFT JOIN $course_cat_table
6478
                ON $course_table.category_code = $course_cat_table.code
6479
            WHERE
6480
                $course_table.code = '$cid'
6481
            LIMIT 1";
6482
6483
    $result = Database::query($sql);
6484
6485
    if (Database::num_rows($result) > 0) {
6486
        $visibility = Database::fetch_array($result);
6487
        $visibility = $visibility['visibility'];
6488
    } else {
6489
        $visibility = 0;
6490
    }
6491
    // Shortcut permissions in case the visibility is "open to the world".
6492
    if ($visibility === COURSE_VISIBILITY_OPEN_WORLD) {
6493
        return true;
6494
    }
6495
6496
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6497
6498
    $sql = "SELECT
6499
                is_tutor, status
6500
            FROM $tbl_course_user
6501
            WHERE
6502
                user_id  = '$userid' AND
6503
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
6504
                c_id = $courseId
6505
            LIMIT 1";
6506
6507
    $result = Database::query($sql);
6508
6509
    if (Database::num_rows($result) > 0) {
6510
        // This user has got a recorded state for this course.
6511
        $cuData = Database::fetch_array($result);
6512
        $is_courseMember = true;
6513
        $is_courseAdmin = ($cuData['status'] == 1);
6514
    }
6515
6516
    if (!$is_courseAdmin) {
6517
        // This user has no status related to this course.
6518
        // Is it the session coach or the session admin?
6519
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
6520
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
6521
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6522
6523
        $sql = "SELECT
6524
                    session.id_coach, session_admin_id, session.id
6525
                FROM
6526
                    $tbl_session as session
6527
                INNER JOIN $tbl_session_course
6528
                    ON session_rel_course.session_id = session.id
6529
                    AND session_rel_course.c_id = '$courseId'
6530
                LIMIT 1";
6531
6532
        $result = Database::query($sql);
6533
        $row = Database::store_result($result);
6534
6535
        if ($row[0]['id_coach'] == $userid) {
6536
            $is_courseMember = true;
6537
            $is_courseAdmin = false;
6538
        } elseif ($row[0]['session_admin_id'] == $userid) {
6539
            $is_courseMember = false;
6540
            $is_courseAdmin = false;
6541
        } else {
6542
            // Check if the current user is the course coach.
6543
            $sql = "SELECT 1
6544
                    FROM $tbl_session_course
6545
                    WHERE session_rel_course.c_id = '$courseId'
6546
                    AND session_rel_course.id_coach = '$userid'
6547
                    LIMIT 1";
6548
6549
            $result = Database::query($sql);
6550
6551
            //if ($row = Database::fetch_array($result)) {
6552
            if (Database::num_rows($result) > 0) {
6553
                $is_courseMember = true;
6554
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
6555
6556
                $sql = "SELECT status FROM $tbl_user
6557
                        WHERE user_id = $userid
6558
                        LIMIT 1";
6559
6560
                $result = Database::query($sql);
6561
6562
                if (Database::result($result, 0, 0) == 1) {
6563
                    $is_courseAdmin = true;
6564
                } else {
6565
                    $is_courseAdmin = false;
6566
                }
6567
            } else {
6568
                // Check if the user is a student is this session.
6569
                $sql = "SELECT  id
6570
                        FROM $tbl_session_course_user
6571
                        WHERE
6572
                            user_id  = '$userid' AND
6573
                            c_id = '$courseId'
6574
                        LIMIT 1";
6575
6576
                if (Database::num_rows($result) > 0) {
6577
                    // This user haa got a recorded state for this course.
6578
                    while ($row = Database::fetch_array($result)) {
6579
                        $is_courseMember = true;
6580
                        $is_courseAdmin = false;
6581
                    }
6582
                }
6583
            }
6584
        }
6585
    }
6586
6587
    switch ($visibility) {
6588
        case COURSE_VISIBILITY_OPEN_WORLD:
6589
            return true;
6590
        case COURSE_VISIBILITY_OPEN_PLATFORM:
6591
            return isset($userid);
6592
        case COURSE_VISIBILITY_REGISTERED:
6593
        case COURSE_VISIBILITY_CLOSED:
6594
            return $is_platformAdmin || $is_courseMember || $is_courseAdmin;
6595
        case COURSE_VISIBILITY_HIDDEN:
6596
            return $is_platformAdmin;
6597
    }
6598
6599
    return false;
6600
}
6601
6602
/**
6603
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
6604
 *
6605
 * @param string the tool of the element
6606
 * @param int the element id in database
6607
 * @param int the session_id to compare with element session id
6608
 *
6609
 * @return bool true if the element is in the session, false else
6610
 */
6611
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
6612
{
6613
    if (is_null($session_id)) {
6614
        $session_id = api_get_session_id();
6615
    }
6616
6617
    $element_id = (int) $element_id;
6618
6619
    if (empty($element_id)) {
6620
        return false;
6621
    }
6622
6623
    // Get information to build query depending of the tool.
6624
    switch ($tool) {
6625
        case TOOL_SURVEY:
6626
            $table_tool = Database::get_course_table(TABLE_SURVEY);
6627
            $key_field = 'survey_id';
6628
            break;
6629
        case TOOL_ANNOUNCEMENT:
6630
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
6631
            $key_field = 'id';
6632
            break;
6633
        case TOOL_AGENDA:
6634
            $table_tool = Database::get_course_table(TABLE_AGENDA);
6635
            $key_field = 'id';
6636
            break;
6637
        case TOOL_GROUP:
6638
            $table_tool = Database::get_course_table(TABLE_GROUP);
6639
            $key_field = 'id';
6640
            break;
6641
        default:
6642
            return false;
6643
    }
6644
    $course_id = api_get_course_int_id();
6645
6646
    $sql = "SELECT session_id FROM $table_tool
6647
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
6648
    $rs = Database::query($sql);
6649
    if ($element_session_id = Database::result($rs, 0, 0)) {
6650
        if ($element_session_id == intval($session_id)) {
6651
            // The element belongs to the session.
6652
            return true;
6653
        }
6654
    }
6655
6656
    return false;
6657
}
6658
6659
/**
6660
 * Replaces "forbidden" characters in a filename string.
6661
 *
6662
 * @param string $filename
6663
 * @param bool   $treat_spaces_as_hyphens
6664
 *
6665
 * @return string
6666
 */
6667
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
6668
{
6669
    // Some non-properly encoded file names can cause the whole file to be
6670
    // skipped when uploaded. Avoid this by detecting the encoding and
6671
    // converting to UTF-8, setting the source as ASCII (a reasonably
6672
    // limited characters set) if nothing could be found (BT#
6673
    $encoding = api_detect_encoding($filename);
6674
    if (empty($encoding)) {
6675
        $encoding = 'ASCII';
6676
        if (!api_is_valid_ascii($filename)) {
6677
            // try iconv and try non standard ASCII a.k.a CP437
6678
            // see BT#15022
6679
            if (function_exists('iconv')) {
6680
                $result = iconv('CP437', 'UTF-8', $filename);
6681
                if (api_is_valid_utf8($result)) {
6682
                    $filename = $result;
6683
                    $encoding = 'UTF-8';
6684
                }
6685
            }
6686
        }
6687
    }
6688
6689
    $filename = api_to_system_encoding($filename, $encoding);
6690
6691
    $url = URLify::filter(
6692
        $filename,
6693
        250,
6694
        '',
6695
        true,
6696
        false,
6697
        false,
6698
        false,
6699
        $treat_spaces_as_hyphens
6700
    );
6701
6702
    // Replace multiple dots at the end.
6703
    $regex = "/\.+$/";
6704
    $url = preg_replace($regex, '', $url);
6705
6706
    return $url;
6707
}
6708
6709
/**
6710
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
6711
 *
6712
 * @author Ivan Tcholakov, 28-JUN-2006.
6713
 */
6714
function api_request_uri()
6715
{
6716
    if (!empty($_SERVER['REQUEST_URI'])) {
6717
        return $_SERVER['REQUEST_URI'];
6718
    }
6719
    $uri = $_SERVER['SCRIPT_NAME'];
6720
    if (!empty($_SERVER['QUERY_STRING'])) {
6721
        $uri .= '?'.$_SERVER['QUERY_STRING'];
6722
    }
6723
    $_SERVER['REQUEST_URI'] = $uri;
6724
6725
    return $uri;
6726
}
6727
6728
/**
6729
 * Gets the current access_url id of the Chamilo Platform.
6730
 *
6731
 * @return int access_url_id of the current Chamilo Installation or 1 if multiple_access_urls is not enabled
6732
 *
6733
 * @author Julio Montoya <[email protected]>
6734
 */
6735
function api_get_current_access_url_id()
6736
{
6737
    if ('cli' === PHP_SAPI) {
6738
        $accessUrlId = api_get_configuration_value('access_url');
6739
        if (!empty($accessUrlId)) {
6740
            return $accessUrlId;
6741
        }
6742
    }
6743
6744
    static $id;
6745
    if (!empty($id)) {
6746
        return (int) $id;
6747
    }
6748
6749
    if (!api_get_multiple_access_url()) {
6750
        // If the feature is not enabled, assume 1 and return before querying
6751
        // the database
6752
        return 1;
6753
    }
6754
6755
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6756
    $path = Database::escape_string(api_get_path(WEB_PATH));
6757
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
6758
    $result = Database::query($sql);
6759
    if (Database::num_rows($result) > 0) {
6760
        $id = Database::result($result, 0, 0);
6761
        if ($id === false) {
6762
            return -1;
6763
        }
6764
6765
        return (int) $id;
6766
    }
6767
6768
    $id = 1;
6769
6770
    //if the url in WEB_PATH was not found, it can only mean that there is
6771
    // either a configuration problem or the first URL has not been defined yet
6772
    // (by default it is http://localhost/). Thus the more sensible thing we can
6773
    // do is return 1 (the main URL) as the user cannot hack this value anyway
6774
    return 1;
6775
}
6776
6777
/**
6778
 * Gets the registered urls from a given user id.
6779
 *
6780
 * @param int $user_id
6781
 *
6782
 * @return array
6783
 *
6784
 * @author Julio Montoya <[email protected]>
6785
 */
6786
function api_get_access_url_from_user($user_id)
6787
{
6788
    $user_id = (int) $user_id;
6789
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
6790
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6791
    $sql = "SELECT access_url_id
6792
            FROM $table_url_rel_user url_rel_user
6793
            INNER JOIN $table_url u
6794
            ON (url_rel_user.access_url_id = u.id)
6795
            WHERE user_id = ".intval($user_id);
6796
    $result = Database::query($sql);
6797
    $list = [];
6798
    while ($row = Database::fetch_array($result, 'ASSOC')) {
6799
        $list[] = $row['access_url_id'];
6800
    }
6801
6802
    return $list;
6803
}
6804
6805
/**
6806
 * Gets the status of a user in a course.
6807
 *
6808
 * @param int $user_id
6809
 * @param int $courseId
6810
 *
6811
 * @return int user status
6812
 */
6813
function api_get_status_of_user_in_course($user_id, $courseId)
6814
{
6815
    $tbl_rel_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6816
    if (!empty($user_id) && !empty($courseId)) {
6817
        $user_id = intval($user_id);
6818
        $courseId = intval($courseId);
6819
        $sql = 'SELECT status
6820
                FROM '.$tbl_rel_course_user.'
6821
                WHERE user_id='.$user_id.' AND c_id = '.$courseId;
6822
        $result = Database::query($sql);
6823
        $row_status = Database::fetch_array($result, 'ASSOC');
6824
6825
        return $row_status['status'];
6826
    } else {
6827
        return 0;
6828
    }
6829
}
6830
6831
/**
6832
 * Checks whether the curent user is in a group or not.
6833
 *
6834
 * @param string        The group id - optional (takes it from session if not given)
6835
 * @param string        The course code - optional (no additional check by course if course code is not given)
6836
 *
6837
 * @return bool
6838
 *
6839
 * @author Ivan Tcholakov
6840
 */
6841
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
6842
{
6843
    if (!empty($courseCodeParam)) {
6844
        $courseCode = api_get_course_id();
6845
        if (!empty($courseCode)) {
6846
            if ($courseCodeParam != $courseCode) {
6847
                return false;
6848
            }
6849
        } else {
6850
            return false;
6851
        }
6852
    }
6853
6854
    $groupId = api_get_group_id();
6855
6856
    if (!empty($groupId)) {
6857
        if (!empty($groupIdParam)) {
6858
            return $groupIdParam == $groupId;
6859
        } else {
6860
            return true;
6861
        }
6862
    }
6863
6864
    return false;
6865
}
6866
6867
/**
6868
 * Checks whether a secret key is valid.
6869
 *
6870
 * @param string $original_key_secret - secret key from (webservice) client
6871
 * @param string $security_key        - security key from Chamilo
6872
 *
6873
 * @return bool - true if secret key is valid, false otherwise
6874
 */
6875
function api_is_valid_secret_key($original_key_secret, $security_key)
6876
{
6877
    if (empty($original_key_secret) || empty($security_key)) {
6878
        return false;
6879
    }
6880
6881
    return (string) $original_key_secret === sha1($security_key);
6882
}
6883
6884
/**
6885
 * Checks whether a user is into course.
6886
 *
6887
 * @param int $course_id - the course id
6888
 * @param int $user_id   - the user id
6889
 *
6890
 * @return bool
6891
 */
6892
function api_is_user_of_course($course_id, $user_id)
6893
{
6894
    $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6895
    $sql = 'SELECT user_id FROM '.$tbl_course_rel_user.'
6896
            WHERE
6897
                c_id ="'.intval($course_id).'" AND
6898
                user_id = "'.intval($user_id).'" AND
6899
                relation_type <> '.COURSE_RELATION_TYPE_RRHH.' ';
6900
    $result = Database::query($sql);
6901
6902
    return Database::num_rows($result) == 1;
6903
}
6904
6905
/**
6906
 * Checks whether the server's operating system is Windows (TM).
6907
 *
6908
 * @return bool - true if the operating system is Windows, false otherwise
6909
 */
6910
function api_is_windows_os()
6911
{
6912
    if (function_exists('php_uname')) {
6913
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
6914
        // We expect that this function will always work for Chamilo 1.8.x.
6915
        $os = php_uname();
6916
    }
6917
    // The following methods are not needed, but let them stay, just in case.
6918
    elseif (isset($_ENV['OS'])) {
6919
        // Sometimes $_ENV['OS'] may not be present (bugs?)
6920
        $os = $_ENV['OS'];
6921
    } elseif (defined('PHP_OS')) {
6922
        // PHP_OS means on which OS PHP was compiled, this is why
6923
        // using PHP_OS is the last choice for detection.
6924
        $os = PHP_OS;
6925
    } else {
6926
        return false;
6927
    }
6928
6929
    return strtolower(substr((string) $os, 0, 3)) == 'win';
6930
}
6931
6932
/**
6933
 * This function informs whether the sent request is XMLHttpRequest.
6934
 */
6935
function api_is_xml_http_request()
6936
{
6937
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
6938
}
6939
6940
/**
6941
 * This wrapper function has been implemented for avoiding some known problems about the function getimagesize().
6942
 *
6943
 * @see http://php.net/manual/en/function.getimagesize.php
6944
 * @see http://www.dokeos.com/forum/viewtopic.php?t=12345
6945
 * @see http://www.dokeos.com/forum/viewtopic.php?t=16355
6946
 *
6947
 * @return int
6948
 */
6949
function api_getimagesize($path)
6950
{
6951
    $image = new Image($path);
6952
6953
    return $image->get_image_size();
6954
}
6955
6956
/**
6957
 * This function resizes an image, with preserving its proportions (or aspect ratio).
6958
 *
6959
 * @author Ivan Tcholakov, MAY-2009.
6960
 *
6961
 * @param int $image         System path or URL of the image
6962
 * @param int $target_width  Targeted width
6963
 * @param int $target_height Targeted height
6964
 *
6965
 * @return array Calculated new width and height
6966
 */
6967
function api_resize_image($image, $target_width, $target_height)
6968
{
6969
    $image_properties = api_getimagesize($image);
6970
6971
    return api_calculate_image_size(
6972
        $image_properties['width'],
6973
        $image_properties['height'],
6974
        $target_width,
6975
        $target_height
6976
    );
6977
}
6978
6979
/**
6980
 * This function calculates new image size, with preserving image's proportions (or aspect ratio).
6981
 *
6982
 * @author Ivan Tcholakov, MAY-2009.
6983
 * @author The initial idea has been taken from code by Patrick Cool, MAY-2004.
6984
 *
6985
 * @param int $image_width   Initial width
6986
 * @param int $image_height  Initial height
6987
 * @param int $target_width  Targeted width
6988
 * @param int $target_height Targeted height
6989
 *
6990
 * @return array Calculated new width and height
6991
 */
6992
function api_calculate_image_size(
6993
    $image_width,
6994
    $image_height,
6995
    $target_width,
6996
    $target_height
6997
) {
6998
    // Only maths is here.
6999
    $result = ['width' => $image_width, 'height' => $image_height];
7000
    if ($image_width <= 0 || $image_height <= 0) {
7001
        return $result;
7002
    }
7003
    $resize_factor_width = $target_width / $image_width;
7004
    $resize_factor_height = $target_height / $image_height;
7005
    $delta_width = $target_width - $image_width * $resize_factor_height;
7006
    $delta_height = $target_height - $image_height * $resize_factor_width;
7007
    if ($delta_width > $delta_height) {
7008
        $result['width'] = ceil($image_width * $resize_factor_height);
7009
        $result['height'] = ceil($image_height * $resize_factor_height);
7010
    } elseif ($delta_width < $delta_height) {
7011
        $result['width'] = ceil($image_width * $resize_factor_width);
7012
        $result['height'] = ceil($image_height * $resize_factor_width);
7013
    } else {
7014
        $result['width'] = ceil($target_width);
7015
        $result['height'] = ceil($target_height);
7016
    }
7017
7018
    return $result;
7019
}
7020
7021
/**
7022
 * Returns a list of Chamilo's tools or
7023
 * checks whether a given identificator is a valid Chamilo's tool.
7024
 *
7025
 * @author Isaac flores paz
7026
 *
7027
 * @param string The tool name to filter
7028
 *
7029
 * @return mixed Filtered string or array
7030
 */
7031
function api_get_tools_lists($my_tool = null)
7032
{
7033
    $tools_list = [
7034
        TOOL_DOCUMENT,
7035
        TOOL_THUMBNAIL,
7036
        TOOL_HOTPOTATOES,
7037
        TOOL_CALENDAR_EVENT,
7038
        TOOL_LINK,
7039
        TOOL_COURSE_DESCRIPTION,
7040
        TOOL_SEARCH,
7041
        TOOL_LEARNPATH,
7042
        TOOL_ANNOUNCEMENT,
7043
        TOOL_FORUM,
7044
        TOOL_THREAD,
7045
        TOOL_POST,
7046
        TOOL_DROPBOX,
7047
        TOOL_QUIZ,
7048
        TOOL_USER,
7049
        TOOL_GROUP,
7050
        TOOL_BLOGS,
7051
        TOOL_CHAT,
7052
        TOOL_STUDENTPUBLICATION,
7053
        TOOL_TRACKING,
7054
        TOOL_HOMEPAGE_LINK,
7055
        TOOL_COURSE_SETTING,
7056
        TOOL_BACKUP,
7057
        TOOL_COPY_COURSE_CONTENT,
7058
        TOOL_RECYCLE_COURSE,
7059
        TOOL_COURSE_HOMEPAGE,
7060
        TOOL_COURSE_RIGHTS_OVERVIEW,
7061
        TOOL_UPLOAD,
7062
        TOOL_COURSE_MAINTENANCE,
7063
        TOOL_SURVEY,
7064
        TOOL_WIKI,
7065
        TOOL_GLOSSARY,
7066
        TOOL_GRADEBOOK,
7067
        TOOL_NOTEBOOK,
7068
        TOOL_ATTENDANCE,
7069
        TOOL_COURSE_PROGRESS,
7070
    ];
7071
    if (empty($my_tool)) {
7072
        return $tools_list;
7073
    }
7074
7075
    return in_array($my_tool, $tools_list) ? $my_tool : '';
7076
}
7077
7078
/**
7079
 * Checks whether we already approved the last version term and condition.
7080
 *
7081
 * @param int user id
7082
 *
7083
 * @return bool true if we pass false otherwise
7084
 */
7085
function api_check_term_condition($userId)
7086
{
7087
    if (api_get_setting('allow_terms_conditions') === 'true') {
7088
        // Check if exists terms and conditions
7089
        if (LegalManager::count() == 0) {
7090
            return true;
7091
        }
7092
7093
        $extraFieldValue = new ExtraFieldValue('user');
7094
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
7095
            $userId,
7096
            'legal_accept'
7097
        );
7098
7099
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
7100
            $result = $data['value'];
7101
            $user_conditions = explode(':', $result);
7102
            $version = $user_conditions[0];
7103
            $langId = $user_conditions[1];
7104
            $realVersion = LegalManager::get_last_version($langId);
7105
7106
            return $version >= $realVersion;
7107
        }
7108
7109
        return false;
7110
    }
7111
7112
    return false;
7113
}
7114
7115
/**
7116
 * Gets all information of a tool into course.
7117
 *
7118
 * @param int The tool id
7119
 *
7120
 * @return array
7121
 */
7122
function api_get_tool_information_by_name($name)
7123
{
7124
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
7125
    $course_id = api_get_course_int_id();
7126
    $sql = "SELECT * FROM $t_tool
7127
            WHERE c_id = $course_id  AND name = '".Database::escape_string($name)."' ";
7128
    $rs = Database::query($sql);
7129
7130
    return Database::fetch_array($rs, 'ASSOC');
7131
}
7132
7133
/**
7134
 * Function used to protect a "global" admin script.
7135
 * The function blocks access when the user has no global platform admin rights.
7136
 * Global admins are the admins that are registered in the main.admin table
7137
 * AND the users who have access to the "principal" portal.
7138
 * That means that there is a record in the main.access_url_rel_user table
7139
 * with his user id and the access_url_id=1.
7140
 *
7141
 * @author Julio Montoya
7142
 *
7143
 * @param int $user_id
7144
 *
7145
 * @return bool
7146
 */
7147
function api_is_global_platform_admin($user_id = null)
7148
{
7149
    $user_id = (int) $user_id;
7150
    if (empty($user_id)) {
7151
        $user_id = api_get_user_id();
7152
    }
7153
    if (api_is_platform_admin_by_id($user_id)) {
7154
        $urlList = api_get_access_url_from_user($user_id);
7155
        // The admin is registered in the first "main" site with access_url_id = 1
7156
        if (in_array(1, $urlList)) {
7157
            return true;
7158
        } else {
7159
            return false;
7160
        }
7161
    }
7162
7163
    return false;
7164
}
7165
7166
/**
7167
 * @param int  $admin_id_to_check
7168
 * @param int  $my_user_id
7169
 * @param bool $allow_session_admin
7170
 *
7171
 * @return bool
7172
 */
7173
function api_global_admin_can_edit_admin(
7174
    $admin_id_to_check,
7175
    $my_user_id = null,
7176
    $allow_session_admin = false
7177
) {
7178
    if (empty($my_user_id)) {
7179
        $my_user_id = api_get_user_id();
7180
    }
7181
7182
    $iam_a_global_admin = api_is_global_platform_admin($my_user_id);
7183
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
7184
7185
    if ($iam_a_global_admin) {
7186
        // Global admin can edit everything
7187
        return true;
7188
    } else {
7189
        // If i'm a simple admin
7190
        $is_platform_admin = api_is_platform_admin_by_id($my_user_id);
7191
7192
        if ($allow_session_admin) {
7193
            $is_platform_admin = api_is_platform_admin_by_id($my_user_id) || (api_get_user_status($my_user_id) == SESSIONADMIN);
7194
        }
7195
7196
        if ($is_platform_admin) {
7197
            if ($user_is_global_admin) {
7198
                return false;
7199
            } else {
7200
                return true;
7201
            }
7202
        } else {
7203
            return false;
7204
        }
7205
    }
7206
}
7207
7208
/**
7209
 * @param int  $admin_id_to_check
7210
 * @param int  $my_user_id
7211
 * @param bool $allow_session_admin
7212
 *
7213
 * @return bool|null
7214
 */
7215
function api_protect_super_admin($admin_id_to_check, $my_user_id = null, $allow_session_admin = false)
7216
{
7217
    if (api_global_admin_can_edit_admin($admin_id_to_check, $my_user_id, $allow_session_admin)) {
7218
        return true;
7219
    } else {
7220
        api_not_allowed();
7221
    }
7222
}
7223
7224
/**
7225
 * Function used to protect a global admin script.
7226
 * The function blocks access when the user has no global platform admin rights.
7227
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
7228
 *
7229
 * @author Julio Montoya
7230
 */
7231
function api_protect_global_admin_script()
7232
{
7233
    if (!api_is_global_platform_admin()) {
7234
        api_not_allowed();
7235
7236
        return false;
7237
    }
7238
7239
    return true;
7240
}
7241
7242
/**
7243
 * Get active template.
7244
 *
7245
 * @param string    theme type (optional: default)
7246
 * @param string    path absolute(abs) or relative(rel) (optional:rel)
7247
 *
7248
 * @return string actived template path
7249
 */
7250
function api_get_template($path_type = 'rel')
7251
{
7252
    $path_types = ['rel', 'abs'];
7253
    $template_path = '';
7254
    if (in_array($path_type, $path_types)) {
7255
        if ($path_type == 'rel') {
7256
            $template_path = api_get_path(SYS_TEMPLATE_PATH);
7257
        } else {
7258
            $template_path = api_get_path(WEB_TEMPLATE_PATH);
7259
        }
7260
    }
7261
    $actived_theme = 'default';
7262
    if (api_get_setting('active_template')) {
7263
        $actived_theme = api_get_setting('active_template');
7264
    }
7265
    $actived_theme_path = $template_path.$actived_theme.DIRECTORY_SEPARATOR;
7266
7267
    return $actived_theme_path;
7268
}
7269
7270
/**
7271
 * Check browser support for specific file types or features
7272
 * This function checks if the user's browser supports a file format or given
7273
 * feature, or returns the current browser and major version when
7274
 * $format=check_browser. Only a limited number of formats and features are
7275
 * checked by this method. Make sure you check its definition first.
7276
 *
7277
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
7278
 *
7279
 * @return bool or return text array if $format=check_browser
7280
 *
7281
 * @author Juan Carlos Raña Trabado
7282
 */
7283
function api_browser_support($format = '')
7284
{
7285
    $browser = new Browser();
7286
    $current_browser = $browser->getBrowser();
7287
    $a_versiontemp = explode('.', $browser->getVersion());
7288
    $current_majorver = $a_versiontemp[0];
7289
7290
    static $result;
7291
7292
    if (isset($result[$format])) {
7293
        return $result[$format];
7294
    }
7295
7296
    // Native svg support
7297
    if ($format == 'svg') {
7298
        if (($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7299
            ($current_browser == 'Firefox' && $current_majorver > 1) ||
7300
            ($current_browser == 'Safari' && $current_majorver >= 4) ||
7301
            ($current_browser == 'Chrome' && $current_majorver >= 1) ||
7302
            ($current_browser == 'Opera' && $current_majorver >= 9)
7303
        ) {
7304
            $result[$format] = true;
7305
7306
            return true;
7307
        } else {
7308
            $result[$format] = false;
7309
7310
            return false;
7311
        }
7312
    } elseif ($format == 'pdf') {
7313
        // native pdf support
7314
        if (($current_browser == 'Chrome' && $current_majorver >= 6) ||
7315
            ('Firefox' === $current_browser && $current_majorver >= 15)
7316
        ) {
7317
            $result[$format] = true;
7318
7319
            return true;
7320
        } else {
7321
            $result[$format] = false;
7322
7323
            return false;
7324
        }
7325
    } elseif ($format == 'tif' || $format == 'tiff') {
7326
        //native tif support
7327
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7328
            $result[$format] = true;
7329
7330
            return true;
7331
        } else {
7332
            $result[$format] = false;
7333
7334
            return false;
7335
        }
7336
    } elseif ($format == 'ogg' || $format == 'ogx' || $format == 'ogv' || $format == 'oga') {
7337
        //native ogg, ogv,oga support
7338
        if (($current_browser == 'Firefox' && $current_majorver >= 3) ||
7339
            ($current_browser == 'Chrome' && $current_majorver >= 3) ||
7340
            ($current_browser == 'Opera' && $current_majorver >= 9)) {
7341
            $result[$format] = true;
7342
7343
            return true;
7344
        } else {
7345
            $result[$format] = false;
7346
7347
            return false;
7348
        }
7349
    } elseif ($format == 'mpg' || $format == 'mpeg') {
7350
        //native mpg support
7351
        if (($current_browser == 'Safari' && $current_majorver >= 5)) {
7352
            $result[$format] = true;
7353
7354
            return true;
7355
        } else {
7356
            $result[$format] = false;
7357
7358
            return false;
7359
        }
7360
    } elseif ($format == 'mp4') {
7361
        //native mp4 support (TODO: Android, iPhone)
7362
        if ($current_browser == 'Android' || $current_browser == 'iPhone') {
7363
            $result[$format] = true;
7364
7365
            return true;
7366
        } else {
7367
            $result[$format] = false;
7368
7369
            return false;
7370
        }
7371
    } elseif ($format == 'mov') {
7372
        //native mov support( TODO:check iPhone)
7373
        if ($current_browser == 'Safari' && $current_majorver >= 5 || $current_browser == 'iPhone') {
7374
            $result[$format] = true;
7375
7376
            return true;
7377
        } else {
7378
            $result[$format] = false;
7379
7380
            return false;
7381
        }
7382
    } elseif ($format == 'avi') {
7383
        //native avi support
7384
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7385
            $result[$format] = true;
7386
7387
            return true;
7388
        } else {
7389
            $result[$format] = false;
7390
7391
            return false;
7392
        }
7393
    } elseif ($format == 'wmv') {
7394
        //native wmv support
7395
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7396
            $result[$format] = true;
7397
7398
            return true;
7399
        } else {
7400
            $result[$format] = false;
7401
7402
            return false;
7403
        }
7404
    } elseif ($format == 'webm') {
7405
        //native webm support (TODO:check IE9, Chrome9, Android)
7406
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7407
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7408
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7409
            ($current_browser == 'Chrome' && $current_majorver >= 9) ||
7410
            $current_browser == 'Android'
7411
        ) {
7412
            $result[$format] = true;
7413
7414
            return true;
7415
        } else {
7416
            $result[$format] = false;
7417
7418
            return false;
7419
        }
7420
    } elseif ($format == 'wav') {
7421
        //native wav support (only some codecs !)
7422
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7423
            ($current_browser == 'Safari' && $current_majorver >= 5) ||
7424
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7425
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7426
            ($current_browser == 'Chrome' && $current_majorver > 9) ||
7427
            $current_browser == 'Android' ||
7428
            $current_browser == 'iPhone'
7429
        ) {
7430
            $result[$format] = true;
7431
7432
            return true;
7433
        } else {
7434
            $result[$format] = false;
7435
7436
            return false;
7437
        }
7438
    } elseif ($format == 'mid' || $format == 'kar') {
7439
        //native midi support (TODO:check Android)
7440
        if ($current_browser == 'Opera' && $current_majorver >= 9 || $current_browser == 'Android') {
7441
            $result[$format] = true;
7442
7443
            return true;
7444
        } else {
7445
            $result[$format] = false;
7446
7447
            return false;
7448
        }
7449
    } elseif ($format == 'wma') {
7450
        //native wma support
7451
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7452
            $result[$format] = true;
7453
7454
            return true;
7455
        } else {
7456
            $result[$format] = false;
7457
7458
            return false;
7459
        }
7460
    } elseif ($format == 'au') {
7461
        //native au support
7462
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7463
            $result[$format] = true;
7464
7465
            return true;
7466
        } else {
7467
            $result[$format] = false;
7468
7469
            return false;
7470
        }
7471
    } elseif ($format == 'mp3') {
7472
        //native mp3 support (TODO:check Android, iPhone)
7473
        if (($current_browser == 'Safari' && $current_majorver >= 5) ||
7474
            ($current_browser == 'Chrome' && $current_majorver >= 6) ||
7475
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7476
            $current_browser == 'Android' ||
7477
            $current_browser == 'iPhone' ||
7478
            $current_browser == 'Firefox'
7479
        ) {
7480
            $result[$format] = true;
7481
7482
            return true;
7483
        } else {
7484
            $result[$format] = false;
7485
7486
            return false;
7487
        }
7488
    } elseif ($format == 'autocapitalize') {
7489
        // Help avoiding showing the autocapitalize option if the browser doesn't
7490
        // support it: this attribute is against the HTML5 standard
7491
        if ($current_browser == 'Safari' || $current_browser == 'iPhone') {
7492
            return true;
7493
        } else {
7494
            return false;
7495
        }
7496
    } elseif ($format == "check_browser") {
7497
        $array_check_browser = [$current_browser, $current_majorver];
7498
7499
        return $array_check_browser;
7500
    } else {
7501
        $result[$format] = false;
7502
7503
        return false;
7504
    }
7505
}
7506
7507
/**
7508
 * This function checks if exist path and file browscap.ini
7509
 * In order for this to work, your browscap configuration setting in php.ini
7510
 * must point to the correct location of the browscap.ini file on your system
7511
 * http://php.net/manual/en/function.get-browser.php.
7512
 *
7513
 * @return bool
7514
 *
7515
 * @author Juan Carlos Raña Trabado
7516
 */
7517
function api_check_browscap()
7518
{
7519
    $setting = ini_get('browscap');
7520
    if ($setting) {
7521
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
7522
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
7523
            return true;
7524
        }
7525
    }
7526
7527
    return false;
7528
}
7529
7530
/**
7531
 * Returns the <script> HTML tag.
7532
 */
7533
function api_get_js($file)
7534
{
7535
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
7536
}
7537
7538
/**
7539
 * Returns the <script> HTML tag.
7540
 *
7541
 * @return string
7542
 */
7543
function api_get_asset($file)
7544
{
7545
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
7546
}
7547
7548
/**
7549
 * Returns the <script> HTML tag.
7550
 *
7551
 * @param string $file
7552
 * @param string $media
7553
 *
7554
 * @return string
7555
 */
7556
function api_get_css_asset($file, $media = 'screen')
7557
{
7558
    return '<link href="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7559
}
7560
7561
/**
7562
 * Returns the <link> HTML tag.
7563
 *
7564
 * @param string $file
7565
 * @param string $media
7566
 */
7567
function api_get_css($file, $media = 'screen')
7568
{
7569
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7570
}
7571
7572
/**
7573
 * Returns the js header to include the jquery library.
7574
 */
7575
function api_get_jquery_js()
7576
{
7577
    return api_get_asset('jquery/dist/jquery.min.js');
7578
}
7579
7580
/**
7581
 * Returns the jquery path.
7582
 *
7583
 * @return string
7584
 */
7585
function api_get_jquery_web_path()
7586
{
7587
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/dist/jquery.min.js';
7588
}
7589
7590
/**
7591
 * @return string
7592
 */
7593
function api_get_jquery_ui_js_web_path()
7594
{
7595
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
7596
}
7597
7598
/**
7599
 * @return string
7600
 */
7601
function api_get_jquery_ui_css_web_path()
7602
{
7603
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
7604
}
7605
7606
/**
7607
 * Returns the jquery-ui library js headers.
7608
 *
7609
 * @param   bool    add the jqgrid library
7610
 *
7611
 * @return string html tags
7612
 */
7613
function api_get_jquery_ui_js($include_jqgrid = false)
7614
{
7615
    $libraries = [];
7616
    if ($include_jqgrid) {
7617
        $libraries[] = 'jqgrid';
7618
    }
7619
7620
    return api_get_jquery_libraries_js($libraries);
7621
}
7622
7623
function api_get_jqgrid_js()
7624
{
7625
    return api_get_jquery_libraries_js(['jqgrid']);
7626
}
7627
7628
/**
7629
 * Returns the jquery library js and css headers.
7630
 *
7631
 * @param   array   list of jquery libraries supported jquery-ui, jqgrid
7632
 * @param   bool    add the jquery library
7633
 *
7634
 * @return string html tags
7635
 */
7636
function api_get_jquery_libraries_js($libraries)
7637
{
7638
    $js = '';
7639
    $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
7640
7641
    //jqgrid js and css
7642
    if (in_array('jqgrid', $libraries)) {
7643
        $languaje = 'en';
7644
        $platform_isocode = strtolower(api_get_language_isocode());
7645
7646
        //languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7647
        $jqgrid_langs = [
7648
            'bg', 'bg1251', 'cat', 'cn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fr', 'gl', 'he', 'hu', 'is', 'it', 'ja', 'nl', 'no', 'pl', 'pt-br', 'pt', 'ro', 'ru', 'sk', 'sr', 'sv', 'tr', 'ua',
7649
        ];
7650
7651
        if (in_array($platform_isocode, $jqgrid_langs)) {
7652
            $languaje = $platform_isocode;
7653
        }
7654
        //$js .= '<link rel="stylesheet" href="'.$js_path.'jqgrid/css/ui.jqgrid.css" type="text/css">';
7655
        $js .= api_get_css($js_path.'jqgrid/css/ui.jqgrid.css');
7656
        $js .= api_get_js('jqgrid/js/i18n/grid.locale-'.$languaje.'.js');
7657
        $js .= api_get_js('jqgrid/js/jquery.jqGrid.min.js');
7658
    }
7659
7660
    //Document multiple upload funcionality
7661
    if (in_array('jquery-upload', $libraries)) {
7662
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
7663
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
7664
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
7665
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
7666
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
7667
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
7668
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
7669
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
7670
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
7671
7672
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
7673
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
7674
    }
7675
7676
    // jquery datepicker
7677
    if (in_array('datepicker', $libraries)) {
7678
        $languaje = 'en-GB';
7679
        $platform_isocode = strtolower(api_get_language_isocode());
7680
7681
        // languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7682
        $datapicker_langs = [
7683
            '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',
7684
        ];
7685
        if (in_array($platform_isocode, $datapicker_langs)) {
7686
            $languaje = $platform_isocode;
7687
        }
7688
7689
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
7690
        $script = '<script>
7691
        $(function(){
7692
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
7693
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
7694
        });
7695
        </script>
7696
        ';
7697
        $js .= $script;
7698
    }
7699
7700
    return $js;
7701
}
7702
7703
/**
7704
 * Returns the URL to the course or session, removing the complexity of the URL
7705
 * building piece by piece.
7706
 *
7707
 * This function relies on api_get_course_info()
7708
 *
7709
 * @param string $courseCode The course code - optional (takes it from context if not given)
7710
 * @param int    $sessionId  The session ID  - optional (takes it from context if not given)
7711
 * @param int    $groupId    The group ID - optional (takes it from context if not given)
7712
 *
7713
 * @return string The URL to a course, a session, or empty string if nothing works e.g. https://localhost/courses/ABC/index.php?session_id=3&gidReq=1
7714
 *
7715
 * @author  Julio Montoya <[email protected]>
7716
 */
7717
function api_get_course_url($courseCode = null, $sessionId = null, $groupId = null)
7718
{
7719
    $courseDirectory = '';
7720
    $url = '';
7721
    // If courseCode not set, get context or []
7722
    if (empty($courseCode)) {
7723
        $courseInfo = api_get_course_info();
7724
    } else {
7725
        $courseInfo = api_get_course_info($courseCode);
7726
    }
7727
7728
    // If course defined, get directory, otherwise keep empty string
7729
    if (!empty($courseInfo['directory'])) {
7730
        $courseDirectory = $courseInfo['directory'];
7731
    }
7732
7733
    // If sessionId not set, get context or 0
7734
    if (empty($sessionId)) {
7735
        $sessionId = api_get_session_id();
7736
    }
7737
7738
    // If groupId not set, get context or 0
7739
    if (empty($groupId)) {
7740
        $groupId = api_get_group_id();
7741
    }
7742
7743
    // Build the URL
7744
    if (!empty($courseDirectory)) {
7745
        // directory not empty, so we do have a course
7746
        $url = api_get_path(WEB_COURSE_PATH).$courseDirectory.'/index.php?id_session='.$sessionId.'&gidReq='.$groupId;
7747
    } elseif (!empty($sessionId) && api_get_configuration_value('remove_session_url') !== true) {
7748
        // if the course was unset and the session was set, send directly to the session
7749
        $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
7750
    }
7751
    // if not valid combination was found, return an empty string
7752
    return $url;
7753
}
7754
7755
/**
7756
 * Check if the current portal has the $_configuration['multiple_access_urls'] parameter on.
7757
 *
7758
 * @return bool true if multi site is enabled
7759
 */
7760
function api_get_multiple_access_url()
7761
{
7762
    global $_configuration;
7763
    if (isset($_configuration['multiple_access_urls']) && $_configuration['multiple_access_urls']) {
7764
        return true;
7765
    }
7766
7767
    return false;
7768
}
7769
7770
/**
7771
 * Just a synonym for api_get_multiple_access_url().
7772
 *
7773
 * @return bool
7774
 */
7775
function api_is_multiple_url_enabled()
7776
{
7777
    return api_get_multiple_access_url();
7778
}
7779
7780
/**
7781
 * Returns a md5 unique id.
7782
 *
7783
 * @todo add more parameters
7784
 */
7785
function api_get_unique_id()
7786
{
7787
    $id = md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
7788
7789
    return $id;
7790
}
7791
7792
/**
7793
 * Get home path.
7794
 *
7795
 * @return string
7796
 */
7797
function api_get_home_path()
7798
{
7799
    // FIX : Start the routing determination from central path definition
7800
    $home = api_get_path(SYS_HOME_PATH);
7801
    if (api_get_multiple_access_url()) {
7802
        $access_url_id = api_get_current_access_url_id();
7803
        $url_info = api_get_access_url($access_url_id);
7804
        $url = api_remove_trailing_slash(preg_replace('/https?:\/\//i', '', $url_info['url']));
7805
        $clean_url = api_replace_dangerous_char($url);
7806
        $clean_url = str_replace('/', '-', $clean_url);
7807
        $clean_url .= '/';
7808
        if ($clean_url != 'localhost/') {
7809
            // means that the multiple URL was not well configured we don't rename the $home variable
7810
            return "{$home}{$clean_url}";
7811
        }
7812
    }
7813
7814
    return $home;
7815
}
7816
7817
/**
7818
 * @param int Course id
7819
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
7820
 * @param int the item id (tool id, exercise id, lp id)
7821
 *
7822
 * @return bool
7823
 */
7824
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
7825
{
7826
    if (api_is_platform_admin()) {
7827
        return false;
7828
    }
7829
    if (api_get_setting('gradebook_locking_enabled') == 'true') {
7830
        if (empty($course_code)) {
7831
            $course_code = api_get_course_id();
7832
        }
7833
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
7834
        $item_id = intval($item_id);
7835
        $link_type = intval($link_type);
7836
        $course_code = Database::escape_string($course_code);
7837
        $sql = "SELECT locked FROM $table
7838
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
7839
        $result = Database::query($sql);
7840
        if (Database::num_rows($result)) {
7841
            return true;
7842
        }
7843
    }
7844
7845
    return false;
7846
}
7847
7848
/**
7849
 * Blocks a page if the item was added in a gradebook.
7850
 *
7851
 * @param int       exercise id, work id, thread id,
7852
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
7853
 * see gradebook/lib/be/linkfactory
7854
 * @param string    course code
7855
 *
7856
 * @return false|null
7857
 */
7858
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
7859
{
7860
    if (api_is_platform_admin()) {
7861
        return false;
7862
    }
7863
7864
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
7865
        $message = Display::return_message(get_lang('ResourceLockedByGradebook'), 'warning');
7866
        api_not_allowed(true, $message);
7867
    }
7868
}
7869
7870
/**
7871
 * Checks the PHP version installed is enough to run Chamilo.
7872
 *
7873
 * @param string Include path (used to load the error page)
7874
 */
7875
function api_check_php_version($my_inc_path = null)
7876
{
7877
    if (!function_exists('version_compare') || version_compare(phpversion(), REQUIRED_PHP_VERSION, '<')) {
7878
        $global_error_code = 1;
7879
        // Incorrect PHP version
7880
        $global_page = $my_inc_path.'global_error_message.inc.php';
7881
        if (file_exists($global_page)) {
7882
            require $global_page;
7883
        }
7884
        exit;
7885
    }
7886
}
7887
7888
/**
7889
 * Checks whether the Archive directory is present and writeable. If not,
7890
 * prints a warning message.
7891
 */
7892
function api_check_archive_dir()
7893
{
7894
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
7895
        $message = Display::return_message(get_lang('ArchivesDirectoryNotWriteableContactAdmin'), 'warning');
7896
        api_not_allowed(true, $message);
7897
    }
7898
}
7899
7900
/**
7901
 * Returns an array of global configuration settings which should be ignored
7902
 * when printing the configuration settings screens.
7903
 *
7904
 * @return array Array of strings, each identifying one of the excluded settings
7905
 */
7906
function api_get_locked_settings()
7907
{
7908
    return [
7909
        'server_type',
7910
        'permanently_remove_deleted_files',
7911
        'account_valid_duration',
7912
        'service_ppt2lp',
7913
        'wcag_anysurfer_public_pages',
7914
        'upload_extensions_list_type',
7915
        'upload_extensions_blacklist',
7916
        'upload_extensions_whitelist',
7917
        'upload_extensions_skip',
7918
        'upload_extensions_replace_by',
7919
        'hide_dltt_markup',
7920
        'split_users_upload_directory',
7921
        'permissions_for_new_directories',
7922
        'permissions_for_new_files',
7923
        'platform_charset',
7924
        'ldap_description',
7925
        'cas_activate',
7926
        'cas_server',
7927
        'cas_server_uri',
7928
        'cas_port',
7929
        'cas_protocol',
7930
        'cas_add_user_activate',
7931
        'update_user_info_cas_with_ldap',
7932
        'languagePriority1',
7933
        'languagePriority2',
7934
        'languagePriority3',
7935
        'languagePriority4',
7936
        'login_is_email',
7937
        'chamilo_database_version',
7938
    ];
7939
}
7940
7941
/**
7942
 * Checks if the user is corrently logged in. Returns the user ID if he is, or
7943
 * false if he isn't. If the user ID is given and is an integer, then the same
7944
 * ID is simply returned.
7945
 *
7946
 * @param  int User ID
7947
 *
7948
 * @return bool Integer User ID is logged in, or false otherwise
7949
 */
7950
function api_user_is_login($user_id = null)
7951
{
7952
    $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id;
7953
7954
    return $user_id && !api_is_anonymous();
7955
}
7956
7957
/**
7958
 * Guess the real ip for register in the database, even in reverse proxy cases.
7959
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
7960
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
7961
 * Note: the result of this function is not SQL-safe. Please escape it before
7962
 * inserting in a database.
7963
 *
7964
 * @return string the user's real ip (unsafe - escape it before inserting to db)
7965
 *
7966
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
7967
 *
7968
 * @version CEV CHANGE 24APR2012
7969
 */
7970
function api_get_real_ip()
7971
{
7972
    $ip = trim($_SERVER['REMOTE_ADDR']);
7973
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
7974
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
7975
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
7976
        } else {
7977
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
7978
        }
7979
        $ip = trim($ip1);
7980
    }
7981
7982
    return $ip;
7983
}
7984
7985
/**
7986
 * Checks whether an IP is included inside an IP range.
7987
 *
7988
 * @param string IP address
7989
 * @param string IP range
7990
 * @param string $ip
7991
 *
7992
 * @return bool True if IP is in the range, false otherwise
7993
 *
7994
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
7995
 * @author Yannick Warnier for improvements and managment of multiple ranges
7996
 *
7997
 * @todo check for IPv6 support
7998
 */
7999
function api_check_ip_in_range($ip, $range)
8000
{
8001
    if (empty($ip) or empty($range)) {
8002
        return false;
8003
    }
8004
    $ip_ip = ip2long($ip);
8005
    // divide range param into array of elements
8006
    if (strpos($range, ',') !== false) {
8007
        $ranges = explode(',', $range);
8008
    } else {
8009
        $ranges = [$range];
8010
    }
8011
    foreach ($ranges as $range) {
8012
        $range = trim($range);
8013
        if (empty($range)) {
8014
            continue;
8015
        }
8016
        if (strpos($range, '/') === false) {
8017
            if (strcmp($ip, $range) === 0) {
8018
                return true; // there is a direct IP match, return OK
8019
            }
8020
            continue; //otherwise, get to the next range
8021
        }
8022
        // the range contains a "/", so analyse completely
8023
        list($net, $mask) = explode("/", $range);
8024
8025
        $ip_net = ip2long($net);
8026
        // mask binary magic
8027
        $ip_mask = ~((1 << (32 - $mask)) - 1);
8028
8029
        $ip_ip_net = $ip_ip & $ip_mask;
8030
        if ($ip_ip_net == $ip_net) {
8031
            return true;
8032
        }
8033
    }
8034
8035
    return false;
8036
}
8037
8038
function api_check_user_access_to_legal($courseInfo)
8039
{
8040
    if (empty($courseInfo)) {
8041
        return false;
8042
    }
8043
8044
    $visibility = (int) $courseInfo['visibility'];
8045
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
8046
8047
    return
8048
        in_array($visibility, $visibilityList) ||
8049
        api_is_drh() ||
8050
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
8051
}
8052
8053
/**
8054
 * Checks if the global chat is enabled or not.
8055
 *
8056
 * @return bool
8057
 */
8058
function api_is_global_chat_enabled()
8059
{
8060
    return
8061
        !api_is_anonymous() &&
8062
        api_get_setting('allow_global_chat') === 'true' &&
8063
        api_get_setting('allow_social_tool') === 'true';
8064
}
8065
8066
/**
8067
 * @todo Fix tool_visible_by_default_at_creation labels
8068
 *
8069
 * @param int   $item_id
8070
 * @param int   $tool_id
8071
 * @param int   $group_id   id
8072
 * @param array $courseInfo
8073
 * @param int   $sessionId
8074
 * @param int   $userId
8075
 */
8076
function api_set_default_visibility(
8077
    $item_id,
8078
    $tool_id,
8079
    $group_id = 0,
8080
    $courseInfo = [],
8081
    $sessionId = 0,
8082
    $userId = 0
8083
) {
8084
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
8085
    $courseId = $courseInfo['real_id'];
8086
    $courseCode = $courseInfo['code'];
8087
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
8088
    $userId = empty($userId) ? api_get_user_id() : $userId;
8089
8090
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
8091
    if (is_null($group_id)) {
8092
        $group_id = 0;
8093
    } else {
8094
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
8095
    }
8096
8097
    $groupInfo = [];
8098
    if (!empty($group_id)) {
8099
        $groupInfo = GroupManager::get_group_properties($group_id);
8100
    }
8101
    $original_tool_id = $tool_id;
8102
8103
    switch ($tool_id) {
8104
        case TOOL_LINK:
8105
        case TOOL_LINK_CATEGORY:
8106
            $tool_id = 'links';
8107
            break;
8108
        case TOOL_DOCUMENT:
8109
            $tool_id = 'documents';
8110
            break;
8111
        case TOOL_LEARNPATH:
8112
            $tool_id = 'learning';
8113
            break;
8114
        case TOOL_ANNOUNCEMENT:
8115
            $tool_id = 'announcements';
8116
            break;
8117
        case TOOL_FORUM:
8118
        case TOOL_FORUM_CATEGORY:
8119
        case TOOL_FORUM_THREAD:
8120
            $tool_id = 'forums';
8121
            break;
8122
        case TOOL_QUIZ:
8123
            $tool_id = 'quiz';
8124
            break;
8125
    }
8126
    $setting = api_get_setting('tool_visible_by_default_at_creation');
8127
8128
    if (isset($setting[$tool_id])) {
8129
        $visibility = 'invisible';
8130
        if ($setting[$tool_id] == 'true') {
8131
            $visibility = 'visible';
8132
        }
8133
8134
        // Read the portal and course default visibility
8135
        if ($tool_id === 'documents') {
8136
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
8137
        }
8138
8139
        api_item_property_update(
8140
            $courseInfo,
8141
            $original_tool_id,
8142
            $item_id,
8143
            $visibility,
8144
            $userId,
8145
            $groupInfo,
8146
            null,
8147
            null,
8148
            null,
8149
            $sessionId
8150
        );
8151
8152
        // Fixes default visibility for tests
8153
        switch ($original_tool_id) {
8154
            case TOOL_QUIZ:
8155
                if (empty($sessionId)) {
8156
                    $objExerciseTmp = new Exercise($courseId);
8157
                    $objExerciseTmp->read($item_id);
8158
                    if ($visibility == 'visible') {
8159
                        $objExerciseTmp->enable();
8160
                        $objExerciseTmp->save();
8161
                    } else {
8162
                        $objExerciseTmp->disable();
8163
                        $objExerciseTmp->save();
8164
                    }
8165
                }
8166
                break;
8167
        }
8168
    }
8169
}
8170
8171
/**
8172
 * @return string
8173
 */
8174
function api_get_security_key()
8175
{
8176
    return api_get_configuration_value('security_key');
8177
}
8178
8179
/**
8180
 * @param int $user_id
8181
 * @param int $courseId
8182
 * @param int $session_id
8183
 *
8184
 * @return array
8185
 */
8186
function api_detect_user_roles($user_id, $courseId, $session_id = 0)
8187
{
8188
    $user_roles = [];
8189
    $courseInfo = api_get_course_info_by_id($courseId);
8190
    $course_code = $courseInfo['code'];
8191
8192
    $url_id = api_get_current_access_url_id();
8193
    if (api_is_platform_admin_by_id($user_id, $url_id)) {
8194
        $user_roles[] = PLATFORM_ADMIN;
8195
    }
8196
8197
    /*if (api_is_drh()) {
8198
        $user_roles[] = DRH;
8199
    }*/
8200
8201
    if (!empty($session_id)) {
8202
        if (SessionManager::user_is_general_coach($user_id, $session_id)) {
8203
            $user_roles[] = SESSION_GENERAL_COACH;
8204
        }
8205
    }
8206
8207
    if (!empty($course_code)) {
8208
        if (empty($session_id)) {
8209
            if (CourseManager::is_course_teacher($user_id, $course_code)) {
8210
                $user_roles[] = COURSEMANAGER;
8211
            }
8212
            if (CourseManager::get_tutor_in_course_status($user_id, $courseInfo['real_id'])) {
8213
                $user_roles[] = COURSE_TUTOR;
8214
            }
8215
8216
            if (CourseManager::is_user_subscribed_in_course($user_id, $course_code)) {
8217
                $user_roles[] = COURSE_STUDENT;
8218
            }
8219
        } else {
8220
            $user_status_in_session = SessionManager::get_user_status_in_course_session(
8221
                $user_id,
8222
                $courseId,
8223
                $session_id
8224
            );
8225
8226
            if (!empty($user_status_in_session)) {
8227
                if ($user_status_in_session == 0) {
8228
                    $user_roles[] = SESSION_STUDENT;
8229
                }
8230
                if ($user_status_in_session == 2) {
8231
                    $user_roles[] = SESSION_COURSE_COACH;
8232
                }
8233
            }
8234
8235
            /*if (api_is_course_session_coach($user_id, $course_code, $session_id)) {
8236
               $user_roles[] = SESSION_COURSE_COACH;
8237
            }*/
8238
        }
8239
    }
8240
8241
    return $user_roles;
8242
}
8243
8244
/**
8245
 * @param int $courseId
8246
 * @param int $session_id
8247
 *
8248
 * @return bool
8249
 */
8250
function api_coach_can_edit_view_results($courseId = null, $session_id = null)
8251
{
8252
    if (api_is_platform_admin()) {
8253
        return true;
8254
    }
8255
8256
    $user_id = api_get_user_id();
8257
8258
    if (empty($courseId)) {
8259
        $courseId = api_get_course_int_id();
8260
    }
8261
8262
    if (empty($session_id)) {
8263
        $session_id = api_get_session_id();
8264
    }
8265
8266
    $roles = api_detect_user_roles($user_id, $courseId, $session_id);
8267
8268
    if (in_array(SESSION_COURSE_COACH, $roles)) {
8269
        //return api_get_setting('session_tutor_reports_visibility') == 'true';
8270
        return true;
8271
    } else {
8272
        if (in_array(COURSEMANAGER, $roles)) {
8273
            return true;
8274
        }
8275
8276
        return false;
8277
    }
8278
}
8279
8280
/**
8281
 * @param string $file
8282
 *
8283
 * @return string
8284
 */
8285
function api_get_js_simple($file)
8286
{
8287
    return '<script src="'.$file.'"></script>'."\n";
8288
}
8289
8290
function api_set_settings_and_plugins()
8291
{
8292
    global $_configuration;
8293
    $_setting = [];
8294
    $_plugins = [];
8295
8296
    // access_url == 1 is the default chamilo location
8297
    $settings_by_access_list = [];
8298
    $access_url_id = api_get_current_access_url_id();
8299
    if ($access_url_id != 1) {
8300
        $url_info = api_get_access_url($_configuration['access_url']);
8301
        if ($url_info['active'] == 1) {
8302
            $settings_by_access = &api_get_settings(null, 'list', $_configuration['access_url'], 1);
8303
            foreach ($settings_by_access as &$row) {
8304
                if (empty($row['variable'])) {
8305
                    $row['variable'] = 0;
8306
                }
8307
                if (empty($row['subkey'])) {
8308
                    $row['subkey'] = 0;
8309
                }
8310
                if (empty($row['category'])) {
8311
                    $row['category'] = 0;
8312
                }
8313
                $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']] = $row;
8314
            }
8315
        }
8316
    }
8317
8318
    $result = api_get_settings(null, 'list', 1);
8319
8320
    foreach ($result as &$row) {
8321
        if ($access_url_id != 1) {
8322
            if ($url_info['active'] == 1) {
8323
                $var = empty($row['variable']) ? 0 : $row['variable'];
8324
                $subkey = empty($row['subkey']) ? 0 : $row['subkey'];
8325
                $category = empty($row['category']) ? 0 : $row['category'];
8326
            }
8327
8328
            if ($row['access_url_changeable'] == 1 && $url_info['active'] == 1) {
8329
                if (isset($settings_by_access_list[$var]) &&
8330
                    $settings_by_access_list[$var][$subkey][$category]['selected_value'] != '') {
8331
                    if ($row['subkey'] == null) {
8332
                        $_setting[$row['variable']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8333
                    } else {
8334
                        $_setting[$row['variable']][$row['subkey']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8335
                    }
8336
                } else {
8337
                    if ($row['subkey'] == null) {
8338
                        $_setting[$row['variable']] = $row['selected_value'];
8339
                    } else {
8340
                        $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8341
                    }
8342
                }
8343
            } else {
8344
                if ($row['subkey'] == null) {
8345
                    $_setting[$row['variable']] = $row['selected_value'];
8346
                } else {
8347
                    $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8348
                }
8349
            }
8350
        } else {
8351
            if ($row['subkey'] == null) {
8352
                $_setting[$row['variable']] = $row['selected_value'];
8353
            } else {
8354
                $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8355
            }
8356
        }
8357
    }
8358
8359
    $result = api_get_settings('Plugins', 'list', $access_url_id);
8360
    $_plugins = [];
8361
    foreach ($result as &$row) {
8362
        $key = &$row['variable'];
8363
        if (is_string($_setting[$key])) {
8364
            $_setting[$key] = [];
8365
        }
8366
        $_setting[$key][] = $row['selected_value'];
8367
        $_plugins[$key][] = $row['selected_value'];
8368
    }
8369
8370
    $_SESSION['_setting'] = $_setting;
8371
    $_SESSION['_plugins'] = $_plugins;
8372
}
8373
8374
/**
8375
 * Modify default memory_limit and max_execution_time limits
8376
 * Needed when processing long tasks.
8377
 */
8378
function api_set_more_memory_and_time_limits()
8379
{
8380
    if (function_exists('ini_set')) {
8381
        api_set_memory_limit('2048M');
8382
        ini_set('max_execution_time', 3600);
8383
    }
8384
}
8385
8386
/**
8387
 * Tries to set memory limit, if authorized and new limit is higher than current.
8388
 *
8389
 * @param string $mem New memory limit
8390
 *
8391
 * @return bool True on success, false on failure or current is higher than suggested
8392
 * @assert (null) === false
8393
 * @assert (-1) === false
8394
 * @assert (0) === true
8395
 * @assert ('1G') === true
8396
 */
8397
function api_set_memory_limit($mem)
8398
{
8399
    //if ini_set() not available, this function is useless
8400
    if (!function_exists('ini_set') || is_null($mem) || $mem == -1) {
8401
        return false;
8402
    }
8403
8404
    $memory_limit = ini_get('memory_limit');
8405
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
8406
        ini_set('memory_limit', $mem);
8407
8408
        return true;
8409
    }
8410
8411
    return false;
8412
}
8413
8414
/**
8415
 * Gets memory limit in bytes.
8416
 *
8417
 * @param string The memory size (128M, 1G, 1000K, etc)
8418
 *
8419
 * @return int
8420
 * @assert (null) === false
8421
 * @assert ('1t')  === 1099511627776
8422
 * @assert ('1g')  === 1073741824
8423
 * @assert ('1m')  === 1048576
8424
 * @assert ('100k') === 102400
8425
 */
8426
function api_get_bytes_memory_limit($mem)
8427
{
8428
    $size = strtolower(substr($mem, -1));
8429
8430
    switch ($size) {
8431
        case 't':
8432
            $mem = intval(substr($mem, -1)) * 1024 * 1024 * 1024 * 1024;
8433
            break;
8434
        case 'g':
8435
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024 * 1024;
8436
            break;
8437
        case 'm':
8438
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024;
8439
            break;
8440
        case 'k':
8441
            $mem = intval(substr($mem, 0, -1)) * 1024;
8442
            break;
8443
        default:
8444
            // we assume it's integer only
8445
            $mem = intval($mem);
8446
            break;
8447
    }
8448
8449
    return $mem;
8450
}
8451
8452
/**
8453
 * Finds all the information about a user from username instead of user id.
8454
 *
8455
 * @param string $officialCode
8456
 *
8457
 * @return array $user_info user_id, lastname, firstname, username, email, ...
8458
 *
8459
 * @author Yannick Warnier <[email protected]>
8460
 */
8461
function api_get_user_info_from_official_code($officialCode)
8462
{
8463
    if (empty($officialCode)) {
8464
        return false;
8465
    }
8466
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
8467
            WHERE official_code ='".Database::escape_string($officialCode)."'";
8468
    $result = Database::query($sql);
8469
    if (Database::num_rows($result) > 0) {
8470
        $result_array = Database::fetch_array($result);
8471
8472
        return _api_format_user($result_array);
8473
    }
8474
8475
    return false;
8476
}
8477
8478
/**
8479
 * @param string $usernameInputId
8480
 * @param string $passwordInputId
8481
 *
8482
 * @return string|null
8483
 */
8484
function api_get_password_checker_js($usernameInputId, $passwordInputId)
8485
{
8486
    $checkPass = api_get_setting('allow_strength_pass_checker');
8487
    $useStrengthPassChecker = $checkPass === 'true';
8488
8489
    if ($useStrengthPassChecker === false) {
8490
        return null;
8491
    }
8492
8493
    $translations = [
8494
        'wordLength' => get_lang('PasswordIsTooShort'),
8495
        'wordNotEmail' => get_lang('YourPasswordCannotBeTheSameAsYourEmail'),
8496
        'wordSimilarToUsername' => get_lang('YourPasswordCannotContainYourUsername'),
8497
        'wordTwoCharacterClasses' => get_lang('WordTwoCharacterClasses'),
8498
        'wordRepetitions' => get_lang('TooManyRepetitions'),
8499
        'wordSequences' => get_lang('YourPasswordContainsSequences'),
8500
        'errorList' => get_lang('ErrorsFound'),
8501
        'veryWeak' => get_lang('PasswordVeryWeak'),
8502
        'weak' => get_lang('PasswordWeak'),
8503
        'normal' => get_lang('PasswordNormal'),
8504
        'medium' => get_lang('PasswordMedium'),
8505
        'strong' => get_lang('PasswordStrong'),
8506
        'veryStrong' => get_lang('PasswordVeryStrong'),
8507
    ];
8508
8509
    $js = api_get_asset('pwstrength-bootstrap/dist/pwstrength-bootstrap.min.js');
8510
    $js .= "<script>
8511
    var errorMessages = {
8512
        password_to_short : \"".get_lang('PasswordIsTooShort')."\",
8513
        same_as_username : \"".get_lang('YourPasswordCannotBeTheSameAsYourUsername')."\"
8514
    };
8515
8516
    $(function() {
8517
        var lang = ".json_encode($translations).";
8518
        var options = {
8519
            common: {
8520
                onLoad: function () {
8521
                    //$('#messages').text('Start typing password');
8522
8523
                    var inputGroup = $('".$passwordInputId."').parents('.input-group');
8524
8525
                    if (inputGroup.length > 0) {
8526
                        inputGroup.find('.progress').insertAfter(inputGroup);
8527
                    }
8528
                }
8529
            },
8530
            ui: {
8531
                showVerdictsInsideProgressBar: true
8532
            },
8533
            onKeyUp: function (evt) {
8534
                $(evt.target).pwstrength('outputErrorList');
8535
            },
8536
            errorMessages : errorMessages,
8537
            viewports: {
8538
                progress: '#password_progress',
8539
                verdict: '#password-verdict',
8540
                errors: '#password-errors'
8541
            },
8542
            usernameField: '$usernameInputId'
8543
        };
8544
        options.i18n = {
8545
            t: function (key) {
8546
                var result = lang[key];
8547
                return result === key ? '' : result; // This assumes you return the
8548
            }
8549
        };
8550
        $('".$passwordInputId."').pwstrength(options);
8551
    });
8552
    </script>";
8553
8554
    return $js;
8555
}
8556
8557
/**
8558
 * create an user extra field called 'captcha_blocked_until_date'.
8559
 *
8560
 * @param string $username
8561
 *
8562
 * @return bool
8563
 */
8564
function api_block_account_captcha($username)
8565
{
8566
    $userInfo = api_get_user_info_from_username($username);
8567
    if (empty($userInfo)) {
8568
        return false;
8569
    }
8570
    $minutesToBlock = api_get_setting('captcha_time_to_block');
8571
    $time = time() + $minutesToBlock * 60;
8572
    UserManager::update_extra_field_value(
8573
        $userInfo['user_id'],
8574
        'captcha_blocked_until_date',
8575
        api_get_utc_datetime($time)
8576
    );
8577
8578
    return true;
8579
}
8580
8581
/**
8582
 * @param string $username
8583
 *
8584
 * @return bool
8585
 */
8586
function api_clean_account_captcha($username)
8587
{
8588
    $userInfo = api_get_user_info_from_username($username);
8589
    if (empty($userInfo)) {
8590
        return false;
8591
    }
8592
    Session::erase('loginFailedCount');
8593
    UserManager::update_extra_field_value(
8594
        $userInfo['user_id'],
8595
        'captcha_blocked_until_date',
8596
        null
8597
    );
8598
8599
    return true;
8600
}
8601
8602
/**
8603
 * @param string $username
8604
 *
8605
 * @return bool
8606
 */
8607
function api_get_user_blocked_by_captcha($username)
8608
{
8609
    $userInfo = api_get_user_info_from_username($username);
8610
    if (empty($userInfo)) {
8611
        return false;
8612
    }
8613
    $data = UserManager::get_extra_user_data_by_field(
8614
        $userInfo['user_id'],
8615
        'captcha_blocked_until_date'
8616
    );
8617
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
8618
        return $data['captcha_blocked_until_date'];
8619
    }
8620
8621
    return false;
8622
}
8623
8624
/**
8625
 * Remove tags from HTML anf return the $in_number_char first non-HTML char
8626
 * Postfix the text with "..." if it has been truncated.
8627
 *
8628
 * @param string $text
8629
 * @param int    $number
8630
 *
8631
 * @return string
8632
 *
8633
 * @author hubert borderiou
8634
 */
8635
function api_get_short_text_from_html($text, $number)
8636
{
8637
    // Delete script and style tags
8638
    $text = preg_replace('/(<(script|style)\b[^>]*>).*?(<\/\2>)/is', "$1$3", $text);
8639
    $text = api_html_entity_decode($text);
8640
    $out_res = api_remove_tags_with_space($text, false);
8641
    $postfix = "...";
8642
    if (strlen($out_res) > $number) {
8643
        $out_res = substr($out_res, 0, $number).$postfix;
8644
    }
8645
8646
    return $out_res;
8647
}
8648
8649
/**
8650
 * Replace tags with a space in a text.
8651
 * If $in_double_quote_replace, replace " with '' (for HTML attribute purpose, for exemple).
8652
 *
8653
 * @return string
8654
 *
8655
 * @author hubert borderiou
8656
 */
8657
function api_remove_tags_with_space($in_html, $in_double_quote_replace = true)
8658
{
8659
    $out_res = $in_html;
8660
    if ($in_double_quote_replace) {
8661
        $out_res = str_replace('"', "''", $out_res);
8662
    }
8663
    // avoid text stuck together when tags are removed, adding a space after >
8664
    $out_res = str_replace(">", "> ", $out_res);
8665
    $out_res = strip_tags($out_res);
8666
8667
    return $out_res;
8668
}
8669
8670
/**
8671
 * If true, the drh can access all content (courses, users) inside a session.
8672
 *
8673
 * @return bool
8674
 */
8675
function api_drh_can_access_all_session_content()
8676
{
8677
    return api_get_setting('drh_can_access_all_session_content') === 'true';
8678
}
8679
8680
/**
8681
 * @param string $tool
8682
 * @param string $setting
8683
 * @param int    $defaultValue
8684
 *
8685
 * @return string
8686
 */
8687
function api_get_default_tool_setting($tool, $setting, $defaultValue)
8688
{
8689
    global $_configuration;
8690
    if (isset($_configuration[$tool]) &&
8691
        isset($_configuration[$tool]['default_settings']) &&
8692
        isset($_configuration[$tool]['default_settings'][$setting])
8693
    ) {
8694
        return $_configuration[$tool]['default_settings'][$setting];
8695
    }
8696
8697
    return $defaultValue;
8698
}
8699
8700
/**
8701
 * Checks if user can login as another user.
8702
 *
8703
 * @param int $loginAsUserId the user id to log in
8704
 * @param int $userId        my user id
8705
 *
8706
 * @return bool
8707
 */
8708
function api_can_login_as($loginAsUserId, $userId = null)
8709
{
8710
    if (empty($userId)) {
8711
        $userId = api_get_user_id();
8712
    }
8713
    if ($loginAsUserId == $userId) {
8714
        return false;
8715
    }
8716
8717
    if (empty($loginAsUserId)) {
8718
        return false;
8719
    }
8720
8721
    if ($loginAsUserId != strval(intval($loginAsUserId))) {
8722
        return false;
8723
    }
8724
8725
    // Check if the user to login is an admin
8726
    if (api_is_platform_admin_by_id($loginAsUserId)) {
8727
        // Only super admins can login to admin accounts
8728
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
8729
            return false;
8730
        }
8731
    }
8732
8733
    $userInfo = api_get_user_info($loginAsUserId);
8734
    $isDrh = function () use ($loginAsUserId) {
8735
        if (api_is_drh()) {
8736
            if (api_drh_can_access_all_session_content()) {
8737
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
8738
                    'drh_all',
8739
                    api_get_user_id()
8740
                );
8741
                $userList = [];
8742
                if (is_array($users)) {
8743
                    foreach ($users as $user) {
8744
                        $userList[] = $user['user_id'];
8745
                    }
8746
                }
8747
                if (in_array($loginAsUserId, $userList)) {
8748
                    return true;
8749
                }
8750
            } else {
8751
                if (api_is_drh() &&
8752
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
8753
                ) {
8754
                    return true;
8755
                }
8756
            }
8757
        }
8758
8759
        return false;
8760
    };
8761
8762
    $loginAsStatusForSessionAdmins = [STUDENT];
8763
8764
    if (api_get_configuration_value('allow_session_admin_login_as_teacher')) {
8765
        $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
8766
    }
8767
8768
    return api_is_platform_admin() ||
8769
        (api_is_session_admin() && in_array($userInfo['status'], $loginAsStatusForSessionAdmins)) ||
8770
        $isDrh();
8771
}
8772
8773
/**
8774
 * @return bool
8775
 */
8776
function api_is_allowed_in_course()
8777
{
8778
    if (api_is_platform_admin()) {
8779
        return true;
8780
    }
8781
8782
    return Session::read('is_allowed_in_course');
8783
}
8784
8785
function api_set_cookie($name, $value, $expires = 0)
8786
{
8787
    $expires = (int) $expires;
8788
    setcookie($name, $value, $expires, '', '', api_is_https(), true);
8789
}
8790
8791
/**
8792
 * Set the cookie to go directly to the course code $in_firstpage
8793
 * after login.
8794
 *
8795
 * @param string $value is the course code of the course to go
8796
 */
8797
function api_set_firstpage_parameter($value)
8798
{
8799
    api_set_cookie('GotoCourse', $value);
8800
}
8801
8802
/**
8803
 * Delete the cookie to go directly to the course code $in_firstpage
8804
 * after login.
8805
 */
8806
function api_delete_firstpage_parameter()
8807
{
8808
    api_set_cookie('GotoCourse', '', time() - 3600);
8809
}
8810
8811
/**
8812
 * @return bool if course_code for direct course access after login is set
8813
 */
8814
function exist_firstpage_parameter()
8815
{
8816
    return isset($_COOKIE['GotoCourse']) && $_COOKIE['GotoCourse'] != '';
8817
}
8818
8819
/**
8820
 * @return string return the course_code of the course where user login
8821
 */
8822
function api_get_firstpage_parameter()
8823
{
8824
    return $_COOKIE['GotoCourse'];
8825
}
8826
8827
/**
8828
 * Return true on https install.
8829
 *
8830
 * @return bool
8831
 */
8832
function api_is_https()
8833
{
8834
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
8835
        $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto'])
8836
    ) {
8837
        $isSecured = true;
8838
    } else {
8839
        if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
8840
            $isSecured = true;
8841
        } else {
8842
            $isSecured = false;
8843
            // last chance
8844
            if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
8845
                $isSecured = true;
8846
            }
8847
        }
8848
    }
8849
8850
    return $isSecured;
8851
}
8852
8853
/**
8854
 * Return protocol (http or https).
8855
 *
8856
 * @return string
8857
 */
8858
function api_get_protocol()
8859
{
8860
    return api_is_https() ? 'https' : 'http';
8861
}
8862
8863
/**
8864
 * Return a string where " are replaced with 2 '
8865
 * It is useful when you pass a PHP variable in a Javascript browser dialog
8866
 * e.g. : alert("<?php get_lang('Message') ?>");
8867
 * and message contains character ".
8868
 *
8869
 * @param string $in_text
8870
 *
8871
 * @return string
8872
 */
8873
function convert_double_quote_to_single($in_text)
8874
{
8875
    return api_preg_replace('/"/', "''", $in_text);
8876
}
8877
8878
/**
8879
 * Get origin.
8880
 *
8881
 * @param string
8882
 *
8883
 * @return string
8884
 */
8885
function api_get_origin()
8886
{
8887
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
8888
}
8889
8890
/**
8891
 * Warns an user that the portal reach certain limit.
8892
 *
8893
 * @param string $limitName
8894
 */
8895
function api_warn_hosting_contact($limitName)
8896
{
8897
    $hostingParams = api_get_configuration_value(1);
8898
    $email = null;
8899
8900
    if (!empty($hostingParams)) {
8901
        if (isset($hostingParams['hosting_contact_mail'])) {
8902
            $email = $hostingParams['hosting_contact_mail'];
8903
        }
8904
    }
8905
8906
    if (!empty($email)) {
8907
        $subject = get_lang('HostingWarningReached');
8908
        $body = get_lang('PortalName').': '.api_get_path(WEB_PATH)." \n ";
8909
        $body .= get_lang('PortalLimitType').': '.$limitName." \n ";
8910
        if (isset($hostingParams[$limitName])) {
8911
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
8912
        }
8913
        api_mail_html(null, $email, $subject, $body);
8914
    }
8915
}
8916
8917
/**
8918
 * Gets value of a variable from app/config/configuration.php
8919
 * Variables that are not set in the configuration.php file but set elsewhere:
8920
 * - virtual_css_theme_folder (vchamilo plugin)
8921
 * - access_url (global.inc.php)
8922
 * - apc/apc_prefix (global.inc.php).
8923
 *
8924
 * @param string $variable
8925
 *
8926
 * @return bool|mixed
8927
 */
8928
function api_get_configuration_value($variable)
8929
{
8930
    global $_configuration;
8931
    // Check the current url id, id = 1 by default
8932
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
8933
8934
    $variable = trim($variable);
8935
8936
    // Check if variable exists
8937
    if (isset($_configuration[$variable])) {
8938
        if (is_array($_configuration[$variable])) {
8939
            // Check if it exists for the sub portal
8940
            if (array_key_exists($urlId, $_configuration[$variable])) {
8941
                return $_configuration[$variable][$urlId];
8942
            } else {
8943
                // Try to found element with id = 1 (master portal)
8944
                if (array_key_exists(1, $_configuration[$variable])) {
8945
                    return $_configuration[$variable][1];
8946
                }
8947
            }
8948
        }
8949
8950
        return $_configuration[$variable];
8951
    }
8952
8953
    return false;
8954
}
8955
8956
/**
8957
 * Retreives and returns a value in a hierarchical configuration array
8958
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
8959
 *
8960
 * @param string $path      the successive array keys, separated by the separator
8961
 * @param mixed  $default   value to be returned if not found, null by default
8962
 * @param string $separator '/' by default
8963
 * @param array  $array     the active configuration array by default
8964
 *
8965
 * @return mixed the found value or $default
8966
 */
8967
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
8968
{
8969
    $pos = strpos($path, $separator);
8970
    if (false === $pos) {
8971
        if (is_null($array)) {
8972
            return api_get_configuration_value($path);
8973
        }
8974
        if (is_array($array) && array_key_exists($path, $array)) {
8975
            return $array[$path];
8976
        }
8977
8978
        return $default;
8979
    }
8980
    $key = substr($path, 0, $pos);
8981
    if (is_null($array)) {
8982
        $newArray = api_get_configuration_value($key);
8983
    } elseif (is_array($array) && array_key_exists($key, $array)) {
8984
        $newArray = $array[$key];
8985
    } else {
8986
        return $default;
8987
    }
8988
    if (is_array($newArray)) {
8989
        $newPath = substr($path, $pos + 1);
8990
8991
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
8992
    }
8993
8994
    return $default;
8995
}
8996
8997
/**
8998
 * Retrieves and returns a value in a hierarchical configuration array
8999
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
9000
 *
9001
 * @param array  $array     the recursive array that contains the value to be returned (or not)
9002
 * @param string $path      the successive array keys, separated by the separator
9003
 * @param mixed  $default   the value to be returned if not found
9004
 * @param string $separator the separator substring
9005
 *
9006
 * @return mixed the found value or $default
9007
 */
9008
function api_array_sub_value($array, $path, $default = null, $separator = '/')
9009
{
9010
    $pos = strpos($path, $separator);
9011
    if (false === $pos) {
9012
        if (is_array($array) && array_key_exists($path, $array)) {
9013
            return $array[$path];
9014
        }
9015
9016
        return $default;
9017
    }
9018
    $key = substr($path, 0, $pos);
9019
    if (is_array($array) && array_key_exists($key, $array)) {
9020
        $newArray = $array[$key];
9021
    } else {
9022
        return $default;
9023
    }
9024
    if (is_array($newArray)) {
9025
        $newPath = substr($path, $pos + 1);
9026
9027
        return api_array_sub_value($newArray, $newPath, $default);
9028
    }
9029
9030
    return $default;
9031
}
9032
9033
/**
9034
 * Returns supported image extensions in the portal.
9035
 *
9036
 * @param bool $supportVectors Whether vector images should also be accepted or not
9037
 *
9038
 * @return array Supported image extensions in the portal
9039
 */
9040
function api_get_supported_image_extensions($supportVectors = true)
9041
{
9042
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
9043
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
9044
    if ($supportVectors) {
9045
        array_push($supportedImageExtensions, 'svg');
9046
    }
9047
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
9048
        array_push($supportedImageExtensions, 'webp');
9049
    }
9050
9051
    return $supportedImageExtensions;
9052
}
9053
9054
/**
9055
 * This setting changes the registration status for the campus.
9056
 *
9057
 * @author Patrick Cool <[email protected]>, Ghent University
9058
 *
9059
 * @version August 2006
9060
 *
9061
 * @param bool $listCampus Whether we authorize
9062
 *
9063
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
9064
 */
9065
function api_register_campus($listCampus = true)
9066
{
9067
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
9068
9069
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
9070
    Database::query($sql);
9071
9072
    if (!$listCampus) {
9073
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
9074
        Database::query($sql);
9075
    }
9076
}
9077
9078
/**
9079
 * Checks whether current user is a student boss.
9080
 *
9081
 * @global array $_user
9082
 *
9083
 * @return bool
9084
 */
9085
function api_is_student_boss()
9086
{
9087
    $_user = api_get_user_info();
9088
9089
    return isset($_user['status']) && $_user['status'] == STUDENT_BOSS;
9090
}
9091
9092
/**
9093
 * Check whether the user type should be exclude.
9094
 * Such as invited or anonymous users.
9095
 *
9096
 * @param bool $checkDB Optional. Whether check the user status
9097
 * @param int  $userId  Options. The user id
9098
 *
9099
 * @return bool
9100
 */
9101
function api_is_excluded_user_type($checkDB = false, $userId = 0)
9102
{
9103
    if ($checkDB) {
9104
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
9105
9106
        if ($userId == 0) {
9107
            return true;
9108
        }
9109
9110
        $userInfo = api_get_user_info($userId);
9111
9112
        switch ($userInfo['status']) {
9113
            case INVITEE:
9114
            case ANONYMOUS:
9115
                return true;
9116
            default:
9117
                return false;
9118
        }
9119
    }
9120
9121
    $isInvited = api_is_invitee();
9122
    $isAnonymous = api_is_anonymous();
9123
9124
    if ($isInvited || $isAnonymous) {
9125
        return true;
9126
    }
9127
9128
    return false;
9129
}
9130
9131
/**
9132
 * Get the user status to ignore in reports.
9133
 *
9134
 * @param string $format Optional. The result type (array or string)
9135
 *
9136
 * @return array|string
9137
 */
9138
function api_get_users_status_ignored_in_reports($format = 'array')
9139
{
9140
    $excludedTypes = [
9141
        INVITEE,
9142
        ANONYMOUS,
9143
    ];
9144
9145
    if ($format == 'string') {
9146
        return implode(', ', $excludedTypes);
9147
    }
9148
9149
    return $excludedTypes;
9150
}
9151
9152
/**
9153
 * Set the Site Use Cookie Warning for 1 year.
9154
 */
9155
function api_set_site_use_cookie_warning_cookie()
9156
{
9157
    api_set_cookie('ChamiloUsesCookies', 'ok', time() + 31556926);
9158
}
9159
9160
/**
9161
 * Return true if the Site Use Cookie Warning Cookie warning exists.
9162
 *
9163
 * @return bool
9164
 */
9165
function api_site_use_cookie_warning_cookie_exist()
9166
{
9167
    return isset($_COOKIE['ChamiloUsesCookies']);
9168
}
9169
9170
/**
9171
 * Given a number of seconds, format the time to show hours, minutes and seconds.
9172
 *
9173
 * @param int    $time         The time in seconds
9174
 * @param string $originFormat Optional. PHP o JS
9175
 *
9176
 * @return string (00h00'00")
9177
 */
9178
function api_format_time($time, $originFormat = 'php')
9179
{
9180
    $h = get_lang('h');
9181
    $hours = $time / 3600;
9182
    $mins = ($time % 3600) / 60;
9183
    $secs = ($time % 60);
9184
9185
    if ($time < 0) {
9186
        $hours = 0;
9187
        $mins = 0;
9188
        $secs = 0;
9189
    }
9190
9191
    if ($originFormat == 'js') {
9192
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
9193
    } else {
9194
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
9195
    }
9196
9197
    return $formattedTime;
9198
}
9199
9200
/**
9201
 * Create a new empty directory with index.html file.
9202
 *
9203
 * @param string $name            The new directory name
9204
 * @param string $parentDirectory Directory parent directory name
9205
 *
9206
 * @return bool Return true if the directory was create. Otherwise return false
9207
 */
9208
function api_create_protected_dir($name, $parentDirectory)
9209
{
9210
    $isCreated = false;
9211
9212
    if (!is_writable($parentDirectory)) {
9213
        return false;
9214
    }
9215
9216
    $fullPath = $parentDirectory.api_replace_dangerous_char($name);
9217
9218
    if (mkdir($fullPath, api_get_permissions_for_new_directories(), true)) {
9219
        $fp = fopen($fullPath.'/index.html', 'w');
9220
9221
        if ($fp) {
9222
            if (fwrite($fp, '<html><head><title></title></head><body></body></html>')) {
9223
                $isCreated = true;
9224
            }
9225
        }
9226
9227
        fclose($fp);
9228
    }
9229
9230
    return $isCreated;
9231
}
9232
9233
/**
9234
 * Sends an HTML email using the phpmailer class (and multipart/alternative to downgrade gracefully)
9235
 * Sender name and email can be specified, if not specified
9236
 * name and email of the platform admin are used.
9237
 *
9238
 * @author Bert Vanderkimpen ICT&O UGent
9239
 * @author Yannick Warnier <[email protected]>
9240
 *
9241
 * @param string    name of recipient
9242
 * @param string    email of recipient
9243
 * @param string    email subject
9244
 * @param string    email body
9245
 * @param string    sender name
9246
 * @param string    sender e-mail
9247
 * @param array  $extra_headers        in form $headers = array($name => $value) to allow parsing
9248
 * @param array  $data_file            (path and filename)
9249
 * @param bool   $embedded_image       True for attaching a embedded file inside content html (optional)
9250
 * @param array  $additionalParameters
9251
 * @param string $sendErrorTo          If there's an error while sending the email, $sendErrorTo will receive a notification
9252
 *
9253
 * @return int true if mail was sent
9254
 *
9255
 * @see             PHPMailer.php
9256
 */
9257
function api_mail_html(
9258
    $recipient_name,
9259
    $recipient_email,
9260
    $subject,
9261
    $message,
9262
    $senderName = '',
9263
    $senderEmail = '',
9264
    $extra_headers = [],
9265
    $data_file = [],
9266
    $embedded_image = false,
9267
    $additionalParameters = [],
9268
    $sendErrorTo = ''
9269
) {
9270
    global $platform_email;
9271
9272
    if (true === api_get_configuration_value('disable_send_mail')) {
9273
        return true;
9274
    }
9275
9276
    $mail = new PHPMailer();
9277
    $mail->Mailer = $platform_email['SMTP_MAILER'];
9278
    $mail->Host = $platform_email['SMTP_HOST'];
9279
    $mail->Port = $platform_email['SMTP_PORT'];
9280
    $mail->CharSet = isset($platform_email['SMTP_CHARSET']) ? $platform_email['SMTP_CHARSET'] : 'UTF-8';
9281
    // Stay far below SMTP protocol 980 chars limit.
9282
    $mail->WordWrap = 200;
9283
    $mail->SMTPOptions = $platform_email['SMTPOptions'] ?? [];
9284
9285
    if ($platform_email['SMTP_AUTH']) {
9286
        $mail->SMTPAuth = 1;
9287
        $mail->Username = $platform_email['SMTP_USER'];
9288
        $mail->Password = $platform_email['SMTP_PASS'];
9289
        if (isset($platform_email['SMTP_SECURE'])) {
9290
            $mail->SMTPSecure = $platform_email['SMTP_SECURE'];
9291
        }
9292
    }
9293
    $mail->SMTPDebug = isset($platform_email['SMTP_DEBUG']) ? $platform_email['SMTP_DEBUG'] : 0;
9294
9295
    // 5 = low, 1 = high
9296
    $mail->Priority = 3;
9297
    $mail->SMTPKeepAlive = true;
9298
9299
    api_set_noreply_and_from_address_to_mailer(
9300
        $mail,
9301
        ['name' => $senderName, 'email' => $senderEmail],
9302
        !empty($extra_headers['reply_to']) ? $extra_headers['reply_to'] : []
9303
    );
9304
9305
    if (!empty($sendErrorTo) && PHPMailer::ValidateAddress($sendErrorTo)) {
9306
        $mail->AddCustomHeader('Errors-To', $sendErrorTo);
9307
    }
9308
9309
    unset($extra_headers['reply_to']);
9310
9311
    $mail->Subject = $subject;
9312
    $mail->AltBody = strip_tags(
9313
        str_replace('<br />', "\n", api_html_entity_decode($message))
9314
    );
9315
9316
    $list = api_get_configuration_value('send_all_emails_to');
9317
    if (!empty($list) && isset($list['emails'])) {
9318
        foreach ($list['emails'] as $email) {
9319
            $mail->AddAddress($email);
9320
        }
9321
    }
9322
9323
    // Send embedded image.
9324
    if ($embedded_image) {
9325
        // Get all images html inside content.
9326
        preg_match_all("/<img\s+.*?src=[\"\']?([^\"\' >]*)[\"\']?[^>]*>/i", $message, $m);
9327
        // Prepare new tag images.
9328
        $new_images_html = [];
9329
        $i = 1;
9330
        if (!empty($m[1])) {
9331
            foreach ($m[1] as $image_path) {
9332
                $real_path = realpath($image_path);
9333
                $filename = basename($image_path);
9334
                $image_cid = $filename.'_'.$i;
9335
                $encoding = 'base64';
9336
                $image_type = mime_content_type($real_path);
9337
                $mail->AddEmbeddedImage(
9338
                    $real_path,
9339
                    $image_cid,
9340
                    $filename,
9341
                    $encoding,
9342
                    $image_type
9343
                );
9344
                $new_images_html[] = '<img src="cid:'.$image_cid.'" />';
9345
                $i++;
9346
            }
9347
        }
9348
9349
        // Replace origin image for new embedded image html.
9350
        $x = 0;
9351
        if (!empty($m[0])) {
9352
            foreach ($m[0] as $orig_img) {
9353
                $message = str_replace($orig_img, $new_images_html[$x], $message);
9354
                $x++;
9355
            }
9356
        }
9357
    }
9358
9359
    $mailView = new Template(null, false, false, false, false, false, false);
9360
9361
    $noReply = api_get_setting('noreply_email_address');
9362
    if (!empty($noReply)) {
9363
        $message .= '<br />'.get_lang('ThisIsAutomaticEmailNoReply');
9364
    }
9365
    $mailView->assign('content', $message);
9366
9367
    if (isset($additionalParameters['link'])) {
9368
        $mailView->assign('link', $additionalParameters['link']);
9369
    }
9370
    $mailView->assign('mail_header_style', api_get_configuration_value('mail_header_style'));
9371
    $mailView->assign('mail_content_style', api_get_configuration_value('mail_content_style'));
9372
    $layout = $mailView->get_template('mail/mail.tpl');
9373
    $mail->Body = $mailView->fetch($layout);
9374
9375
    // Attachment.
9376
    if (!empty($data_file)) {
9377
        foreach ($data_file as $file_attach) {
9378
            if (!empty($file_attach['path']) && !empty($file_attach['filename'])) {
9379
                $mail->AddAttachment($file_attach['path'], $file_attach['filename']);
9380
            }
9381
        }
9382
    }
9383
9384
    // Only valid addresses are accepted.
9385
    if (is_array($recipient_email)) {
9386
        foreach ($recipient_email as $dest) {
9387
            if (api_valid_email($dest)) {
9388
                $mail->AddAddress($dest, $recipient_name);
9389
            }
9390
        }
9391
    } else {
9392
        if (api_valid_email($recipient_email)) {
9393
            $mail->AddAddress($recipient_email, $recipient_name);
9394
        } else {
9395
            return 0;
9396
        }
9397
    }
9398
9399
    if (is_array($extra_headers) && count($extra_headers) > 0) {
9400
        foreach ($extra_headers as $key => $value) {
9401
            switch (strtolower($key)) {
9402
                case 'encoding':
9403
                case 'content-transfer-encoding':
9404
                    $mail->Encoding = $value;
9405
                    break;
9406
                case 'charset':
9407
                    $mail->CharSet = $value;
9408
                    break;
9409
                case 'contenttype':
9410
                case 'content-type':
9411
                    $mail->ContentType = $value;
9412
                    break;
9413
                default:
9414
                    $mail->AddCustomHeader($key, $value);
9415
                    break;
9416
            }
9417
        }
9418
    } else {
9419
        if (!empty($extra_headers)) {
9420
            $mail->AddCustomHeader($extra_headers);
9421
        }
9422
    }
9423
9424
    // WordWrap the html body (phpMailer only fixes AltBody) FS#2988
9425
    $mail->Body = $mail->WrapText($mail->Body, $mail->WordWrap);
9426
9427
    if (!empty($platform_email['DKIM']) &&
9428
        !empty($platform_email['DKIM_SELECTOR']) &&
9429
        !empty($platform_email['DKIM_DOMAIN']) &&
9430
        (!empty($platform_email['DKIM_PRIVATE_KEY_STRING']) || !empty($platform_email['DKIM_PRIVATE_KEY']))) {
9431
        $mail->DKIM_selector = $platform_email['DKIM_SELECTOR'];
9432
        $mail->DKIM_domain = $platform_email['DKIM_DOMAIN'];
9433
        if (!empty($platform_email['SMTP_UNIQUE_SENDER'])) {
9434
            $mail->DKIM_identity = $platform_email['SMTP_FROM_EMAIL'];
9435
        }
9436
        $mail->DKIM_private_string = $platform_email['DKIM_PRIVATE_KEY_STRING'];
9437
        $mail->DKIM_private = $platform_email['DKIM_PRIVATE_KEY'];
9438
    }
9439
9440
    // Send the mail message.
9441
    $sent = $mail->Send();
9442
    if (!$sent) {
9443
        error_log('ERROR: mail not sent to '.$recipient_name.' ('.$recipient_email.') because of '.$mail->ErrorInfo.'<br />');
9444
    }
9445
9446
    if ($mail->SMTPDebug > 1) {
9447
        error_log(
9448
            "Mail debug:: ".
9449
            "Protocol: ".$mail->Mailer.' :: '.
9450
            "Host/Port: ".$mail->Host.':'.$mail->Port.' :: '.
9451
            "Authent/Open: ".($mail->SMTPAuth ? 'Authent' : 'Open').' :: '.
9452
            ($mail->SMTPAuth ? "  User/Pass: ".$mail->Username.':'.$mail->Password : '').' :: '.
9453
            "Sender: ".$mail->Sender.
9454
            "Recipient email: ".$recipient_email.
9455
            "Subject: ".$subject
9456
        );
9457
    }
9458
9459
    if (!$sent) {
9460
        return 0;
9461
    }
9462
9463
    if (!empty($additionalParameters)) {
9464
        $plugin = new AppPlugin();
9465
        $smsPlugin = $plugin->getSMSPluginLibrary();
9466
        if ($smsPlugin) {
9467
            $smsPlugin->send($additionalParameters);
9468
        }
9469
    }
9470
9471
    // Clear all the addresses.
9472
    $mail->ClearAddresses();
9473
9474
    // Clear all attachments
9475
    $mail->ClearAttachments();
9476
9477
    return 1;
9478
}
9479
9480
/**
9481
 * Checks access to a course group.
9482
 *
9483
 * @param string $tool       Possible values: GroupManager::GROUP_TOOL_*
9484
 * @param bool   $showHeader
9485
 */
9486
function api_protect_course_group($tool, $showHeader = true)
9487
{
9488
    $groupId = api_get_group_id();
9489
    if (!empty($groupId)) {
9490
        if (api_is_platform_admin()) {
9491
            return true;
9492
        }
9493
9494
        if (api_is_allowed_to_edit(false, true, true)) {
9495
            return true;
9496
        }
9497
9498
        $userId = api_get_user_id();
9499
        $sessionId = api_get_session_id();
9500
        if (!empty($sessionId)) {
9501
            if (api_is_coach($sessionId, api_get_course_int_id())) {
9502
                return true;
9503
            }
9504
9505
            if (api_is_drh()) {
9506
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
9507
                    return true;
9508
                }
9509
            }
9510
        }
9511
9512
        $groupInfo = GroupManager::get_group_properties($groupId);
9513
9514
        // Group doesn't exists
9515
        if (empty($groupInfo)) {
9516
            api_not_allowed($showHeader);
9517
        }
9518
9519
        // Check group access
9520
        $allow = GroupManager::user_has_access(
9521
            $userId,
9522
            $groupInfo['iid'],
9523
            $tool
9524
        );
9525
9526
        if (!$allow) {
9527
            api_not_allowed($showHeader);
9528
        }
9529
    }
9530
9531
    return false;
9532
}
9533
9534
/**
9535
 * Check if a date is in a date range.
9536
 *
9537
 * @param datetime $startDate
9538
 * @param datetime $endDate
9539
 * @param datetime $currentDate
9540
 *
9541
 * @return bool true if date is in rage, false otherwise
9542
 */
9543
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
9544
{
9545
    $startDate = strtotime(api_get_local_time($startDate));
9546
    $endDate = strtotime(api_get_local_time($endDate));
9547
    $currentDate = strtotime(api_get_local_time($currentDate));
9548
9549
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
9550
        return true;
9551
    }
9552
9553
    return false;
9554
}
9555
9556
/**
9557
 * Eliminate the duplicates of a multidimensional array by sending the key.
9558
 *
9559
 * @param array $array multidimensional array
9560
 * @param int   $key   key to find to compare
9561
 *
9562
 * @return array
9563
 */
9564
function api_unique_multidim_array($array, $key)
9565
{
9566
    $temp_array = [];
9567
    $i = 0;
9568
    $key_array = [];
9569
9570
    foreach ($array as $val) {
9571
        if (!in_array($val[$key], $key_array)) {
9572
            $key_array[$i] = $val[$key];
9573
            $temp_array[$i] = $val;
9574
        }
9575
        $i++;
9576
    }
9577
9578
    return $temp_array;
9579
}
9580
9581
/**
9582
 * Limit the access to Session Admins when the limit_session_admin_role
9583
 * configuration variable is set to true.
9584
 */
9585
function api_protect_limit_for_session_admin()
9586
{
9587
    $limitAdmin = api_get_setting('limit_session_admin_role');
9588
    if (api_is_session_admin() && $limitAdmin === 'true') {
9589
        api_not_allowed(true);
9590
    }
9591
}
9592
9593
/**
9594
 * Limits that a session admin has access to list users.
9595
 * When limit_session_admin_list_users configuration variable is set to true.
9596
 */
9597
function api_protect_session_admin_list_users()
9598
{
9599
    $limitAdmin = api_get_configuration_value('limit_session_admin_list_users');
9600
9601
    if (api_is_session_admin() && true === $limitAdmin) {
9602
        api_not_allowed(true);
9603
    }
9604
}
9605
9606
/**
9607
 * @return bool
9608
 */
9609
function api_is_student_view_active()
9610
{
9611
    $studentView = Session::read('studentview');
9612
9613
    return $studentView == 'studentview';
9614
}
9615
9616
/**
9617
 * Adds a file inside the upload/$type/id.
9618
 *
9619
 * @param string $type
9620
 * @param array  $file
9621
 * @param int    $itemId
9622
 * @param string $cropParameters
9623
 *
9624
 * @return array|bool
9625
 */
9626
function api_upload_file($type, $file, $itemId, $cropParameters = '')
9627
{
9628
    $upload = process_uploaded_file($file);
9629
    if ($upload) {
9630
        $name = api_replace_dangerous_char($file['name']);
9631
9632
        // No "dangerous" files
9633
        $name = disable_dangerous_file($name);
9634
9635
        $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
9636
        $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
9637
9638
        if (!is_dir($path)) {
9639
            mkdir($path, api_get_permissions_for_new_directories(), true);
9640
        }
9641
9642
        $pathToSave = $path.$name;
9643
        $result = moveUploadedFile($file, $pathToSave);
9644
9645
        if ($result) {
9646
            if (!empty($cropParameters)) {
9647
                $image = new Image($pathToSave);
9648
                $image->crop($cropParameters);
9649
            }
9650
9651
            return ['path_to_save' => $pathId.$name];
9652
        }
9653
    }
9654
9655
    return false;
9656
}
9657
9658
/**
9659
 * @param string $type
9660
 * @param int    $itemId
9661
 * @param string $file
9662
 *
9663
 * @return bool
9664
 */
9665
function api_get_uploaded_web_url($type, $itemId, $file)
9666
{
9667
    return api_get_uploaded_file($type, $itemId, $file, true);
9668
}
9669
9670
/**
9671
 * @param string $type
9672
 * @param int    $itemId
9673
 * @param string $file
9674
 * @param bool   $getUrl
9675
 *
9676
 * @return bool
9677
 */
9678
function api_get_uploaded_file($type, $itemId, $file, $getUrl = false)
9679
{
9680
    $itemId = (int) $itemId;
9681
    $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
9682
    $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
9683
    $file = basename($file);
9684
    $file = $path.'/'.$file;
9685
    if (Security::check_abs_path($file, $path) && is_file($file) && file_exists($file)) {
9686
        if ($getUrl) {
9687
            return str_replace(api_get_path(SYS_UPLOAD_PATH), api_get_path(WEB_UPLOAD_PATH), $file);
9688
        }
9689
9690
        return $file;
9691
    }
9692
9693
    return false;
9694
}
9695
9696
/**
9697
 * @param string $type
9698
 * @param int    $itemId
9699
 * @param string $file
9700
 * @param string $title
9701
 */
9702
function api_download_uploaded_file($type, $itemId, $file, $title = '')
9703
{
9704
    $file = api_get_uploaded_file($type, $itemId, $file);
9705
    if ($file) {
9706
        if (Security::check_abs_path($file, api_get_path(SYS_UPLOAD_PATH).$type)) {
9707
            DocumentManager::file_send_for_download($file, true, $title);
9708
            exit;
9709
        }
9710
    }
9711
    api_not_allowed(true);
9712
}
9713
9714
/**
9715
 * @param string $type
9716
 * @param string $file
9717
 */
9718
function api_remove_uploaded_file($type, $file)
9719
{
9720
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
9721
    $path = $typePath.'/'.$file;
9722
    if (Security::check_abs_path($path, $typePath) && file_exists($path) && is_file($path)) {
9723
        unlink($path);
9724
    }
9725
}
9726
9727
/**
9728
 * @param string $type
9729
 * @param int    $itemId
9730
 * @param string $file
9731
 *
9732
 * @return bool
9733
 */
9734
function api_remove_uploaded_file_by_id($type, $itemId, $file)
9735
{
9736
    $file = api_get_uploaded_file($type, $itemId, $file, false);
9737
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
9738
    if (Security::check_abs_path($file, $typePath) && file_exists($file) && is_file($file)) {
9739
        unlink($file);
9740
9741
        return true;
9742
    }
9743
9744
    return false;
9745
}
9746
9747
/**
9748
 * Converts string value to float value.
9749
 *
9750
 * 3.141516 => 3.141516
9751
 * 3,141516 => 3.141516
9752
 *
9753
 * @todo WIP
9754
 *
9755
 * @param string $number
9756
 *
9757
 * @return float
9758
 */
9759
function api_float_val($number)
9760
{
9761
    $number = (float) str_replace(',', '.', trim($number));
9762
9763
    return $number;
9764
}
9765
9766
/**
9767
 * Converts float values
9768
 * Example if $decimals = 2.
9769
 *
9770
 * 3.141516 => 3.14
9771
 * 3,141516 => 3,14
9772
 *
9773
 * @param string $number            number in iso code
9774
 * @param int    $decimals
9775
 * @param string $decimalSeparator
9776
 * @param string $thousandSeparator
9777
 *
9778
 * @return bool|string
9779
 */
9780
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
9781
{
9782
    $number = api_float_val($number);
9783
9784
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
9785
}
9786
9787
/**
9788
 * Set location url with a exit break by default.
9789
 *
9790
 * @param string $url
9791
 * @param bool   $exit
9792
 */
9793
function api_location($url, $exit = true)
9794
{
9795
    header('Location: '.$url);
9796
9797
    if ($exit) {
9798
        exit;
0 ignored issues
show
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...
9799
    }
9800
}
9801
9802
/**
9803
 * @return string
9804
 */
9805
function api_get_web_url()
9806
{
9807
    if (api_get_setting('server_type') === 'test') {
9808
        return api_get_path(WEB_PATH).'web/app_dev.php/';
9809
    } else {
9810
        return api_get_path(WEB_PATH).'web/';
9811
    }
9812
}
9813
9814
/**
9815
 * @param string $from
9816
 * @param string $to
9817
 *
9818
 * @return string
9819
 */
9820
function api_get_relative_path($from, $to)
9821
{
9822
    // some compatibility fixes for Windows paths
9823
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
9824
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
9825
    $from = str_replace('\\', '/', $from);
9826
    $to = str_replace('\\', '/', $to);
9827
9828
    $from = explode('/', $from);
9829
    $to = explode('/', $to);
9830
    $relPath = $to;
9831
9832
    foreach ($from as $depth => $dir) {
9833
        // find first non-matching dir
9834
        if ($dir === $to[$depth]) {
9835
            // ignore this directory
9836
            array_shift($relPath);
9837
        } else {
9838
            // get number of remaining dirs to $from
9839
            $remaining = count($from) - $depth;
9840
            if ($remaining > 1) {
9841
                // add traversals up to first matching dir
9842
                $padLength = (count($relPath) + $remaining - 1) * -1;
9843
                $relPath = array_pad($relPath, $padLength, '..');
9844
                break;
9845
            } else {
9846
                $relPath[0] = './'.$relPath[0];
9847
            }
9848
        }
9849
    }
9850
9851
    return implode('/', $relPath);
9852
}
9853
9854
/**
9855
 * Unserialize content using Brummann\Polyfill\Unserialize.
9856
 *
9857
 * @param string $type
9858
 * @param string $serialized
9859
 *
9860
 * @return mixed
9861
 */
9862
function api_unserialize_content($type, $serialized, $ignoreErrors = false)
9863
{
9864
    switch ($type) {
9865
        case 'career':
9866
        case 'sequence_graph':
9867
            $allowedClasses = [Graph::class, VerticesMap::class, Vertices::class, Edges::class];
9868
            break;
9869
        case 'lp':
9870
            $allowedClasses = [
9871
                learnpath::class,
9872
                learnpathItem::class,
9873
                aicc::class,
9874
                aiccBlock::class,
9875
                aiccItem::class,
9876
                aiccObjective::class,
9877
                aiccResource::class,
9878
                scorm::class,
9879
                scormItem::class,
9880
                scormMetadata::class,
9881
                scormOrganization::class,
9882
                scormResource::class,
9883
                Link::class,
9884
                LpItem::class,
9885
            ];
9886
            break;
9887
        case 'course':
9888
            $allowedClasses = [
9889
                Course::class,
9890
                Announcement::class,
9891
                Attendance::class,
9892
                CalendarEvent::class,
9893
                CourseCopyLearnpath::class,
9894
                CourseCopyTestCategory::class,
9895
                CourseDescription::class,
9896
                CourseSession::class,
9897
                Document::class,
9898
                Forum::class,
9899
                ForumCategory::class,
9900
                ForumPost::class,
9901
                ForumTopic::class,
9902
                Glossary::class,
9903
                GradeBookBackup::class,
9904
                Link::class,
9905
                LinkCategory::class,
9906
                Quiz::class,
9907
                QuizQuestion::class,
9908
                QuizQuestionOption::class,
9909
                ScormDocument::class,
9910
                Survey::class,
9911
                SurveyInvitation::class,
9912
                SurveyQuestion::class,
9913
                Thematic::class,
9914
                ToolIntro::class,
9915
                Wiki::class,
9916
                Work::class,
9917
                stdClass::class,
9918
            ];
9919
            break;
9920
        case 'not_allowed_classes':
9921
        default:
9922
            $allowedClasses = false;
9923
    }
9924
9925
    if ($ignoreErrors) {
9926
        return @Unserialize::unserialize(
9927
            $serialized,
9928
            ['allowed_classes' => $allowedClasses]
9929
        );
9930
    }
9931
9932
    return Unserialize::unserialize(
9933
        $serialized,
9934
        ['allowed_classes' => $allowedClasses]
9935
    );
9936
}
9937
9938
/**
9939
 * Set the From and ReplyTo properties to PHPMailer instance.
9940
 *
9941
 * @throws \PHPMailer\PHPMailer\Exception
9942
 */
9943
function api_set_noreply_and_from_address_to_mailer(PHPMailer $mailer, array $sender, array $replyToAddress = [])
9944
{
9945
    $platformEmail = $GLOBALS['platform_email'];
9946
9947
    $noReplyAddress = api_get_setting('noreply_email_address');
9948
    $avoidReplyToAddress = false;
9949
9950
    if (!empty($noReplyAddress)) {
9951
        $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
9952
    }
9953
9954
    $notification = new Notification();
9955
    // If the parameter is set don't use the admin.
9956
    $senderName = !empty($sender['name']) ? $sender['name'] : $notification->getDefaultPlatformSenderName();
9957
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $notification->getDefaultPlatformSenderEmail();
9958
9959
    // Send errors to the platform admin
9960
    $adminEmail = api_get_setting('emailAdministrator');
9961
    if (PHPMailer::ValidateAddress($adminEmail)) {
9962
        $mailer->AddCustomHeader('Errors-To: '.$adminEmail);
9963
    }
9964
9965
    // Reply to first
9966
    if (!$avoidReplyToAddress) {
9967
        if (
9968
            !empty($replyToAddress) &&
9969
            PHPMailer::ValidateAddress($replyToAddress['mail'])
9970
        ) {
9971
            $mailer->AddReplyTo($replyToAddress['mail'], $replyToAddress['name']);
9972
            //$mailer->Sender = $replyToAddress['mail'];
9973
        }
9974
    }
9975
9976
    //If the SMTP configuration only accept one sender
9977
    if (
9978
        isset($platformEmail['SMTP_UNIQUE_SENDER']) &&
9979
        $platformEmail['SMTP_UNIQUE_SENDER']
9980
    ) {
9981
        $senderName = $notification->getDefaultPlatformSenderName();
9982
        $senderEmail = $notification->getDefaultPlatformSenderEmail();
9983
9984
        if (PHPMailer::ValidateAddress($senderEmail)) {
9985
            //force-set Sender to $senderEmail, otherwise SetFrom only does it if it is currently empty
9986
            $mailer->Sender = $senderEmail;
9987
        }
9988
    }
9989
9990
    $mailer->SetFrom($senderEmail, $senderName, !$avoidReplyToAddress);
9991
}
9992
9993
/**
9994
 * @param string $template
9995
 *
9996
 * @return string
9997
 */
9998
function api_find_template($template)
9999
{
10000
    return Template::findTemplateFilePath($template);
10001
}
10002
10003
/**
10004
 * Returns an array of languages (English names like "english", "french", etc)
10005
 * to ISO 639-1 codes (fr, es, etc) for use (for example) to show flags
10006
 * Note: 'english' is returned as 'gb'.
10007
 *
10008
 * @return array
10009
 */
10010
function api_get_language_list_for_flag()
10011
{
10012
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
10013
    $sql = "SELECT english_name, isocode FROM $table
10014
            ORDER BY original_name ASC";
10015
    static $languages = [];
10016
    if (empty($languages)) {
10017
        $result = Database::query($sql);
10018
        while ($row = Database::fetch_array($result)) {
10019
            $languages[$row['english_name']] = $row['isocode'];
10020
        }
10021
        $languages['english'] = 'gb';
10022
    }
10023
10024
    return $languages;
10025
}
10026
10027
/**
10028
 * Generate the Javascript required for the on-page translation of
10029
 * multi-language strings.
10030
 *
10031
 * @throws Exception
10032
 *
10033
 * @return string
10034
 */
10035
function api_get_language_translate_html()
10036
{
10037
    $translate = api_get_configuration_value('translate_html');
10038
10039
    if (!$translate) {
10040
        return '';
10041
    }
10042
10043
    $languageList = api_get_languages();
10044
    $hideAll = '';
10045
    foreach ($languageList['all'] as $language) {
10046
        $hideAll .= '
10047
        $("span:lang('.$language['isocode'].')").filter(
10048
            function(e, val) {
10049
                // Only find the spans if they have set the lang
10050
                if ($(this).attr("lang") == null) {
10051
                    return false;
10052
                }
10053
10054
                // Ignore ckeditor classes
10055
                return !this.className.match(/cke(.*)/);
10056
        }).hide();'."\n";
10057
    }
10058
10059
    $userInfo = api_get_user_info();
10060
    $languageId = 0;
10061
    if (!empty($userInfo['language'])) {
10062
        $languageId = api_get_language_id($userInfo['language']);
10063
    } elseif (!empty($_GET['language'])) {
10064
        $languageId = api_get_language_id($_GET['language']);
10065
    }
10066
    $languageInfo = api_get_language_info($languageId);
10067
    $isoCode = 'en';
10068
10069
    if (!empty($languageInfo)) {
10070
        $isoCode = $languageInfo['isocode'];
10071
    }
10072
10073
    return '
10074
            $(function() {
10075
                '.$hideAll.'
10076
                var defaultLanguageFromUser = "'.$isoCode.'";
10077
10078
                $("span:lang('.$isoCode.')").filter(
10079
                    function() {
10080
                        // Ignore ckeditor classes
10081
                        return !this.className.match(/cke(.*)/);
10082
                }).show();
10083
10084
                var defaultLanguage = "";
10085
                var langFromUserFound = false;
10086
10087
                $(this).find("span").filter(
10088
                    function() {
10089
                        // Ignore ckeditor classes
10090
                        return !this.className.match(/cke(.*)/);
10091
                }).each(function() {
10092
                    defaultLanguage = $(this).attr("lang");
10093
                    if (defaultLanguage) {
10094
                        $(this).before().next("br").remove();
10095
                        if (defaultLanguageFromUser == defaultLanguage) {
10096
                            langFromUserFound = true;
10097
                        }
10098
                    }
10099
                });
10100
10101
                // Show default language
10102
                if (langFromUserFound == false && defaultLanguage) {
10103
                    $("span:lang("+defaultLanguage+")").filter(
10104
                    function() {
10105
                            // Ignore ckeditor classes
10106
                            return !this.className.match(/cke(.*)/);
10107
                    }).show();
10108
                }
10109
            });
10110
    ';
10111
}
10112
10113
/**
10114
 * Filter a multi-language HTML string (for the multi-language HTML
10115
 * feature) into the given language (strip the rest).
10116
 *
10117
 * @param string $htmlString The HTML string to "translate". Usually <p><span lang="en">Some string</span></p><p><span lang="fr">Une chaîne</span></p>
10118
 * @param string $language   The language in which we want to get the
10119
 *
10120
 * @throws Exception
10121
 *
10122
 * @return string The filtered string in the given language, or the full string if no translated string was identified
10123
 */
10124
function api_get_filtered_multilingual_HTML_string($htmlString, $language = null)
10125
{
10126
    if (api_get_configuration_value('translate_html') != true) {
10127
        return $htmlString;
10128
    }
10129
    $userInfo = api_get_user_info();
10130
    $languageId = 0;
10131
    if (!empty($language)) {
10132
        $languageId = api_get_language_id($language);
10133
    } elseif (!empty($userInfo['language'])) {
10134
        $languageId = api_get_language_id($userInfo['language']);
10135
    }
10136
    $languageInfo = api_get_language_info($languageId);
10137
    $isoCode = 'en';
10138
10139
    if (!empty($languageInfo)) {
10140
        $isoCode = $languageInfo['isocode'];
10141
    }
10142
10143
    // Split HTML in the separate language strings
10144
    // Note: some strings might look like <p><span ..>...</span></p> but others might be like combine 2 <span> in 1 <p>
10145
    if (!preg_match('/<span.*?lang="(\w\w)">/is', $htmlString)) {
10146
        return $htmlString;
10147
    }
10148
    $matches = [];
10149
    preg_match_all('/<span.*?lang="(\w\w)">(.*?)<\/span>/is', $htmlString, $matches);
10150
    if (!empty($matches)) {
10151
        // matches[0] are the full string
10152
        // matches[1] are the languages
10153
        // matches[2] are the strings
10154
        foreach ($matches[1] as $id => $match) {
10155
            if ($match == $isoCode) {
10156
                return $matches[2][$id];
10157
            }
10158
        }
10159
        // Could find the pattern but could not find our language. Return the first language found.
10160
        return $matches[2][0];
10161
    }
10162
    // Could not find pattern. Just return the whole string. We shouldn't get here.
10163
    return $htmlString;
10164
}
10165
10166
/**
10167
 * Get the print.css file for current theme.
10168
 * Only the file path or the file contents when $getFileContents is true.
10169
 */
10170
function api_get_print_css(bool $getFileContents = true, bool $useWebPath = false): string
10171
{
10172
    $sysCssPath = api_get_path(SYS_CSS_PATH);
10173
    $cssFile = $sysCssPath.'themes/'.api_get_visual_theme().'/print.css';
10174
10175
    if (!file_exists($cssFile)) {
10176
        $cssFile = $sysCssPath.'print.css';
10177
    }
10178
10179
    if ($getFileContents) {
10180
        return file_get_contents($cssFile);
10181
    }
10182
10183
    if ($useWebPath) {
10184
        return str_replace($sysCssPath, api_get_path(WEB_CSS_PATH), $cssFile);
10185
    }
10186
10187
    return $cssFile;
10188
}
10189
10190
function api_protect_webservices()
10191
{
10192
    if (api_get_configuration_value('disable_webservices')) {
10193
        echo "Webservices are disabled. \n";
10194
        echo "To enable, add \$_configuration['disable_webservices'] = true; in configuration.php";
10195
        exit;
10196
    }
10197
}
10198