Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/inc/lib/api.lib.php (3 issues)

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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $GLOB...'allowSelfReg'] : false could also return false which is incompatible with the documented return type true. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
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
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
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;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_courseMember does not seem to be defined for all execution paths leading up to this point.
Loading history...
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;
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