api_set_setting()   F
last analyzed

Complexity

Conditions 23
Paths 2257

Size

Total Lines 87
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 63
nc 2257
nop 5
dl 0
loc 87
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\SettingsCurrent;
6
use Chamilo\CourseBundle\Entity\CItemProperty;
7
use Chamilo\UserBundle\Entity\User;
8
use ChamiloSession as Session;
9
use League\OAuth2\Client\Provider\GenericProvider;
10
use PHPMailer\PHPMailer\OAuth;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, OAuth. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use PHPMailer\PHPMailer\PHPMailer;
12
use Symfony\Component\Finder\Finder;
13
14
/**
15
 * This is a code library for Chamilo.
16
 * It is included by default in every Chamilo file (through including the global.inc.php)
17
 * This library is in process of being transferred to src/Chamilo/CoreBundle/Component/Utils/ChamiloApi.
18
 * Whenever a function is transferred to the ChamiloApi class, the places where it is used should include
19
 * the "use Chamilo\CoreBundle\Component\Utils\ChamiloApi;" statement.
20
 */
21
22
// PHP version requirement.
23
define('REQUIRED_PHP_VERSION', '7.4');
24
define('REQUIRED_MIN_MEMORY_LIMIT', '128');
25
define('REQUIRED_MIN_UPLOAD_MAX_FILESIZE', '10');
26
define('REQUIRED_MIN_POST_MAX_SIZE', '10');
27
28
// USER STATUS CONSTANTS
29
/** global status of a user: student */
30
define('STUDENT', 5);
31
/** global status of a user: course manager */
32
define('COURSEMANAGER', 1);
33
/** global status of a user: session admin */
34
define('SESSIONADMIN', 3);
35
/** global status of a user: human ressource manager */
36
define('DRH', 4);
37
/** global status of a user: human ressource manager */
38
define('ANONYMOUS', 6);
39
/** global status of a user: low security, necessary for inserting data from
40
 * the teacher through HTMLPurifier */
41
define('COURSEMANAGERLOWSECURITY', 10);
42
// Soft user status
43
define('PLATFORM_ADMIN', 11);
44
define('SESSION_COURSE_COACH', 12);
45
define('SESSION_GENERAL_COACH', 13);
46
define('COURSE_STUDENT', 14); //student subscribed in a course
47
define('SESSION_STUDENT', 15); //student subscribed in a session course
48
define('COURSE_TUTOR', 16); // student is tutor of a course (NOT in session)
49
define('STUDENT_BOSS', 17); // student is boss
50
define('INVITEE', 20);
51
define('HRM_REQUEST', 21); //HRM has request for vinculation with user
52
define('COURSE_EXLEARNER', 22);
53
54
// Table of status
55
$_status_list[COURSEMANAGER] = 'teacher'; // 1
56
$_status_list[SESSIONADMIN] = 'session_admin'; // 3
57
$_status_list[DRH] = 'drh'; // 4
58
$_status_list[STUDENT] = 'user'; // 5
59
$_status_list[ANONYMOUS] = 'anonymous'; // 6
60
$_status_list[INVITEE] = 'invited'; // 20
61
62
// COURSE VISIBILITY CONSTANTS
63
/** only visible for course admin */
64
define('COURSE_VISIBILITY_CLOSED', 0);
65
/** only visible for users registered in the course */
66
define('COURSE_VISIBILITY_REGISTERED', 1);
67
/** Open for all registered users on the platform */
68
define('COURSE_VISIBILITY_OPEN_PLATFORM', 2);
69
/** Open for the whole world */
70
define('COURSE_VISIBILITY_OPEN_WORLD', 3);
71
/** Invisible to all except admin */
72
define('COURSE_VISIBILITY_HIDDEN', 4);
73
74
define('COURSE_REQUEST_PENDING', 0);
75
define('COURSE_REQUEST_ACCEPTED', 1);
76
define('COURSE_REQUEST_REJECTED', 2);
77
define('DELETE_ACTION_ENABLED', false);
78
79
// EMAIL SENDING RECIPIENT CONSTANTS
80
define('SEND_EMAIL_EVERYONE', 1);
81
define('SEND_EMAIL_STUDENTS', 2);
82
define('SEND_EMAIL_TEACHERS', 3);
83
84
// SESSION VISIBILITY CONSTANTS
85
define('SESSION_VISIBLE_READ_ONLY', 1);
86
define('SESSION_VISIBLE', 2);
87
define('SESSION_INVISIBLE', 3); // not available
88
define('SESSION_AVAILABLE', 4);
89
90
define('SESSION_LINK_TARGET', '_self');
91
92
define('SUBSCRIBE_ALLOWED', 1);
93
define('SUBSCRIBE_NOT_ALLOWED', 0);
94
define('UNSUBSCRIBE_ALLOWED', 1);
95
define('UNSUBSCRIBE_NOT_ALLOWED', 0);
96
97
// SURVEY VISIBILITY CONSTANTS
98
define('SURVEY_VISIBLE_TUTOR', 0);
99
define('SURVEY_VISIBLE_TUTOR_STUDENT', 1);
100
define('SURVEY_VISIBLE_PUBLIC', 2);
101
102
// CONSTANTS defining all tools, using the english version
103
/* When you add a new tool you must add it into function api_get_tools_lists() too */
104
define('TOOL_DOCUMENT', 'document');
105
define('TOOL_LP_FINAL_ITEM', 'final_item');
106
define('TOOL_READOUT_TEXT', 'readout_text');
107
define('TOOL_THUMBNAIL', 'thumbnail');
108
define('TOOL_HOTPOTATOES', 'hotpotatoes');
109
define('TOOL_CALENDAR_EVENT', 'calendar_event');
110
define('TOOL_LINK', 'link');
111
define('TOOL_LINK_CATEGORY', 'link_category');
112
define('TOOL_COURSE_DESCRIPTION', 'course_description');
113
define('TOOL_SEARCH', 'search');
114
define('TOOL_LEARNPATH', 'learnpath');
115
define('TOOL_LEARNPATH_CATEGORY', 'learnpath_category');
116
define('TOOL_AGENDA', 'agenda');
117
define('TOOL_ANNOUNCEMENT', 'announcement');
118
define('TOOL_FORUM', 'forum');
119
define('TOOL_FORUM_CATEGORY', 'forum_category');
120
define('TOOL_FORUM_THREAD', 'forum_thread');
121
define('TOOL_FORUM_POST', 'forum_post');
122
define('TOOL_FORUM_ATTACH', 'forum_attachment');
123
define('TOOL_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
124
define('TOOL_THREAD', 'thread');
125
define('TOOL_POST', 'post');
126
define('TOOL_DROPBOX', 'dropbox');
127
define('TOOL_QUIZ', 'quiz');
128
define('TOOL_TEST_CATEGORY', 'test_category');
129
define('TOOL_USER', 'user');
130
define('TOOL_GROUP', 'group');
131
define('TOOL_BLOGS', 'blog_management');
132
define('TOOL_CHAT', 'chat');
133
define('TOOL_STUDENTPUBLICATION', 'student_publication');
134
define('TOOL_TRACKING', 'tracking');
135
define('TOOL_HOMEPAGE_LINK', 'homepage_link');
136
define('TOOL_COURSE_SETTING', 'course_setting');
137
define('TOOL_BACKUP', 'backup');
138
define('TOOL_COPY_COURSE_CONTENT', 'copy_course_content');
139
define('TOOL_RECYCLE_COURSE', 'recycle_course');
140
define('TOOL_COURSE_HOMEPAGE', 'course_homepage');
141
define('TOOL_COURSE_RIGHTS_OVERVIEW', 'course_rights');
142
define('TOOL_UPLOAD', 'file_upload');
143
define('TOOL_COURSE_MAINTENANCE', 'course_maintenance');
144
define('TOOL_SURVEY', 'survey');
145
define('TOOL_WIKI', 'wiki');
146
define('TOOL_GLOSSARY', 'glossary');
147
define('TOOL_GRADEBOOK', 'gradebook');
148
define('TOOL_NOTEBOOK', 'notebook');
149
define('TOOL_ATTENDANCE', 'attendance');
150
define('TOOL_COURSE_PROGRESS', 'course_progress');
151
define('TOOL_PORTFOLIO', 'portfolio');
152
define('TOOL_PORTFOLIO_COMMENT', 'portfolio_comment');
153
define('TOOL_PLAGIARISM', 'compilatio');
154
define('TOOL_XAPI', 'xapi');
155
define('TOOL_H5P', 'h5p');
156
157
// CONSTANTS defining Chamilo interface sections
158
define('SECTION_CAMPUS', 'mycampus');
159
define('SECTION_COURSES', 'mycourses');
160
define('SECTION_CATALOG', 'catalog');
161
define('SECTION_MYPROFILE', 'myprofile');
162
define('SECTION_MYAGENDA', 'myagenda');
163
define('SECTION_COURSE_ADMIN', 'course_admin');
164
define('SECTION_PLATFORM_ADMIN', 'platform_admin');
165
define('SECTION_MYGRADEBOOK', 'mygradebook');
166
define('SECTION_TRACKING', 'session_my_space');
167
define('SECTION_SOCIAL', 'social-network');
168
define('SECTION_DASHBOARD', 'dashboard');
169
define('SECTION_REPORTS', 'reports');
170
define('SECTION_GLOBAL', 'global');
171
define('SECTION_INCLUDE', 'include');
172
define('SECTION_CUSTOMPAGE', 'custompage');
173
174
// CONSTANT name for local authentication source
175
define('PLATFORM_AUTH_SOURCE', 'platform');
176
define('CAS_AUTH_SOURCE', 'cas');
177
define('LDAP_AUTH_SOURCE', 'extldap');
178
define('IMS_LTI_SOURCE', 'ims_lti');
179
180
// CONSTANT defining the default HotPotatoes files directory
181
define('DIR_HOTPOTATOES', '/HotPotatoes_files');
182
183
// event logs types
184
define('LOG_COURSE_DELETE', 'course_deleted');
185
define('LOG_COURSE_CREATE', 'course_created');
186
define('LOG_COURSE_SETTINGS_CHANGED', 'course_settings_changed');
187
188
// @todo replace 'soc_gr' with social_group
189
define('LOG_GROUP_PORTAL_CREATED', 'soc_gr_created');
190
define('LOG_GROUP_PORTAL_UPDATED', 'soc_gr_updated');
191
define('LOG_GROUP_PORTAL_DELETED', 'soc_gr_deleted');
192
define('LOG_GROUP_PORTAL_USER_DELETE_ALL', 'soc_gr_delete_users');
193
194
define('LOG_GROUP_PORTAL_ID', 'soc_gr_portal_id');
195
define('LOG_GROUP_PORTAL_REL_USER_ARRAY', 'soc_gr_user_array');
196
197
define('LOG_GROUP_PORTAL_USER_SUBSCRIBED', 'soc_gr_u_subs');
198
define('LOG_GROUP_PORTAL_USER_UNSUBSCRIBED', 'soc_gr_u_unsubs');
199
define('LOG_GROUP_PORTAL_USER_UPDATE_ROLE', 'soc_gr_update_role');
200
define('LOG_GROUP_PORTAL_COURSE_SUBSCRIBED', 'soc_gr_c_subs');
201
define('LOG_GROUP_PORTAL_COURSE_UNSUBSCRIBED', 'soc_gr_c_unsubs');
202
define('LOG_GROUP_PORTAL_SESSION_SUBSCRIBED', 'soc_gr_s_subs');
203
define('LOG_GROUP_PORTAL_SESSION_UNSUBSCRIBED', 'soc_gr_s_unsubs');
204
205
define('LOG_USER_DELETE', 'user_deleted');
206
define('LOG_USER_CREATE', 'user_created');
207
define('LOG_USER_UPDATE', 'user_updated');
208
define('LOG_USER_PASSWORD_UPDATE', 'user_password_updated');
209
define('LOG_USER_ENABLE', 'user_enable');
210
define('LOG_USER_DISABLE', 'user_disable');
211
define('LOG_USER_ANONYMIZE', 'user_anonymized');
212
define('LOG_USER_FIELD_CREATE', 'user_field_created');
213
define('LOG_USER_FIELD_DELETE', 'user_field_deleted');
214
define('LOG_SESSION_CREATE', 'session_created');
215
define('LOG_SESSION_DELETE', 'session_deleted');
216
define('LOG_SESSION_ADD_USER_COURSE', 'session_add_user_course');
217
define('LOG_SESSION_DELETE_USER_COURSE', 'session_delete_user_course');
218
define('LOG_SESSION_ADD_USER', 'session_add_user');
219
define('LOG_SESSION_DELETE_USER', 'session_delete_user');
220
define('LOG_SESSION_ADD_COURSE', 'session_add_course');
221
define('LOG_SESSION_DELETE_COURSE', 'session_delete_course');
222
define('LOG_SESSION_CATEGORY_CREATE', 'session_cat_created'); //changed in 1.9.8
223
define('LOG_SESSION_CATEGORY_DELETE', 'session_cat_deleted'); //changed in 1.9.8
224
define('LOG_CONFIGURATION_SETTINGS_CHANGE', 'settings_changed');
225
define('LOG_PLATFORM_LANGUAGE_CHANGE', 'platform_lng_changed'); //changed in 1.9.8
226
define('LOG_SUBSCRIBE_USER_TO_COURSE', 'user_subscribed');
227
define('LOG_UNSUBSCRIBE_USER_FROM_COURSE', 'user_unsubscribed');
228
define('LOG_ATTEMPTED_FORCED_LOGIN', 'attempted_forced_login');
229
define('LOG_PLUGIN_CHANGE', 'plugin_changed');
230
define('LOG_HOMEPAGE_CHANGED', 'homepage_changed');
231
define('LOG_PROMOTION_CREATE', 'promotion_created');
232
define('LOG_PROMOTION_DELETE', 'promotion_deleted');
233
define('LOG_CAREER_CREATE', 'career_created');
234
define('LOG_CAREER_DELETE', 'career_deleted');
235
define('LOG_USER_PERSONAL_DOC_DELETED', 'user_doc_deleted');
236
define('LOG_WIKI_ACCESS', 'wiki_page_view');
237
define('LOG_EXERCISE_CREATE', 'exe_created');
238
define('LOG_EXERCISE_UPDATE', 'exe_updated');
239
define('LOG_EXERCISE_DELETE', 'exe_deleted');
240
define('LOG_LP_CREATE', 'lp_created');
241
define('LOG_LP_UPDATE', 'lp_updated');
242
define('LOG_LP_DELETE', 'lp_deleted');
243
// All results from an exercise
244
define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
245
define('LOG_EXERCISE_RESULT_DELETE_INCOMPLETE', 'exe_incomplete_results_deleted');
246
// Logs only the one attempt
247
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
248
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
249
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
250
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
251
252
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
253
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
254
define('LOG_MY_FOLDER_DELETE', 'my_folder_deleted');
255
define('LOG_MY_FOLDER_COPY', 'my_folder_copied');
256
define('LOG_MY_FOLDER_CUT', 'my_folder_cut');
257
define('LOG_MY_FOLDER_PASTE', 'my_folder_pasted');
258
define('LOG_MY_FOLDER_UPLOAD', 'my_folder_uploaded');
259
260
// Event logs data types (max 20 chars)
261
define('LOG_COURSE_CODE', 'course_code');
262
define('LOG_COURSE_ID', 'course_id');
263
define('LOG_USER_ID', 'user_id');
264
define('LOG_USER_OBJECT', 'user_object');
265
define('LOG_USER_FIELD_VARIABLE', 'user_field_variable');
266
define('LOG_SESSION_ID', 'session_id');
267
268
define('LOG_QUESTION_ID', 'question_id');
269
define('LOG_SESSION_CATEGORY_ID', 'session_category_id');
270
define('LOG_CONFIGURATION_SETTINGS_CATEGORY', 'settings_category');
271
define('LOG_CONFIGURATION_SETTINGS_VARIABLE', 'settings_variable');
272
define('LOG_PLATFORM_LANGUAGE', 'default_platform_language');
273
define('LOG_PLUGIN_UPLOAD', 'plugin_upload');
274
define('LOG_PLUGIN_ENABLE', 'plugin_enable');
275
define('LOG_PLUGIN_SETTINGS_CHANGE', 'plugin_settings_change');
276
define('LOG_CAREER_ID', 'career_id');
277
define('LOG_PROMOTION_ID', 'promotion_id');
278
define('LOG_GRADEBOOK_LOCKED', 'gradebook_locked');
279
define('LOG_GRADEBOOK_UNLOCKED', 'gradebook_unlocked');
280
define('LOG_GRADEBOOK_ID', 'gradebook_id');
281
define('LOG_WIKI_PAGE_ID', 'wiki_page_id');
282
define('LOG_EXERCISE_ID', 'exercise_id');
283
define('LOG_EXERCISE_AND_USER_ID', 'exercise_and_user_id');
284
define('LOG_LP_ID', 'lp_id');
285
define('LOG_EXERCISE_ATTEMPT_QUESTION_ID', 'exercise_a_q_id');
286
define('LOG_EXERCISE_ATTEMPT', 'exe_id');
287
288
define('LOG_WORK_DIR_DELETE', 'work_dir_delete');
289
define('LOG_WORK_FILE_DELETE', 'work_file_delete');
290
define('LOG_WORK_DATA', 'work_data_array');
291
292
define('LOG_MY_FOLDER_PATH', 'path');
293
define('LOG_MY_FOLDER_NEW_PATH', 'new_path');
294
295
define('LOG_TERM_CONDITION_ACCEPTED', 'term_condition_accepted');
296
define('LOG_USER_CONFIRMED_EMAIL', 'user_confirmed_email');
297
define('LOG_USER_REMOVED_LEGAL_ACCEPT', 'user_removed_legal_accept');
298
299
define('LOG_USER_DELETE_ACCOUNT_REQUEST', 'user_delete_account_request');
300
301
define('LOG_QUESTION_CREATED', 'question_created');
302
define('LOG_QUESTION_UPDATED', 'question_updated');
303
define('LOG_QUESTION_DELETED', 'question_deleted');
304
define('LOG_QUESTION_REMOVED_FROM_QUIZ', 'question_removed_from_quiz');
305
306
define('LOG_SURVEY_ID', 'survey_id');
307
define('LOG_SURVEY_CREATED', 'survey_created');
308
define('LOG_SURVEY_DELETED', 'survey_deleted');
309
define('LOG_SURVEY_CLEAN_RESULTS', 'survey_clean_results');
310
311
define('LOG_WS', 'access_ws_');
312
313
define('USERNAME_PURIFIER', '/[^0-9A-Za-z_\.@\$-]/');
314
315
//used when login_is_email setting is true
316
define('USERNAME_PURIFIER_MAIL', '/[^0-9A-Za-z_\.@]/');
317
define('USERNAME_PURIFIER_SHALLOW', '/\s/');
318
319
// This constant is a result of Windows OS detection, it has a boolean value:
320
// true whether the server runs on Windows OS, false otherwise.
321
define('IS_WINDOWS_OS', api_is_windows_os());
322
323
// Checks for installed optional php-extensions.
324
// intl extension (from PECL), it is installed by default as of PHP 5.3.0.
325
define('INTL_INSTALLED', function_exists('intl_get_error_code'));
326
// iconv extension, for PHP5 on Windows it is installed by default.
327
define('ICONV_INSTALLED', function_exists('iconv'));
328
define('MBSTRING_INSTALLED', function_exists('mb_strlen')); // mbstring extension.
329
330
// Patterns for processing paths. Examples.
331
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
332
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
333
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
334
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
335
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
336
// basic (leaf elements)
337
define('REL_CODE_PATH', 'REL_CODE_PATH');
338
define('REL_COURSE_PATH', 'REL_COURSE_PATH');
339
define('REL_HOME_PATH', 'REL_HOME_PATH');
340
341
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
342
define('WEB_PATH', 'WEB_PATH');
343
define('WEB_APP_PATH', 'WEB_APP_PATH');
344
define('SYS_PATH', 'SYS_PATH');
345
define('SYS_APP_PATH', 'SYS_APP_PATH');
346
define('SYS_UPLOAD_PATH', 'SYS_UPLOAD_PATH');
347
define('WEB_UPLOAD_PATH', 'WEB_UPLOAD_PATH');
348
349
define('REL_PATH', 'REL_PATH');
350
define('WEB_COURSE_PATH', 'WEB_COURSE_PATH');
351
define('SYS_COURSE_PATH', 'SYS_COURSE_PATH');
352
define('WEB_CODE_PATH', 'WEB_CODE_PATH');
353
define('SYS_CODE_PATH', 'SYS_CODE_PATH');
354
define('SYS_LANG_PATH', 'SYS_LANG_PATH');
355
define('WEB_IMG_PATH', 'WEB_IMG_PATH');
356
define('WEB_CSS_PATH', 'WEB_CSS_PATH');
357
define('WEB_PUBLIC_PATH', 'WEB_PUBLIC_PATH');
358
define('SYS_CSS_PATH', 'SYS_CSS_PATH');
359
define('SYS_PLUGIN_PATH', 'SYS_PLUGIN_PATH');
360
define('WEB_PLUGIN_PATH', 'WEB_PLUGIN_PATH');
361
define('WEB_PLUGIN_ASSET_PATH', 'WEB_PLUGIN_ASSET_PATH');
362
define('SYS_ARCHIVE_PATH', 'SYS_ARCHIVE_PATH');
363
define('WEB_ARCHIVE_PATH', 'WEB_ARCHIVE_PATH');
364
define('SYS_INC_PATH', 'SYS_INC_PATH');
365
define('LIBRARY_PATH', 'LIBRARY_PATH');
366
define('CONFIGURATION_PATH', 'CONFIGURATION_PATH');
367
define('WEB_LIBRARY_PATH', 'WEB_LIBRARY_PATH');
368
define('WEB_LIBRARY_JS_PATH', 'WEB_LIBRARY_JS_PATH');
369
define('WEB_AJAX_PATH', 'WEB_AJAX_PATH');
370
define('SYS_TEST_PATH', 'SYS_TEST_PATH');
371
define('WEB_TEMPLATE_PATH', 'WEB_TEMPLATE_PATH');
372
define('SYS_TEMPLATE_PATH', 'SYS_TEMPLATE_PATH');
373
define('SYS_PUBLIC_PATH', 'SYS_PUBLIC_PATH');
374
define('SYS_HOME_PATH', 'SYS_HOME_PATH');
375
define('WEB_HOME_PATH', 'WEB_HOME_PATH');
376
define('WEB_FONTS_PATH', 'WEB_FONTS_PATH');
377
define('SYS_FONTS_PATH', 'SYS_FONTS_PATH');
378
379
define('SYS_DEFAULT_COURSE_DOCUMENT_PATH', 'SYS_DEFAULT_COURSE_DOCUMENT_PATH');
380
define('REL_DEFAULT_COURSE_DOCUMENT_PATH', 'REL_DEFAULT_COURSE_DOCUMENT_PATH');
381
define('WEB_DEFAULT_COURSE_DOCUMENT_PATH', 'WEB_DEFAULT_COURSE_DOCUMENT_PATH');
382
383
// Relations type with Course manager
384
define('COURSE_RELATION_TYPE_COURSE_MANAGER', 1);
385
define('SESSION_RELATION_TYPE_COURSE_MANAGER', 1);
386
387
// Relations type with Human resources manager
388
define('COURSE_RELATION_TYPE_RRHH', 1);
389
define('SESSION_RELATION_TYPE_RRHH', 1);
390
391
//User image sizes
392
define('USER_IMAGE_SIZE_ORIGINAL', 1);
393
define('USER_IMAGE_SIZE_BIG', 2);
394
define('USER_IMAGE_SIZE_MEDIUM', 3);
395
define('USER_IMAGE_SIZE_SMALL', 4);
396
397
// Relation type between users
398
define('USER_UNKNOWN', 0);
399
define('USER_RELATION_TYPE_UNKNOWN', 1);
400
define('USER_RELATION_TYPE_PARENT', 2); // should be deprecated is useless
401
define('USER_RELATION_TYPE_FRIEND', 3);
402
define('USER_RELATION_TYPE_GOODFRIEND', 4); // should be deprecated is useless
403
define('USER_RELATION_TYPE_ENEMY', 5); // should be deprecated is useless
404
define('USER_RELATION_TYPE_DELETED', 6);
405
define('USER_RELATION_TYPE_RRHH', 7);
406
define('USER_RELATION_TYPE_BOSS', 8);
407
define('USER_RELATION_TYPE_HRM_REQUEST', 9);
408
409
// Gradebook link constants
410
// Please do not change existing values, they are used in the database !
411
define('GRADEBOOK_ITEM_LIMIT', 1000);
412
413
define('LINK_EXERCISE', 1);
414
define('LINK_DROPBOX', 2);
415
define('LINK_STUDENTPUBLICATION', 3);
416
define('LINK_LEARNPATH', 4);
417
define('LINK_FORUM_THREAD', 5);
418
//define('LINK_WORK',6);
419
define('LINK_ATTENDANCE', 7);
420
define('LINK_SURVEY', 8);
421
define('LINK_HOTPOTATOES', 9);
422
define('LINK_PORTFOLIO', 10);
423
424
// Score display types constants
425
define('SCORE_DIV', 1); // X / Y
426
define('SCORE_PERCENT', 2); // XX %
427
define('SCORE_DIV_PERCENT', 3); // X / Y (XX %)
428
define('SCORE_AVERAGE', 4); // XX %
429
define('SCORE_DECIMAL', 5); // 0.50  (X/Y)
430
define('SCORE_BAR', 6); // Uses the Display::bar_progress function
431
define('SCORE_SIMPLE', 7); // X
432
define('SCORE_IGNORE_SPLIT', 8); //  ??
433
define('SCORE_DIV_PERCENT_WITH_CUSTOM', 9); // X / Y (XX %) - Good!
434
define('SCORE_CUSTOM', 10); // Good!
435
define('SCORE_DIV_SIMPLE_WITH_CUSTOM', 11); // X - Good!
436
define('SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS', 12); // X - Good!
437
define('SCORE_ONLY_SCORE', 13); // X - Good!
438
define('SCORE_NUMERIC', 14);
439
440
define('SCORE_BOTH', 1);
441
define('SCORE_ONLY_DEFAULT', 2);
442
define('SCORE_ONLY_CUSTOM', 3);
443
444
// From display.lib.php
445
446
define('MAX_LENGTH_BREADCRUMB', 100);
447
define('ICON_SIZE_ATOM', 8);
448
define('ICON_SIZE_TINY', 16);
449
define('ICON_SIZE_SMALL', 22);
450
define('ICON_SIZE_MEDIUM', 32);
451
define('ICON_SIZE_LARGE', 48);
452
define('ICON_SIZE_BIG', 64);
453
define('ICON_SIZE_HUGE', 128);
454
define('SHOW_TEXT_NEAR_ICONS', false);
455
456
// Session catalog
457
define('CATALOG_COURSES', 0);
458
define('CATALOG_SESSIONS', 1);
459
define('CATALOG_COURSES_SESSIONS', 2);
460
461
// Hook type events, pre-process and post-process.
462
// All means to be executed for both hook event types
463
define('HOOK_EVENT_TYPE_PRE', 0);
464
define('HOOK_EVENT_TYPE_POST', 1);
465
define('HOOK_EVENT_TYPE_ALL', 10);
466
467
define('CAREER_STATUS_ACTIVE', 1);
468
define('CAREER_STATUS_INACTIVE', 0);
469
470
define('PROMOTION_STATUS_ACTIVE', 1);
471
define('PROMOTION_STATUS_INACTIVE', 0);
472
473
// Group permissions
474
define('GROUP_PERMISSION_OPEN', '1');
475
define('GROUP_PERMISSION_CLOSED', '2');
476
477
// Group user permissions
478
define('GROUP_USER_PERMISSION_ADMIN', '1'); // the admin of a group
479
define('GROUP_USER_PERMISSION_READER', '2'); // a normal user
480
define('GROUP_USER_PERMISSION_PENDING_INVITATION', '3'); // When an admin/moderator invites a user
481
define('GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER', '4'); // an user joins a group
482
define('GROUP_USER_PERMISSION_MODERATOR', '5'); // a moderator
483
define('GROUP_USER_PERMISSION_ANONYMOUS', '6'); // an anonymous user
484
define('GROUP_USER_PERMISSION_HRM', '7'); // a human resources manager
485
486
define('GROUP_IMAGE_SIZE_ORIGINAL', 1);
487
define('GROUP_IMAGE_SIZE_BIG', 2);
488
define('GROUP_IMAGE_SIZE_MEDIUM', 3);
489
define('GROUP_IMAGE_SIZE_SMALL', 4);
490
define('GROUP_TITLE_LENGTH', 50);
491
492
// Exercise
493
// @todo move into a class
494
define('ALL_ON_ONE_PAGE', 1);
495
define('ONE_PER_PAGE', 2);
496
497
define('EXERCISE_FEEDBACK_TYPE_END', 0); //Feedback 		 - show score and expected answers
498
define('EXERCISE_FEEDBACK_TYPE_DIRECT', 1); //DirectFeedback - Do not show score nor answers
499
define('EXERCISE_FEEDBACK_TYPE_EXAM', 2); // NoFeedback 	 - Show score only
500
define('EXERCISE_FEEDBACK_TYPE_POPUP', 3); // Popup BT#15827
501
502
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS', 0); //show score and expected answers
503
define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS', 1); //Do not show score nor answers
504
define('RESULT_DISABLE_SHOW_SCORE_ONLY', 2); //Show score only
505
define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES', 3); //Show final score only with categories
506
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT', 4);
507
define('RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK', 5);
508
define('RESULT_DISABLE_RANKING', 6);
509
define('RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER', 7);
510
define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING', 8);
511
define('RESULT_DISABLE_RADAR', 9);
512
define('RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK', 10);
513
514
define('EXERCISE_MAX_NAME_SIZE', 80);
515
516
// Question types (edit next array as well when adding values)
517
// @todo move into a class
518
define('UNIQUE_ANSWER', 1);
519
define('MULTIPLE_ANSWER', 2);
520
define('FILL_IN_BLANKS', 3);
521
define('MATCHING', 4);
522
define('FREE_ANSWER', 5);
523
define('HOT_SPOT', 6);
524
define('HOT_SPOT_ORDER', 7);
525
define('HOT_SPOT_DELINEATION', 8);
526
define('MULTIPLE_ANSWER_COMBINATION', 9);
527
define('UNIQUE_ANSWER_NO_OPTION', 10);
528
define('MULTIPLE_ANSWER_TRUE_FALSE', 11);
529
define('MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE', 12);
530
define('ORAL_EXPRESSION', 13);
531
define('GLOBAL_MULTIPLE_ANSWER', 14);
532
define('MEDIA_QUESTION', 15);
533
define('CALCULATED_ANSWER', 16);
534
define('UNIQUE_ANSWER_IMAGE', 17);
535
define('DRAGGABLE', 18);
536
define('MATCHING_DRAGGABLE', 19);
537
define('ANNOTATION', 20);
538
define('READING_COMPREHENSION', 21);
539
define('MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY', 22);
540
define('UPLOAD_ANSWER', 23);
541
define('MATCHING_COMBINATION', 24);
542
define('MATCHING_DRAGGABLE_COMBINATION', 25);
543
define('HOT_SPOT_COMBINATION', 26);
544
define('FILL_IN_BLANKS_COMBINATION', 27);
545
define('MULTIPLE_ANSWER_DROPDOWN_COMBINATION', 28);
546
define('MULTIPLE_ANSWER_DROPDOWN', 29);
547
define('ANSWER_IN_OFFICE_DOC', 30);
548
549
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
550
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);
551
define('EXERCISE_CATEGORY_RANDOM_DISABLED', 0);
552
553
// Question selection type
554
define('EX_Q_SELECTION_ORDERED', 1);
555
define('EX_Q_SELECTION_RANDOM', 2);
556
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_ORDERED', 3);
557
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED', 4);
558
define('EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM', 5);
559
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM', 6);
560
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_ORDERED_NO_GROUPED', 7);
561
define('EX_Q_SELECTION_CATEGORIES_RANDOM_QUESTIONS_RANDOM_NO_GROUPED', 8);
562
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_ORDERED', 9);
563
define('EX_Q_SELECTION_CATEGORIES_ORDERED_BY_PARENT_QUESTIONS_RANDOM', 10);
564
565
// Used to save the skill_rel_item table
566
define('ITEM_TYPE_EXERCISE', 1);
567
define('ITEM_TYPE_HOTPOTATOES', 2);
568
define('ITEM_TYPE_LINK', 3);
569
define('ITEM_TYPE_LEARNPATH', 4);
570
define('ITEM_TYPE_GRADEBOOK', 5);
571
define('ITEM_TYPE_STUDENT_PUBLICATION', 6);
572
//define('ITEM_TYPE_FORUM', 7);
573
define('ITEM_TYPE_ATTENDANCE', 8);
574
define('ITEM_TYPE_SURVEY', 9);
575
define('ITEM_TYPE_FORUM_THREAD', 10);
576
define('ITEM_TYPE_PORTFOLIO', 11);
577
define('ITEM_TYPE_GRADEBOOK_EVALUATION', 12);
578
579
// one big string with all question types, for the validator in pear/HTML/QuickForm/Rule/QuestionType
580
define(
581
    'QUESTION_TYPES',
582
    UNIQUE_ANSWER.':'.
583
    MULTIPLE_ANSWER.':'.
584
    FILL_IN_BLANKS.':'.
585
    MATCHING.':'.
586
    FREE_ANSWER.':'.
587
    HOT_SPOT.':'.
588
    HOT_SPOT_ORDER.':'.
589
    HOT_SPOT_DELINEATION.':'.
590
    MULTIPLE_ANSWER_COMBINATION.':'.
591
    UNIQUE_ANSWER_NO_OPTION.':'.
592
    MULTIPLE_ANSWER_TRUE_FALSE.':'.
593
    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE.':'.
594
    ORAL_EXPRESSION.':'.
595
    ANSWER_IN_OFFICE_DOC.':'.
596
    GLOBAL_MULTIPLE_ANSWER.':'.
597
    MEDIA_QUESTION.':'.
598
    CALCULATED_ANSWER.':'.
599
    UNIQUE_ANSWER_IMAGE.':'.
600
    DRAGGABLE.':'.
601
    MATCHING_DRAGGABLE.':'.
602
    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY.':'.
603
    ANNOTATION
604
);
605
606
//Some alias used in the QTI exports
607
define('MCUA', 1);
608
define('TF', 1);
609
define('MCMA', 2);
610
define('FIB', 3);
611
612
// Skills
613
define('SKILL_TYPE_REQUIREMENT', 'required');
614
define('SKILL_TYPE_ACQUIRED', 'acquired');
615
define('SKILL_TYPE_BOTH', 'both');
616
617
// Message
618
define('MESSAGE_STATUS_NEW', '0');
619
define('MESSAGE_STATUS_UNREAD', '1');
620
//2 ??
621
define('MESSAGE_STATUS_DELETED', '3');
622
define('MESSAGE_STATUS_OUTBOX', '4');
623
define('MESSAGE_STATUS_INVITATION_PENDING', '5');
624
define('MESSAGE_STATUS_INVITATION_ACCEPTED', '6');
625
define('MESSAGE_STATUS_INVITATION_DENIED', '7');
626
define('MESSAGE_STATUS_WALL', '8');
627
define('MESSAGE_STATUS_WALL_DELETE', '9');
628
define('MESSAGE_STATUS_WALL_POST', '10');
629
define('MESSAGE_STATUS_CONVERSATION', '11');
630
define('MESSAGE_STATUS_FORUM', '12');
631
define('MESSAGE_STATUS_PROMOTED', '13');
632
633
// Images
634
define('IMAGE_WALL_SMALL_SIZE', 200);
635
define('IMAGE_WALL_MEDIUM_SIZE', 500);
636
define('IMAGE_WALL_BIG_SIZE', 2000);
637
define('IMAGE_WALL_SMALL', 'small');
638
define('IMAGE_WALL_MEDIUM', 'medium');
639
define('IMAGE_WALL_BIG', 'big');
640
641
// Social PLUGIN PLACES
642
define('SOCIAL_LEFT_PLUGIN', 1);
643
define('SOCIAL_CENTER_PLUGIN', 2);
644
define('SOCIAL_RIGHT_PLUGIN', 3);
645
define('CUT_GROUP_NAME', 50);
646
647
/**
648
 * FormValidator Filter.
649
 */
650
define('NO_HTML', 1);
651
define('STUDENT_HTML', 2);
652
define('TEACHER_HTML', 3);
653
define('STUDENT_HTML_FULLPAGE', 4);
654
define('TEACHER_HTML_FULLPAGE', 5);
655
656
// Timeline
657
define('TIMELINE_STATUS_ACTIVE', '1');
658
define('TIMELINE_STATUS_INACTIVE', '2');
659
660
// Event email template class
661
define('EVENT_EMAIL_TEMPLATE_ACTIVE', 1);
662
define('EVENT_EMAIL_TEMPLATE_INACTIVE', 0);
663
664
// Course home
665
define('SHORTCUTS_HORIZONTAL', 0);
666
define('SHORTCUTS_VERTICAL', 1);
667
668
// Image class
669
define('IMAGE_PROCESSOR', 'gd'); // 'imagick' or 'gd' strings
670
671
// Course copy
672
define('FILE_SKIP', 1);
673
define('FILE_RENAME', 2);
674
define('FILE_OVERWRITE', 3);
675
define('UTF8_CONVERT', false); //false by default
676
677
define('DOCUMENT', 'file');
678
define('FOLDER', 'folder');
679
680
define('RESOURCE_ASSET', 'asset');
681
define('RESOURCE_DOCUMENT', 'document');
682
define('RESOURCE_GLOSSARY', 'glossary');
683
define('RESOURCE_EVENT', 'calendar_event');
684
define('RESOURCE_LINK', 'link');
685
define('RESOURCE_COURSEDESCRIPTION', 'course_description');
686
define('RESOURCE_LEARNPATH', 'learnpath');
687
define('RESOURCE_LEARNPATH_CATEGORY', 'learnpath_category');
688
define('RESOURCE_ANNOUNCEMENT', 'announcement');
689
define('RESOURCE_FORUM', 'forum');
690
define('RESOURCE_FORUMTOPIC', 'thread');
691
define('RESOURCE_FORUMPOST', 'post');
692
define('RESOURCE_QUIZ', 'quiz');
693
define('RESOURCE_TEST_CATEGORY', 'test_category');
694
define('RESOURCE_QUIZQUESTION', 'Exercise_Question');
695
define('RESOURCE_TOOL_INTRO', 'Tool introduction');
696
define('RESOURCE_LINKCATEGORY', 'Link_Category');
697
define('RESOURCE_FORUMCATEGORY', 'Forum_Category');
698
define('RESOURCE_SCORM', 'Scorm');
699
define('RESOURCE_SURVEY', 'survey');
700
define('RESOURCE_SURVEYQUESTION', 'survey_question');
701
define('RESOURCE_SURVEYINVITATION', 'survey_invitation');
702
define('RESOURCE_WIKI', 'wiki');
703
define('RESOURCE_THEMATIC', 'thematic');
704
define('RESOURCE_ATTENDANCE', 'attendance');
705
define('RESOURCE_WORK', 'work');
706
define('RESOURCE_SESSION_COURSE', 'session_course');
707
define('RESOURCE_GRADEBOOK', 'gradebook');
708
define('RESOURCE_XAPI_TOOL', 'xapi_tool');
709
define('RESOURCE_H5P_TOOL', 'h5p_tool');
710
define('ADD_THEMATIC_PLAN', 6);
711
712
// Max online users to show per page (whoisonline)
713
define('MAX_ONLINE_USERS', 12);
714
715
// Number of characters maximum to show in preview of course blog posts
716
define('BLOG_MAX_PREVIEW_CHARS', 800);
717
// HTML string to replace with a 'Read more...' link
718
define('BLOG_PAGE_BREAK', '<div style="page-break-after: always"><span style="display: none;">&nbsp;</span></div>');
719
720
// Make sure the CHAMILO_LOAD_WYSIWYG constant is defined
721
// To remove CKeditor libs from HTML, set this constant to true before loading
722
if (!defined('CHAMILO_LOAD_WYSIWYG')) {
723
    define('CHAMILO_LOAD_WYSIWYG', true);
724
}
725
726
/* Constants for course home */
727
define('TOOL_PUBLIC', 'Public');
728
define('TOOL_PUBLIC_BUT_HIDDEN', 'PublicButHide');
729
define('TOOL_COURSE_ADMIN', 'courseAdmin');
730
define('TOOL_PLATFORM_ADMIN', 'platformAdmin');
731
define('TOOL_AUTHORING', 'toolauthoring');
732
define('TOOL_INTERACTION', 'toolinteraction');
733
define('TOOL_COURSE_PLUGIN', 'toolcourseplugin'); //all plugins that can be enabled in courses
734
define('TOOL_ADMIN', 'tooladmin');
735
define('TOOL_ADMIN_PLATFORM', 'tooladminplatform');
736
define('TOOL_DRH', 'tool_drh');
737
define('TOOL_STUDENT_VIEW', 'toolstudentview');
738
define('TOOL_ADMIN_VISIBLE', 'tooladminvisible');
739
740
/**
741
 * Inclusion of internationalization libraries.
742
 */
743
require_once __DIR__.'/internationalization.lib.php';
744
745
/**
746
 * Returns a path to a certain resource within the Chamilo area, specifyed through a parameter.
747
 * Also, this function provides conversion between path types, in this case the input path points inside the Chamilo area too.
748
 *
749
 * See $_configuration['course_folder'] in the configuration.php to alter the WEB_COURSE_PATH and SYS_COURSE_PATH parameters.
750
751
 *
752
 * @param string $path (optional)   A path which type is to be converted. Also, it may be a defined constant for a path.
753
 *                     This parameter has meaning when $type parameter has one of the following values: TO_WEB, TO_SYS, TO_REL. Otherwise it is ignored.
754
 *
755
 * @return string the requested path or the converted path
756
 *
757
 * Notes about the current behaviour model:
758
 * 1. Windows back-slashes are converted to slashes in the result.
759
 * 2. A semi-absolute web-path is detected by its leading slash. On Linux systems, absolute system paths start with
760
 * a slash too, so an additional check about presence of leading system server base is implemented. For example, the function is
761
 * able to distinguish type difference between /var/www/chamilo/courses/ (SYS) and /chamilo/courses/ (REL).
762
 * 3. The function api_get_path() returns only these three types of paths, which in some sense are absolute. The function has
763
 * no mechanism for processing relative web/system paths, such as: lesson01.html, ./lesson01.html, ../css/my_styles.css.
764
 * It has not been identified as needed yet.
765
 * 4. Also, resolving the meta-symbols "." and ".." within paths has not been implemented, it is to be identified as needed.
766
 *
767
 * For examples go to: *
768
 * See main/admin/system_status.php?section=paths
769
 *
770
 * Vchamilo changes : allow using an alternate configuration
771
 * to get vchamilo  instance paths
772
 */
773
function api_get_path($path = '', $configuration = [])
774
{
775
    global $paths;
776
777
    // get proper configuration data if exists
778
    global $_configuration;
779
780
    $emptyConfigurationParam = false;
781
    if (empty($configuration)) {
782
        $configuration = (array) $_configuration;
783
        $emptyConfigurationParam = true;
784
    }
785
786
    $course_folder = 'courses/';
787
    static $root_web = '';
788
    $root_sys = null;
789
    if ($_configuration) {
790
        $root_sys = $_configuration['root_sys'];
791
    }
792
793
    // If no $root_web has been set so far *and* no custom config has been passed to the function
794
    // then re-use the previously-calculated (run-specific) $root_web and skip this complex calculation
795
    if (empty($root_web) || $emptyConfigurationParam === false || empty($configuration)) {
796
        // Resolve master hostname.
797
        if (!empty($configuration) && array_key_exists('root_web', $configuration)) {
798
            $root_web = $configuration['root_web'];
799
        } else {
800
            $root_web = '';
801
            // Try guess it from server.
802
            if (defined('SYSTEM_INSTALLATION') && SYSTEM_INSTALLATION) {
803
                if (($pos = strpos(($requested_page_rel = api_get_self()), 'main/install')) !== false) {
804
                    $root_rel = substr($requested_page_rel, 0, $pos);
805
                    // See http://www.mediawiki.org/wiki/Manual:$wgServer
806
                    $server_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
807
                    $server_name =
808
                        isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME']
809
                            : (isset($_SERVER['HOSTNAME']) ? $_SERVER['HOSTNAME']
810
                            : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
811
                                : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR']
812
                                    : 'localhost')));
813
                    if (isset($_SERVER['SERVER_PORT']) &&
814
                        !strpos($server_name, ':') &&
815
                        (($server_protocol == 'http' && $_SERVER['SERVER_PORT'] != 80) ||
816
                        ($server_protocol == 'https' && $_SERVER['SERVER_PORT'] != 443))
817
                    ) {
818
                        $server_name .= ":".$_SERVER['SERVER_PORT'];
819
                    }
820
                    $root_web = $server_protocol.'://'.$server_name.$root_rel;
821
                    $root_sys = str_replace('\\', '/', realpath(__DIR__.'/../../../')).'/';
822
                }
823
                // Here we give up, so we don't touch anything.
824
            }
825
        }
826
    }
827
828
    if (isset($configuration['multiple_access_urls']) &&
829
        $configuration['multiple_access_urls']
830
    ) {
831
        // To avoid that the api_get_access_url() function fails since global.inc.php also calls the api.lib.php.
832
        if (isset($configuration['access_url']) && !empty($configuration['access_url'])) {
833
            // We look into the DB the function api_get_access_url
834
            $urlInfo = api_get_access_url($configuration['access_url']);
835
            // Avoid default value
836
            $defaultValues = ['http://localhost/', 'https://localhost/'];
837
            if (!empty($urlInfo['url']) && !in_array($urlInfo['url'], $defaultValues)) {
838
                $root_web = $urlInfo['active'] == 1 ? $urlInfo['url'] : $configuration['root_web'];
839
            }
840
        }
841
    }
842
843
    $paths = [];
844
    // Initialise cache with default values.
845
    if (!array_key_exists($root_web, $paths)) {
846
        $paths[$root_web] = [
847
            WEB_PATH => '',
848
            SYS_PATH => '',
849
            REL_PATH => '',
850
            WEB_COURSE_PATH => '',
851
            SYS_COURSE_PATH => '',
852
            REL_COURSE_PATH => '',
853
            WEB_CODE_PATH => 'main/',
854
            SYS_CODE_PATH => 'main/',
855
            REL_CODE_PATH => '/main/',
856
            SYS_LANG_PATH => 'lang/',
857
            WEB_IMG_PATH => 'img/',
858
            WEB_CSS_PATH => 'web/css/',
859
            SYS_CSS_PATH => 'app/Resources/public/css/',
860
            SYS_PLUGIN_PATH => 'plugin/',
861
            WEB_PLUGIN_PATH => 'plugin/',
862
            WEB_PLUGIN_ASSET_PATH => 'public/plugins/',
863
            SYS_ARCHIVE_PATH => 'app/cache/',
864
            WEB_ARCHIVE_PATH => 'app/cache/',
865
            SYS_HOME_PATH => 'app/home/',
866
            WEB_HOME_PATH => 'app/home/',
867
            REL_HOME_PATH => 'app/home/',
868
            SYS_APP_PATH => 'app/',
869
            WEB_APP_PATH => 'app/',
870
            SYS_UPLOAD_PATH => 'app/upload/',
871
            SYS_INC_PATH => 'inc/',
872
            CONFIGURATION_PATH => 'app/config/',
873
            LIBRARY_PATH => 'inc/lib/',
874
            WEB_LIBRARY_PATH => 'inc/lib/',
875
            WEB_LIBRARY_JS_PATH => 'inc/lib/javascript/',
876
            WEB_AJAX_PATH => 'inc/ajax/',
877
            SYS_TEST_PATH => 'tests/',
878
            WEB_TEMPLATE_PATH => 'template/',
879
            SYS_TEMPLATE_PATH => 'template/',
880
            WEB_UPLOAD_PATH => 'app/upload/',
881
            WEB_PUBLIC_PATH => 'web/',
882
            SYS_PUBLIC_PATH => 'web/',
883
            WEB_FONTS_PATH => 'fonts/',
884
            SYS_FONTS_PATH => 'fonts/',
885
        ];
886
    }
887
888
    $isInitialized = [];
889
    $course_folder = isset($configuration['course_folder']) ? $configuration['course_folder'] : $course_folder;
890
    $root_rel = isset($configuration['url_append']) ? $configuration['url_append'] : '';
891
    if (!empty($root_rel)) {
892
        // Adds "/" to the root_rel
893
        $hasSlash = substr($configuration['url_append'], 0, 1);
894
        if ($hasSlash !== '/') {
895
            $root_rel = '/'.$root_rel;
896
        }
897
    }
898
899
    // Web server base and system server base.
900
    if (!array_key_exists($root_web, $isInitialized)) {
901
        // process absolute global roots
902
        if (!empty($configuration)) {
903
            $code_folder = 'main';
904
        } else {
905
            $code_folder = $paths[$root_web][REL_CODE_PATH];
906
        }
907
908
        // Support for the installation process.
909
        // Developers might use the function api_get_path() directly or indirectly (this is difficult to be traced), at the moment when
910
        // configuration has not been created yet. This is why this function should be upgraded to return correct results in this case.
911
912
        // Dealing with trailing slashes.
913
        $slashed_root_web = api_add_trailing_slash($root_web);
914
        $root_sys = api_add_trailing_slash($root_sys);
915
        $root_rel = api_add_trailing_slash($root_rel);
916
        $code_folder = api_add_trailing_slash($code_folder);
917
        $course_folder = api_add_trailing_slash($course_folder);
918
919
        // Initialization of a table that contains common-purpose paths.
920
        $paths[$root_web][REL_PATH] = $root_rel;
921
        $paths[$root_web][REL_COURSE_PATH] = $root_rel.$course_folder;
922
        $paths[$root_web][REL_CODE_PATH] = $root_rel.preg_replace('#^/#', '', $code_folder);
923
        $paths[$root_web][REL_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][REL_PATH].'main/default_course_document/';
924
925
        $paths[$root_web][WEB_PATH] = $slashed_root_web;
926
        $paths[$root_web][WEB_CODE_PATH] = $paths[$root_web][WEB_PATH].$code_folder;
927
        $paths[$root_web][WEB_COURSE_PATH] = $paths[$root_web][WEB_PATH].$course_folder;
928
        $paths[$root_web][WEB_DEFAULT_COURSE_DOCUMENT_PATH] = $paths[$root_web][WEB_CODE_PATH].'default_course_document/';
929
        $paths[$root_web][WEB_APP_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_APP_PATH];
930
        $paths[$root_web][WEB_PLUGIN_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_PATH];
931
        $paths[$root_web][WEB_PLUGIN_ASSET_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PLUGIN_ASSET_PATH];
932
        $paths[$root_web][WEB_ARCHIVE_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_ARCHIVE_PATH];
933
934
        $paths[$root_web][WEB_CSS_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_CSS_PATH];
935
        $paths[$root_web][WEB_IMG_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_IMG_PATH];
936
        $paths[$root_web][WEB_LIBRARY_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_PATH];
937
        $paths[$root_web][WEB_LIBRARY_JS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_LIBRARY_JS_PATH];
938
        $paths[$root_web][WEB_AJAX_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_AJAX_PATH];
939
        $paths[$root_web][WEB_FONTS_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_FONTS_PATH];
940
        $paths[$root_web][WEB_TEMPLATE_PATH] = $paths[$root_web][WEB_CODE_PATH].$paths[$root_web][WEB_TEMPLATE_PATH];
941
        $paths[$root_web][WEB_UPLOAD_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_UPLOAD_PATH];
942
        $paths[$root_web][WEB_PUBLIC_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][WEB_PUBLIC_PATH];
943
        $paths[$root_web][WEB_HOME_PATH] = $paths[$root_web][WEB_PATH].$paths[$root_web][REL_HOME_PATH];
944
945
        $paths[$root_web][SYS_PATH] = $root_sys;
946
        $paths[$root_web][SYS_CODE_PATH] = $root_sys.preg_replace('#^/#', '', $code_folder);
947
        $paths[$root_web][SYS_TEST_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_TEST_PATH];
948
        $paths[$root_web][SYS_TEMPLATE_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_TEMPLATE_PATH];
949
        $paths[$root_web][SYS_PUBLIC_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PUBLIC_PATH];
950
        $paths[$root_web][SYS_CSS_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_CSS_PATH];
951
        $paths[$root_web][SYS_FONTS_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_FONTS_PATH];
952
        $paths[$root_web][SYS_ARCHIVE_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_ARCHIVE_PATH];
953
        $paths[$root_web][SYS_APP_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_APP_PATH];
954
        $paths[$root_web][SYS_COURSE_PATH] = $paths[$root_web][SYS_APP_PATH].$course_folder;
955
        $paths[$root_web][SYS_UPLOAD_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_UPLOAD_PATH];
956
        $paths[$root_web][SYS_LANG_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_LANG_PATH];
957
        $paths[$root_web][SYS_HOME_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_HOME_PATH];
958
        $paths[$root_web][SYS_PLUGIN_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][SYS_PLUGIN_PATH];
959
        $paths[$root_web][SYS_INC_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][SYS_INC_PATH];
960
961
        $paths[$root_web][LIBRARY_PATH] = $paths[$root_web][SYS_CODE_PATH].$paths[$root_web][LIBRARY_PATH];
962
        $paths[$root_web][CONFIGURATION_PATH] = $paths[$root_web][SYS_PATH].$paths[$root_web][CONFIGURATION_PATH];
963
964
        global $virtualChamilo;
965
        if (!empty($virtualChamilo)) {
966
            $paths[$root_web][SYS_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_ARCHIVE_PATH]);
967
            $paths[$root_web][SYS_HOME_PATH] = api_add_trailing_slash($virtualChamilo[SYS_HOME_PATH]);
968
            $paths[$root_web][SYS_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[SYS_COURSE_PATH]);
969
            $paths[$root_web][SYS_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[SYS_UPLOAD_PATH]);
970
971
            $paths[$root_web][WEB_HOME_PATH] = api_add_trailing_slash($virtualChamilo[WEB_HOME_PATH]);
972
            $paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
973
            $paths[$root_web][WEB_ARCHIVE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_ARCHIVE_PATH]);
974
            //$paths[$root_web][WEB_COURSE_PATH] = api_add_trailing_slash($virtualChamilo[WEB_COURSE_PATH]);
975
976
            // WEB_UPLOAD_PATH should be handle by apache htaccess in the vhost
977
978
            // RewriteEngine On
979
            // RewriteRule /app/upload/(.*)$ http://localhost/other/upload/my-chamilo111-net/$1 [QSA,L]
980
981
            //$paths[$root_web][WEB_UPLOAD_PATH] = api_add_trailing_slash($virtualChamilo[WEB_UPLOAD_PATH]);
982
            //$paths[$root_web][REL_PATH] = $virtualChamilo[REL_PATH];
983
            //$paths[$root_web][REL_COURSE_PATH] = $virtualChamilo[REL_COURSE_PATH];
984
        }
985
986
        $isInitialized[$root_web] = true;
987
    }
988
989
    $path = trim($path);
990
991
    // Retrieving a common-purpose path.
992
    if (isset($paths[$root_web][$path])) {
993
        return $paths[$root_web][$path];
994
    }
995
996
    // Second purification.
997
998
    // Replacing Windows back slashes.
999
    $path = str_replace('\\', '/', $path);
1000
    // Query strings sometimes mighth wrongly appear in non-URLs.
1001
    // Let us check remove them from all types of paths.
1002
    if (($pos = strpos($path, '?')) !== false) {
1003
        $path = substr($path, 0, $pos);
1004
    }
1005
1006
    // Detection of the input path type. Conversion to semi-absolute type ( /chamilo/main/inc/.... ).
1007
1008
    if (preg_match(VALID_WEB_PATH, $path)) {
1009
        // A special case: When a URL points to the document download script directly, without
1010
        // mod-rewrite translation, we have to translate it into an "ordinary" web path.
1011
        // For example:
1012
        // http://localhost/chamilo/main/document/download.php?doc_url=/image.png&cDir=/
1013
        // becomes
1014
        // http://localhost/chamilo/courses/TEST/document/image.png
1015
        // TEST is a course directory name, so called "system course code".
1016
        if (strpos($path, 'download.php') !== false) { // Fast detection first.
1017
            $path = urldecode($path);
1018
            if (preg_match('/(.*)main\/document\/download.php\?doc_url=\/(.*)&cDir=\/(.*)?/', $path, $matches)) {
1019
                $sys_course_code =
1020
                    isset($_SESSION['_course']['sysCode'])  // User is inside a course?
1021
                        ? $_SESSION['_course']['sysCode']   // Yes, then use course's directory name.
1022
                        : '{SYS_COURSE_CODE}'; // No, then use a fake code, it may be processed later.
1023
                $path = $matches[1].'courses/'.$sys_course_code.'/document/'.str_replace('//', '/', $matches[3].'/'.$matches[2]);
1024
            }
1025
        }
1026
        // Replacement of the present web server base with a slash '/'.
1027
        $path = preg_replace(VALID_WEB_SERVER_BASE, '/', $path);
1028
    }
1029
1030
    // Path now is semi-absolute. It is convenient at this moment repeated slashes to be removed.
1031
    $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
1032
1033
    return $path;
1034
}
1035
1036
/**
1037
 * Gets a modified version of the path for the CDN, if defined in
1038
 * configuration.php.
1039
 *
1040
 * @param string $web_path The path of the resource without CDN
1041
 *
1042
 * @return string The path of the resource converted to CDN
1043
 *
1044
 * @author Yannick Warnier <[email protected]>
1045
 */
1046
function api_get_cdn_path($web_path)
1047
{
1048
    global $_configuration;
1049
    if (!empty($_configuration['cdn_enable'])) {
1050
        $web_root = api_get_path(WEB_PATH);
1051
        $ext = substr($web_path, strrpos($web_path, '.'));
1052
        if (isset($ext[2])) { // faster version of strlen to check if len>2
1053
            // Check for CDN definitions
1054
            if (!empty($ext)) {
1055
                foreach ($_configuration['cdn'] as $host => $exts) {
1056
                    if (in_array($ext, $exts)) {
1057
                        //Use host as defined in $_configuration['cdn'], without
1058
                        // trailing slash
1059
                        return str_replace($web_root, $host.'/', $web_path);
1060
                    }
1061
                }
1062
            }
1063
        }
1064
    }
1065
1066
    return $web_path;
1067
}
1068
1069
/**
1070
 * @return bool Return true if CAS authentification is activated
1071
 */
1072
function api_is_cas_activated()
1073
{
1074
    return api_get_setting('cas_activate') == "true";
1075
}
1076
1077
/**
1078
 * @return bool Return true if LDAP authentification is activated
1079
 */
1080
function api_is_ldap_activated()
1081
{
1082
    global $extAuthSource;
1083
1084
    return is_array($extAuthSource[LDAP_AUTH_SOURCE]);
1085
}
1086
1087
/**
1088
 * @return bool Return true if Facebook authentification is activated
1089
 */
1090
function api_is_facebook_auth_activated()
1091
{
1092
    global $_configuration;
1093
1094
    return isset($_configuration['facebook_auth']) && $_configuration['facebook_auth'] == 1;
1095
}
1096
1097
/**
1098
 * Adds to a given path a trailing slash if it is necessary (adds "/" character at the end of the string).
1099
 *
1100
 * @param string $path the input path
1101
 *
1102
 * @return string returns the modified path
1103
 */
1104
function api_add_trailing_slash($path)
1105
{
1106
    return substr($path, -1) == '/' ? $path : $path.'/';
1107
}
1108
1109
/**
1110
 * Removes from a given path the trailing slash if it is necessary (removes "/" character from the end of the string).
1111
 *
1112
 * @param string $path the input path
1113
 *
1114
 * @return string returns the modified path
1115
 */
1116
function api_remove_trailing_slash($path)
1117
{
1118
    return substr($path, -1) == '/' ? substr($path, 0, -1) : $path;
1119
}
1120
1121
/**
1122
 * Checks the RFC 3986 syntax of a given URL.
1123
 *
1124
 * @param string $url      the URL to be checked
1125
 * @param bool   $absolute whether the URL is absolute (beginning with a scheme such as "http:")
1126
 *
1127
 * @return string|false Returns the URL if it is valid, FALSE otherwise.
1128
 *                      This function is an adaptation from the function valid_url(), Drupal CMS.
1129
 *
1130
 * @see http://drupal.org
1131
 * Note: The built-in function filter_var($urs, FILTER_VALIDATE_URL) has a bug for some versions of PHP.
1132
 * @see http://bugs.php.net/51192
1133
 */
1134
function api_valid_url($url, $absolute = false)
1135
{
1136
    if ($absolute) {
1137
        if (preg_match("
1138
            /^                                                      # Start at the beginning of the text
1139
            (?:ftp|https?|feed):\/\/                                # Look for ftp, http, https or feed schemes
1140
            (?:                                                     # Userinfo (optional) which is typically
1141
                (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*    # a username or a username and password
1142
                (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@        # combination
1143
            )?
1144
            (?:
1145
                (?:[a-z0-9\-\.]|%[0-9a-f]{2})+                      # A domain name or a IPv4 address
1146
                |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\])       # or a well formed IPv6 address
1147
            )
1148
            (?::[0-9]+)?                                            # Server port number (optional)
1149
            (?:[\/|\?]
1150
                (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
1151
            *)?
1152
            $/xi", $url)) {
1153
            return $url;
1154
        }
1155
1156
        return false;
1157
    } else {
1158
        return preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url) ? $url : false;
1159
    }
1160
}
1161
1162
/**
1163
 * Checks whether a given string looks roughly like an email address.
1164
 *
1165
 * @param string $address the e-mail address to be checked
1166
 *
1167
 * @return mixed returns the e-mail if it is valid, FALSE otherwise
1168
 */
1169
function api_valid_email($address)
1170
{
1171
    return filter_var($address, FILTER_VALIDATE_EMAIL);
1172
}
1173
1174
/* PROTECTION FUNCTIONS
1175
   Use these functions to protect your scripts. */
1176
1177
/**
1178
 * Function used to protect a course script.
1179
 * The function blocks access when
1180
 * - there is no $_SESSION["_course"] defined; or
1181
 * - $is_allowed_in_course is set to false (this depends on the course
1182
 * visibility and user status).
1183
 *
1184
 * This is only the first proposal, test and improve!
1185
 *
1186
 * @param bool Option to print headers when displaying error message. Default: false
1187
 * @param bool whether session admins should be allowed or not
1188
 * @param bool $checkTool check if tool is available for users (user, group)
1189
 *
1190
 * @return bool True if the user has access to the current course or is out of a course context, false otherwise
1191
 *
1192
 * @todo replace global variable
1193
 *
1194
 * @author Roan Embrechts
1195
 */
1196
function api_protect_course_script($print_headers = false, $allow_session_admins = false, $checkTool = '')
1197
{
1198
    $course_info = api_get_course_info();
1199
    if (empty($course_info)) {
1200
        api_not_allowed($print_headers);
1201
1202
        return false;
1203
    }
1204
1205
    if (api_is_drh()) {
1206
        return true;
1207
    }
1208
1209
    // Session admin has access to course
1210
    $sessionAccess = api_get_configuration_value('session_admins_access_all_content');
1211
    if ($sessionAccess) {
1212
        $allow_session_admins = true;
1213
    }
1214
1215
    if (api_is_platform_admin($allow_session_admins)) {
1216
        return true;
1217
    }
1218
1219
    $isAllowedInCourse = api_is_allowed_in_course();
1220
1221
    $is_visible = false;
1222
    if (isset($course_info) && isset($course_info['visibility'])) {
1223
        switch ($course_info['visibility']) {
1224
            default:
1225
            case COURSE_VISIBILITY_CLOSED:
1226
                // Completely closed: the course is only accessible to the teachers. - 0
1227
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1228
                    $is_visible = true;
1229
                }
1230
                break;
1231
            case COURSE_VISIBILITY_REGISTERED:
1232
                // Private - access authorized to course members only - 1
1233
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1234
                    $is_visible = true;
1235
                }
1236
                break;
1237
            case COURSE_VISIBILITY_OPEN_PLATFORM:
1238
                // Open - access allowed for users registered on the platform - 2
1239
                if ($isAllowedInCourse && api_get_user_id() && !api_is_anonymous()) {
1240
                    $is_visible = true;
1241
                }
1242
                break;
1243
            case COURSE_VISIBILITY_OPEN_WORLD:
1244
                //Open - access allowed for the whole world - 3
1245
                $is_visible = true;
1246
                break;
1247
            case COURSE_VISIBILITY_HIDDEN:
1248
                //Completely closed: the course is only accessible to the teachers. - 0
1249
                if (api_is_platform_admin()) {
1250
                    $is_visible = true;
1251
                }
1252
                break;
1253
        }
1254
1255
        // If password is set and user is not registered to the course then the course is not visible.
1256
        if (false == $isAllowedInCourse &&
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
1257
            isset($course_info['registration_code']) && !empty($course_info['registration_code'])
1258
        ) {
1259
            $is_visible = false;
1260
        }
1261
    }
1262
1263
    if (!empty($checkTool)) {
1264
        if (!api_is_allowed_to_edit(true, true, true)) {
1265
            $toolInfo = api_get_tool_information_by_name($checkTool);
1266
            if (!empty($toolInfo) && isset($toolInfo['visibility']) && $toolInfo['visibility'] == 0) {
1267
                api_not_allowed(true);
1268
1269
                return false;
1270
            }
1271
        }
1272
    }
1273
1274
    // Check session visibility
1275
    $session_id = api_get_session_id();
1276
1277
    if (!empty($session_id)) {
1278
        // $isAllowedInCourse was set in local.inc.php
1279
        if (!$isAllowedInCourse) {
1280
            $is_visible = false;
1281
        }
1282
1283
        // Check if course is inside session.
1284
        if (!SessionManager::relation_session_course_exist($session_id, $course_info['real_id'])) {
1285
            $is_visible = false;
1286
        }
1287
    }
1288
1289
    if (!$is_visible) {
1290
        api_not_allowed($print_headers);
1291
1292
        return false;
1293
    }
1294
1295
    if ($is_visible && 'true' === api_get_plugin_setting('positioning', 'tool_enable')) {
1296
        $plugin = Positioning::create();
1297
        $block = $plugin->get('block_course_if_initial_exercise_not_attempted');
1298
        if ('true' === $block) {
1299
            $currentPath = $_SERVER['PHP_SELF'];
1300
            // Allowed only this course paths.
1301
            $paths = [
1302
                '/plugin/positioning/start.php',
1303
                '/plugin/positioning/start_student.php',
1304
                '/main/course_home/course_home.php',
1305
                '/main/exercise/overview.php',
1306
            ];
1307
1308
            if (!in_array($currentPath, $paths, true)) {
1309
                // Check if entering an exercise.
1310
                global $current_course_tool;
1311
                if ('quiz' !== $current_course_tool) {
1312
                    $initialData = $plugin->getInitialExercise($course_info['real_id'], $session_id);
1313
                    if ($initialData && isset($initialData['exercise_id'])) {
1314
                        $results = Event::getExerciseResultsByUser(
0 ignored issues
show
Bug introduced by
The method getExerciseResultsByUser() does not exist on Event. ( Ignorable by Annotation )

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

1314
                        /** @scrutinizer ignore-call */ 
1315
                        $results = Event::getExerciseResultsByUser(

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

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

Loading history...
1315
                            api_get_user_id(),
1316
                            $initialData['exercise_id'],
1317
                            $course_info['real_id'],
1318
                            $session_id
1319
                        );
1320
                        if (empty($results)) {
1321
                            api_not_allowed($print_headers);
1322
1323
                            return false;
1324
                        }
1325
                    }
1326
                }
1327
            }
1328
        }
1329
    }
1330
1331
    api_block_inactive_user();
1332
1333
    return true;
1334
}
1335
1336
/**
1337
 * Function used to protect an admin script.
1338
 *
1339
 * The function blocks access when the user has no platform admin rights
1340
 * with an error message printed on default output
1341
 *
1342
 * @param bool Whether to allow session admins as well
1343
 * @param bool Whether to allow HR directors as well
1344
 * @param string An optional message (already passed through get_lang)
1345
 * @param bool Whether to allow session coach as well
1346
 *
1347
 * @return bool True if user is allowed, false otherwise.
1348
 *              The function also outputs an error message in case not allowed
1349
 *
1350
 * @author Roan Embrechts (original author)
1351
 */
1352
function api_protect_admin_script($allow_sessions_admins = false, $allow_drh = false, $message = null, $allow_session_coach = false)
1353
{
1354
    if (!api_is_platform_admin($allow_sessions_admins, $allow_drh)) {
1355
        if (!($allow_session_coach && api_is_coach())) {
1356
            api_not_allowed(true, $message);
1357
1358
            return false;
1359
        }
1360
    }
1361
    api_block_inactive_user();
1362
1363
    return true;
1364
}
1365
1366
/**
1367
 * Blocks inactive users with a currently active session from accessing more pages "live".
1368
 *
1369
 * @return bool Returns true if the feature is disabled or the user account is still enabled.
1370
 *              Returns false (and shows a message) if the feature is enabled *and* the user is disabled.
1371
 */
1372
function api_block_inactive_user()
1373
{
1374
    $data = true;
1375
    if (api_get_configuration_value('security_block_inactive_users_immediately') != 1) {
1376
        return $data;
1377
    }
1378
1379
    $userId = api_get_user_id();
1380
    $homeUrl = api_get_path(WEB_PATH);
1381
    if ($userId == 0) {
1382
        return $data;
1383
    }
1384
1385
    $sql = "SELECT active FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1386
            WHERE id = $userId";
1387
1388
    $result = Database::query($sql);
1389
    if (Database::num_rows($result) > 0) {
1390
        $result_array = Database::fetch_array($result);
1391
        $data = (bool) $result_array['active'];
1392
    }
1393
    if ($data == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
1394
        $tpl = new Template(null, true, true, false, true, false, true, 0);
1395
        $tpl->assign('hide_login_link', 1);
1396
1397
        //api_not_allowed(true, get_lang('AccountInactive'));
1398
        // we were not in a course, return to home page
1399
        $msg = Display::return_message(
1400
            get_lang('AccountInactive'),
1401
            'error',
1402
            false
1403
        );
1404
1405
        $msg .= '<p class="text-center">
1406
                 <a class="btn btn-default" href="'.$homeUrl.'">'.get_lang('BackHome').'</a></p>';
1407
1408
        if (api_is_anonymous()) {
1409
            $form = api_get_not_allowed_login_form();
1410
            $msg .= '<div class="well">';
1411
            $msg .= $form->returnForm();
1412
            $msg .= '</div>';
1413
        }
1414
1415
        $tpl->assign('content', $msg);
1416
        $tpl->display_one_col_template();
1417
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1418
    }
1419
1420
    return $data;
1421
}
1422
1423
/**
1424
 * Function used to protect a teacher script.
1425
 * The function blocks access when the user has no teacher rights.
1426
 *
1427
 * @return bool True if the current user can access the script, false otherwise
1428
 *
1429
 * @author Yoselyn Castillo
1430
 */
1431
function api_protect_teacher_script()
1432
{
1433
    if (!api_is_allowed_to_edit()) {
1434
        api_not_allowed(true);
1435
1436
        return false;
1437
    }
1438
1439
    return true;
1440
}
1441
1442
/**
1443
 * Function used to prevent anonymous users from accessing a script.
1444
 *
1445
 * @param bool|true $printHeaders
1446
 *
1447
 * @author Roan Embrechts
1448
 *
1449
 * @return bool
1450
 */
1451
function api_block_anonymous_users($printHeaders = true)
1452
{
1453
    $user = api_get_user_info();
1454
    if (empty($user['user_id']) || api_is_anonymous($user['user_id'], true)) {
1455
        api_not_allowed($printHeaders);
1456
1457
        return false;
1458
    }
1459
    api_block_inactive_user();
1460
1461
    return true;
1462
}
1463
1464
/**
1465
 * Returns a rough evaluation of the browser's name and version based on very
1466
 * simple regexp.
1467
 *
1468
 * @return array with the navigator name and version ['name' => '...', 'version' => '...']
1469
 */
1470
function api_get_navigator()
1471
{
1472
    $navigator = 'Unknown';
1473
    $version = 0;
1474
1475
    if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1476
        return ['name' => 'Unknown', 'version' => '0.0.0'];
1477
    }
1478
1479
    if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false) {
1480
        $navigator = 'Opera';
1481
        list(, $version) = explode('Opera', $_SERVER['HTTP_USER_AGENT']);
1482
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== false) {
1483
        $navigator = 'Edge';
1484
        list(, $version) = explode('Edge', $_SERVER['HTTP_USER_AGENT']);
1485
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
1486
        $navigator = 'Internet Explorer';
1487
        list(, $version) = explode('MSIE ', $_SERVER['HTTP_USER_AGENT']);
1488
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false) {
1489
        $navigator = 'Chrome';
1490
        list(, $version) = explode('Chrome', $_SERVER['HTTP_USER_AGENT']);
1491
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false) {
1492
        $navigator = 'Safari';
1493
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Version/') !== false) {
1494
            // If this Safari does have the "Version/" string in its user agent
1495
            // then use that as a version indicator rather than what's after
1496
            // "Safari/" which is rather a "build number" or something
1497
            list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1498
        } else {
1499
            list(, $version) = explode('Safari/', $_SERVER['HTTP_USER_AGENT']);
1500
        }
1501
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== false) {
1502
        $navigator = 'Firefox';
1503
        list(, $version) = explode('Firefox', $_SERVER['HTTP_USER_AGENT']);
1504
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape') !== false) {
1505
        $navigator = 'Netscape';
1506
        if (stripos($_SERVER['HTTP_USER_AGENT'], 'Netscape/') !== false) {
1507
            list(, $version) = explode('Netscape', $_SERVER['HTTP_USER_AGENT']);
1508
        } else {
1509
            list(, $version) = explode('Navigator', $_SERVER['HTTP_USER_AGENT']);
1510
        }
1511
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false) {
1512
        $navigator = 'Konqueror';
1513
        list(, $version) = explode('Konqueror', $_SERVER['HTTP_USER_AGENT']);
1514
    } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'applewebkit') !== false) {
1515
        $navigator = 'AppleWebKit';
1516
        list(, $version) = explode('Version/', $_SERVER['HTTP_USER_AGENT']);
1517
    } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false) {
1518
        $navigator = 'Mozilla';
1519
        list(, $version) = explode('; rv:', $_SERVER['HTTP_USER_AGENT']);
1520
    }
1521
1522
    // Now cut extra stuff around (mostly *after*) the version number
1523
    $version = preg_replace('/^([\/\s])?([\d\.]+)?.*/', '\2', $version);
1524
1525
    if (strpos($version, '.') === false) {
1526
        $version = number_format(doubleval($version), 1);
1527
    }
1528
1529
    return ['name' => $navigator, 'version' => $version];
1530
}
1531
/**
1532
 * Check if it is a desktop or mobile browser.
1533
 */
1534
function api_is_browser_mobile(): bool
1535
{
1536
    if (empty($_SERVER['HTTP_USER_AGENT'])) {
1537
        static $isMobile = false;
1538
    } elseif (
1539
        strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false
1540
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false
1541
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Silk/') !== false
1542
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Kindle') !== false
1543
        || strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false
1544
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false
1545
        || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mobi') !== false
1546
    ) {
1547
        $isMobile = true;
1548
    } else {
1549
        $isMobile = false;
1550
    }
1551
1552
    return $isMobile;
1553
}
1554
1555
/**
1556
 * @return true if user self registration is allowed, false otherwise
1557
 */
1558
function api_is_self_registration_allowed()
1559
{
1560
    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...
1561
}
1562
1563
/**
1564
 * This function returns the id of the user which is stored in the $_user array.
1565
 *
1566
 * example: The function can be used to check if a user is logged in
1567
 *          if (api_get_user_id())
1568
 *
1569
 * @return int the id of the current user, 0 if is empty
1570
 */
1571
function api_get_user_id()
1572
{
1573
    $userInfo = Session::read('_user');
1574
    if ($userInfo && isset($userInfo['user_id'])) {
1575
        return (int) $userInfo['user_id'];
1576
    }
1577
1578
    return 0;
1579
}
1580
1581
/**
1582
 * Gets the list of courses a specific user is subscribed to.
1583
 *
1584
 * @param int       User ID
1585
 * @param bool $fetch_session Whether to get session courses or not - NOT YET IMPLEMENTED
1586
 *
1587
 * @return array Array of courses in the form [0]=>('code'=>xxx,'db'=>xxx,'dir'=>xxx,'status'=>d)
1588
 *
1589
 * @deprecated use CourseManager::get_courses_list_by_user_id()
1590
 */
1591
function api_get_user_courses($userId, $fetch_session = true)
1592
{
1593
    // Get out if not integer
1594
    if ($userId != strval(intval($userId))) {
1595
        return [];
1596
    }
1597
1598
    $t_course = Database::get_main_table(TABLE_MAIN_COURSE);
1599
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1600
1601
    $sql = "SELECT cc.id as real_id, cc.code code, cc.directory dir, cu.status status
1602
            FROM $t_course cc, $t_course_user cu
1603
            WHERE
1604
                cc.id = cu.c_id AND
1605
                cu.user_id = $userId AND
1606
                cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH;
1607
    $result = Database::query($sql);
1608
    if ($result === false) {
1609
        return [];
1610
    }
1611
1612
    $courses = [];
1613
    while ($row = Database::fetch_array($result)) {
1614
        // we only need the database name of the course
1615
        $courses[] = $row;
1616
    }
1617
1618
    return $courses;
1619
}
1620
1621
/**
1622
 * Formats user information into a standard array
1623
 * This function should be only used inside api_get_user_info().
1624
 *
1625
 * @param array Non-standard user array
0 ignored issues
show
Documentation Bug introduced by
The doc comment Non-standard at position 0 could not be parsed: Unknown type name 'Non-standard' at position 0 in Non-standard.
Loading history...
1626
 * @param bool $add_password
1627
 * @param bool $loadAvatars  turn off to improve performance
1628
 *
1629
 * @return array Standard user array
1630
 */
1631
function _api_format_user($user, $add_password = false, $loadAvatars = true)
1632
{
1633
    $result = [];
1634
1635
    $result['firstname'] = null;
1636
    $result['lastname'] = null;
1637
1638
    if (isset($user['firstname']) && isset($user['lastname'])) { // with only lowercase
1639
        $result['firstname'] = $user['firstname'];
1640
        $result['lastname'] = $user['lastname'];
1641
    } elseif (isset($user['firstName']) && isset($user['lastName'])) { // with uppercase letters
1642
        $result['firstname'] = isset($user['firstName']) ? $user['firstName'] : null;
1643
        $result['lastname'] = isset($user['lastName']) ? $user['lastName'] : null;
1644
    }
1645
1646
    if (isset($user['email'])) {
1647
        $result['mail'] = isset($user['email']) ? $user['email'] : null;
1648
        $result['email'] = isset($user['email']) ? $user['email'] : null;
1649
    } else {
1650
        $result['mail'] = isset($user['mail']) ? $user['mail'] : null;
1651
        $result['email'] = isset($user['mail']) ? $user['mail'] : null;
1652
    }
1653
1654
    $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1655
    $result['complete_name_with_username'] = $result['complete_name'];
1656
1657
    if (!empty($user['username']) && !api_get_configuration_value('hide_username_with_complete_name')) {
1658
        $result['complete_name_with_username'] = $result['complete_name'].' ('.$user['username'].')';
1659
    }
1660
1661
    $showEmail = api_get_setting('show_email_addresses') === 'true';
1662
    if (!empty($user['email'])) {
1663
        $result['complete_name_with_email_forced'] = $result['complete_name'].' ('.$user['email'].')';
1664
        if ($showEmail) {
1665
            $result['complete_name_with_email'] = $result['complete_name'].' ('.$user['email'].')';
1666
        }
1667
    } else {
1668
        $result['complete_name_with_email'] = $result['complete_name'];
1669
        $result['complete_name_with_email_forced'] = $result['complete_name'];
1670
    }
1671
1672
    // Kept for historical reasons
1673
    $result['firstName'] = $result['firstname'];
1674
    $result['lastName'] = $result['lastname'];
1675
1676
    $attributes = [
1677
        'phone',
1678
        'address',
1679
        'picture_uri',
1680
        'official_code',
1681
        'status',
1682
        'active',
1683
        'auth_source',
1684
        'username',
1685
        'theme',
1686
        'language',
1687
        'creator_id',
1688
        'registration_date',
1689
        'hr_dept_id',
1690
        'expiration_date',
1691
        'last_login',
1692
        'user_is_online',
1693
    ];
1694
1695
    if (api_get_setting('extended_profile') === 'true') {
1696
        $attributes[] = 'competences';
1697
        $attributes[] = 'diplomas';
1698
        $attributes[] = 'teach';
1699
        $attributes[] = 'openarea';
1700
    }
1701
1702
    foreach ($attributes as $attribute) {
1703
        $result[$attribute] = isset($user[$attribute]) ? $user[$attribute] : null;
1704
    }
1705
1706
    $user_id = (int) $user['user_id'];
1707
    // Maintain the user_id index for backwards compatibility
1708
    $result['user_id'] = $result['id'] = $user_id;
1709
1710
    $hasCertificates = Certificate::getCertificateByUser($user_id);
1711
    $result['has_certificates'] = 0;
1712
    if (!empty($hasCertificates)) {
1713
        $result['has_certificates'] = 1;
1714
    }
1715
1716
    $result['icon_status'] = '';
1717
    $result['icon_status_medium'] = '';
1718
1719
    $result['is_admin'] = UserManager::is_admin($user_id);
1720
1721
    // Getting user avatar.
1722
    if ($loadAvatars) {
1723
        $result['avatar'] = '';
1724
        $result['avatar_no_query'] = '';
1725
        $result['avatar_small'] = '';
1726
        $result['avatar_medium'] = '';
1727
1728
        if (!isset($user['avatar'])) {
1729
            $originalFile = UserManager::getUserPicture(
1730
                $user_id,
1731
                USER_IMAGE_SIZE_ORIGINAL,
1732
                null,
1733
                $result
1734
            );
1735
            $result['avatar'] = $originalFile;
1736
            $avatarString = explode('?', $result['avatar']);
1737
            $result['avatar_no_query'] = reset($avatarString);
1738
        } else {
1739
            $result['avatar'] = $user['avatar'];
1740
            $avatarString = explode('?', $user['avatar']);
1741
            $result['avatar_no_query'] = reset($avatarString);
1742
        }
1743
1744
        if (!isset($user['avatar_small'])) {
1745
            $smallFile = UserManager::getUserPicture(
1746
                $user_id,
1747
                USER_IMAGE_SIZE_SMALL,
1748
                null,
1749
                $result
1750
            );
1751
            $result['avatar_small'] = $smallFile;
1752
        } else {
1753
            $result['avatar_small'] = $user['avatar_small'];
1754
        }
1755
1756
        if (!isset($user['avatar_medium'])) {
1757
            $mediumFile = UserManager::getUserPicture(
1758
                $user_id,
1759
                USER_IMAGE_SIZE_MEDIUM,
1760
                null,
1761
                $result
1762
            );
1763
            $result['avatar_medium'] = $mediumFile;
1764
        } else {
1765
            $result['avatar_medium'] = $user['avatar_medium'];
1766
        }
1767
1768
        $urlImg = api_get_path(WEB_IMG_PATH);
1769
        $iconStatus = '';
1770
        $iconStatusMedium = '';
1771
        $label = '';
1772
        switch ($result['status']) {
1773
            case STUDENT:
1774
                if ($result['has_certificates']) {
1775
                    $iconStatus = $urlImg.'icons/svg/identifier_graduated.svg';
1776
                    $label = get_lang('Graduated');
1777
                } else {
1778
                    $iconStatus = $urlImg.'icons/svg/identifier_student.svg';
1779
                    $label = get_lang('Student');
1780
                }
1781
                break;
1782
            case COURSEMANAGER:
1783
                if ($result['is_admin']) {
1784
                    $iconStatus = $urlImg.'icons/svg/identifier_admin.svg';
1785
                    $label = get_lang('Admin');
1786
                } else {
1787
                    $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1788
                    $label = get_lang('Teacher');
1789
                }
1790
                break;
1791
            case STUDENT_BOSS:
1792
                $iconStatus = $urlImg.'icons/svg/identifier_teacher.svg';
1793
                $label = get_lang('StudentBoss');
1794
                break;
1795
        }
1796
1797
        if (!empty($iconStatus)) {
1798
            $iconStatusMedium = '<img src="'.$iconStatus.'" width="32px" height="32px">';
1799
            $iconStatus = '<img src="'.$iconStatus.'" width="22px" height="22px">';
1800
        }
1801
1802
        $result['icon_status'] = $iconStatus;
1803
        $result['icon_status_label'] = $label;
1804
        $result['icon_status_medium'] = $iconStatusMedium;
1805
    }
1806
1807
    if (isset($user['user_is_online'])) {
1808
        $result['user_is_online'] = $user['user_is_online'] == true ? 1 : 0;
1809
    }
1810
    if (isset($user['user_is_online_in_chat'])) {
1811
        $result['user_is_online_in_chat'] = (int) $user['user_is_online_in_chat'];
1812
    }
1813
1814
    if ($add_password) {
1815
        $result['password'] = $user['password'];
1816
    }
1817
1818
    if (isset($result['profile_completed'])) {
1819
        $result['profile_completed'] = $user['profile_completed'];
1820
    }
1821
1822
    $result['profile_url'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user_id;
1823
1824
    // Send message link
1825
    $userIdHash = UserManager::generateUserHash($user_id);
1826
    $sendMessage = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&hash='.$userIdHash;
1827
    $result['complete_name_with_message_link'] = Display::url(
1828
        $result['complete_name_with_username'],
1829
        $sendMessage,
1830
        ['class' => 'ajax']
1831
    );
1832
1833
    if (isset($user['extra'])) {
1834
        $result['extra'] = $user['extra'];
1835
    }
1836
1837
    return $result;
1838
}
1839
1840
/**
1841
 * Finds all the information about a user.
1842
 * If no parameter is passed you find all the information about the current user.
1843
 *
1844
 * @param int  $user_id
1845
 * @param bool $checkIfUserOnline
1846
 * @param bool $showPassword
1847
 * @param bool $loadExtraData
1848
 * @param bool $loadOnlyVisibleExtraData Get the user extra fields that are visible
1849
 * @param bool $loadAvatars              turn off to improve performance and if avatars are not needed
1850
 * @param bool $updateCache              update apc cache if exists
1851
 *
1852
 * @return mixed $user_info user_id, lastname, firstname, username, email, etc or false on error
1853
 *
1854
 * @author Patrick Cool <[email protected]>
1855
 * @author Julio Montoya
1856
 *
1857
 * @version 21 September 2004
1858
 */
1859
function api_get_user_info(
1860
    $user_id = 0,
1861
    $checkIfUserOnline = false,
1862
    $showPassword = false,
1863
    $loadExtraData = false,
1864
    $loadOnlyVisibleExtraData = false,
1865
    $loadAvatars = true,
1866
    $updateCache = false
1867
) {
1868
    $apcVar = null;
1869
    $user = false;
1870
    $cacheAvailable = api_get_configuration_value('apc');
1871
1872
    if (empty($user_id)) {
1873
        $userFromSession = Session::read('_user');
1874
1875
        if (isset($userFromSession)) {
1876
            if ($cacheAvailable === true &&
1877
                (
1878
                    empty($userFromSession['is_anonymous']) &&
1879
                    (isset($userFromSession['status']) && $userFromSession['status'] != ANONYMOUS)
1880
                )
1881
            ) {
1882
                $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userFromSession['user_id'];
1883
                if (apcu_exists($apcVar)) {
1884
                    if ($updateCache) {
1885
                        apcu_store($apcVar, $userFromSession, 60);
1886
                    }
1887
                    $user = apcu_fetch($apcVar);
1888
                } else {
1889
                    $user = _api_format_user(
1890
                        $userFromSession,
1891
                        $showPassword,
1892
                        $loadAvatars
1893
                    );
1894
                    apcu_store($apcVar, $user, 60);
1895
                }
1896
            } else {
1897
                $user = _api_format_user(
1898
                    $userFromSession,
1899
                    $showPassword,
1900
                    $loadAvatars
1901
                );
1902
            }
1903
1904
            return $user;
1905
        }
1906
1907
        return false;
1908
    }
1909
1910
    // Make sure user_id is safe
1911
    $user_id = (int) $user_id;
1912
1913
    // Re-use user information if not stale and already stored in APCu
1914
    if ($cacheAvailable === true) {
1915
        $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1916
        if (apcu_exists($apcVar) && $updateCache == false && $checkIfUserOnline == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
1917
            $user = apcu_fetch($apcVar);
1918
1919
            return $user;
1920
        }
1921
    }
1922
1923
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
1924
            WHERE id = $user_id";
1925
    $result = Database::query($sql);
1926
    if (Database::num_rows($result) > 0) {
1927
        $result_array = Database::fetch_array($result);
1928
        $result_array['user_is_online_in_chat'] = 0;
1929
        if ($checkIfUserOnline) {
1930
            $use_status_in_platform = user_is_online($user_id);
1931
            $result_array['user_is_online'] = $use_status_in_platform;
1932
            $user_online_in_chat = 0;
1933
            if ($use_status_in_platform) {
1934
                $user_status = UserManager::get_extra_user_data_by_field(
1935
                    $user_id,
1936
                    'user_chat_status',
1937
                    false,
1938
                    true
1939
                );
1940
                if ((int) $user_status['user_chat_status'] == 1) {
1941
                    $user_online_in_chat = 1;
1942
                }
1943
            }
1944
            $result_array['user_is_online_in_chat'] = $user_online_in_chat;
1945
        }
1946
1947
        if ($loadExtraData) {
1948
            $fieldValue = new ExtraFieldValue('user');
1949
            $result_array['extra'] = $fieldValue->getAllValuesForAnItem(
1950
                $user_id,
1951
                $loadOnlyVisibleExtraData
1952
            );
1953
        }
1954
        $user = _api_format_user($result_array, $showPassword, $loadAvatars);
1955
    }
1956
1957
    if ($cacheAvailable === true) {
1958
        apcu_store($apcVar, $user, 60);
1959
    }
1960
1961
    return $user;
1962
}
1963
1964
/**
1965
 * @param int $userId
1966
 *
1967
 * @return User
1968
 */
1969
function api_get_user_entity($userId)
1970
{
1971
    $userId = (int) $userId;
1972
    $repo = UserManager::getRepository();
1973
1974
    /** @var User $user */
1975
    $user = $repo->find($userId);
1976
1977
    return $user;
1978
}
1979
1980
/**
1981
 * Finds all the information about a user from username instead of user id.
1982
 *
1983
 * @param string $username
1984
 *
1985
 * @return mixed $user_info array user_id, lastname, firstname, username, email or false on error
1986
 *
1987
 * @author Yannick Warnier <[email protected]>
1988
 */
1989
function api_get_user_info_from_username($username, $authSource = null)
1990
{
1991
    if (empty($username)) {
1992
        return false;
1993
    }
1994
    $username = trim($username);
1995
1996
    $andAuthSource = "";
1997
    if (isset($authSource)) {
1998
        $authSource = Database::escape_string($authSource);
1999
        $andAuthSource = " AND auth_source = '$authSource'";
2000
    }
2001
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
2002
            WHERE username='".Database::escape_string($username)."' $andAuthSource";
2003
    $result = Database::query($sql);
2004
    if (Database::num_rows($result) > 0) {
2005
        $resultArray = Database::fetch_array($result);
2006
2007
        return _api_format_user($resultArray);
2008
    }
2009
2010
    return false;
2011
}
2012
2013
/**
2014
 * Get first user with an email.
2015
 *
2016
 * @param string $email
2017
 *
2018
 * @return array|bool
2019
 */
2020
function api_get_user_info_from_email($email = '')
2021
{
2022
    if (empty($email)) {
2023
        return false;
2024
    }
2025
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
2026
            WHERE email ='".Database::escape_string($email)."' LIMIT 1";
2027
    $result = Database::query($sql);
2028
    if (Database::num_rows($result) > 0) {
2029
        $resultArray = Database::fetch_array($result);
2030
2031
        return _api_format_user($resultArray);
2032
    }
2033
2034
    return false;
2035
}
2036
2037
/**
2038
 * @return string
2039
 */
2040
function api_get_course_id()
2041
{
2042
    return Session::read('_cid', null);
2043
}
2044
2045
/**
2046
 * Returns the current course id (integer).
2047
 *
2048
 * @param string $code Optional course code
2049
 *
2050
 * @return int
2051
 */
2052
function api_get_course_int_id($code = null)
2053
{
2054
    if (!empty($code)) {
2055
        $code = Database::escape_string($code);
2056
        $row = Database::select(
2057
            'id',
2058
            Database::get_main_table(TABLE_MAIN_COURSE),
2059
            ['where' => ['code = ?' => [$code]]],
2060
            'first'
2061
        );
2062
2063
        if (is_array($row) && isset($row['id'])) {
2064
            return $row['id'];
2065
        } else {
2066
            return false;
2067
        }
2068
    }
2069
2070
    return Session::read('_real_cid', 0);
2071
}
2072
2073
/**
2074
 * Returns the current course directory.
2075
 *
2076
 * This function relies on api_get_course_info()
2077
 *
2078
 * @param string    The course code - optional (takes it from session if not given)
2079
 *
2080
 * @return string The directory where the course is located inside the Chamilo "courses" directory
2081
 *
2082
 * @author Yannick Warnier <[email protected]>
2083
 */
2084
function api_get_course_path($course_code = null)
2085
{
2086
    $info = !empty($course_code) ? api_get_course_info($course_code) : api_get_course_info();
2087
2088
    return $info['path'];
2089
}
2090
2091
/**
2092
 * Gets a course setting from the current course_setting table. Try always using integer values.
2093
 *
2094
 * @param string $settingName The name of the setting we want from the table
2095
 * @param array  $courseInfo
2096
 * @param bool   $force       force checking the value in the database
2097
 *
2098
 * @return mixed The value of that setting in that table. Return -1 if not found.
2099
 */
2100
function api_get_course_setting($settingName, $courseInfo = [], $force = false)
2101
{
2102
    if (empty($courseInfo)) {
2103
        $courseInfo = api_get_course_info();
2104
    }
2105
2106
    if (empty($courseInfo) || empty($settingName)) {
2107
        return -1;
2108
    }
2109
2110
    $courseId = isset($courseInfo['real_id']) && !empty($courseInfo['real_id']) ? $courseInfo['real_id'] : 0;
2111
2112
    if (empty($courseId)) {
2113
        return -1;
2114
    }
2115
2116
    static $courseSettingInfo = [];
2117
2118
    if ($force) {
2119
        $courseSettingInfo = [];
2120
    }
2121
2122
    if (!isset($courseSettingInfo[$courseId])) {
2123
        $table = Database::get_course_table(TABLE_COURSE_SETTING);
2124
        $settingName = Database::escape_string($settingName);
2125
2126
        $sql = "SELECT variable, value FROM $table
2127
                WHERE c_id = $courseId ";
2128
        $res = Database::query($sql);
2129
        if (Database::num_rows($res) > 0) {
2130
            $result = Database::store_result($res, 'ASSOC');
2131
            $courseSettingInfo[$courseId] = array_column($result, 'value', 'variable');
2132
2133
            if (isset($courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'])) {
2134
                $value = $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'];
2135
                if (!is_null($value)) {
2136
                    $result = explode(',', $value);
2137
                    $courseSettingInfo[$courseId]['email_alert_manager_on_new_quiz'] = $result;
2138
                }
2139
            }
2140
        }
2141
    }
2142
2143
    if (isset($courseSettingInfo[$courseId]) && array_key_exists($settingName, $courseSettingInfo[$courseId])) {
2144
        return $courseSettingInfo[$courseId][$settingName];
2145
    }
2146
2147
    return -1;
2148
}
2149
2150
function api_get_course_plugin_setting($plugin, $settingName, $courseInfo = [])
2151
{
2152
    $value = api_get_course_setting($settingName, $courseInfo, true);
2153
2154
    if (-1 === $value) {
2155
        // Check global settings
2156
        $value = api_get_plugin_setting($plugin, $settingName);
2157
        if ($value === 'true') {
2158
            return 1;
2159
        }
2160
        if ($value === 'false') {
2161
            return 0;
2162
        }
2163
        if (null === $value) {
2164
            return -1;
2165
        }
2166
    }
2167
2168
    return $value;
2169
}
2170
2171
/**
2172
 * Gets an anonymous user ID.
2173
 *
2174
 * For some tools that need tracking, like the learnpath tool, it is necessary
2175
 * to have a usable user-id to enable some kind of tracking, even if not
2176
 * perfect. An anonymous ID is taken from the users table by looking for a
2177
 * status of "6" (anonymous).
2178
 *
2179
 * @return int User ID of the anonymous user, or O if no anonymous user found
2180
 */
2181
function api_get_anonymous_id()
2182
{
2183
    // Find if another anon is connected now
2184
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2185
    $tableU = Database::get_main_table(TABLE_MAIN_USER);
2186
    $ip = Database::escape_string(api_get_real_ip());
2187
    $max = (int) api_get_configuration_value('max_anonymous_users');
2188
    if ($max >= 2) {
2189
        $sql = "SELECT * FROM $table as TEL
2190
                JOIN $tableU as U
2191
                ON U.user_id = TEL.login_user_id
2192
                WHERE TEL.user_ip = '$ip'
2193
                    AND U.status = ".ANONYMOUS."
2194
                    AND U.user_id != 2 ";
2195
2196
        $result = Database::query($sql);
2197
        if (empty(Database::num_rows($result))) {
2198
            $login = uniqid('anon_');
2199
            $email = ' [email protected]';
2200
            if (api_get_setting('login_is_email') == 'true') {
2201
                $login = $login."@localhost.local";
2202
                $email = $login;
2203
            }
2204
            $anonList = UserManager::get_user_list(['status' => ANONYMOUS], ['registration_date ASC']);
2205
            if (count($anonList) >= $max) {
2206
                foreach ($anonList as $userToDelete) {
2207
                    UserManager::delete_user($userToDelete['user_id']);
2208
                    break;
2209
                }
2210
            }
2211
            // Return the user ID
2212
            return UserManager::create_user(
0 ignored issues
show
Bug Best Practice introduced by
The expression return UserManager::crea...$email, $login, $login) could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

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

Loading history...
2213
                $login,
2214
                'anon',
2215
                ANONYMOUS,
2216
                $email,
2217
                $login,
2218
                $login
2219
            );
2220
        } else {
2221
            $row = Database::fetch_array($result, 'ASSOC');
2222
2223
            return $row['user_id'];
2224
        }
2225
    }
2226
2227
    $table = Database::get_main_table(TABLE_MAIN_USER);
2228
    $sql = "SELECT user_id
2229
            FROM $table
2230
            WHERE status = ".ANONYMOUS." ";
2231
    $res = Database::query($sql);
2232
    if (Database::num_rows($res) > 0) {
2233
        $row = Database::fetch_array($res, 'ASSOC');
2234
2235
        return $row['user_id'];
2236
    }
2237
2238
    // No anonymous user was found.
2239
    return 0;
2240
}
2241
2242
/**
2243
 * @param string $courseCode
2244
 * @param int    $sessionId
2245
 * @param int    $groupId
2246
 *
2247
 * @return string
2248
 */
2249
function api_get_cidreq_params($courseCode, $sessionId = 0, $groupId = 0)
2250
{
2251
    $courseCode = !empty($courseCode) ? htmlspecialchars($courseCode) : '';
2252
    $sessionId = !empty($sessionId) ? (int) $sessionId : 0;
2253
    $groupId = !empty($groupId) ? (int) $groupId : 0;
2254
2255
    $url = 'cidReq='.$courseCode;
2256
    $url .= '&id_session='.$sessionId;
2257
    $url .= '&gidReq='.$groupId;
2258
2259
    return $url;
2260
}
2261
2262
/**
2263
 * Returns the current course url part including session, group, and gradebook params.
2264
 *
2265
 * @param bool   $addSessionId
2266
 * @param bool   $addGroupId
2267
 * @param string $origin
2268
 *
2269
 * @return string Course & session references to add to a URL
2270
 */
2271
function api_get_cidreq($addSessionId = true, $addGroupId = true, $origin = '')
2272
{
2273
    $courseCode = api_get_course_id();
2274
    $url = empty($courseCode) ? '' : 'cidReq='.urlencode(htmlspecialchars($courseCode));
2275
    $origin = empty($origin) ? api_get_origin() : urlencode(Security::remove_XSS($origin));
2276
2277
    if ($addSessionId) {
2278
        if (!empty($url)) {
2279
            $url .= api_get_session_id() == 0 ? '&id_session=0' : '&id_session='.api_get_session_id();
2280
        }
2281
    }
2282
2283
    if ($addGroupId) {
2284
        if (!empty($url)) {
2285
            $url .= api_get_group_id() == 0 ? '&gidReq=0' : '&gidReq='.api_get_group_id();
2286
        }
2287
    }
2288
2289
    if (!empty($url)) {
2290
        $url .= '&gradebook='.intval(api_is_in_gradebook());
2291
        $url .= '&origin='.$origin;
2292
    }
2293
2294
    return $url;
2295
}
2296
2297
/**
2298
 * Get if we visited a gradebook page.
2299
 *
2300
 * @return bool
2301
 */
2302
function api_is_in_gradebook()
2303
{
2304
    return Session::read('in_gradebook', false);
2305
}
2306
2307
/**
2308
 * Set that we are in a page inside a gradebook.
2309
 */
2310
function api_set_in_gradebook()
2311
{
2312
    Session::write('in_gradebook', true);
2313
}
2314
2315
/**
2316
 * Remove gradebook session.
2317
 */
2318
function api_remove_in_gradebook()
2319
{
2320
    Session::erase('in_gradebook');
2321
}
2322
2323
/**
2324
 * Returns the current course info array see api_format_course_array()
2325
 * If the course_code is given, the returned array gives info about that
2326
 * particular course, if none given it gets the course info from the session.
2327
 *
2328
 * @param string $course_code
2329
 *
2330
 * @return array
2331
 */
2332
function api_get_course_info($course_code = null)
2333
{
2334
    if (!empty($course_code)) {
2335
        $course_code = Database::escape_string($course_code);
2336
        $courseId = api_get_course_int_id($course_code);
2337
2338
        if (empty($courseId)) {
2339
            return [];
2340
        }
2341
2342
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2343
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2344
        $sql = "SELECT
2345
                    course.*,
2346
                    course_category.code faCode,
2347
                    course_category.name faName
2348
                FROM $course_table
2349
                LEFT JOIN $course_cat_table
2350
                ON course.category_code = course_category.code
2351
                WHERE course.id = $courseId";
2352
        $result = Database::query($sql);
2353
        $courseInfo = [];
2354
        if (Database::num_rows($result) > 0) {
2355
            $data = Database::fetch_array($result);
2356
            $courseInfo = api_format_course_array($data);
2357
        }
2358
2359
        return $courseInfo;
2360
    }
2361
2362
    global $_course;
2363
    if ($_course == '-1') {
2364
        $_course = [];
2365
    }
2366
2367
    return $_course;
2368
}
2369
2370
/**
2371
 * @param int $courseId
2372
 *
2373
 * @return \Chamilo\CoreBundle\Entity\Course
2374
 */
2375
function api_get_course_entity($courseId = 0)
2376
{
2377
    if (empty($courseId)) {
2378
        $courseId = api_get_course_int_id();
2379
    }
2380
2381
    return Database::getManager()->getRepository('ChamiloCoreBundle:Course')->find($courseId);
2382
}
2383
2384
function api_get_group_entity($id = 0)
2385
{
2386
    if (empty($id)) {
2387
        $id = api_get_group_id();
2388
    }
2389
2390
    return Database::getManager()->getRepository('ChamiloCourseBundle:CGroupInfo')->find($id);
2391
}
2392
2393
/**
2394
 * @param int $id
2395
 *
2396
 * @return \Chamilo\CoreBundle\Entity\Session
2397
 */
2398
function api_get_session_entity($id = 0)
2399
{
2400
    if (empty($id)) {
2401
        $id = api_get_session_id();
2402
    }
2403
2404
    return Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($id);
2405
}
2406
2407
/**
2408
 * Returns the current course info array.
2409
2410
 * Now if the course_code is given, the returned array gives info about that
2411
 * particular course, not specially the current one.
2412
 *
2413
 * @param int $id Numeric ID of the course
2414
 *
2415
 * @return array The course info as an array formatted by api_format_course_array, including category.name
2416
 */
2417
function api_get_course_info_by_id($id = null)
2418
{
2419
    if (!empty($id)) {
2420
        $id = (int) $id;
2421
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
2422
        $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
2423
        $sql = "SELECT
2424
                    course.*,
2425
                    course_category.code faCode,
2426
                    course_category.name faName
2427
                FROM $course_table
2428
                LEFT JOIN $course_cat_table
2429
                ON course.category_code = course_category.code
2430
                WHERE course.id = $id";
2431
        $result = Database::query($sql);
2432
        $_course = [];
2433
        if (Database::num_rows($result) > 0) {
2434
            $row = Database::fetch_array($result);
2435
            $_course = api_format_course_array($row);
2436
        }
2437
2438
        return $_course;
2439
    }
2440
2441
    global $_course;
2442
    if ($_course == '-1') {
2443
        $_course = [];
2444
    }
2445
2446
    return $_course;
2447
}
2448
2449
/**
2450
 * Reformat the course array (output by api_get_course_info()) in order, mostly,
2451
 * to switch from 'code' to 'id' in the array. This is a legacy feature and is
2452
 * now possibly causing massive confusion as a new "id" field has been added to
2453
 * the course table in 1.9.0.
2454
 *
2455
 * @param $course_data
2456
 *
2457
 * @return array
2458
 *
2459
 * @todo eradicate the false "id"=code field of the $_course array and use the int id
2460
 */
2461
function api_format_course_array($course_data)
2462
{
2463
    if (empty($course_data)) {
2464
        return [];
2465
    }
2466
2467
    $_course = [];
2468
    $_course['id'] = $course_data['code'];
2469
    $_course['real_id'] = $course_data['id'];
2470
2471
    // Added
2472
    $_course['code'] = $course_data['code'];
2473
    $_course['name'] = $course_data['title'];
2474
    $_course['title'] = $course_data['title'];
2475
    $_course['official_code'] = $course_data['visual_code'];
2476
    $_course['visual_code'] = $course_data['visual_code'];
2477
    $_course['sysCode'] = $course_data['code'];
2478
    $_course['path'] = $course_data['directory']; // Use as key in path.
2479
    $_course['directory'] = $course_data['directory'];
2480
    $_course['creation_date'] = $course_data['creation_date'];
2481
    $_course['titular'] = $course_data['tutor_name'];
2482
    $_course['tutor_name'] = $course_data['tutor_name'];
2483
    $_course['language'] = $course_data['course_language'];
2484
    $_course['extLink']['url'] = $course_data['department_url'];
2485
    $_course['extLink']['name'] = $course_data['department_name'];
2486
    $_course['categoryCode'] = $course_data['faCode'];
2487
    $_course['category_code'] = $course_data['faCode'];
2488
    $_course['categoryName'] = $course_data['faName'];
2489
    $_course['visibility'] = $course_data['visibility'];
2490
    $_course['subscribe_allowed'] = $course_data['subscribe'];
2491
    $_course['subscribe'] = $course_data['subscribe'];
2492
    $_course['unsubscribe'] = $course_data['unsubscribe'];
2493
    $_course['course_language'] = $course_data['course_language'];
2494
    $_course['activate_legal'] = isset($course_data['activate_legal']) ? $course_data['activate_legal'] : false;
2495
    $_course['legal'] = $course_data['legal'];
2496
    $_course['show_score'] = $course_data['show_score']; //used in the work tool
2497
    $_course['department_name'] = $course_data['department_name'];
2498
    $_course['department_url'] = $course_data['department_url'];
2499
2500
    $courseSys = api_get_path(SYS_COURSE_PATH).$course_data['directory'];
2501
    $webCourseHome = api_get_path(WEB_COURSE_PATH).$course_data['directory'];
2502
2503
    // Course password
2504
    $_course['registration_code'] = !empty($course_data['registration_code']) ? sha1($course_data['registration_code']) : null;
2505
    $_course['disk_quota'] = $course_data['disk_quota'];
2506
    $_course['course_public_url'] = $webCourseHome.'/index.php';
2507
    $_course['course_sys_path'] = $courseSys.'/';
2508
2509
    if (array_key_exists('add_teachers_to_sessions_courses', $course_data)) {
2510
        $_course['add_teachers_to_sessions_courses'] = $course_data['add_teachers_to_sessions_courses'];
2511
    }
2512
2513
    // Course image
2514
    $_course['course_image_source'] = '';
2515
    if (file_exists($courseSys.'/course-pic85x85.png')) {
2516
        $url_image = $webCourseHome.'/course-pic85x85.png';
2517
        $_course['course_image_source'] = $courseSys.'/course-pic85x85.png';
2518
    } else {
2519
        $url_image = Display::return_icon(
2520
            'course.png',
2521
            null,
2522
            null,
2523
            ICON_SIZE_LARGE,
2524
            null,
2525
            true,
2526
            true
2527
        );
2528
    }
2529
    $_course['course_image'] = $url_image;
2530
2531
    // Course large image
2532
    $_course['course_image_large_source'] = '';
2533
    if (file_exists($courseSys.'/course-pic.png')) {
2534
        $url_image = $webCourseHome.'/course-pic.png';
2535
        $_course['course_image_large_source'] = $courseSys.'/course-pic.png';
2536
    } else {
2537
        $url_image = Display::return_icon(
2538
            'session_default.png',
2539
            null,
2540
            null,
2541
            null,
2542
            null,
2543
            true,
2544
            true
2545
        );
2546
    }
2547
2548
    $_course['course_image_large'] = $url_image;
2549
2550
    // email pictures
2551
    // Course image
2552
    $url_image = null;
2553
    $_course['course_email_image_source'] = '';
2554
    $mailPicture = $courseSys.'/course-email-pic-cropped.png';
2555
    if (file_exists($mailPicture)) {
2556
        $url_image = $webCourseHome.'/course-email-pic-cropped.png';
2557
        $_course['course_email_image_source'] = $mailPicture;
2558
    }
2559
    $_course['course_email_image'] = $url_image;
2560
2561
    // Course large image
2562
    $url_image = null;
2563
    $_course['course_email_image_large_source'] = '';
2564
    $mailPicture = $courseSys.'/course-email-pic.png';
2565
    if (file_exists($mailPicture)) {
2566
        $url_image = $webCourseHome.'/course-email-pic.png';
2567
        $_course['course_email_image_large_source'] = $mailPicture;
2568
    }
2569
2570
    $_course['course_email_image_large'] = $url_image;
2571
2572
    return $_course;
2573
}
2574
2575
/**
2576
 * Returns a difficult to guess password.
2577
 *
2578
 * @param int $length the length of the password
2579
 *
2580
 * @return string the generated password
2581
 */
2582
function api_generate_password($length = 8)
2583
{
2584
    if ($length < 2) {
2585
        $length = 2;
2586
    }
2587
2588
    $charactersLowerCase = Security::CHAR_LOWER;
2589
    $charactersUpperCase = Security::CHAR_UPPER;
2590
2591
    $minNumbers = 2;
2592
    $length = $length - $minNumbers;
2593
    $minLowerCase = round($length / 2);
2594
    $minUpperCase = $length - $minLowerCase;
2595
2596
    $password = '';
2597
    $passwordRequirements = api_get_configuration_value('password_requirements');
2598
2599
    $factory = new RandomLib\Factory();
2600
    $generator = $factory->getMediumStrengthGenerator();
2601
2602
    if (!empty($passwordRequirements)) {
2603
        $length = $passwordRequirements['min']['length'];
2604
        $minNumbers = $passwordRequirements['min']['numeric'];
2605
        $minLowerCase = $passwordRequirements['min']['lowercase'];
2606
        $minUpperCase = $passwordRequirements['min']['uppercase'];
2607
        $minSpecials = $passwordRequirements['min']['specials'];
2608
2609
        $rest = $length - $minNumbers - $minLowerCase - $minUpperCase - $minSpecials;
2610
        // Add the rest to fill the length requirement
2611
        if ($rest > 0) {
2612
            $password .= $generator->generateString(
2613
                $rest,
2614
                $charactersLowerCase.$charactersUpperCase
2615
            );
2616
        }
2617
2618
        $password .= $generator->generateString($minSpecials, Security::CHAR_SYMBOLS);
2619
    }
2620
2621
    // Min digits default 2
2622
    for ($i = 0; $i < $minNumbers; $i++) {
2623
        $password .= $generator->generateInt(2, 9);
2624
    }
2625
2626
    // Min lowercase
2627
    $password .= $generator->generateString($minLowerCase, $charactersLowerCase);
2628
2629
    // Min uppercase
2630
    $password .= $generator->generateString($minUpperCase, $charactersUpperCase);
2631
    $password = str_shuffle($password);
2632
2633
    return $password;
2634
}
2635
2636
/**
2637
 * Checks a password to see wether it is OK to use.
2638
 *
2639
 * @param string $password
2640
 *
2641
 * @return bool if the password is acceptable, false otherwise
2642
 *              Notes about what a password "OK to use" is:
2643
 *              1. The password should be at least 5 characters long.
2644
 *              2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed.
2645
 *              3. The password should contain at least 3 letters.
2646
 *              4. It should contain at least 2 digits.
2647
 *              Settings will change if the configuration value is set: password_requirements
2648
 */
2649
function api_check_password($password)
2650
{
2651
    $passwordRequirements = Security::getPasswordRequirements();
2652
2653
    $minLength = $passwordRequirements['min']['length'];
2654
    $minNumbers = $passwordRequirements['min']['numeric'];
2655
    // Optional
2656
    $minLowerCase = $passwordRequirements['min']['lowercase'];
2657
    $minUpperCase = $passwordRequirements['min']['uppercase'];
2658
    $minSpecials = $passwordRequirements['min']['specials'];
2659
2660
    $minLetters = $minLowerCase + $minUpperCase;
2661
    $passwordLength = api_strlen($password);
2662
2663
    $conditions = [
2664
        'min_length' => $passwordLength >= $minLength,
2665
    ];
2666
2667
    $digits = 0;
2668
    $lowerCase = 0;
2669
    $upperCase = 0;
2670
    $specials = 0;
2671
2672
    for ($i = 0; $i < $passwordLength; $i++) {
2673
        $currentCharacter = api_substr($password, $i, 1);
2674
        $currentCharacterCode = api_ord($currentCharacter);
2675
        if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) {
2676
            $upperCase++;
2677
        }
2678
2679
        if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) {
2680
            $lowerCase++;
2681
        }
2682
        if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) {
2683
            $digits++;
2684
        }
2685
2686
        if (false !== strpos(Security::CHAR_SYMBOLS, $currentCharacter)) {
2687
            $specials++;
2688
        }
2689
    }
2690
2691
    // Min number of digits
2692
    $conditions['min_numeric'] = $digits >= $minNumbers;
2693
2694
    if (!empty($minUpperCase)) {
2695
        // Uppercase
2696
        $conditions['min_uppercase'] = $upperCase >= $minUpperCase;
2697
    }
2698
2699
    if (!empty($minLowerCase)) {
2700
        // Lowercase
2701
        $conditions['min_lowercase'] = $lowerCase >= $minLowerCase;
2702
    }
2703
2704
    if (!empty($minSpecials)) {
2705
        $conditions['min_specials'] = $specials >= $minSpecials;
2706
    }
2707
2708
    // Min letters
2709
    $letters = $upperCase + $lowerCase;
2710
    $conditions['min_letters'] = $letters >= $minLetters;
2711
2712
    $isPasswordOk = true;
2713
    foreach ($conditions as $condition) {
2714
        if ($condition === false) {
2715
            $isPasswordOk = false;
2716
            break;
2717
        }
2718
    }
2719
2720
    if ($isPasswordOk === false) {
2721
        $output = get_lang('NewPasswordRequirementsNotMatched').'<br />';
2722
        $output .= Security::getPasswordRequirementsToString($conditions);
2723
2724
        Display::addFlash(Display::return_message($output, 'warning', false));
2725
    }
2726
2727
    return $isPasswordOk;
2728
}
2729
2730
/**
2731
 * Clears the user ID from the session if it was the anonymous user. Generally
2732
 * used on out-of-tools pages to remove a user ID that could otherwise be used
2733
 * in the wrong context.
2734
 * This function is to be used in conjunction with the api_set_anonymous()
2735
 * function to simulate the user existence in case of an anonymous visit.
2736
 *
2737
 * @param bool      database check switch - passed to api_is_anonymous()
2738
 *
2739
 * @return bool true if succesfully unregistered, false if not anonymous
2740
 */
2741
function api_clear_anonymous($db_check = false)
2742
{
2743
    global $_user;
2744
    if (isset($_user['user_id']) && api_is_anonymous($_user['user_id'], $db_check)) {
2745
        unset($_user['user_id']);
2746
        Session::erase('_uid');
2747
2748
        return true;
2749
    }
2750
2751
    return false;
2752
}
2753
2754
/**
2755
 * Returns the status string corresponding to the status code.
2756
 *
2757
 * @author Noel Dieschburg
2758
 *
2759
 * @param int $status_code The integer status code (usually in the form of a constant)
2760
 *
2761
 * @return string
2762
 */
2763
function get_status_from_code($status_code)
2764
{
2765
    switch ($status_code) {
2766
        case STUDENT:
2767
            return get_lang('Student', '');
2768
        case COURSEMANAGER:
2769
            return get_lang('Teacher', '');
2770
        case SESSIONADMIN:
2771
            return get_lang('SessionsAdmin', '');
2772
        case DRH:
2773
            return get_lang('Drh', '');
2774
        case ANONYMOUS:
2775
            return get_lang('Anonymous', '');
2776
        case PLATFORM_ADMIN:
2777
            return get_lang('Administrator', '');
2778
        case SESSION_COURSE_COACH:
2779
            return get_lang('SessionCourseCoach', '');
2780
        case SESSION_GENERAL_COACH:
2781
            return get_lang('SessionGeneralCoach', '');
2782
        case COURSE_TUTOR:
2783
            return get_lang('CourseAssistant', '');
2784
        case STUDENT_BOSS:
2785
            return get_lang('StudentBoss', '');
2786
        case INVITEE:
2787
            return get_lang('Invitee', '');
2788
    }
2789
2790
    return '';
2791
}
2792
2793
/**
2794
 * Sets the current user as anonymous if it hasn't been identified yet. This
2795
 * function should be used inside a tool only. The function api_clear_anonymous()
2796
 * acts in the opposite direction by clearing the anonymous user's data every
2797
 * time we get on a course homepage or on a neutral page (index, admin, my space).
2798
 *
2799
 * @return bool true if set user as anonymous, false if user was already logged in or anonymous id could not be found
2800
 */
2801
function api_set_anonymous()
2802
{
2803
    global $_user;
2804
2805
    if (!empty($_user['user_id'])) {
2806
        return false;
2807
    }
2808
2809
    $user_id = api_get_anonymous_id();
2810
    if ($user_id == 0) {
2811
        return false;
2812
    }
2813
2814
    if (isset($_user['is_anonymous'])) {
2815
        return false;
2816
    }
2817
2818
    Session::erase('_user');
2819
    $_user['user_id'] = $user_id;
2820
    $_user['is_anonymous'] = true;
2821
    $GLOBALS['_user'] = $_user;
2822
    Session::write('_user', $_user);
2823
2824
    return true;
2825
}
2826
2827
/**
2828
 * Gets the current Chamilo (not PHP/cookie) session ID.
2829
 *
2830
 * @return int O if no active session, the session ID otherwise
2831
 */
2832
function api_get_session_id()
2833
{
2834
    return (int) Session::read('id_session', 0);
2835
}
2836
2837
/**
2838
 * Gets the current Chamilo (not social network) group ID.
2839
 *
2840
 * @return int O if no active group, the group id otherwise
2841
 */
2842
function api_get_group_id()
2843
{
2844
    return (int) Session::read('_gid', 0);
2845
}
2846
2847
/**
2848
 * Gets the current or given session name.
2849
 *
2850
 * @param   int     Session ID (optional)
2851
 *
2852
 * @return string The session name, or null if not found
2853
 */
2854
function api_get_session_name($session_id = 0)
2855
{
2856
    if (empty($session_id)) {
2857
        $session_id = api_get_session_id();
2858
        if (empty($session_id)) {
2859
            return null;
2860
        }
2861
    }
2862
    $t = Database::get_main_table(TABLE_MAIN_SESSION);
2863
    $s = "SELECT name FROM $t WHERE id = ".(int) $session_id;
2864
    $r = Database::query($s);
2865
    $c = Database::num_rows($r);
2866
    if ($c > 0) {
2867
        //technically, there can be only one, but anyway we take the first
2868
        $rec = Database::fetch_array($r);
2869
2870
        return $rec['name'];
2871
    }
2872
2873
    return null;
2874
}
2875
2876
/**
2877
 * Gets the session info by id.
2878
 *
2879
 * @param int $id Session ID
2880
 *
2881
 * @return array information of the session
2882
 */
2883
function api_get_session_info($id)
2884
{
2885
    return SessionManager::fetch($id);
2886
}
2887
2888
/**
2889
 * Gets the session visibility by session id.
2890
 *
2891
 * @param int  $session_id
2892
 * @param int  $courseId
2893
 * @param bool $ignore_visibility_for_admins
2894
 * @param int  $userId
2895
 *
2896
 * @return int
2897
 *             0 = session still available,
2898
 *             SESSION_VISIBLE_READ_ONLY = 1,
2899
 *             SESSION_VISIBLE = 2,
2900
 *             SESSION_INVISIBLE = 3
2901
 */
2902
function api_get_session_visibility(
2903
    $session_id,
2904
    $courseId = null,
2905
    $ignore_visibility_for_admins = true,
2906
    $userId = 0
2907
) {
2908
    if (api_is_platform_admin()) {
2909
        if ($ignore_visibility_for_admins) {
2910
            return SESSION_AVAILABLE;
2911
        }
2912
    }
2913
2914
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
2915
2916
    $now = time();
2917
    if (empty($session_id)) {
2918
        return 0; // Means that the session is still available.
2919
    }
2920
2921
    $session_id = (int) $session_id;
2922
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2923
2924
    $result = Database::query("SELECT * FROM $tbl_session WHERE id = $session_id");
2925
2926
    if (Database::num_rows($result) <= 0) {
2927
        return SESSION_INVISIBLE;
2928
    }
2929
2930
    $row = Database::fetch_array($result, 'ASSOC');
2931
    $visibility = $row['visibility'];
2932
2933
    // I don't care the session visibility.
2934
    if (empty($row['access_start_date']) && empty($row['access_end_date'])) {
2935
2936
        // Session duration per student.
2937
        if (isset($row['duration']) && !empty($row['duration'])) {
2938
            if (api_get_configuration_value('session_coach_access_after_duration_end') == true && api_is_teacher()) {
2939
                return SESSION_AVAILABLE;
2940
            }
2941
2942
            $duration = $row['duration'] * 24 * 60 * 60;
2943
            $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser($session_id, $userId);
2944
2945
            // If there is a session duration but there is no previous
2946
            // access by the user, then the session is still available
2947
            if (0 == count($courseAccess)) {
2948
                return SESSION_AVAILABLE;
2949
            }
2950
2951
            $currentTime = time();
2952
            $firstAccess = isset($courseAccess['login_course_date'])
2953
                ? api_strtotime($courseAccess['login_course_date'], 'UTC')
2954
                : 0;
2955
            $userDurationData = SessionManager::getUserSession($userId, $session_id);
2956
            $userDuration = isset($userDurationData['duration'])
2957
                ? (intval($userDurationData['duration']) * 24 * 60 * 60)
2958
                : 0;
2959
2960
            $totalDuration = $firstAccess + $duration + $userDuration;
2961
2962
            return $totalDuration > $currentTime ? SESSION_AVAILABLE : $visibility;
2963
        }
2964
2965
        return SESSION_AVAILABLE;
2966
    }
2967
2968
    // If start date was set.
2969
    if (!empty($row['access_start_date'])) {
2970
        $visibility = $now > api_strtotime($row['access_start_date'], 'UTC') ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2971
    }
2972
2973
    // If the end date was set.
2974
    if (!empty($row['access_end_date'])) {
2975
        // Only if date_start said that it was ok
2976
        if ($visibility === SESSION_AVAILABLE) {
2977
            $visibility = $now < api_strtotime($row['access_end_date'], 'UTC')
2978
                ? SESSION_AVAILABLE // Date still available
2979
                : $row['visibility']; // Session ends
2980
        }
2981
    }
2982
2983
    // If I'm a coach the visibility can change in my favor depending in the coach dates.
2984
    $isCoach = api_is_coach($session_id, $courseId, $userId);
2985
2986
    if ($isCoach) {
2987
        // Test start date.
2988
        if (!empty($row['coach_access_start_date'])) {
2989
            $start = api_strtotime($row['coach_access_start_date'], 'UTC');
2990
            $visibility = $start < $now ? SESSION_AVAILABLE : SESSION_INVISIBLE;
2991
        }
2992
2993
        // Test end date.
2994
        if (!empty($row['coach_access_end_date'])) {
2995
            if ($visibility === SESSION_AVAILABLE) {
2996
                $endDateCoach = api_strtotime($row['coach_access_end_date'], 'UTC');
2997
                $visibility = $endDateCoach >= $now ? SESSION_AVAILABLE : $row['visibility'];
2998
            }
2999
        }
3000
    }
3001
3002
    return $visibility;
3003
}
3004
3005
/**
3006
 * This function returns a (star) session icon if the session is not null and
3007
 * the user is not a student.
3008
 *
3009
 * @param int $sessionId
3010
 * @param int $statusId  User status id - if 5 (student) or in student view, will return empty
3011
 *
3012
 * @return string Session icon
3013
 */
3014
function api_get_session_image($sessionId, $statusId)
3015
{
3016
    $sessionId = (int) $sessionId;
3017
    $image = '';
3018
    $studentView = !empty($_SESSION['studentview']) && $_SESSION['studentview'] == 'studentview';
3019
    if ($statusId != STUDENT && !$studentView) {
3020
        // Check whether is not a student
3021
        if ($sessionId > 0) {
3022
            $image = '&nbsp;&nbsp;'.Display::return_icon(
3023
                'star.png',
3024
                get_lang('SessionSpecificResource'),
3025
                ['align' => 'absmiddle'],
3026
                ICON_SIZE_SMALL
3027
            );
3028
        }
3029
    }
3030
3031
    return $image;
3032
}
3033
3034
/**
3035
 * This function add an additional condition according to the session of the course.
3036
 *
3037
 * @param int    $session_id        session id
3038
 * @param bool   $and               optional, true if more than one condition false if the only condition in the query
3039
 * @param bool   $with_base_content optional, true to accept content with session=0 as well,
3040
 *                                  false for strict session condition
3041
 * @param string $session_field
3042
 *
3043
 * @return string condition of the session
3044
 */
3045
function api_get_session_condition(
3046
    $session_id,
3047
    $and = true,
3048
    $with_base_content = false,
3049
    $session_field = 'session_id'
3050
) {
3051
    $session_id = (int) $session_id;
3052
3053
    if (empty($session_field)) {
3054
        $session_field = 'session_id';
3055
    }
3056
    // Condition to show resources by session
3057
    $condition_add = $and ? ' AND ' : ' WHERE ';
3058
3059
    if ($with_base_content) {
3060
        $condition_session = $condition_add." ( $session_field = $session_id OR $session_field = 0 OR $session_field IS NULL) ";
3061
    } else {
3062
        if (empty($session_id)) {
3063
            $condition_session = $condition_add." ($session_field = $session_id OR $session_field IS NULL)";
3064
        } else {
3065
            $condition_session = $condition_add." $session_field = $session_id ";
3066
        }
3067
    }
3068
3069
    return $condition_session;
3070
}
3071
3072
/**
3073
 * Returns the value of a setting from the web-adjustable admin config settings.
3074
 *
3075
 * WARNING true/false are stored as string, so when comparing you need to check e.g.
3076
 * if (api_get_setting('show_navigation_menu') == 'true') //CORRECT
3077
 * instead of
3078
 * if (api_get_setting('show_navigation_menu') == true) //INCORRECT
3079
 *
3080
 * @param string $variable The variable name
3081
 * @param string $key      The subkey (sub-variable) if any. Defaults to NULL
3082
 *
3083
 * @return string
3084
 *
3085
 * @author René Haentjens
3086
 * @author Bart Mollet
3087
 */
3088
function api_get_setting($variable, $key = null)
3089
{
3090
    global $_setting;
3091
    if ($variable == 'header_extra_content') {
3092
        $filename = api_get_home_path().'header_extra_content.txt';
3093
        if (file_exists($filename)) {
3094
            $value = file_get_contents($filename);
3095
3096
            return $value;
3097
        } else {
3098
            return '';
3099
        }
3100
    }
3101
    if ($variable == 'footer_extra_content') {
3102
        $filename = api_get_home_path().'footer_extra_content.txt';
3103
        if (file_exists($filename)) {
3104
            $value = file_get_contents($filename);
3105
3106
            return $value;
3107
        } else {
3108
            return '';
3109
        }
3110
    }
3111
    $value = null;
3112
    if (is_null($key)) {
3113
        $value = ((isset($_setting[$variable]) && $_setting[$variable] != '') ? $_setting[$variable] : null);
3114
    } else {
3115
        if (isset($_setting[$variable][$key])) {
3116
            $value = $_setting[$variable][$key];
3117
        }
3118
    }
3119
3120
    return $value;
3121
}
3122
3123
/**
3124
 * @param string $plugin
3125
 * @param string $variable
3126
 *
3127
 * @return string
3128
 */
3129
function api_get_plugin_setting($plugin, $variable)
3130
{
3131
    $settings = api_get_configuration_value('plugin_settings');
3132
3133
    if (!empty($settings) && isset($settings[$plugin]) && isset($settings[$plugin][$variable])) {
3134
        return $settings[$plugin][$variable];
3135
    }
3136
3137
    $variableName = $plugin.'_'.$variable;
3138
    $result = api_get_setting($variableName);
3139
3140
    if (isset($result[$plugin])) {
3141
        $value = $result[$plugin];
3142
        $unSerialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
3143
3144
        if (false !== $unSerialized) {
3145
            $value = $unSerialized;
3146
        }
3147
3148
        return $value;
3149
    }
3150
3151
    return null;
3152
}
3153
3154
/**
3155
 * Returns the value of a setting from the web-adjustable admin config settings.
3156
 */
3157
function api_get_settings_params($params)
3158
{
3159
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3160
3161
    return Database::select('*', $table, ['where' => $params]);
3162
}
3163
3164
/**
3165
 * @param array $params example: [id = ? => '1']
3166
 *
3167
 * @return array
3168
 */
3169
function api_get_settings_params_simple($params)
3170
{
3171
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3172
3173
    return Database::select('*', $table, ['where' => $params], 'one');
3174
}
3175
3176
/**
3177
 * Returns the value of a setting from the web-adjustable admin config settings.
3178
 */
3179
function api_delete_settings_params($params)
3180
{
3181
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
3182
    $result = Database::delete($table, $params);
3183
3184
    return $result;
3185
}
3186
3187
/**
3188
 * Returns an escaped version of $_SERVER['PHP_SELF'] to avoid XSS injection.
3189
 *
3190
 * @return string Escaped version of $_SERVER['PHP_SELF']
3191
 */
3192
function api_get_self()
3193
{
3194
    return htmlentities($_SERVER['PHP_SELF']);
3195
}
3196
3197
/* USER PERMISSIONS */
3198
3199
/**
3200
 * Checks whether current user is a platform administrator.
3201
 *
3202
 * @param bool $allowSessionAdmins Whether session admins should be considered admins or not
3203
 * @param bool $allowDrh           Whether HR directors should be considered admins or not
3204
 *
3205
 * @return bool true if the user has platform admin rights,
3206
 *              false otherwise
3207
 *
3208
 * @see usermanager::is_admin(user_id) for a user-id specific function
3209
 */
3210
function api_is_platform_admin($allowSessionAdmins = false, $allowDrh = false)
3211
{
3212
    $isAdmin = Session::read('is_platformAdmin');
3213
    if ($isAdmin) {
3214
        return true;
3215
    }
3216
    $user = api_get_user_info();
3217
3218
    return
3219
        isset($user['status']) &&
3220
        (
3221
            ($allowSessionAdmins && $user['status'] == SESSIONADMIN) ||
3222
            ($allowDrh && $user['status'] == DRH)
3223
        );
3224
}
3225
3226
/**
3227
 * Checks whether the user given as user id is in the admin table.
3228
 *
3229
 * @param int $user_id If none provided, will use current user
3230
 * @param int $url     URL ID. If provided, also check if the user is active on given URL
3231
 *
3232
 * @return bool True if the user is admin, false otherwise
3233
 */
3234
function api_is_platform_admin_by_id($user_id = null, $url = null)
3235
{
3236
    $user_id = (int) $user_id;
3237
    if (empty($user_id)) {
3238
        $user_id = api_get_user_id();
3239
    }
3240
    $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3241
    $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3242
    $res = Database::query($sql);
3243
    $is_admin = Database::num_rows($res) === 1;
3244
    if (!$is_admin || !isset($url)) {
3245
        return $is_admin;
3246
    }
3247
    // We get here only if $url is set
3248
    $url = (int) $url;
3249
    $url_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3250
    $sql = "SELECT * FROM $url_user_table
3251
            WHERE access_url_id = $url AND user_id = $user_id";
3252
    $res = Database::query($sql);
3253
    $result = Database::num_rows($res) === 1;
3254
3255
    return $result;
3256
}
3257
3258
/**
3259
 * Returns the user's numeric status ID from the users table.
3260
 *
3261
 * @param int $user_id If none provided, will use current user
3262
 *
3263
 * @return int User's status (1 for teacher, 5 for student, etc)
3264
 */
3265
function api_get_user_status($user_id = null)
3266
{
3267
    $user_id = (int) $user_id;
3268
    if (empty($user_id)) {
3269
        $user_id = api_get_user_id();
3270
    }
3271
    $table = Database::get_main_table(TABLE_MAIN_USER);
3272
    $sql = "SELECT status FROM $table WHERE user_id = $user_id ";
3273
    $result = Database::query($sql);
3274
    $status = null;
3275
    if (Database::num_rows($result)) {
3276
        $row = Database::fetch_array($result);
3277
        $status = $row['status'];
3278
    }
3279
3280
    return $status;
3281
}
3282
3283
/**
3284
 * Checks whether current user is allowed to create courses.
3285
 *
3286
 * @return bool true if the user has course creation rights,
3287
 *              false otherwise
3288
 */
3289
function api_is_allowed_to_create_course()
3290
{
3291
    if (api_is_platform_admin()) {
3292
        return true;
3293
    }
3294
3295
    // Teachers can only create courses
3296
    if (api_is_teacher()) {
3297
        if (api_get_setting('allow_users_to_create_courses') === 'true') {
3298
            return true;
3299
        } else {
3300
            return false;
3301
        }
3302
    }
3303
3304
    return Session::read('is_allowedCreateCourse');
3305
}
3306
3307
/**
3308
 * Checks whether the current user is a course administrator.
3309
 *
3310
 * @return bool True if current user is a course administrator
3311
 */
3312
function api_is_course_admin()
3313
{
3314
    if (api_is_platform_admin()) {
3315
        return true;
3316
    }
3317
3318
    return Session::read('is_courseAdmin');
3319
}
3320
3321
/**
3322
 * Checks whether the current user is a course coach
3323
 * Based on the presence of user in session.id_coach (session general coach).
3324
 *
3325
 * @return bool True if current user is a course coach
3326
 */
3327
function api_is_session_general_coach()
3328
{
3329
    return Session::read('is_session_general_coach');
3330
}
3331
3332
/**
3333
 * Checks whether the current user is a course tutor
3334
 * Based on the presence of user in session_rel_course_rel_user.user_id with status = 2.
3335
 *
3336
 * @return bool True if current user is a course tutor
3337
 */
3338
function api_is_course_tutor()
3339
{
3340
    return Session::read('is_courseTutor');
3341
}
3342
3343
/**
3344
 * @param int $user_id
3345
 * @param int $courseId
3346
 * @param int $session_id
3347
 *
3348
 * @return bool
3349
 */
3350
function api_is_course_session_coach($user_id, $courseId, $session_id)
3351
{
3352
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3353
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3354
3355
    $user_id = (int) $user_id;
3356
    $session_id = (int) $session_id;
3357
    $courseId = (int) $courseId;
3358
3359
    $sql = "SELECT DISTINCT session.id
3360
            FROM $session_table
3361
            INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3362
            ON session.id = session_rc_ru.session_id
3363
            WHERE
3364
                session_rc_ru.user_id = '".$user_id."'  AND
3365
                session_rc_ru.c_id = '$courseId' AND
3366
                session_rc_ru.status = 2 AND
3367
                session_rc_ru.session_id = '$session_id'";
3368
    $result = Database::query($sql);
3369
3370
    return Database::num_rows($result) > 0;
3371
}
3372
3373
/**
3374
 * Checks whether the current user is a course or session coach.
3375
 *
3376
 * @param int $session_id
3377
 * @param int $courseId
3378
 * @param bool  Check whether we are in student view and, if we are, return false
3379
 * @param int $userId
3380
 *
3381
 * @return bool True if current user is a course or session coach
3382
 */
3383
function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
3384
{
3385
    $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3386
3387
    if (!empty($session_id)) {
3388
        $session_id = (int) $session_id;
3389
    } else {
3390
        $session_id = api_get_session_id();
3391
    }
3392
3393
    // The student preview was on
3394
    if ($check_student_view && api_is_student_view_active()) {
3395
        return false;
3396
    }
3397
3398
    if (!empty($courseId)) {
3399
        $courseId = (int) $courseId;
3400
    } else {
3401
        $courseId = api_get_course_int_id();
3402
    }
3403
3404
    $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
3405
    $session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3406
    $sessionIsCoach = [];
3407
3408
    if (!empty($courseId)) {
3409
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3410
                FROM $session_table s
3411
                INNER JOIN $session_rel_course_rel_user_table session_rc_ru
3412
                ON session_rc_ru.session_id = s.id AND session_rc_ru.user_id = '".$userId."'
3413
                WHERE
3414
                    session_rc_ru.c_id = '$courseId' AND
3415
                    session_rc_ru.status = 2 AND
3416
                    session_rc_ru.session_id = '$session_id'";
3417
        $result = Database::query($sql);
3418
        $sessionIsCoach = Database::store_result($result);
3419
    }
3420
3421
    if (!empty($session_id)) {
3422
        $sql = "SELECT DISTINCT id, name, access_start_date, access_end_date
3423
                FROM $session_table
3424
                WHERE session.id_coach = $userId AND id = $session_id
3425
                ORDER BY access_start_date, access_end_date, name";
3426
        $result = Database::query($sql);
3427
        if (!empty($sessionIsCoach)) {
3428
            $sessionIsCoach = array_merge(
3429
                $sessionIsCoach,
3430
                Database::store_result($result)
3431
            );
3432
        } else {
3433
            $sessionIsCoach = Database::store_result($result);
3434
        }
3435
    }
3436
3437
    return count($sessionIsCoach) > 0;
3438
}
3439
3440
/**
3441
 * Checks whether the current user is a session administrator.
3442
 *
3443
 * @return bool True if current user is a course administrator
3444
 */
3445
function api_is_session_admin()
3446
{
3447
    $user = api_get_user_info();
3448
3449
    return isset($user['status']) && $user['status'] == SESSIONADMIN;
3450
}
3451
3452
/**
3453
 * Checks whether the current user is a human resources manager.
3454
 *
3455
 * @return bool True if current user is a human resources manager
3456
 */
3457
function api_is_drh()
3458
{
3459
    $user = api_get_user_info();
3460
3461
    return isset($user['status']) && $user['status'] == DRH;
3462
}
3463
3464
/**
3465
 * Checks whether the current user is a student.
3466
 *
3467
 * @return bool True if current user is a human resources manager
3468
 */
3469
function api_is_student()
3470
{
3471
    $user = api_get_user_info();
3472
3473
    return isset($user['status']) && $user['status'] == STUDENT;
3474
}
3475
3476
/**
3477
 * Checks whether the current user has the status 'teacher'.
3478
 *
3479
 * @return bool True if current user is a human resources manager
3480
 */
3481
function api_is_teacher()
3482
{
3483
    $user = api_get_user_info();
3484
3485
    return isset($user['status']) && $user['status'] == COURSEMANAGER;
3486
}
3487
3488
/**
3489
 * Checks whether the current user is a invited user.
3490
 *
3491
 * @return bool
3492
 */
3493
function api_is_invitee()
3494
{
3495
    $user = api_get_user_info();
3496
3497
    return isset($user['status']) && $user['status'] == INVITEE;
3498
}
3499
3500
/**
3501
 * This function checks whether a session is assigned into a category.
3502
 *
3503
 * @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...
3504
 * @param string    - category name
3505
 *
3506
 * @return bool - true if is found, otherwise false
3507
 */
3508
function api_is_session_in_category($session_id, $category_name)
3509
{
3510
    $session_id = (int) $session_id;
3511
    $category_name = Database::escape_string($category_name);
3512
    $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3513
    $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
3514
3515
    $sql = "SELECT 1
3516
            FROM $tbl_session
3517
            WHERE $session_id IN (
3518
                SELECT s.id FROM $tbl_session s, $tbl_session_category sc
3519
                WHERE
3520
                  s.session_category_id = sc.id AND
3521
                  sc.name LIKE '%$category_name'
3522
            )";
3523
    $rs = Database::query($sql);
3524
3525
    if (Database::num_rows($rs) > 0) {
3526
        return true;
3527
    } else {
3528
        return false;
3529
    }
3530
}
3531
3532
/**
3533
 * Displays the title of a tool.
3534
 * Normal use: parameter is a string:
3535
 * api_display_tool_title("My Tool").
3536
 *
3537
 * Optionally, there can be a subtitle below
3538
 * the normal title, and / or a supra title above the normal title.
3539
 *
3540
 * e.g. supra title:
3541
 * group
3542
 * GROUP PROPERTIES
3543
 *
3544
 * e.g. subtitle:
3545
 * AGENDA
3546
 * calender & events tool
3547
 *
3548
 * @author Hugues Peeters <[email protected]>
3549
 *
3550
 * @param mixed $title_element - it could either be a string or an array
3551
 *                             containing 'supraTitle', 'mainTitle',
3552
 *                             'subTitle'
3553
 */
3554
function api_display_tool_title($title_element)
3555
{
3556
    if (is_string($title_element)) {
3557
        $tit = $title_element;
3558
        unset($title_element);
3559
        $title_element = [];
3560
        $title_element['mainTitle'] = $tit;
3561
    }
3562
    echo '<h3>';
3563
    if (!empty($title_element['supraTitle'])) {
3564
        echo '<small>'.$title_element['supraTitle'].'</small><br />';
3565
    }
3566
    if (!empty($title_element['mainTitle'])) {
3567
        echo $title_element['mainTitle'];
3568
    }
3569
    if (!empty($title_element['subTitle'])) {
3570
        echo '<br /><small>'.$title_element['subTitle'].'</small>';
3571
    }
3572
    echo '</h3>';
3573
}
3574
3575
/**
3576
 * Displays options for switching between student view and course manager view.
3577
 *
3578
 * Changes in version 1.2 (Patrick Cool)
3579
 * Student view switch now behaves as a real switch. It maintains its current state until the state
3580
 * is changed explicitly
3581
 *
3582
 * Changes in version 1.1 (Patrick Cool)
3583
 * student view now works correctly in subfolders of the document tool
3584
 * student view works correctly in the new links tool
3585
 *
3586
 * Example code for using this in your tools:
3587
 * //if ($is_courseAdmin && api_get_setting('student_view_enabled') == 'true') {
3588
 * //   display_tool_view_option($isStudentView);
3589
 * //}
3590
 * //and in later sections, use api_is_allowed_to_edit()
3591
 *
3592
 * @author Roan Embrechts
3593
 * @author Patrick Cool
3594
 * @author Julio Montoya, changes added in Chamilo
3595
 *
3596
 * @version 1.2
3597
 *
3598
 * @todo rewrite code so it is easier to understand
3599
 */
3600
function api_display_tool_view_option()
3601
{
3602
    if (api_get_setting('student_view_enabled') != 'true') {
3603
        return '';
3604
    }
3605
3606
    $sourceurl = '';
3607
    $is_framed = false;
3608
    // Exceptions apply for all multi-frames pages
3609
    if (strpos($_SERVER['REQUEST_URI'], 'chat/chat_banner.php') !== false) {
3610
        // The chat is a multiframe bit that doesn't work too well with the student_view, so do not show the link
3611
        return '';
3612
    }
3613
3614
    // Uncomment to remove student view link from document view page
3615
    if (strpos($_SERVER['REQUEST_URI'], 'lp/lp_header.php') !== false) {
3616
        if (empty($_GET['lp_id'])) {
3617
            return '';
3618
        }
3619
        $sourceurl = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
3620
        $sourceurl = str_replace(
3621
            'lp/lp_header.php',
3622
            'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.intval($_GET['lp_id']).'&isStudentView='.($_SESSION['studentview'] == 'studentview' ? 'false' : 'true'),
3623
            $sourceurl
3624
        );
3625
        //showinframes doesn't handle student view anyway...
3626
        //return '';
3627
        $is_framed = true;
3628
    }
3629
3630
    // Check whether the $_SERVER['REQUEST_URI'] contains already url parameters (thus a questionmark)
3631
    if (!$is_framed) {
3632
        if (strpos($_SERVER['REQUEST_URI'], '?') === false) {
3633
            $sourceurl = api_get_self().'?'.api_get_cidreq();
3634
        } else {
3635
            $sourceurl = $_SERVER['REQUEST_URI'];
3636
        }
3637
    }
3638
3639
    $output_string = '';
3640
    if (!empty($_SESSION['studentview'])) {
3641
        if ($_SESSION['studentview'] == 'studentview') {
3642
            // We have to remove the isStudentView=true from the $sourceurl
3643
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3644
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3645
            $output_string .= '<a class="btn btn-primary btn-sm" href="'.$sourceurl.'&isStudentView=false" target="_self">'.
3646
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToTeacherView').'</a>';
3647
        } elseif ($_SESSION['studentview'] == 'teacherview') {
3648
            // Switching to teacherview
3649
            $sourceurl = str_replace('&isStudentView=true', '', $sourceurl);
3650
            $sourceurl = str_replace('&isStudentView=false', '', $sourceurl);
3651
            $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3652
                Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3653
        }
3654
    } else {
3655
        $output_string .= '<a class="btn btn-default btn-sm" href="'.$sourceurl.'&isStudentView=true" target="_self">'.
3656
            Display::returnFontAwesomeIcon('eye').' '.get_lang('SwitchToStudentView').'</a>';
3657
    }
3658
    $output_string = Security::remove_XSS($output_string);
3659
    $html = Display::tag('div', $output_string, ['class' => 'view-options']);
3660
3661
    return $html;
3662
}
3663
3664
// TODO: This is for the permission section.
3665
/**
3666
 * Function that removes the need to directly use is_courseAdmin global in
3667
 * tool scripts. It returns true or false depending on the user's rights in
3668
 * this particular course.
3669
 * Optionally checking for tutor and coach roles here allows us to use the
3670
 * student_view feature altogether with these roles as well.
3671
 *
3672
 * @param bool  Whether to check if the user has the tutor role
3673
 * @param bool  Whether to check if the user has the coach role
3674
 * @param bool  Whether to check if the user has the session coach role
3675
 * @param bool  check the student view or not
3676
 *
3677
 * @author Roan Embrechts
3678
 * @author Patrick Cool
3679
 * @author Julio Montoya
3680
 *
3681
 * @version 1.1, February 2004
3682
 *
3683
 * @return bool true: the user has the rights to edit, false: he does not
3684
 */
3685
function api_is_allowed_to_edit(
3686
    $tutor = false,
3687
    $coach = false,
3688
    $session_coach = false,
3689
    $check_student_view = true
3690
) {
3691
    $allowSessionAdminEdit = api_get_configuration_value('session_admins_edit_courses_content') === true;
3692
3693
    // Admins can edit anything.
3694
    if (api_is_platform_admin($allowSessionAdminEdit)) {
3695
        //The student preview was on
3696
        if ($check_student_view && api_is_student_view_active()) {
3697
            return false;
3698
        }
3699
3700
        return true;
3701
    }
3702
3703
    $sessionId = api_get_session_id();
3704
3705
    if ($sessionId && api_get_configuration_value('session_courses_read_only_mode')) {
3706
        $efv = new ExtraFieldValue('course');
3707
        $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
3708
            api_get_course_int_id(),
3709
            'session_courses_read_only_mode'
3710
        );
3711
3712
        if (!empty($lockExrafieldField['value'])) {
3713
            return false;
3714
        }
3715
    }
3716
3717
    $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
3718
    $session_visibility = api_get_session_visibility($sessionId);
3719
    $is_courseAdmin = api_is_course_admin();
3720
3721
    if (!$is_courseAdmin && $tutor) {
3722
        // If we also want to check if the user is a tutor...
3723
        $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
3724
    }
3725
3726
    if (!$is_courseAdmin && $coach) {
3727
        // If we also want to check if the user is a coach...';
3728
        // Check if session visibility is read only for coaches.
3729
        if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3730
            $is_allowed_coach_to_edit = false;
3731
        }
3732
3733
        if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3734
            // Check if coach is allowed to edit a course.
3735
            $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3736
        }
3737
    }
3738
3739
    if (!$is_courseAdmin && $session_coach) {
3740
        $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
3741
    }
3742
3743
    // Check if the student_view is enabled, and if so, if it is activated.
3744
    if (api_get_setting('student_view_enabled') == 'true') {
3745
        if (!empty($sessionId)) {
3746
            // Check if session visibility is read only for coaches.
3747
            if ($session_visibility == SESSION_VISIBLE_READ_ONLY) {
3748
                $is_allowed_coach_to_edit = false;
3749
            }
3750
3751
            if (api_get_setting('allow_coach_to_edit_course_session') == 'true') {
3752
                // Check if coach is allowed to edit a course.
3753
                $is_allowed = $is_allowed_coach_to_edit;
3754
            } else {
3755
                $is_allowed = false;
3756
            }
3757
            if ($check_student_view) {
3758
                $is_allowed = $is_allowed && $_SESSION['studentview'] != 'studentview';
3759
            }
3760
        } else {
3761
            if ($check_student_view) {
3762
                $is_allowed = $is_courseAdmin && $_SESSION['studentview'] != 'studentview';
3763
            } else {
3764
                $is_allowed = $is_courseAdmin;
3765
            }
3766
        }
3767
3768
        return $is_allowed;
3769
    } else {
3770
        return $is_courseAdmin;
3771
    }
3772
}
3773
3774
/**
3775
 * Returns true if user is a course coach of at least one course in session.
3776
 *
3777
 * @param int $sessionId
3778
 *
3779
 * @return bool
3780
 */
3781
function api_is_coach_of_course_in_session($sessionId)
3782
{
3783
    if (api_is_platform_admin()) {
3784
        return true;
3785
    }
3786
3787
    $userId = api_get_user_id();
3788
    $courseList = UserManager::get_courses_list_by_session(
3789
        $userId,
3790
        $sessionId
3791
    );
3792
3793
    // Session visibility.
3794
    $visibility = api_get_session_visibility(
3795
        $sessionId,
3796
        null,
3797
        false
3798
    );
3799
3800
    if ($visibility != SESSION_VISIBLE && !empty($courseList)) {
3801
        // Course Coach session visibility.
3802
        $blockedCourseCount = 0;
3803
        $closedVisibilityList = [
3804
            COURSE_VISIBILITY_CLOSED,
3805
            COURSE_VISIBILITY_HIDDEN,
3806
        ];
3807
3808
        foreach ($courseList as $course) {
3809
            // Checking session visibility
3810
            $sessionCourseVisibility = api_get_session_visibility(
3811
                $sessionId,
3812
                $course['real_id']
3813
            );
3814
3815
            $courseIsVisible = !in_array(
3816
                $course['visibility'],
3817
                $closedVisibilityList
3818
            );
3819
            if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3820
                $blockedCourseCount++;
3821
            }
3822
        }
3823
3824
        // If all courses are blocked then no show in the list.
3825
        if ($blockedCourseCount === count($courseList)) {
3826
            $visibility = SESSION_INVISIBLE;
3827
        } else {
3828
            $visibility = SESSION_VISIBLE;
3829
        }
3830
    }
3831
3832
    switch ($visibility) {
3833
        case SESSION_VISIBLE_READ_ONLY:
3834
        case SESSION_VISIBLE:
3835
        case SESSION_AVAILABLE:
3836
            return true;
3837
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
3838
        case SESSION_INVISIBLE:
3839
            return false;
3840
    }
3841
3842
    return false;
3843
}
3844
3845
/**
3846
 * Checks if a student can edit contents in a session depending
3847
 * on the session visibility.
3848
 *
3849
 * @param bool $tutor Whether to check if the user has the tutor role
3850
 * @param bool $coach Whether to check if the user has the coach role
3851
 *
3852
 * @return bool true: the user has the rights to edit, false: he does not
3853
 */
3854
function api_is_allowed_to_session_edit($tutor = false, $coach = false)
3855
{
3856
    if (api_is_allowed_to_edit($tutor, $coach)) {
3857
        // If I'm a teacher, I will return true in order to not affect the normal behaviour of Chamilo tools.
3858
        return true;
3859
    } else {
3860
        $sessionId = api_get_session_id();
3861
3862
        if (0 == $sessionId) {
3863
            // I'm not in a session so i will return true to not affect the normal behaviour of Chamilo tools.
3864
            return true;
3865
        } else {
3866
            // I'm in a session and I'm a student
3867
            // Get the session visibility
3868
            $session_visibility = api_get_session_visibility($sessionId);
3869
            // if 5 the session is still available
3870
            switch ($session_visibility) {
3871
                case SESSION_VISIBLE_READ_ONLY: // 1
3872
                    return false;
3873
                case SESSION_VISIBLE:           // 2
3874
                    return true;
3875
                case SESSION_INVISIBLE:         // 3
3876
                    return false;
3877
                case SESSION_AVAILABLE:         //5
3878
                    return true;
3879
            }
3880
        }
3881
    }
3882
3883
    return false;
3884
}
3885
3886
/**
3887
 * Checks whether the user is allowed in a specific tool for a specific action.
3888
 *
3889
 * @param string $tool   the tool we are checking if the user has a certain permission
3890
 * @param string $action the action we are checking (add, edit, delete, move, visibility)
3891
 *
3892
 * @return bool
3893
 *
3894
 * @author Patrick Cool <[email protected]>, Ghent University
3895
 * @author Julio Montoya
3896
 *
3897
 * @version 1.0
3898
 */
3899
function api_is_allowed($tool, $action, $task_id = 0)
3900
{
3901
    $_user = api_get_user_info();
3902
    $_course = api_get_course_info();
3903
3904
    if (api_is_course_admin()) {
3905
        return true;
3906
    }
3907
3908
    if (is_array($_course) and count($_course) > 0) {
3909
        require_once __DIR__.'/../../permissions/permissions_functions.inc.php';
3910
3911
        // Getting the permissions of this user.
3912
        if ($task_id == 0) {
3913
            $user_permissions = get_permissions('user', $_user['user_id']);
3914
            $_SESSION['total_permissions'][$_course['code']] = $user_permissions;
3915
        }
3916
3917
        // Getting the permissions of the task.
3918
        if ($task_id != 0) {
3919
            $task_permissions = get_permissions('task', $task_id);
3920
            $_SESSION['total_permissions'][$_course['code']] = $task_permissions;
3921
        }
3922
        //print_r($_SESSION['total_permissions']);
3923
3924
        // Getting the permissions of the groups of the user
3925
        //$groups_of_user = GroupManager::get_group_ids($_course['db_name'], $_user['user_id']);
3926
3927
        //foreach($groups_of_user as $group)
3928
        //   $this_group_permissions = get_permissions('group', $group);
3929
3930
        // Getting the permissions of the courseroles of the user
3931
        $user_courserole_permissions = get_roles_permissions('user', $_user['user_id']);
3932
3933
        // Getting the permissions of the platformroles of the user
3934
        //$user_platformrole_permissions = get_roles_permissions('user', $_user['user_id'], ', platform');
3935
3936
        // Getting the permissions of the roles of the groups of the user
3937
        //foreach($groups_of_user as $group)
3938
        //    $this_group_courserole_permissions = get_roles_permissions('group', $group);
3939
3940
        // Getting the permissions of the platformroles of the groups of the user
3941
        //foreach($groups_of_user as $group)
3942
        //    $this_group_platformrole_permissions = get_roles_permissions('group', $group, 'platform');
3943
    }
3944
3945
    // If the permissions are limited, we have to map the extended ones to the limited ones.
3946
    if (api_get_setting('permissions') == 'limited') {
3947
        if ($action == 'Visibility') {
3948
            $action = 'Edit';
3949
        }
3950
        if ($action == 'Move') {
3951
            $action = 'Edit';
3952
        }
3953
    }
3954
3955
    // The session that contains all the permissions already exists for this course
3956
    // so there is no need to requery everything.
3957
    //my_print_r($_SESSION['total_permissions'][$_course['code']][$tool]);
3958
    if (is_array($_SESSION['total_permissions'][$_course['code']][$tool])) {
3959
        if (in_array($action, $_SESSION['total_permissions'][$_course['code']][$tool])) {
3960
            return true;
3961
        } else {
3962
            return false;
3963
        }
3964
    }
3965
3966
    return false;
3967
}
3968
3969
/**
3970
 * Tells whether this user is an anonymous user.
3971
 *
3972
 * @param int  $user_id  User ID (optional, will take session ID if not provided)
3973
 * @param bool $db_check Whether to check in the database (true) or simply in
3974
 *                       the session (false) to see if the current user is the anonymous user
3975
 *
3976
 * @return bool true if this user is anonymous, false otherwise
3977
 */
3978
function api_is_anonymous($user_id = null, $db_check = false)
3979
{
3980
    if (!isset($user_id)) {
3981
        $user_id = api_get_user_id();
3982
    }
3983
3984
    if ($db_check) {
3985
        $info = api_get_user_info($user_id);
3986
        if (false === $info || $info['status'] == ANONYMOUS) {
3987
            return true;
3988
        }
3989
    }
3990
3991
    $_user = api_get_user_info();
3992
3993
    if (isset($_user['status']) && $_user['status'] == ANONYMOUS) {
3994
        //if ($_user['user_id'] == 0) {
3995
        // In some cases, api_set_anonymous doesn't seem to be triggered in local.inc.php. Make sure it is.
3996
        // Occurs in agenda for admin links - YW
3997
        // it occurs when pages are opened directly without entering first the course home page. To fix it add
3998
        // $use_anonymous = true;
3999
        // before including global.inc.php in the page
4000
        global $use_anonymous;
4001
        if (isset($use_anonymous) && $use_anonymous) {
4002
            api_set_anonymous();
4003
        }
4004
4005
        return true;
4006
    }
4007
4008
    return (isset($_user['is_anonymous']) && $_user['is_anonymous'] === true) || $_user === false;
4009
}
4010
4011
/**
4012
 * Displays message "You are not allowed here..." and exits the entire script.
4013
 *
4014
 * @param bool   $print_headers Whether or not to print headers (default = false -> does not print them)
4015
 * @param string $message
4016
 * @param int    $responseCode
4017
 */
4018
function api_not_allowed(
4019
    $print_headers = false,
4020
    $message = null,
4021
    $responseCode = 0
4022
) {
4023
    if (api_get_setting('sso_authentication') === 'true') {
4024
        global $osso;
4025
        if ($osso) {
4026
            $osso->logout();
4027
        }
4028
    }
4029
    $home_url = api_get_path(WEB_PATH);
4030
    $user_id = api_get_user_id();
4031
    $course = api_get_course_id();
4032
4033
    global $this_section;
4034
4035
    if (CustomPages::enabled() && (empty($user_id) || api_is_anonymous())) {
4036
        // Why the CustomPages::enabled() need to be to set the request_uri
4037
        $_SESSION['request_uri'] = $_SERVER['REQUEST_URI'];
4038
        CustomPages::display(CustomPages::INDEX_UNLOGGED);
4039
    }
4040
4041
    $origin = api_get_origin();
4042
4043
    $msg = null;
4044
    if (isset($message)) {
4045
        $msg = $message;
4046
    } else {
4047
        $msg = Display::return_message(
4048
            get_lang('NotAllowedClickBack').'
4049
            <script>function goBack(){window.history.back();}</script>',
4050
            'error',
4051
            false
4052
        );
4053
        $msg .= '<p class="text-center">
4054
             <a onclick="goBack();" class="btn btn-default" href="'.$home_url.'">'.get_lang('GoBack').'</a>
4055
             </p>';
4056
    }
4057
4058
    $msg = Display::div($msg, ['align' => 'center']);
4059
4060
    $show_headers = 0;
4061
    if ($print_headers && $origin != 'learnpath') {
4062
        $show_headers = 1;
4063
    }
4064
4065
    $hideBreadCrumb = false;
4066
    if (api_get_configuration_value('hide_breadcrumb_if_not_allowed')) {
4067
        $hideBreadCrumb = true;
4068
    }
4069
4070
    $tpl = new Template(null, $show_headers, $show_headers, $hideBreadCrumb, true, false, true, $responseCode);
4071
    $tpl->assign('hide_login_link', 1);
4072
    $tpl->assign('content', $msg);
4073
4074
    if (($user_id != 0 && !api_is_anonymous()) &&
4075
        (!isset($course) || $course == -1) &&
4076
        empty($_GET['cidReq'])
4077
    ) {
4078
        // if the access is not authorized and there is some login information
4079
        // but the cidReq is not found, assume we are missing course data and send the user
4080
        // to the user_portal
4081
        $tpl->display_one_col_template();
4082
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
4083
    }
4084
4085
    $tplPlugin = new AppPlugin();
4086
    $loginTopRegionContent = $tplPlugin->load_region('login_top', $tpl, true);
4087
    $loginBottomRegionContent = $tplPlugin->load_region('login_bottom', $tpl, true);
4088
4089
    if (!empty($_SERVER['REQUEST_URI']) &&
4090
        (
4091
            !empty($_GET['cidReq']) ||
4092
            $this_section == SECTION_MYPROFILE ||
4093
            $this_section == SECTION_PLATFORM_ADMIN
4094
        )
4095
    ) {
4096
        $courseCode = api_get_course_id();
4097
        // Only display form and return to the previous URL if there was a course ID included
4098
        if ($user_id != 0 && !api_is_anonymous()) {
4099
            //if there is a user ID, then the user is not allowed but the session is still there. Say so and exit
4100
            $tpl->assign('content', $msg);
4101
            $tpl->display_one_col_template();
4102
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
4103
        }
4104
4105
        if (!is_null($courseCode)) {
4106
            api_set_firstpage_parameter($courseCode);
4107
        }
4108
4109
        // If the user has no user ID, then his session has expired
4110
        $form = api_get_not_allowed_login_form();
4111
4112
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (above)
4113
        $content = Display::return_message(get_lang('NotAllowed'), 'error', false);
4114
4115
        if (!empty($courseCode)) {
4116
            $content .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
4117
        }
4118
4119
        if (api_is_cas_activated()) {
4120
            $content .= Display::return_message(sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")), '', false);
4121
            $content .= Display::div(
4122
                Template::displayCASLoginButton(),
4123
                ['align' => 'center']
4124
            );
4125
            $content .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4126
            $content .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4127
            $content .= "<div style='display:none;'>";
4128
        }
4129
        $content .= PHP_EOL.$loginTopRegionContent;
4130
        $content .= '<div class="well">';
4131
        $content .= $form->returnForm();
4132
        $content .= '</div>';
4133
        $content .= PHP_EOL.$loginBottomRegionContent;
4134
        if (api_is_cas_activated()) {
4135
            $content .= "</div>";
4136
        }
4137
4138
        if (!empty($courseCode)) {
4139
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4140
                get_lang('ReturnToCourseHomepage').'</a></p>';
4141
        } else {
4142
            $content .= '<hr/><p style="text-align:center"><a href="'.$home_url.'">'.
4143
                get_lang('BackHome').'</a></p>';
4144
        }
4145
4146
        $tpl->setLoginBodyClass();
4147
        $tpl->assign('content', $content);
4148
        $tpl->display_one_col_template();
4149
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
4150
    }
4151
4152
    if ($user_id != 0 && !api_is_anonymous()) {
4153
        $tpl->display_one_col_template();
4154
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
4155
    }
4156
4157
    $msg = null;
4158
    // The session is over and we were not in a course,
4159
    // or we try to get directly to a private course without being logged
4160
    $courseId = api_get_course_int_id();
4161
    if (!empty($courseId)) {
4162
        api_set_firstpage_parameter(api_get_course_id());
4163
        $tpl->setLoginBodyClass();
4164
4165
        // see same text in auth/gotocourse.php and main_api.lib.php function api_not_allowed (bellow)
4166
        $msg = Display::return_message(get_lang('NotAllowed'), 'error', false);
4167
        $msg .= '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>';
4168
        $casEnabled = api_is_cas_activated();
4169
        if ($casEnabled) {
4170
            $msg .= Display::return_message(
4171
                sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")),
4172
                '',
4173
                false
4174
            );
4175
            $msg .= Display::div(
4176
                Template::displayCASLoginButton(),
4177
                ['align' => 'center']
4178
            );
4179
            $msg .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
4180
            $msg .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
4181
            $msg .= "<div style='display:none;'>";
4182
        }
4183
        $form = api_get_not_allowed_login_form();
4184
        $msg .= PHP_EOL.$loginTopRegionContent;
4185
        $msg .= '<div class="well">';
4186
        $msg .= $form->returnForm();
4187
        $msg .= '</div>';
4188
        $msg .= PHP_EOL.$loginBottomRegionContent;
4189
        if ($casEnabled) {
4190
            $msg .= "</div>";
4191
        }
4192
    } else {
4193
        // we were not in a course, return to home page
4194
        $msg = Display::return_message(
4195
            get_lang('NotAllowed'),
4196
            'error',
4197
            false
4198
        );
4199
4200
        $msg .= '<p class="text-center">
4201
                 <a class="btn btn-default" href="'.$home_url.'">'.get_lang('BackHome').'</a>
4202
                 </p>';
4203
4204
        if (!empty($message)) {
4205
            $msg = $message;
4206
        }
4207
4208
        if (api_is_anonymous()) {
4209
            $form = api_get_not_allowed_login_form();
4210
            $msg .= PHP_EOL.$loginTopRegionContent;
4211
            $msg .= '<div class="well">';
4212
            $msg .= $form->returnForm();
4213
            $msg .= '</div>';
4214
            $msg .= PHP_EOL.$loginBottomRegionContent;
4215
        }
4216
    }
4217
4218
    $tpl->assign('content', $msg);
4219
    $tpl->display_one_col_template();
4220
    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
4221
}
4222
4223
/**
4224
 * @return FormValidator
4225
 */
4226
function api_get_not_allowed_login_form()
4227
{
4228
    $action = api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']);
4229
    $action = str_replace('&amp;', '&', $action);
4230
    Session::write('redirect_after_not_allow_page', $action);
4231
    $action .= '&redirect_after_not_allow_page=1';
4232
4233
    $form = new FormValidator(
4234
        'formLogin',
4235
        'post',
4236
        $action,
4237
        null,
4238
        ['class' => 'form-stacked']
4239
    );
4240
    $params = [
4241
        'placeholder' => get_lang('UserName'),
4242
        'class' => 'col-md-3',
4243
    ];
4244
    if (api_browser_support('autocapitalize')) {
4245
        $params['autocapitalize'] = 'none';
4246
    }
4247
4248
    $form->addElement(
4249
        'text',
4250
        'login',
4251
        null,
4252
        $params
4253
    );
4254
    $form->addElement(
4255
        'password',
4256
        'password',
4257
        null,
4258
        ['placeholder' => get_lang('Password'), 'class' => 'col-md-3']
4259
    ); //new
4260
    $form->addButtonNext(get_lang('LoginEnter'), 'submitAuth');
4261
4262
    return $form;
4263
}
4264
4265
/**
4266
 * Gets a UNIX timestamp from a database (MySQL) datetime format string.
4267
 *
4268
 * @param string $last_post_datetime standard output date in a sql query
4269
 *
4270
 * @return int timestamp
4271
 *
4272
 * @author Toon Van Hoecke <[email protected]>
4273
 *
4274
 * @version October 2003
4275
 * @desc convert sql date to unix timestamp
4276
 */
4277
function convert_sql_date($last_post_datetime)
4278
{
4279
    list($last_post_date, $last_post_time) = explode(' ', $last_post_datetime);
4280
    list($year, $month, $day) = explode('-', $last_post_date);
4281
    list($hour, $min, $sec) = explode(':', $last_post_time);
4282
4283
    return gmmktime((int) $hour, (int) $min, (int) $sec, (int) $month, (int) $day, (int) $year);
4284
}
4285
4286
/**
4287
 * Gets item visibility from the item_property table.
4288
 *
4289
 * Getting the visibility is done by getting the last updated visibility entry,
4290
 * using the largest session ID found if session 0 and another was found (meaning
4291
 * the only one that is actually from the session, in case there are results from
4292
 * session 0 *AND* session n).
4293
 *
4294
 * @param array  $_course  Course properties array (result of api_get_course_info())
4295
 * @param string $tool     Tool (learnpath, document, etc)
4296
 * @param int    $id       The item ID in the given tool
4297
 * @param int    $session  The session ID (optional)
4298
 * @param int    $user_id
4299
 * @param string $type
4300
 * @param string $group_id
4301
 *
4302
 * @return int -1 on error, 0 if invisible, 1 if visible
4303
 */
4304
function api_get_item_visibility(
4305
    $_course,
4306
    $tool,
4307
    $id,
4308
    $session = 0,
4309
    $user_id = null,
4310
    $type = null,
4311
    $group_id = null
4312
) {
4313
    if (!is_array($_course) || count($_course) == 0 || empty($tool) || empty($id)) {
4314
        return -1;
4315
    }
4316
4317
    $tool = Database::escape_string($tool);
4318
    $id = (int) $id;
4319
    $session = (int) $session;
4320
    $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
4321
    $course_id = (int) $_course['real_id'];
4322
4323
    $userCondition = '';
4324
    if (!empty($user_id)) {
4325
        $user_id = (int) $user_id;
4326
        $userCondition = " AND to_user_id = $user_id ";
4327
    }
4328
4329
    $typeCondition = '';
4330
    if (!empty($type)) {
4331
        $type = Database::escape_string($type);
4332
        $typeCondition = " AND lastedit_type = '$type' ";
4333
    }
4334
4335
    $groupCondition = '';
4336
    if (!empty($group_id)) {
4337
        $group_id = (int) $group_id;
4338
        $groupCondition = " AND to_group_id = '$group_id' ";
4339
    }
4340
4341
    $lpVisibilityCondition = '';
4342
    if ($tool === 'learnpath') {
4343
        $lpVisibilityCondition = " AND lastedit_type != 'LearnpathSubscription' ";
4344
    }
4345
4346
    $sql = "SELECT visibility
4347
            FROM $TABLE_ITEMPROPERTY
4348
            WHERE
4349
                c_id = $course_id AND
4350
                tool = '$tool' AND
4351
                ref = $id AND
4352
                (session_id = $session OR session_id = 0 OR session_id IS NULL)
4353
                $userCondition $typeCondition $groupCondition $lpVisibilityCondition
4354
            ORDER BY session_id DESC, lastedit_date DESC
4355
            LIMIT 1";
4356
4357
    $res = Database::query($sql);
4358
    if ($res === false || Database::num_rows($res) == 0) {
4359
        return -1;
4360
    }
4361
    $row = Database::fetch_array($res);
4362
4363
    return (int) $row['visibility'];
4364
}
4365
4366
/**
4367
 * Delete a row in the c_item_property table.
4368
 *
4369
 * @param array  $courseInfo
4370
 * @param string $tool
4371
 * @param int    $itemId
4372
 * @param int    $userId
4373
 * @param int    $groupId    group.iid
4374
 * @param int    $sessionId
4375
 *
4376
 * @return false|null
4377
 */
4378
function api_item_property_delete(
4379
    $courseInfo,
4380
    $tool,
4381
    $itemId,
4382
    $userId,
4383
    $groupId = 0,
4384
    $sessionId = 0
4385
) {
4386
    if (empty($courseInfo)) {
4387
        return false;
4388
    }
4389
4390
    $courseId = (int) $courseInfo['real_id'];
4391
4392
    if (empty($courseId) || empty($tool) || empty($itemId)) {
4393
        return false;
4394
    }
4395
4396
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4397
    $tool = Database::escape_string($tool);
4398
    $itemId = intval($itemId);
4399
    $userId = intval($userId);
4400
    $groupId = intval($groupId);
4401
    $sessionId = intval($sessionId);
4402
4403
    $groupCondition = " AND to_group_id = $groupId ";
4404
    if (empty($groupId)) {
4405
        $groupCondition = " AND (to_group_id is NULL OR to_group_id = 0) ";
4406
    }
4407
4408
    $userCondition = " AND to_user_id = $userId ";
4409
    if (empty($userId)) {
4410
        $userCondition = " AND (to_user_id is NULL OR to_user_id = 0) ";
4411
    }
4412
    $sessionCondition = api_get_session_condition($sessionId, true, false, 'session_id');
4413
    $sql = "DELETE FROM $table
4414
            WHERE
4415
                c_id = $courseId AND
4416
                tool  = '$tool' AND
4417
                ref = $itemId
4418
                $sessionCondition
4419
                $userCondition
4420
                $groupCondition
4421
            ";
4422
4423
    Database::query($sql);
4424
}
4425
4426
/**
4427
 * Updates or adds item properties to the Item_propetry table
4428
 * Tool and lastedit_type are language independant strings (langvars->get_lang!).
4429
 *
4430
 * @param array  $_course        array with course properties
4431
 * @param string $tool           tool id, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4432
 * @param int    $item_id        id of the item itself, linked to key of every tool ('id', ...)
4433
 * @param string $last_edit_type add or update action
4434
 *                               (1) message to be translated (in trad4all) : e.g. DocumentAdded, DocumentUpdated;
4435
 *                               (2) "delete"
4436
 *                               (3) "visible"
4437
 *                               (4) "invisible"
4438
 * @param int    $user_id        id of the editing/adding user
4439
 * @param array  $groupInfo      must include group.iid/group.od
4440
 * @param int    $to_user_id     id of the intended user (always has priority over $to_group_id !), only relevant for $type (1)
4441
 * @param string $start_visible  0000-00-00 00:00:00 format
4442
 * @param string $end_visible    0000-00-00 00:00:00 format
4443
 * @param int    $session_id     The session ID, if any, otherwise will default to 0
4444
 *
4445
 * @return bool false if update fails
4446
 *
4447
 * @author Toon Van Hoecke <[email protected]>, Ghent University
4448
 *
4449
 * @version January 2005
4450
 * @desc update the item_properties table (if entry not exists, insert) of the course
4451
 */
4452
function api_item_property_update(
4453
    $_course,
4454
    $tool,
4455
    $item_id,
4456
    $last_edit_type,
4457
    $user_id,
4458
    $groupInfo = [],
4459
    $to_user_id = null,
4460
    $start_visible = '',
4461
    $end_visible = '',
4462
    $session_id = 0
4463
) {
4464
    if (empty($_course)) {
4465
        return false;
4466
    }
4467
4468
    $course_id = $_course['real_id'];
4469
4470
    if (empty($course_id)) {
4471
        return false;
4472
    }
4473
4474
    $to_group_id = 0;
4475
    if (!empty($groupInfo) && isset($groupInfo['iid'])) {
4476
        $to_group_id = (int) $groupInfo['iid'];
4477
    }
4478
4479
    $em = Database::getManager();
4480
4481
    // Definition of variables.
4482
    $tool = Database::escape_string($tool);
4483
    $item_id = (int) $item_id;
4484
    $lastEditTypeNoFilter = $last_edit_type;
4485
    $last_edit_type = Database::escape_string($last_edit_type);
4486
    $user_id = (int) $user_id;
4487
4488
    $startVisible = "NULL";
4489
    if (!empty($start_visible)) {
4490
        $start_visible = Database::escape_string($start_visible);
4491
        $startVisible = "'$start_visible'";
4492
    }
4493
4494
    $endVisible = "NULL";
4495
    if (!empty($end_visible)) {
4496
        $end_visible = Database::escape_string($end_visible);
4497
        $endVisible = "'$end_visible'";
4498
    }
4499
4500
    $to_filter = '';
4501
    $time = api_get_utc_datetime();
4502
4503
    if (!empty($session_id)) {
4504
        $session_id = (int) $session_id;
4505
    } else {
4506
        $session_id = api_get_session_id();
4507
    }
4508
4509
    // Definition of tables.
4510
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4511
4512
    if ($to_user_id <= 0) {
4513
        $to_user_id = null; // No to_user_id set
4514
    }
4515
4516
    if (!is_null($to_user_id)) {
4517
        // $to_user_id has more priority than $to_group_id
4518
        $to_user_id = (int) $to_user_id;
4519
        $to_field = 'to_user_id';
4520
        $to_value = $to_user_id;
4521
    } else {
4522
        // $to_user_id is not set.
4523
        $to_field = 'to_group_id';
4524
        $to_value = $to_group_id;
4525
    }
4526
4527
    $toValueCondition = empty($to_value) ? 'NULL' : "'$to_value'";
4528
    // Set filters for $to_user_id and $to_group_id, with priority for $to_user_id
4529
    $condition_session = " AND session_id = $session_id ";
4530
    if (empty($session_id)) {
4531
        $condition_session = ' AND (session_id = 0 OR session_id IS NULL) ';
4532
    }
4533
4534
    $filter = " c_id = $course_id AND tool = '$tool' AND ref = $item_id $condition_session ";
4535
4536
    // Check whether $to_user_id and $to_group_id are passed in the function call.
4537
    // If both are not passed (both are null) then it is a message for everybody and $to_group_id should be 0 !
4538
    if (is_null($to_user_id) && is_null($to_group_id)) {
4539
        $to_group_id = 0;
4540
    }
4541
4542
    if (!is_null($to_user_id)) {
4543
        // Set filter to intended user.
4544
        $to_filter = " AND to_user_id = $to_user_id $condition_session";
4545
    } else {
4546
        // Set filter to intended group.
4547
        if (($to_group_id != 0) && $to_group_id == strval(intval($to_group_id))) {
4548
            $to_filter = " AND to_group_id = $to_group_id $condition_session";
4549
        }
4550
    }
4551
4552
    // Adding filter if set.
4553
    $filter .= $to_filter;
4554
4555
    // Update if possible
4556
    $set_type = '';
4557
4558
    switch ($lastEditTypeNoFilter) {
4559
        case 'delete':
4560
            // delete = make item only visible for the platform admin.
4561
            $visibility = '2';
4562
            if (!empty($session_id)) {
4563
                // Check whether session id already exist into item_properties for updating visibility or add it.
4564
                $sql = "SELECT session_id FROM $tableItemProperty
4565
                        WHERE
4566
                            c_id = $course_id AND
4567
                            tool = '$tool' AND
4568
                            ref = $item_id AND
4569
                            session_id = $session_id";
4570
                $rs = Database::query($sql);
4571
                if (Database::num_rows($rs) > 0) {
4572
                    $sql = "UPDATE $tableItemProperty
4573
                            SET lastedit_type       = '".str_replace('_', '', ucwords($tool))."Deleted',
4574
                                lastedit_date       = '$time',
4575
                                lastedit_user_id    = $user_id,
4576
                                visibility          = $visibility,
4577
                                session_id          = $session_id $set_type
4578
                            WHERE $filter";
4579
                    $result = Database::query($sql);
4580
                } else {
4581
                    $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)
4582
                            VALUES ($course_id, '$tool',$item_id, '$time', $user_id, '$time', '$last_edit_type',$user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4583
                    $result = Database::query($sql);
4584
                    $id = Database::insert_id();
4585
                    if ($id) {
4586
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4587
                        Database::query($sql);
4588
                    }
4589
                }
4590
            } else {
4591
                $sql = "UPDATE $tableItemProperty
4592
                        SET
4593
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Deleted',
4594
                            lastedit_date='$time',
4595
                            lastedit_user_id = $user_id,
4596
                            visibility = $visibility $set_type
4597
                        WHERE $filter";
4598
                $result = Database::query($sql);
4599
            }
4600
            break;
4601
        case 'visible': // Change item to visible.
4602
            $visibility = '1';
4603
            if (!empty($session_id)) {
4604
                // Check whether session id already exist into item_properties for updating visibility or add it.
4605
                $sql = "SELECT session_id FROM $tableItemProperty
4606
                        WHERE
4607
                            c_id = $course_id AND
4608
                            tool = '$tool' AND
4609
                            ref = $item_id AND
4610
                            session_id = $session_id";
4611
                $rs = Database::query($sql);
4612
                if (Database::num_rows($rs) > 0) {
4613
                    $sql = "UPDATE $tableItemProperty
4614
                            SET
4615
                                lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4616
                                lastedit_date='$time',
4617
                                lastedit_user_id = $user_id,
4618
                                visibility = $visibility,
4619
                                session_id = $session_id $set_type
4620
                            WHERE $filter";
4621
                    $result = Database::query($sql);
4622
                } else {
4623
                    $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)
4624
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4625
                    $result = Database::query($sql);
4626
                    $id = Database::insert_id();
4627
                    if ($id) {
4628
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4629
                        Database::query($sql);
4630
                    }
4631
                }
4632
            } else {
4633
                $sql = "UPDATE $tableItemProperty
4634
                        SET
4635
                            lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
4636
                            lastedit_date='$time',
4637
                            lastedit_user_id = $user_id,
4638
                            visibility = $visibility $set_type
4639
                        WHERE $filter";
4640
                $result = Database::query($sql);
4641
            }
4642
            break;
4643
        case 'invisible': // Change item to invisible.
4644
            $visibility = '0';
4645
            if (!empty($session_id)) {
4646
                // Check whether session id already exist into item_properties for updating visibility or add it
4647
                $sql = "SELECT session_id FROM $tableItemProperty
4648
                        WHERE
4649
                            c_id = $course_id AND
4650
                            tool = '$tool' AND
4651
                            ref = $item_id AND
4652
                            session_id = $session_id";
4653
                $rs = Database::query($sql);
4654
                if (Database::num_rows($rs) > 0) {
4655
                    $sql = "UPDATE $tableItemProperty
4656
                            SET
4657
                                lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4658
                                lastedit_date = '$time',
4659
                                lastedit_user_id = $user_id,
4660
                                visibility = $visibility,
4661
                                session_id = $session_id $set_type
4662
                            WHERE $filter";
4663
                    $result = Database::query($sql);
4664
                } else {
4665
                    $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)
4666
                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
4667
                    $result = Database::query($sql);
4668
                    $id = Database::insert_id();
4669
                    if ($id) {
4670
                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
4671
                        Database::query($sql);
4672
                    }
4673
                }
4674
            } else {
4675
                $sql = "UPDATE $tableItemProperty
4676
                        SET
4677
                            lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
4678
                            lastedit_date = '$time',
4679
                            lastedit_user_id = $user_id,
4680
                            visibility = $visibility $set_type
4681
                        WHERE $filter";
4682
                $result = Database::query($sql);
4683
            }
4684
            break;
4685
        default: // The item will be added or updated.
4686
            $set_type = ", lastedit_type = '$last_edit_type' ";
4687
            $visibility = '1';
4688
            //$filter .= $to_filter; already added
4689
            $sql = "UPDATE $tableItemProperty
4690
                    SET
4691
                      lastedit_date = '$time',
4692
                      lastedit_user_id = $user_id $set_type
4693
                    WHERE $filter";
4694
            $result = Database::query($sql);
4695
    }
4696
4697
    // Insert if no entries are found (can only happen in case of $last_edit_type switch is 'default').
4698
    if ($result == false || Database::affected_rows($result) == 0) {
4699
        $objCourse = $em->find('ChamiloCoreBundle:Course', intval($course_id));
4700
        $objTime = new DateTime('now', new DateTimeZone('UTC'));
4701
        $objUser = api_get_user_entity($user_id);
4702
        if (empty($objUser)) {
4703
            // Use anonymous
4704
            $user_id = api_get_anonymous_id();
4705
            $objUser = api_get_user_entity($user_id);
4706
        }
4707
4708
        $objGroup = null;
4709
        if (!empty($to_group_id)) {
4710
            $objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', $to_group_id);
4711
        }
4712
4713
        $objToUser = api_get_user_entity($to_user_id);
4714
        $objSession = $em->find('ChamiloCoreBundle:Session', intval($session_id));
4715
4716
        $startVisibleDate = !empty($start_visible) ? new DateTime($start_visible, new DateTimeZone('UTC')) : null;
4717
        $endVisibleDate = !empty($endVisibleDate) ? new DateTime($endVisibleDate, new DateTimeZone('UTC')) : null;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $endVisibleDate seems to never exist and therefore empty should always be true.
Loading history...
4718
4719
        $cItemProperty = new CItemProperty($objCourse);
4720
        $cItemProperty
4721
            ->setTool($tool)
4722
            ->setRef($item_id)
4723
            ->setInsertDate($objTime)
4724
            ->setInsertUser($objUser)
4725
            ->setLasteditDate($objTime)
4726
            ->setLasteditType($last_edit_type)
4727
            ->setGroup($objGroup)
4728
            ->setToUser($objToUser)
4729
            ->setVisibility($visibility)
4730
            ->setStartVisible($startVisibleDate)
4731
            ->setEndVisible($endVisibleDate)
4732
            ->setSession($objSession);
4733
4734
        $em->persist($cItemProperty);
4735
        $em->flush();
4736
4737
        $id = $cItemProperty->getIid();
4738
4739
        if ($id) {
4740
            $cItemProperty->setId($id);
4741
            $em->merge($cItemProperty);
4742
            $em->flush();
4743
4744
            return false;
4745
        }
4746
    }
4747
4748
    return true;
4749
}
4750
4751
/**
4752
 * Gets item property by tool.
4753
 *
4754
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4755
 * @param string $course_code
4756
 * @param int    $session_id
4757
 *
4758
 * @return array All fields from c_item_property (all rows found) or empty array
4759
 */
4760
function api_get_item_property_by_tool($tool, $course_code, $session_id = null)
4761
{
4762
    $course_info = api_get_course_info($course_code);
4763
    $tool = Database::escape_string($tool);
4764
4765
    // Definition of tables.
4766
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4767
    $session_id = (int) $session_id;
4768
    $session_condition = ' AND session_id = '.$session_id;
4769
    if (empty($session_id)) {
4770
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4771
    }
4772
    $course_id = $course_info['real_id'];
4773
4774
    $sql = "SELECT * FROM $item_property_table
4775
            WHERE
4776
                c_id = $course_id AND
4777
                tool = '$tool'
4778
                $session_condition ";
4779
    $rs = Database::query($sql);
4780
    $list = [];
4781
    if (Database::num_rows($rs) > 0) {
4782
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4783
            $list[] = $row;
4784
        }
4785
    }
4786
4787
    return $list;
4788
}
4789
4790
/**
4791
 * Gets item property by tool and user.
4792
 *
4793
 * @param int $userId
4794
 * @param int $tool
4795
 * @param int $courseId
4796
 * @param int $session_id
4797
 *
4798
 * @return array
4799
 */
4800
function api_get_item_property_list_by_tool_by_user(
4801
    $userId,
4802
    $tool,
4803
    $courseId,
4804
    $session_id = 0
4805
) {
4806
    $userId = intval($userId);
4807
    $tool = Database::escape_string($tool);
4808
    $session_id = intval($session_id);
4809
    $courseId = intval($courseId);
4810
4811
    // Definition of tables.
4812
    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4813
    $session_condition = ' AND session_id = '.$session_id;
4814
    if (empty($session_id)) {
4815
        $session_condition = " AND (session_id = 0 OR session_id IS NULL) ";
4816
    }
4817
    $sql = "SELECT * FROM $item_property_table
4818
            WHERE
4819
                insert_user_id = $userId AND
4820
                c_id = $courseId AND
4821
                tool = '$tool'
4822
                $session_condition ";
4823
4824
    $rs = Database::query($sql);
4825
    $list = [];
4826
    if (Database::num_rows($rs) > 0) {
4827
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4828
            $list[] = $row;
4829
        }
4830
    }
4831
4832
    return $list;
4833
}
4834
4835
/**
4836
 * Gets item property id from tool of a course.
4837
 *
4838
 * @param string $course_code course code
4839
 * @param string $tool        tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4840
 * @param int    $ref         id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4841
 * @param int    $sessionId   Session ID (optional)
4842
 *
4843
 * @return int
4844
 */
4845
function api_get_item_property_id($course_code, $tool, $ref, $sessionId = 0)
4846
{
4847
    $course_info = api_get_course_info($course_code);
4848
    $tool = Database::escape_string($tool);
4849
    $ref = (int) $ref;
4850
4851
    // Definition of tables.
4852
    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4853
    $course_id = $course_info['real_id'];
4854
    $sessionId = (int) $sessionId;
4855
    $sessionCondition = " AND session_id = $sessionId ";
4856
    if (empty($sessionId)) {
4857
        $sessionCondition = ' AND (session_id = 0 OR session_id IS NULL) ';
4858
    }
4859
    $sql = "SELECT id FROM $tableItemProperty
4860
            WHERE
4861
                c_id = $course_id AND
4862
                tool = '$tool' AND
4863
                ref = $ref
4864
                $sessionCondition";
4865
    $rs = Database::query($sql);
4866
    $item_property_id = '';
4867
    if (Database::num_rows($rs) > 0) {
4868
        $row = Database::fetch_array($rs);
4869
        $item_property_id = $row['id'];
4870
    }
4871
4872
    return $item_property_id;
4873
}
4874
4875
/**
4876
 * Inserts a record in the track_e_item_property table (No update).
4877
 *
4878
 * @param string $tool
4879
 * @param int    $ref
4880
 * @param string $title
4881
 * @param string $content
4882
 * @param int    $progress
4883
 *
4884
 * @return bool|int
4885
 */
4886
function api_track_item_property_update($tool, $ref, $title, $content, $progress)
4887
{
4888
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4889
    $course_id = api_get_course_int_id(); //numeric
4890
    $course_code = api_get_course_id(); //alphanumeric
4891
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4892
    if (!empty($item_property_id)) {
4893
        $sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
4894
                course_id           = '$course_id',
4895
                item_property_id    = '$item_property_id',
4896
                title               = '".Database::escape_string($title)."',
4897
                content             = '".Database::escape_string($content)."',
4898
                progress            = '".intval($progress)."',
4899
                lastedit_date       = '".api_get_utc_datetime()."',
4900
                lastedit_user_id    = '".api_get_user_id()."',
4901
                session_id          = '".api_get_session_id()."'";
4902
        $result = Database::query($sql);
4903
        $affected_rows = Database::affected_rows($result);
4904
4905
        return $affected_rows;
4906
    }
4907
4908
    return false;
4909
}
4910
4911
/**
4912
 * @param string $tool
4913
 * @param int    $ref
4914
 *
4915
 * @return array|resource
4916
 */
4917
function api_get_track_item_property_history($tool, $ref)
4918
{
4919
    $tbl_stats_item_property = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
4920
    $course_id = api_get_course_int_id(); //numeric
4921
    $course_code = api_get_course_id(); //alphanumeric
4922
    $item_property_id = api_get_item_property_id($course_code, $tool, $ref);
4923
    $sql = "SELECT * FROM $tbl_stats_item_property
4924
            WHERE item_property_id = $item_property_id AND course_id = $course_id
4925
            ORDER BY lastedit_date DESC";
4926
    $result = Database::query($sql);
4927
    if ($result === false or $result === null) {
4928
        $result = [];
4929
    } else {
4930
        $result = Database::store_result($result, 'ASSOC');
4931
    }
4932
4933
    return $result;
4934
}
4935
4936
/**
4937
 * Gets item property data from tool of a course id.
4938
 *
4939
 * @param int    $course_id
4940
 * @param string $tool       tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4941
 * @param int    $ref        id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4942
 * @param int    $session_id
4943
 * @param int    $groupId
4944
 *
4945
 * @return array with all fields from c_item_property, empty array if not found or false if course could not be found
4946
 */
4947
function api_get_item_property_info($course_id, $tool, $ref, $session_id = 0, $groupId = 0)
4948
{
4949
    $courseInfo = api_get_course_info_by_id($course_id);
4950
4951
    if (empty($courseInfo)) {
4952
        return false;
4953
    }
4954
4955
    $tool = Database::escape_string($tool);
4956
    $course_id = $courseInfo['real_id'];
4957
    $ref = (int) $ref;
4958
    $session_id = (int) $session_id;
4959
4960
    $sessionCondition = " session_id = $session_id";
4961
    if (empty($session_id)) {
4962
        $sessionCondition = ' (session_id = 0 OR session_id IS NULL) ';
4963
    }
4964
4965
    // Definition of tables.
4966
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
4967
4968
    $sql = "SELECT * FROM $table
4969
            WHERE
4970
                c_id = $course_id AND
4971
                tool = '$tool' AND
4972
                ref = $ref AND
4973
                $sessionCondition ";
4974
4975
    if (!empty($groupId)) {
4976
        $groupId = (int) $groupId;
4977
        $sql .= " AND to_group_id = $groupId ";
4978
    }
4979
4980
    $rs = Database::query($sql);
4981
    $row = [];
4982
    if (Database::num_rows($rs) > 0) {
4983
        $row = Database::fetch_array($rs, 'ASSOC');
4984
    }
4985
4986
    return $row;
4987
}
4988
4989
/**
4990
 * Gets the last item property data from tool of a course id, in chronological order.
4991
 *
4992
 * @param string $tool      tool name, linked to 'rubrique' of the course tool_list (Warning: language sensitive !!)
4993
 * @param int    $ref       id of the item itself, linked to key of every tool ('id', ...), "*" = all items of the tool
4994
 * @param int    $sessionId
4995
 * @param int    $groupId
4996
 *
4997
 * @return array with all fields from c_item_property, empty array if not found or false if course could not be found
4998
 */
4999
function api_get_last_item_property_info(int $courseId, string $tool, int $ref, int $sessionId = null, int $groupId = null): array
5000
{
5001
    $tool = Database::escape_string($tool);
5002
    // Definition of tables.
5003
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
5004
    $sessionCondition = " session_id = $sessionId";
5005
    if (empty($sessionId)) {
5006
        $sessionCondition = ' (session_id = 0 OR session_id IS NULL) ';
5007
    }
5008
5009
    $sql = "SELECT * FROM $table
5010
            WHERE
5011
                c_id = $courseId AND
5012
                tool = '$tool' AND
5013
                ref = $ref AND
5014
                $sessionCondition ";
5015
5016
    if (!empty($groupId)) {
5017
        $sql .= " AND to_group_id = $groupId ";
5018
    }
5019
    // Add criteria to only get the last one
5020
    $sql .= "ORDER BY lastedit_date DESC LIMIT 1";
5021
    $rs = Database::query($sql);
5022
    $row = [];
5023
    if (Database::num_rows($rs) > 0) {
5024
        $row = Database::fetch_array($rs, 'ASSOC');
5025
    }
5026
5027
    return $row;
5028
}
5029
5030
/**
5031
 * Displays a combo box so the user can select his/her preferred language.
5032
 *
5033
 * @param string The desired name= value for the select
5034
 * @param bool Whether we use the JQuery Chozen library or not
5035
 * (in some cases, like the indexing language picker, it can alter the presentation)
5036
 *
5037
 * @return string
5038
 */
5039
function api_get_languages_combo($name = 'language')
5040
{
5041
    $ret = '';
5042
    $platformLanguage = api_get_setting('platformLanguage');
5043
5044
    // Retrieve a complete list of all the languages.
5045
    $language_list = api_get_languages();
5046
5047
    if (count($language_list['name']) < 2) {
5048
        return $ret;
5049
    }
5050
5051
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
5052
    if (isset($_SESSION['user_language_choice'])) {
5053
        $default = $_SESSION['user_language_choice'];
5054
    } else {
5055
        $default = $platformLanguage;
5056
    }
5057
5058
    $languages = $language_list['name'];
5059
    $folder = $language_list['folder'];
5060
5061
    $ret .= '<select name="'.$name.'" id="language_chosen" class="selectpicker form-control">';
5062
    foreach ($languages as $key => $value) {
5063
        if ($folder[$key] == $default) {
5064
            $selected = ' selected="selected"';
5065
        } else {
5066
            $selected = '';
5067
        }
5068
        $ret .= sprintf('<option value=%s" %s>%s</option>', $folder[$key], $selected, $value);
5069
    }
5070
    $ret .= '</select>';
5071
5072
    return $ret;
5073
}
5074
5075
/**
5076
 * Displays a form (drop down menu) so the user can select his/her preferred language.
5077
 * The form works with or without javascript.
5078
 *
5079
 * @param  bool Hide form if only one language available (defaults to false = show the box anyway)
5080
 * @param bool $showAsButton
5081
 *
5082
 * @return string|null Display the box directly
5083
 */
5084
function api_display_language_form($hide_if_no_choice = false, $showAsButton = false)
5085
{
5086
    // Retrieve a complete list of all the languages.
5087
    $language_list = api_get_languages();
5088
    if (count($language_list['name']) <= 1 && $hide_if_no_choice) {
5089
        return null; //don't show any form
5090
    }
5091
5092
    // The the current language of the user so that his/her language occurs as selected in the dropdown menu.
5093
    if (isset($_SESSION['user_language_choice'])) {
5094
        $user_selected_language = $_SESSION['user_language_choice'];
5095
    }
5096
    if (empty($user_selected_language)) {
5097
        $user_selected_language = api_get_setting('platformLanguage');
5098
    }
5099
5100
    $currentLanguageId = api_get_language_id($user_selected_language);
5101
    $currentLanguageInfo = api_get_language_info($currentLanguageId);
5102
5103
    $countryCode = languageCodeToCountryIsoCodeForFlags($currentLanguageInfo['isocode']);
5104
    $url = api_get_self();
5105
    if ($showAsButton) {
5106
        $html = '<div class="btn-group">
5107
              <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
5108
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
5109
                '.$currentLanguageInfo['original_name'].'
5110
                <span class="caret">
5111
                </span>
5112
              </button>';
5113
    } else {
5114
        $html = '
5115
            <a href="'.$url.'" class="dropdown-toggle" data-toggle="dropdown" role="button">
5116
                <span class="flag-icon flag-icon-'.$countryCode.'"></span>
5117
                '.$currentLanguageInfo['original_name'].'
5118
                <span class="caret"></span>
5119
            </a>
5120
            ';
5121
    }
5122
5123
    $html .= '<ul class="dropdown-menu" role="menu">';
5124
    foreach ($language_list['all'] as $key => $data) {
5125
        $urlLink = $url.'?language='.$data['english_name'];
5126
        $html .= '<li><a href="'.$urlLink.'"><span class="flag-icon flag-icon-'.languageCodeToCountryIsoCodeForFlags($data['isocode']).'"></span> '.$data['original_name'].'</a></li>';
5127
    }
5128
    $html .= '</ul>';
5129
5130
    if ($showAsButton) {
5131
        $html .= '</div>';
5132
    }
5133
5134
    return $html;
5135
}
5136
5137
/**
5138
 * Return a country code based on a language in order to show a country flag.
5139
 * Note: Showing a "language" flag is arguably a bad idea, as several countries
5140
 * share languages and the right flag cannot be shown for all of them.
5141
 *
5142
 * @param string $languageIsoCode
5143
 *
5144
 * @return string
5145
 */
5146
function languageCodeToCountryIsoCodeForFlags($languageIsoCode)
5147
{
5148
    $allow = api_get_configuration_value('language_flags_by_country');
5149
5150
    // @todo save in DB
5151
    switch ($languageIsoCode) {
5152
        case 'ar':
5153
            $country = 'ae';
5154
            break;
5155
        case 'bs':
5156
            $country = 'ba';
5157
            break;
5158
        case 'ca':
5159
            $country = 'es';
5160
            if ($allow) {
5161
                $country = 'catalan';
5162
            }
5163
            break;
5164
        case 'cs':
5165
            $country = 'cz';
5166
            break;
5167
        case 'da':
5168
            $country = 'dk';
5169
            break;
5170
        case 'el':
5171
            $country = 'gr';
5172
            break;
5173
        case 'en':
5174
            $country = 'gb';
5175
            break;
5176
        case 'eu': // Euskera
5177
            $country = 'es';
5178
            if ($allow) {
5179
                $country = 'basque';
5180
            }
5181
            break;
5182
        case 'gl': // galego
5183
            $country = 'es';
5184
            if ($allow) {
5185
                $country = 'galician';
5186
            }
5187
            break;
5188
        case 'he':
5189
            $country = 'il';
5190
            break;
5191
        case 'ja':
5192
            $country = 'jp';
5193
            break;
5194
        case 'ka':
5195
            $country = 'ge';
5196
            break;
5197
        case 'ko':
5198
            $country = 'kr';
5199
            break;
5200
        case 'ms':
5201
            $country = 'my';
5202
            break;
5203
        case 'pt-BR':
5204
            $country = 'br';
5205
            break;
5206
        case 'qu':
5207
            $country = 'pe';
5208
            break;
5209
        case 'sl':
5210
            $country = 'si';
5211
            break;
5212
        case 'sv':
5213
            $country = 'se';
5214
            break;
5215
        case 'uk': // Ukraine
5216
            $country = 'ua';
5217
            break;
5218
        case 'vi': // Vietnam - GH#4231
5219
            $country = 'vn';
5220
            break;
5221
        case 'zh-TW':
5222
        case 'zh':
5223
            $country = 'cn';
5224
            break;
5225
        default:
5226
            $country = $languageIsoCode;
5227
            break;
5228
    }
5229
    $country = strtolower($country);
5230
5231
    return $country;
5232
}
5233
5234
/**
5235
 * Returns a list of all the languages that are made available by the admin.
5236
 *
5237
 * @return array An array with all languages. Structure of the array is
5238
 *               array['name'] = An array with the name of every language
5239
 *               array['folder'] = An array with the corresponding names of the language-folders in the filesystem
5240
 */
5241
function api_get_languages()
5242
{
5243
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5244
    $sql = "SELECT * FROM $tbl_language WHERE available='1'
5245
            ORDER BY original_name ASC";
5246
    $result = Database::query($sql);
5247
    $language_list = [];
5248
    while ($row = Database::fetch_array($result)) {
5249
        $language_list['name'][] = $row['original_name'];
5250
        $language_list['folder'][] = $row['dokeos_folder'];
5251
        $language_list['all'][] = $row;
5252
    }
5253
5254
    return $language_list;
5255
}
5256
5257
/**
5258
 * Returns a list of all the languages that are made available by the admin.
5259
 *
5260
 * @return array
5261
 */
5262
function api_get_languages_to_array()
5263
{
5264
    $tbl_language = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5265
    $sql = "SELECT * FROM $tbl_language
5266
            WHERE available='1' ORDER BY original_name ASC";
5267
    $result = Database::query($sql);
5268
    $languages = [];
5269
    while ($row = Database::fetch_array($result)) {
5270
        $languages[$row['dokeos_folder']] = $row['original_name'];
5271
    }
5272
5273
    return $languages;
5274
}
5275
5276
/**
5277
 * Returns the id (the database id) of a language.
5278
 *
5279
 * @param   string  language name (the corresponding name of the language-folder in the filesystem)
5280
 *
5281
 * @return int id of the language
5282
 */
5283
function api_get_language_id($language)
5284
{
5285
    if (empty($language)) {
5286
        return null;
5287
    }
5288
5289
    static $staticResult = [];
5290
5291
    if (isset($staticResult[$language])) {
5292
        return $staticResult[$language];
5293
    } else {
5294
        $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
5295
        $language = Database::escape_string($language);
5296
        $sql = "SELECT id FROM $table
5297
                WHERE dokeos_folder = '$language' LIMIT 1";
5298
        $result = Database::query($sql);
5299
        $row = Database::fetch_array($result);
5300
5301
        $staticResult[$language] = $row['id'];
5302
5303
        return $row['id'];
5304
    }
5305
}
5306
5307
/**
5308
 * Gets language of the requested type for the current user. Types are :
5309
 * user_profil_lang : profile language of current user
5310
 * user_select_lang : language selected by user at login
5311
 * course_lang : language of the current course
5312
 * platform_lang : default platform language.
5313
 *
5314
 * @param string $lang_type
5315
 *
5316
 * @return string
5317
 */
5318
function api_get_language_from_type($lang_type)
5319
{
5320
    $return = false;
5321
    switch ($lang_type) {
5322
        case 'platform_lang':
5323
            $temp_lang = api_get_setting('platformLanguage');
5324
            if (!empty($temp_lang)) {
5325
                $return = $temp_lang;
5326
            }
5327
            break;
5328
        case 'user_profil_lang':
5329
            $_user = api_get_user_info();
5330
            if (isset($_user['language']) && !empty($_user['language'])) {
5331
                $return = $_user['language'];
5332
            }
5333
            break;
5334
        case 'user_selected_lang':
5335
            if (isset($_SESSION['user_language_choice']) && !empty($_SESSION['user_language_choice'])) {
5336
                $return = $_SESSION['user_language_choice'];
5337
            }
5338
            break;
5339
        case 'course_lang':
5340
            global $_course;
5341
            $cidReq = null;
5342
            if (empty($_course)) {
5343
                // Code modified because the local.inc.php file it's declarated after this work
5344
                // causing the function api_get_course_info() returns a null value
5345
                $cidReq = isset($_GET["cidReq"]) ? Database::escape_string($_GET["cidReq"]) : null;
5346
                $cDir = (!empty($_GET['cDir']) ? $_GET['cDir'] : null);
5347
                if (empty($cidReq) && !empty($cDir)) {
5348
                    $c = CourseManager::getCourseCodeFromDirectory($cDir);
5349
                    if ($c) {
5350
                        $cidReq = $c;
5351
                    }
5352
                }
5353
            }
5354
            $_course = api_get_course_info($cidReq);
5355
            if (isset($_course['language']) && !empty($_course['language'])) {
5356
                $return = $_course['language'];
5357
                $showCourseInUserLanguage = api_get_course_setting('show_course_in_user_language');
5358
                if ($showCourseInUserLanguage == 1) {
5359
                    $userInfo = api_get_user_info();
5360
                    if (isset($userInfo['language'])) {
5361
                        $return = $userInfo['language'];
5362
                    }
5363
                }
5364
            }
5365
            break;
5366
        default:
5367
            $return = false;
5368
            break;
5369
    }
5370
5371
    return $return;
5372
}
5373
5374
/**
5375
 * Get the language information by its id.
5376
 *
5377
 * @param int $languageId
5378
 *
5379
 * @throws Exception
5380
 *
5381
 * @return array
5382
 */
5383
function api_get_language_info($languageId)
5384
{
5385
    if (empty($languageId)) {
5386
        return [];
5387
    }
5388
5389
    $language = Database::getManager()
5390
        ->find('ChamiloCoreBundle:Language', $languageId);
5391
5392
    if (!$language) {
5393
        return [];
5394
    }
5395
5396
    return [
5397
        'id' => $language->getId(),
5398
        'original_name' => $language->getOriginalName(),
5399
        'english_name' => $language->getEnglishName(),
5400
        'isocode' => $language->getIsocode(),
5401
        'dokeos_folder' => $language->getDokeosFolder(),
5402
        'available' => $language->getAvailable(),
5403
        'parent_id' => $language->getParent() ? $language->getParent()->getId() : null,
5404
    ];
5405
}
5406
5407
/**
5408
 * Returns the name of the visual (CSS) theme to be applied on the current page.
5409
 * The returned name depends on the platform, course or user -wide settings.
5410
 *
5411
 * @return string The visual theme's name, it is the name of a folder inside web/css/themes
5412
 */
5413
function api_get_visual_theme()
5414
{
5415
    static $visual_theme;
5416
5417
    $course_id = api_get_course_id();
5418
    $courseThemeAvailable = false;
5419
    // If called from CLI or from inside a course, it should be reloaded.
5420
    if ('cli' === PHP_SAPI) {
5421
        $visual_theme = null;
5422
    } elseif (!empty($course_id)) {
5423
        $courseThemeAvailable = api_get_setting('allow_course_theme') == 'true';
5424
        if ($courseThemeAvailable) {
5425
            $visual_theme = null;
5426
        }
5427
    }
5428
5429
    if (!isset($visual_theme)) {
5430
        $cacheAvailable = api_get_configuration_value('apc');
5431
        $userThemeAvailable = api_get_setting('user_selected_theme') == 'true';
5432
        // only use a shared cache if no user-based or course-based theme is allowed
5433
        $useCache = ($cacheAvailable && !$userThemeAvailable && !$courseThemeAvailable);
5434
        $apcVar = '';
5435
        if ($useCache) {
5436
            $apcVar = api_get_configuration_value('apc_prefix').'my_campus_visual_theme';
5437
            if (apcu_exists($apcVar)) {
5438
                return apcu_fetch($apcVar);
5439
            }
5440
        }
5441
5442
        $accessUrlId = api_get_current_access_url_id();
5443
        if ('cli' === PHP_SAPI) {
5444
            $accessUrlId = api_get_configuration_value('access_url');
5445
        }
5446
5447
        // Get style directly from DB
5448
        $styleFromDatabase = api_get_settings_params_simple(
5449
            [
5450
                'variable = ? AND access_url = ?' => [
5451
                    'stylesheets',
5452
                    $accessUrlId,
5453
                ],
5454
            ]
5455
        );
5456
        if ($styleFromDatabase) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $styleFromDatabase of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5457
            $platform_theme = $styleFromDatabase['selected_value'];
5458
        } else {
5459
            $platform_theme = api_get_setting('stylesheets');
5460
        }
5461
5462
        // Platform's theme.
5463
        $visual_theme = $platform_theme;
5464
        if ($userThemeAvailable) {
5465
            $user_info = api_get_user_info();
5466
            if (isset($user_info['theme'])) {
5467
                $user_theme = $user_info['theme'];
5468
5469
                if (!empty($user_theme)) {
5470
                    $visual_theme = $user_theme;
5471
                    // User's theme.
5472
                }
5473
            }
5474
        }
5475
5476
        if (!empty($course_id)) {
5477
            if ($courseThemeAvailable) {
5478
                $course_theme = api_get_course_setting('course_theme', api_get_course_info());
5479
5480
                if (!empty($course_theme) && $course_theme != -1) {
5481
                    if (!empty($course_theme)) {
5482
                        // Course's theme.
5483
                        $visual_theme = $course_theme;
5484
                    }
5485
                }
5486
5487
                $allow_lp_theme = api_get_course_setting('allow_learning_path_theme');
5488
                if ($allow_lp_theme == 1) {
5489
                    global $lp_theme_css, $lp_theme_config;
5490
                    // These variables come from the file lp_controller.php.
5491
                    if (!$lp_theme_config) {
5492
                        if (!empty($lp_theme_css)) {
5493
                            // LP's theme.
5494
                            $visual_theme = $lp_theme_css;
5495
                        }
5496
                    }
5497
                }
5498
            }
5499
        }
5500
5501
        if (empty($visual_theme)) {
5502
            $visual_theme = 'chamilo';
5503
        }
5504
5505
        global $lp_theme_log;
5506
        if ($lp_theme_log) {
5507
            $visual_theme = $platform_theme;
5508
        }
5509
        if ($useCache) {
5510
            apcu_store($apcVar, $visual_theme, 120);
5511
        }
5512
    }
5513
5514
    return $visual_theme;
5515
}
5516
5517
/**
5518
 * Returns a list of CSS themes currently available in the CSS folder
5519
 * The folder must have a default.css file.
5520
 *
5521
 * @param bool $getOnlyThemeFromVirtualInstance Used by the vchamilo plugin
5522
 *
5523
 * @return array list of themes directories from the css folder
5524
 *               Note: Directory names (names of themes) in the file system should contain ASCII-characters only
5525
 */
5526
function api_get_themes($getOnlyThemeFromVirtualInstance = false)
5527
{
5528
    // This configuration value is set by the vchamilo plugin
5529
    $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
5530
5531
    $readCssFolder = function ($dir) use ($virtualTheme) {
5532
        $finder = new Finder();
5533
        $themes = $finder->directories()->in($dir)->depth(0)->sortByName();
5534
        $list = [];
5535
        /** @var Symfony\Component\Finder\SplFileInfo $theme */
5536
        foreach ($themes as $theme) {
5537
            $folder = $theme->getFilename();
5538
            // A theme folder is consider if there's a default.css file
5539
            if (!file_exists($theme->getPathname().'/default.css')) {
5540
                continue;
5541
            }
5542
            $name = ucwords(str_replace('_', ' ', $folder));
5543
            if ($folder == $virtualTheme) {
5544
                continue;
5545
            }
5546
            $list[$folder] = $name;
5547
        }
5548
5549
        return $list;
5550
    };
5551
5552
    $dir = api_get_path(SYS_CSS_PATH).'themes/';
5553
    $list = $readCssFolder($dir);
5554
5555
    if (!empty($virtualTheme)) {
5556
        $newList = $readCssFolder($dir.'/'.$virtualTheme);
5557
        if ($getOnlyThemeFromVirtualInstance) {
5558
            return $newList;
5559
        }
5560
        $list = $list + $newList;
5561
        asort($list);
5562
    }
5563
5564
    return $list;
5565
}
5566
5567
/**
5568
 * Find the largest sort value in a given user_course_category
5569
 * This function is used when we are moving a course to a different category
5570
 * and also when a user subscribes to courses (the new course is added at the end of the main category.
5571
 *
5572
 * @author Patrick Cool <[email protected]>, Ghent University
5573
 *
5574
 * @param int $user_course_category the id of the user_course_category
5575
 * @param int $user_id
5576
 *
5577
 * @return int the value of the highest sort of the user_course_category
5578
 */
5579
function api_max_sort_value($user_course_category, $user_id)
5580
{
5581
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5582
    $sql = "SELECT max(sort) as max_sort FROM $tbl_course_user
5583
            WHERE
5584
                user_id='".intval($user_id)."' AND
5585
                relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
5586
                user_course_cat='".intval($user_course_category)."'";
5587
    $result_max = Database::query($sql);
5588
    if (Database::num_rows($result_max) == 1) {
5589
        $row_max = Database::fetch_array($result_max);
5590
5591
        return $row_max['max_sort'];
5592
    }
5593
5594
    return 0;
5595
}
5596
5597
/**
5598
 * Transforms a number of seconds in hh:mm:ss format.
5599
 *
5600
 * @author Julian Prud'homme
5601
 *
5602
 * @param int    $seconds      number of seconds
5603
 * @param string $space
5604
 * @param bool   $showSeconds
5605
 * @param bool   $roundMinutes
5606
 *
5607
 * @return string the formatted time
5608
 */
5609
function api_time_to_hms($seconds, $space = ':', $showSeconds = true, $roundMinutes = false)
5610
{
5611
    // $seconds = -1 means that we have wrong data in the db.
5612
    if ($seconds == -1) {
5613
        return
5614
            get_lang('Unknown').
5615
            Display::return_icon(
5616
                'info2.gif',
5617
                get_lang('WrongDatasForTimeSpentOnThePlatform'),
5618
                ['align' => 'absmiddle', 'hspace' => '3px']
5619
            );
5620
    }
5621
5622
    // How many hours ?
5623
    $hours = floor($seconds / 3600);
5624
5625
    // How many minutes ?
5626
    $min = floor(($seconds - ($hours * 3600)) / 60);
5627
5628
    if ($roundMinutes) {
5629
        if ($min >= 45) {
5630
            $min = 45;
5631
        }
5632
5633
        if ($min >= 30 && $min <= 44) {
5634
            $min = 30;
5635
        }
5636
5637
        if ($min >= 15 && $min <= 29) {
5638
            $min = 15;
5639
        }
5640
5641
        if ($min >= 0 && $min <= 14) {
5642
            $min = 0;
5643
        }
5644
    }
5645
5646
    // How many seconds
5647
    $sec = floor($seconds - ($hours * 3600) - ($min * 60));
5648
5649
    if ($hours < 10) {
5650
        $hours = "0$hours";
5651
    }
5652
5653
    if ($sec < 10) {
5654
        $sec = "0$sec";
5655
    }
5656
5657
    if ($min < 10) {
5658
        $min = "0$min";
5659
    }
5660
5661
    $seconds = '';
5662
    if ($showSeconds) {
5663
        $seconds = $space.$sec;
5664
    }
5665
5666
    return $hours.$space.$min.$seconds;
5667
}
5668
5669
/* FILE SYSTEM RELATED FUNCTIONS */
5670
5671
/**
5672
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5673
 * The return value is based on the platform administrator's setting
5674
 * "Administration > Configuration settings > Security > Permissions for new directories".
5675
 *
5676
 * @return int returns the permissions in the format "Owner-Group-Others, Read-Write-Execute", as an integer value
5677
 */
5678
function api_get_permissions_for_new_directories()
5679
{
5680
    static $permissions;
5681
    if (!isset($permissions)) {
5682
        $permissions = trim(api_get_setting('permissions_for_new_directories'));
5683
        // The default value 0777 is according to that in the platform administration panel after fresh system installation.
5684
        $permissions = octdec(!empty($permissions) ? $permissions : '0777');
5685
    }
5686
5687
    return $permissions;
5688
}
5689
5690
/**
5691
 * Returns the permissions to be assigned to every newly created directory by the web-server.
5692
 * The return value is based on the platform administrator's setting
5693
 * "Administration > Configuration settings > Security > Permissions for new files".
5694
 *
5695
 * @return int returns the permissions in the format
5696
 *             "Owner-Group-Others, Read-Write-Execute", as an integer value
5697
 */
5698
function api_get_permissions_for_new_files()
5699
{
5700
    static $permissions;
5701
    if (!isset($permissions)) {
5702
        $permissions = trim(api_get_setting('permissions_for_new_files'));
5703
        // The default value 0666 is according to that in the platform
5704
        // administration panel after fresh system installation.
5705
        $permissions = octdec(!empty($permissions) ? $permissions : '0666');
5706
    }
5707
5708
    return $permissions;
5709
}
5710
5711
/**
5712
 * Deletes a file, or a folder and its contents.
5713
 *
5714
 * @author      Aidan Lister <[email protected]>
5715
 *
5716
 * @version     1.0.3
5717
 *
5718
 * @param string $dirname Directory to delete
5719
 * @param       bool     Deletes only the content or not
5720
 * @param bool $strict if one folder/file fails stop the loop
5721
 *
5722
 * @return bool Returns TRUE on success, FALSE on failure
5723
 *
5724
 * @see http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
5725
 *
5726
 * @author      Yannick Warnier, adaptation for the Chamilo LMS, April, 2008
5727
 * @author      Ivan Tcholakov, a sanity check about Directory class creation has been added, September, 2009
5728
 */
5729
function rmdirr($dirname, $delete_only_content_in_folder = false, $strict = false)
5730
{
5731
    $res = true;
5732
    // A sanity check.
5733
    if (!file_exists($dirname)) {
5734
        return false;
5735
    }
5736
    $php_errormsg = '';
5737
    // Simple delete for a file.
5738
    if (is_file($dirname) || is_link($dirname)) {
5739
        $res = unlink($dirname);
5740
        if ($res === false) {
5741
            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);
5742
        }
5743
5744
        return $res;
5745
    }
5746
5747
    // Loop through the folder.
5748
    $dir = dir($dirname);
5749
    // A sanity check.
5750
    $is_object_dir = is_object($dir);
5751
    if ($is_object_dir) {
5752
        while (false !== $entry = $dir->read()) {
5753
            // Skip pointers.
5754
            if ($entry == '.' || $entry == '..') {
5755
                continue;
5756
            }
5757
5758
            // Recurse.
5759
            if ($strict) {
5760
                $result = rmdirr("$dirname/$entry");
5761
                if ($result == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
5762
                    $res = false;
5763
                    break;
5764
                }
5765
            } else {
5766
                rmdirr("$dirname/$entry");
5767
            }
5768
        }
5769
    }
5770
5771
    // Clean up.
5772
    if ($is_object_dir) {
5773
        $dir->close();
5774
    }
5775
5776
    if ($delete_only_content_in_folder == false) {
5777
        $res = rmdir($dirname);
5778
        if ($res === false) {
5779
            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);
5780
        }
5781
    }
5782
5783
    return $res;
5784
}
5785
5786
// TODO: This function is to be simplified. File access modes to be implemented.
5787
/**
5788
 * function adapted from a php.net comment
5789
 * copy recursively a folder.
5790
 *
5791
 * @param string $source       the source folder
5792
 * @param string $dest         the dest folder
5793
 * @param array  $exclude      an array of excluded file_name (without extension)
5794
 * @param array  $copied_files the returned array of copied files
5795
 */
5796
function copyr($source, $dest, $exclude = [], $copied_files = [])
5797
{
5798
    if (empty($dest)) {
5799
        return false;
5800
    }
5801
    // Simple copy for a file
5802
    if (is_file($source)) {
5803
        $path_info = pathinfo($source);
5804
        if (!in_array($path_info['filename'], $exclude)) {
5805
            copy($source, $dest);
5806
        }
5807
5808
        return true;
5809
    } elseif (!is_dir($source)) {
5810
        //then source is not a dir nor a file, return
5811
        return false;
5812
    }
5813
5814
    // Make destination directory.
5815
    if (!is_dir($dest)) {
5816
        mkdir($dest, api_get_permissions_for_new_directories());
5817
    }
5818
5819
    // Loop through the folder.
5820
    $dir = dir($source);
5821
    while (false !== $entry = $dir->read()) {
5822
        // Skip pointers
5823
        if ($entry == '.' || $entry == '..') {
5824
            continue;
5825
        }
5826
5827
        // Deep copy directories.
5828
        if ($dest !== "$source/$entry") {
5829
            $files = copyr("$source/$entry", "$dest/$entry", $exclude, $copied_files);
5830
        }
5831
    }
5832
    // Clean up.
5833
    $dir->close();
5834
5835
    return true;
5836
}
5837
5838
/**
5839
 * @param string $pathname
5840
 * @param string $base_path_document
5841
 * @param int    $session_id
5842
 * @param array
5843
 * @param string
5844
 *
5845
 * @return mixed True if directory already exists, false if a file already exists at
5846
 *               the destination and null if everything goes according to plan
5847
 *@todo: Using DIRECTORY_SEPARATOR is not recommended, this is an obsolete approach.
5848
 * Documentation header to be added here.
5849
 */
5850
function copy_folder_course_session(
5851
    $pathname,
5852
    $base_path_document,
5853
    $session_id,
5854
    $course_info,
5855
    $document,
5856
    $source_course_id,
5857
    $originalFolderNameList = [],
5858
    $originalBaseName = ''
5859
) {
5860
    // Check whether directory already exists.
5861
    if (empty($pathname) || is_dir($pathname)) {
5862
        return true;
5863
    }
5864
5865
    // Ensure that a file with the same name does not already exist.
5866
    if (is_file($pathname)) {
5867
        trigger_error('copy_folder_course_session(): File exists', E_USER_WARNING);
5868
5869
        return false;
5870
    }
5871
5872
    //error_log('checking:');
5873
    //error_log(str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5874
    $baseNoDocument = str_replace('document', '', $originalBaseName);
5875
    $folderTitles = explode('/', $baseNoDocument);
5876
    $folderTitles = array_filter($folderTitles);
5877
5878
    //error_log($baseNoDocument);error_log(print_r($folderTitles, 1));
5879
5880
    $table = Database::get_course_table(TABLE_DOCUMENT);
5881
    $session_id = (int) $session_id;
5882
    $source_course_id = (int) $source_course_id;
5883
    $course_id = $course_info['real_id'];
5884
    $folders = explode(DIRECTORY_SEPARATOR, str_replace($base_path_document.DIRECTORY_SEPARATOR, '', $pathname));
5885
    $new_pathname = $base_path_document;
5886
5887
    $path = '';
5888
    foreach ($folders as $index => $folder) {
5889
        $new_pathname .= DIRECTORY_SEPARATOR.$folder;
5890
        $path .= DIRECTORY_SEPARATOR.$folder;
5891
5892
        if (!file_exists($new_pathname)) {
5893
            $path = Database::escape_string($path);
5894
            //error_log("path: $path");
5895
            $sql = "SELECT * FROM $table
5896
                    WHERE
5897
                        c_id = $source_course_id AND
5898
                        path = '$path' AND
5899
                        filetype = 'folder' AND
5900
                        session_id = '$session_id'";
5901
            $rs1 = Database::query($sql);
5902
            $num_rows = Database::num_rows($rs1);
5903
5904
            if (0 == $num_rows) {
5905
                mkdir($new_pathname, api_get_permissions_for_new_directories());
5906
                $title = basename($new_pathname);
5907
5908
                if (isset($folderTitles[$index + 1])) {
5909
                    $checkPath = $folderTitles[$index + 1];
5910
                    //error_log("check $checkPath");
5911
                    if (isset($originalFolderNameList[$checkPath])) {
5912
                        $title = $originalFolderNameList[$checkPath];
5913
                        //error_log('use this name: '.$title);
5914
                    }
5915
                }
5916
5917
                // Insert new folder with destination session_id.
5918
                $params = [
5919
                    'c_id' => $course_id,
5920
                    'path' => $path,
5921
                    'comment' => $document->comment,
5922
                    'title' => $title,
5923
                    'filetype' => 'folder',
5924
                    'size' => '0',
5925
                    'session_id' => $session_id,
5926
                ];
5927
5928
                //error_log("old $folder"); error_log("Add doc $title in $path");
5929
                $document_id = Database::insert($table, $params);
5930
                if ($document_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $document_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
5931
                    $sql = "UPDATE $table SET id = iid WHERE iid = $document_id";
5932
                    Database::query($sql);
5933
5934
                    api_item_property_update(
5935
                        $course_info,
5936
                        TOOL_DOCUMENT,
5937
                        $document_id,
5938
                        'FolderCreated',
5939
                        api_get_user_id(),
5940
                        0,
5941
                        0,
5942
                        null,
5943
                        null,
5944
                        $session_id
5945
                    );
5946
                }
5947
            }
5948
        }
5949
    }
5950
}
5951
5952
// TODO: chmodr() is a better name. Some corrections are needed. Documentation header to be added here.
5953
/**
5954
 * @param string $path
5955
 */
5956
function api_chmod_R($path, $filemode)
5957
{
5958
    if (!is_dir($path)) {
5959
        return chmod($path, $filemode);
5960
    }
5961
5962
    $handler = opendir($path);
5963
    while ($file = readdir($handler)) {
5964
        if ($file != '.' && $file != '..') {
5965
            $fullpath = "$path/$file";
5966
            if (!is_dir($fullpath)) {
5967
                if (!chmod($fullpath, $filemode)) {
5968
                    return false;
5969
                }
5970
            } else {
5971
                if (!api_chmod_R($fullpath, $filemode)) {
5972
                    return false;
5973
                }
5974
            }
5975
        }
5976
    }
5977
5978
    closedir($handler);
5979
5980
    return chmod($path, $filemode);
5981
}
5982
5983
// TODO: Where the following function has been copy/pased from? There is no information about author and license. Style, coding conventions...
5984
/**
5985
 * Parse info file format. (e.g: file.info).
5986
 *
5987
 * Files should use an ini-like format to specify values.
5988
 * White-space generally doesn't matter, except inside values.
5989
 * e.g.
5990
 *
5991
 * @verbatim
5992
 *   key = value
5993
 *   key = "value"
5994
 *   key = 'value'
5995
 *   key = "multi-line
5996
 *
5997
 *   value"
5998
 *   key = 'multi-line
5999
 *
6000
 *   value'
6001
 *   key
6002
 *   =
6003
 *   'value'
6004
 * @endverbatim
6005
 *
6006
 * Arrays are created using a GET-like syntax:
6007
 *
6008
 * @verbatim
6009
 *   key[] = "numeric array"
6010
 *   key[index] = "associative array"
6011
 *   key[index][] = "nested numeric array"
6012
 *   key[index][index] = "nested associative array"
6013
 * @endverbatim
6014
 *
6015
 * PHP constants are substituted in, but only when used as the entire value:
6016
 *
6017
 * Comments should start with a semi-colon at the beginning of a line.
6018
 *
6019
 * This function is NOT for placing arbitrary module-specific settings. Use
6020
 * variable_get() and variable_set() for that.
6021
 *
6022
 * Information stored in the module.info file:
6023
 * - name: The real name of the module for display purposes.
6024
 * - description: A brief description of the module.
6025
 * - dependencies: An array of shortnames of other modules this module depends on.
6026
 * - package: The name of the package of modules this module belongs to.
6027
 *
6028
 * Example of .info file:
6029
 * <code>
6030
 * @verbatim
6031
 *   name = Forum
6032
 *   description = Enables threaded discussions about general topics.
6033
 *   dependencies[] = taxonomy
6034
 *   dependencies[] = comment
6035
 *   package = Core - optional
6036
 *   version = VERSION
6037
 * @endverbatim
6038
 * </code>
6039
 *
6040
 * @param string $filename
6041
 *                         The file we are parsing. Accepts file with relative or absolute path.
6042
 *
6043
 * @return
6044
 *   The info array
6045
 */
6046
function api_parse_info_file($filename)
6047
{
6048
    $info = [];
6049
6050
    if (!file_exists($filename)) {
6051
        return $info;
6052
    }
6053
6054
    $data = file_get_contents($filename);
6055
    if (preg_match_all('
6056
        @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
6057
        ((?:
6058
          [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
6059
          \[[^\[\]]*\]                  # unless they are balanced and not nested
6060
        )+?)
6061
        \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
6062
        (?:
6063
          ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
6064
          (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
6065
          ([^\r\n]*?)                   # Non-quoted string
6066
        )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
6067
        @msx', $data, $matches, PREG_SET_ORDER)) {
6068
        $key = $value1 = $value2 = $value3 = '';
6069
        foreach ($matches as $match) {
6070
            // Fetch the key and value string.
6071
            $i = 0;
6072
            foreach (['key', 'value1', 'value2', 'value3'] as $var) {
6073
                $$var = isset($match[++$i]) ? $match[$i] : '';
6074
            }
6075
            $value = stripslashes(substr($value1, 1, -1)).stripslashes(substr($value2, 1, -1)).$value3;
6076
6077
            // Parse array syntax.
6078
            $keys = preg_split('/\]?\[/', rtrim($key, ']'));
6079
            $last = array_pop($keys);
6080
            $parent = &$info;
6081
6082
            // Create nested arrays.
6083
            foreach ($keys as $key) {
6084
                if ($key == '') {
6085
                    $key = count($parent);
6086
                }
6087
                if (!isset($parent[$key]) || !is_array($parent[$key])) {
6088
                    $parent[$key] = [];
6089
                }
6090
                $parent = &$parent[$key];
6091
            }
6092
6093
            // Handle PHP constants.
6094
            if (defined($value)) {
6095
                $value = constant($value);
6096
            }
6097
6098
            // Insert actual value.
6099
            if ($last == '') {
6100
                $last = count($parent);
6101
            }
6102
            $parent[$last] = $value;
6103
        }
6104
    }
6105
6106
    return $info;
6107
}
6108
6109
/**
6110
 * Gets Chamilo version from the configuration files.
6111
 *
6112
 * @return string A string of type "1.8.4", or an empty string if the version could not be found
6113
 */
6114
function api_get_version()
6115
{
6116
    return (string) api_get_configuration_value('system_version');
6117
}
6118
6119
/**
6120
 * Gets the software name (the name/brand of the Chamilo-based customized system).
6121
 *
6122
 * @return string
6123
 */
6124
function api_get_software_name()
6125
{
6126
    $name = api_get_configuration_value('software_name');
6127
    if (!empty($name)) {
6128
        return $name;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $name also could return the type boolean which is incompatible with the documented return type string.
Loading history...
6129
    } else {
6130
        return 'Chamilo';
6131
    }
6132
}
6133
6134
/**
6135
 * Checks whether status given in parameter exists in the platform.
6136
 *
6137
 * @param mixed the status (can be either int either string)
6138
 *
6139
 * @return bool if the status exists, else returns false
6140
 */
6141
function api_status_exists($status_asked)
6142
{
6143
    global $_status_list;
6144
6145
    return in_array($status_asked, $_status_list) ? true : isset($_status_list[$status_asked]);
6146
}
6147
6148
/**
6149
 * Checks whether status given in parameter exists in the platform. The function
6150
 * returns the status ID or false if it does not exist, but given the fact there
6151
 * is no "0" status, the return value can be checked against
6152
 * if(api_status_key()) to know if it exists.
6153
 *
6154
 * @param   mixed   The status (can be either int or string)
6155
 *
6156
 * @return mixed Status ID if exists, false otherwise
6157
 */
6158
function api_status_key($status)
6159
{
6160
    global $_status_list;
6161
6162
    return isset($_status_list[$status]) ? $status : array_search($status, $_status_list);
6163
}
6164
6165
/**
6166
 * Gets the status langvars list.
6167
 *
6168
 * @return string[] the list of status with their translations
6169
 */
6170
function api_get_status_langvars()
6171
{
6172
    return [
6173
        COURSEMANAGER => get_lang('Teacher', ''),
6174
        SESSIONADMIN => get_lang('SessionsAdmin', ''),
6175
        DRH => get_lang('Drh', ''),
6176
        STUDENT => get_lang('Student', ''),
6177
        ANONYMOUS => get_lang('Anonymous', ''),
6178
        STUDENT_BOSS => get_lang('RoleStudentBoss', ''),
6179
        INVITEE => get_lang('Invited'),
6180
    ];
6181
}
6182
6183
/**
6184
 * The function that retrieves all the possible settings for a certain config setting.
6185
 *
6186
 * @author Patrick Cool <[email protected]>, Ghent University
6187
 */
6188
function api_get_settings_options($var)
6189
{
6190
    $table_settings_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6191
    $var = Database::escape_string($var);
6192
    $sql = "SELECT * FROM $table_settings_options
6193
            WHERE variable = '$var'
6194
            ORDER BY id";
6195
    $result = Database::query($sql);
6196
    $settings_options_array = [];
6197
    while ($row = Database::fetch_array($result, 'ASSOC')) {
6198
        $settings_options_array[] = $row;
6199
    }
6200
6201
    return $settings_options_array;
6202
}
6203
6204
/**
6205
 * @param array $params
6206
 */
6207
function api_set_setting_option($params)
6208
{
6209
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6210
    if (empty($params['id'])) {
6211
        Database::insert($table, $params);
6212
    } else {
6213
        Database::update($table, $params, ['id = ? ' => $params['id']]);
6214
    }
6215
}
6216
6217
/**
6218
 * @param array $params
6219
 */
6220
function api_set_setting_simple($params)
6221
{
6222
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6223
    $url_id = api_get_current_access_url_id();
6224
6225
    if (empty($params['id'])) {
6226
        $params['access_url'] = $url_id;
6227
        Database::insert($table, $params);
6228
    } else {
6229
        Database::update($table, $params, ['id = ? ' => [$params['id']]]);
6230
    }
6231
}
6232
6233
/**
6234
 * @param int $id
6235
 */
6236
function api_delete_setting_option($id)
6237
{
6238
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
6239
    if (!empty($id)) {
6240
        Database::delete($table, ['id = ? ' => $id]);
6241
    }
6242
}
6243
6244
/**
6245
 * Sets a platform configuration setting to a given value.
6246
 *
6247
 * @param string    The variable we want to update
6248
 * @param string    The value we want to record
6249
 * @param string    The sub-variable if any (in most cases, this will remain null)
6250
 * @param string    The category if any (in most cases, this will remain null)
6251
 * @param int       The access_url for which this parameter is valid
6252
 * @param string $cat
6253
 *
6254
 * @return bool|null
6255
 */
6256
function api_set_setting($var, $value, $subvar = null, $cat = null, $access_url = 1)
6257
{
6258
    if (empty($var)) {
6259
        return false;
6260
    }
6261
    $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6262
    $var = Database::escape_string($var);
6263
    $value = Database::escape_string($value);
6264
    $access_url = (int) $access_url;
6265
    if (empty($access_url)) {
6266
        $access_url = 1;
6267
    }
6268
    $select = "SELECT id FROM $t_settings WHERE variable = '$var' ";
6269
    if (!empty($subvar)) {
6270
        $subvar = Database::escape_string($subvar);
6271
        $select .= " AND subkey = '$subvar'";
6272
    }
6273
    if (!empty($cat)) {
6274
        $cat = Database::escape_string($cat);
6275
        $select .= " AND category = '$cat'";
6276
    }
6277
    if ($access_url > 1) {
6278
        $select .= " AND access_url = $access_url";
6279
    } else {
6280
        $select .= " AND access_url = 1 ";
6281
    }
6282
6283
    $res = Database::query($select);
6284
    if (Database::num_rows($res) > 0) {
6285
        // Found item for this access_url.
6286
        $row = Database::fetch_array($res);
6287
        $sql = "UPDATE $t_settings SET selected_value = '$value'
6288
                WHERE id = ".$row['id'];
6289
        Database::query($sql);
6290
    } else {
6291
        // Item not found for this access_url, we have to check if it exist with access_url = 1
6292
        $select = "SELECT * FROM $t_settings
6293
                   WHERE variable = '$var' AND access_url = 1 ";
6294
        // Just in case
6295
        if ($access_url == 1) {
6296
            if (!empty($subvar)) {
6297
                $select .= " AND subkey = '$subvar'";
6298
            }
6299
            if (!empty($cat)) {
6300
                $select .= " AND category = '$cat'";
6301
            }
6302
            $res = Database::query($select);
6303
            if (Database::num_rows($res) > 0) {
6304
                // We have a setting for access_url 1, but none for the current one, so create one.
6305
                $row = Database::fetch_array($res);
6306
                $insert = "INSERT INTO $t_settings (variable, subkey, type,category, selected_value, title, comment, scope, subkeytext, access_url)
6307
                        VALUES
6308
                        ('".$row['variable']."',".(!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6309
                        "'".$row['type']."','".$row['category']."',".
6310
                        "'$value','".$row['title']."',".
6311
                        "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".(!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6312
                        "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url)";
6313
                Database::query($insert);
6314
            } else {
6315
                // Such a setting does not exist.
6316
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all', 0);
6317
            }
6318
        } else {
6319
            // Other access url.
6320
            if (!empty($subvar)) {
6321
                $select .= " AND subkey = '$subvar'";
6322
            }
6323
            if (!empty($cat)) {
6324
                $select .= " AND category = '$cat'";
6325
            }
6326
            $res = Database::query($select);
6327
6328
            if (Database::num_rows($res) > 0) {
6329
                // We have a setting for access_url 1, but none for the current one, so create one.
6330
                $row = Database::fetch_array($res);
6331
                if ($row['access_url_changeable'] == 1) {
6332
                    $insert = "INSERT INTO $t_settings (variable,subkey, type,category, selected_value,title, comment,scope, subkeytext,access_url, access_url_changeable) VALUES
6333
                            ('".$row['variable']."',".
6334
                            (!empty($row['subkey']) ? "'".$row['subkey']."'" : "NULL").",".
6335
                            "'".$row['type']."','".$row['category']."',".
6336
                            "'$value','".$row['title']."',".
6337
                            "".(!empty($row['comment']) ? "'".$row['comment']."'" : "NULL").",".
6338
                            (!empty($row['scope']) ? "'".$row['scope']."'" : "NULL").",".
6339
                            "".(!empty($row['subkeytext']) ? "'".$row['subkeytext']."'" : "NULL").",$access_url,".$row['access_url_changeable'].")";
6340
                    Database::query($insert);
6341
                }
6342
            } else { // Such a setting does not exist.
6343
                //error_log(__FILE__.':'.__LINE__.': Attempting to update setting '.$var.' ('.$subvar.') which does not exist at all. The access_url is: '.$access_url.' ',0);
6344
            }
6345
        }
6346
    }
6347
}
6348
6349
/**
6350
 * Sets a whole category of settings to one specific value.
6351
 *
6352
 * @param string    Category
6353
 * @param string    Value
6354
 * @param int       Access URL. Optional. Defaults to 1
6355
 * @param array     Optional array of filters on field type
6356
 * @param string $category
6357
 * @param string $value
6358
 *
6359
 * @return bool
6360
 */
6361
function api_set_settings_category($category, $value = null, $access_url = 1, $fieldtype = [])
6362
{
6363
    if (empty($category)) {
6364
        return false;
6365
    }
6366
    $category = Database::escape_string($category);
6367
    $t_s = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6368
    $access_url = (int) $access_url;
6369
    if (empty($access_url)) {
6370
        $access_url = 1;
6371
    }
6372
    if (isset($value)) {
6373
        $value = Database::escape_string($value);
6374
        $sql = "UPDATE $t_s SET selected_value = '$value'
6375
                WHERE category = '$category' AND access_url = $access_url";
6376
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6377
            $sql .= " AND ( ";
6378
            $i = 0;
6379
            foreach ($fieldtype as $type) {
6380
                if ($i > 0) {
6381
                    $sql .= ' OR ';
6382
                }
6383
                $type = Database::escape_string($type);
6384
                $sql .= " type='".$type."' ";
6385
                $i++;
6386
            }
6387
            $sql .= ")";
6388
        }
6389
        $res = Database::query($sql);
6390
6391
        return $res !== false;
6392
    } else {
6393
        $sql = "UPDATE $t_s SET selected_value = NULL
6394
                WHERE category = '$category' AND access_url = $access_url";
6395
        if (is_array($fieldtype) && count($fieldtype) > 0) {
6396
            $sql .= " AND ( ";
6397
            $i = 0;
6398
            foreach ($fieldtype as $type) {
6399
                if ($i > 0) {
6400
                    $sql .= ' OR ';
6401
                }
6402
                $type = Database::escape_string($type);
6403
                $sql .= " type='".$type."' ";
6404
                $i++;
6405
            }
6406
            $sql .= ")";
6407
        }
6408
        $res = Database::query($sql);
6409
6410
        return $res !== false;
6411
    }
6412
}
6413
6414
/**
6415
 * Gets all available access urls in an array (as in the database).
6416
 *
6417
 * @return array An array of database records
6418
 */
6419
function api_get_access_urls($from = 0, $to = 1000000, $order = 'url', $direction = 'ASC')
6420
{
6421
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6422
    $from = (int) $from;
6423
    $to = (int) $to;
6424
    $order = Database::escape_string($order);
6425
    $direction = Database::escape_string($direction);
6426
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
6427
6428
    $sql = "SELECT id, url, description, active, created_by, tms
6429
            FROM $table
6430
            ORDER BY `$order` $direction
6431
            LIMIT $to OFFSET $from";
6432
    $res = Database::query($sql);
6433
6434
    return Database::store_result($res);
6435
}
6436
6437
/**
6438
 * Gets the access url info in an array.
6439
 *
6440
 * @param int  $id            Id of the access url
6441
 * @param bool $returnDefault Set to false if you want the real URL if URL 1 is still 'http://localhost/'
6442
 *
6443
 * @return array All the info (url, description, active, created_by, tms)
6444
 *               from the access_url table
6445
 *
6446
 * @author Julio Montoya
6447
 */
6448
function api_get_access_url($id, $returnDefault = true)
6449
{
6450
    static $staticResult;
6451
    $id = (int) $id;
6452
6453
    if (isset($staticResult[$id])) {
6454
        $result = $staticResult[$id];
6455
    } else {
6456
        // Calling the Database:: library dont work this is handmade.
6457
        $table_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6458
        $sql = "SELECT url, description, active, created_by, tms
6459
                FROM $table_access_url WHERE id = '$id' ";
6460
        $res = Database::query($sql);
6461
        $result = @Database::fetch_array($res);
6462
        $staticResult[$id] = $result;
6463
    }
6464
6465
    // If the result url is 'http://localhost/' (the default) and the root_web
6466
    // (=current url) is different, and the $id is = 1 (which might mean
6467
    // api_get_current_access_url_id() returned 1 by default), then return the
6468
    // root_web setting instead of the current URL
6469
    // This is provided as an option to avoid breaking the storage of URL-specific
6470
    // homepages in home/localhost/
6471
    if ($id === 1 && $returnDefault === false) {
6472
        $currentUrl = api_get_current_access_url_id();
6473
        // only do this if we are on the main URL (=1), otherwise we could get
6474
        // information on another URL instead of the one asked as parameter
6475
        if ($currentUrl === 1) {
6476
            $rootWeb = api_get_path(WEB_PATH);
6477
            $default = 'http://localhost/';
6478
            if ($result['url'] === $default && $rootWeb != $default) {
6479
                $result['url'] = $rootWeb;
6480
            }
6481
        }
6482
    }
6483
6484
    return $result;
6485
}
6486
6487
/**
6488
 * Gets all the current settings for a specific access url.
6489
 *
6490
 * @param string    The category, if any, that we want to get
6491
 * @param string    Whether we want a simple list (display a category) or
6492
 * a grouped list (group by variable as in settings.php default). Values: 'list' or 'group'
6493
 * @param int       Access URL's ID. Optional. Uses 1 by default, which is the unique URL
6494
 *
6495
 * @return array Array of database results for the current settings of the current access URL
6496
 */
6497
function api_get_settings($cat = null, $ordering = 'list', $access_url = 1, $url_changeable = 0)
6498
{
6499
    // Try getting settings from cache first (avoids query w/ ~375 rows result)
6500
    $apcVarName = '';
6501
    $apcVar = [];
6502
    $cacheAvailable = api_get_configuration_value('apc');
6503
    if ($cacheAvailable) {
6504
        $apcVarName = api_get_configuration_value('apc_prefix').'settings';
6505
        $catName = (empty($cat) ? 'global' : $cat);
6506
6507
        if (apcu_exists($apcVarName)) {
6508
            $apcVar = apcu_fetch($apcVarName);
6509
            if (!empty($apcVar[$catName]) && !empty($apcVar[$catName][$ordering]) && isset($apcVar[$catName][$ordering][$url_changeable])) {
6510
                return $apcVar[$catName][$ordering][$url_changeable];
6511
            }
6512
        }
6513
    }
6514
    // Could not find settings in cache (or already expired), so query DB
6515
    $table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
6516
    $access_url = (int) $access_url;
6517
    $where_condition = '';
6518
    if ($url_changeable == 1) {
6519
        $where_condition = " AND access_url_changeable= '1' ";
6520
    }
6521
    if (empty($access_url) || $access_url == -1) {
6522
        $access_url = 1;
6523
    }
6524
    $sql = "SELECT * FROM $table
6525
            WHERE access_url = $access_url  $where_condition ";
6526
6527
    if (!empty($cat)) {
6528
        $cat = Database::escape_string($cat);
6529
        $sql .= " AND category='$cat' ";
6530
    }
6531
    if ($ordering == 'group') {
6532
        $sql .= " ORDER BY id ASC";
6533
    } else {
6534
        $sql .= " ORDER BY 1,2 ASC";
6535
    }
6536
    $result = Database::query($sql);
6537
    if ($result === null) {
6538
        return [];
6539
    }
6540
    $result = Database::store_result($result, 'ASSOC');
6541
6542
    if ($cacheAvailable) {
6543
        // If we got here, it means cache is available but the settings
6544
        // were not recently stored, so now we have them, let's store them
6545
        if (empty($apcVar[$catName])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $catName does not seem to be defined for all execution paths leading up to this point.
Loading history...
6546
            $apcVar[$catName] = [];
6547
        }
6548
        if (empty($apcVar[$catName][$ordering])) {
6549
            $apcVar[$catName][$ordering] = [];
6550
        }
6551
        $apcVar[$catName][$ordering][$url_changeable] = $result;
6552
        apcu_store($apcVarName, $apcVar, 600);
6553
    }
6554
6555
    return $result;
6556
}
6557
6558
/**
6559
 * @param string $value       The value we want to record
6560
 * @param string $variable    The variable name we want to insert
6561
 * @param string $subKey      The subkey for the variable we want to insert
6562
 * @param string $type        The type for the variable we want to insert
6563
 * @param string $category    The category for the variable we want to insert
6564
 * @param string $title       The title
6565
 * @param string $comment     The comment
6566
 * @param string $scope       The scope
6567
 * @param string $subKeyText  The subkey text
6568
 * @param int    $accessUrlId The access_url for which this parameter is valid
6569
 * @param int    $visibility  The changeability of this setting for non-master urls
6570
 *
6571
 * @return int The setting ID
6572
 */
6573
function api_add_setting(
6574
    $value,
6575
    $variable,
6576
    $subKey = '',
6577
    $type = 'textfield',
6578
    $category = '',
6579
    $title = '',
6580
    $comment = '',
6581
    $scope = '',
6582
    $subKeyText = '',
6583
    $accessUrlId = 1,
6584
    $visibility = 0
6585
) {
6586
    $em = Database::getManager();
6587
    $settingRepo = $em->getRepository('ChamiloCoreBundle:SettingsCurrent');
6588
    $accessUrlId = (int) $accessUrlId ?: 1;
6589
6590
    if (is_array($value)) {
6591
        $value = serialize($value);
6592
    } else {
6593
        $value = trim($value);
6594
    }
6595
6596
    $criteria = ['variable' => $variable, 'accessUrl' => $accessUrlId];
6597
6598
    if (!empty($subKey)) {
6599
        $criteria['subkey'] = $subKey;
6600
    }
6601
6602
    // Check if this variable doesn't exist already
6603
    /** @var SettingsCurrent $setting */
6604
    $setting = $settingRepo->findOneBy($criteria);
6605
6606
    if ($setting) {
0 ignored issues
show
introduced by
$setting is of type Chamilo\CoreBundle\Entity\SettingsCurrent, thus it always evaluated to true.
Loading history...
6607
        $setting->setSelectedValue($value);
6608
6609
        $em->persist($setting);
6610
        $em->flush();
6611
6612
        return $setting->getId();
6613
    }
6614
6615
    // Item not found for this access_url, we have to check if the whole thing is missing
6616
    // (in which case we ignore the insert) or if there *is* a record but just for access_url = 1
6617
    $setting = new SettingsCurrent();
6618
    $setting
6619
        ->setVariable($variable)
6620
        ->setSelectedValue($value)
6621
        ->setType($type)
6622
        ->setCategory($category)
6623
        ->setSubkey($subKey)
6624
        ->setTitle($title)
6625
        ->setComment($comment)
6626
        ->setScope($scope)
6627
        ->setSubkeytext($subKeyText)
6628
        ->setAccessUrl($accessUrlId)
6629
        ->setAccessUrlChangeable($visibility);
6630
6631
    $em->persist($setting);
6632
    $em->flush();
6633
6634
    return $setting->getId();
6635
}
6636
6637
/**
6638
 * Checks wether a user can or can't view the contents of a course.
6639
 *
6640
 * @deprecated use CourseManager::is_user_subscribed_in_course
6641
 *
6642
 * @param int $userid User id or NULL to get it from $_SESSION
6643
 * @param int $cid    course id to check whether the user is allowed
6644
 *
6645
 * @return bool
6646
 */
6647
function api_is_course_visible_for_user($userid = null, $cid = null)
6648
{
6649
    if ($userid === null) {
6650
        $userid = api_get_user_id();
6651
    }
6652
    if (empty($userid) || strval(intval($userid)) != $userid) {
6653
        if (api_is_anonymous()) {
6654
            $userid = api_get_anonymous_id();
6655
        } else {
6656
            return false;
6657
        }
6658
    }
6659
    $cid = Database::escape_string($cid);
6660
6661
    $courseInfo = api_get_course_info($cid);
6662
    $courseId = $courseInfo['real_id'];
6663
    $is_platformAdmin = api_is_platform_admin();
6664
6665
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
6666
    $course_cat_table = Database::get_main_table(TABLE_MAIN_CATEGORY);
6667
6668
    $sql = "SELECT
6669
                $course_table.category_code,
6670
                $course_table.visibility,
6671
                $course_table.code,
6672
                $course_cat_table.code
6673
            FROM $course_table
6674
            LEFT JOIN $course_cat_table
6675
                ON $course_table.category_code = $course_cat_table.code
6676
            WHERE
6677
                $course_table.code = '$cid'
6678
            LIMIT 1";
6679
6680
    $result = Database::query($sql);
6681
6682
    if (Database::num_rows($result) > 0) {
6683
        $visibility = Database::fetch_array($result);
6684
        $visibility = $visibility['visibility'];
6685
    } else {
6686
        $visibility = 0;
6687
    }
6688
    // Shortcut permissions in case the visibility is "open to the world".
6689
    if ($visibility === COURSE_VISIBILITY_OPEN_WORLD) {
6690
        return true;
6691
    }
6692
6693
    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6694
6695
    $sql = "SELECT
6696
                is_tutor, status
6697
            FROM $tbl_course_user
6698
            WHERE
6699
                user_id  = '$userid' AND
6700
                relation_type <> '".COURSE_RELATION_TYPE_RRHH."' AND
6701
                c_id = $courseId
6702
            LIMIT 1";
6703
6704
    $result = Database::query($sql);
6705
6706
    if (Database::num_rows($result) > 0) {
6707
        // This user has got a recorded state for this course.
6708
        $cuData = Database::fetch_array($result);
6709
        $is_courseMember = true;
6710
        $is_courseAdmin = ($cuData['status'] == 1);
6711
    }
6712
6713
    if (!$is_courseAdmin) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_courseAdmin does not seem to be defined for all execution paths leading up to this point.
Loading history...
6714
        // This user has no status related to this course.
6715
        // Is it the session coach or the session admin?
6716
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
6717
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
6718
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6719
6720
        $sql = "SELECT
6721
                    session.id_coach, session_admin_id, session.id
6722
                FROM
6723
                    $tbl_session as session
6724
                INNER JOIN $tbl_session_course
6725
                    ON session_rel_course.session_id = session.id
6726
                    AND session_rel_course.c_id = '$courseId'
6727
                LIMIT 1";
6728
6729
        $result = Database::query($sql);
6730
        $row = Database::store_result($result);
6731
6732
        if ($row[0]['id_coach'] == $userid) {
6733
            $is_courseMember = true;
6734
            $is_courseAdmin = false;
6735
        } elseif ($row[0]['session_admin_id'] == $userid) {
6736
            $is_courseMember = false;
6737
            $is_courseAdmin = false;
6738
        } else {
6739
            // Check if the current user is the course coach.
6740
            $sql = "SELECT 1
6741
                    FROM $tbl_session_course
6742
                    WHERE session_rel_course.c_id = '$courseId'
6743
                    AND session_rel_course.id_coach = '$userid'
6744
                    LIMIT 1";
6745
6746
            $result = Database::query($sql);
6747
6748
            //if ($row = Database::fetch_array($result)) {
6749
            if (Database::num_rows($result) > 0) {
6750
                $is_courseMember = true;
6751
                $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
6752
6753
                $sql = "SELECT status FROM $tbl_user
6754
                        WHERE user_id = $userid
6755
                        LIMIT 1";
6756
6757
                $result = Database::query($sql);
6758
6759
                if (Database::result($result, 0, 0) == 1) {
6760
                    $is_courseAdmin = true;
6761
                } else {
6762
                    $is_courseAdmin = false;
6763
                }
6764
            } else {
6765
                // Check if the user is a student is this session.
6766
                $sql = "SELECT  id
6767
                        FROM $tbl_session_course_user
6768
                        WHERE
6769
                            user_id  = '$userid' AND
6770
                            c_id = '$courseId'
6771
                        LIMIT 1";
6772
6773
                if (Database::num_rows($result) > 0) {
6774
                    // This user haa got a recorded state for this course.
6775
                    while ($row = Database::fetch_array($result)) {
6776
                        $is_courseMember = true;
6777
                        $is_courseAdmin = false;
6778
                    }
6779
                }
6780
            }
6781
        }
6782
    }
6783
6784
    switch ($visibility) {
6785
        case COURSE_VISIBILITY_OPEN_WORLD:
6786
            return true;
6787
        case COURSE_VISIBILITY_OPEN_PLATFORM:
6788
            return isset($userid);
6789
        case COURSE_VISIBILITY_REGISTERED:
6790
        case COURSE_VISIBILITY_CLOSED:
6791
            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...
6792
        case COURSE_VISIBILITY_HIDDEN:
6793
            return $is_platformAdmin;
6794
    }
6795
6796
    return false;
6797
}
6798
6799
/**
6800
 * Returns whether an element (forum, message, survey ...) belongs to a session or not.
6801
 *
6802
 * @param string the tool of the element
6803
 * @param int the element id in database
6804
 * @param int the session_id to compare with element session id
6805
 *
6806
 * @return bool true if the element is in the session, false else
6807
 */
6808
function api_is_element_in_the_session($tool, $element_id, $session_id = null)
6809
{
6810
    if (is_null($session_id)) {
6811
        $session_id = api_get_session_id();
6812
    }
6813
6814
    $element_id = (int) $element_id;
6815
6816
    if (empty($element_id)) {
6817
        return false;
6818
    }
6819
6820
    // Get information to build query depending of the tool.
6821
    switch ($tool) {
6822
        case TOOL_SURVEY:
6823
            $table_tool = Database::get_course_table(TABLE_SURVEY);
6824
            $key_field = 'survey_id';
6825
            break;
6826
        case TOOL_ANNOUNCEMENT:
6827
            $table_tool = Database::get_course_table(TABLE_ANNOUNCEMENT);
6828
            $key_field = 'id';
6829
            break;
6830
        case TOOL_AGENDA:
6831
            $table_tool = Database::get_course_table(TABLE_AGENDA);
6832
            $key_field = 'id';
6833
            break;
6834
        case TOOL_GROUP:
6835
            $table_tool = Database::get_course_table(TABLE_GROUP);
6836
            $key_field = 'id';
6837
            break;
6838
        default:
6839
            return false;
6840
    }
6841
    $course_id = api_get_course_int_id();
6842
6843
    $sql = "SELECT session_id FROM $table_tool
6844
            WHERE c_id = $course_id AND $key_field =  ".$element_id;
6845
    $rs = Database::query($sql);
6846
    if ($element_session_id = Database::result($rs, 0, 0)) {
6847
        if ($element_session_id == intval($session_id)) {
6848
            // The element belongs to the session.
6849
            return true;
6850
        }
6851
    }
6852
6853
    return false;
6854
}
6855
6856
/**
6857
 * Replaces "forbidden" characters in a filename string.
6858
 *
6859
 * @param string $filename
6860
 * @param bool   $treat_spaces_as_hyphens
6861
 *
6862
 * @return string
6863
 */
6864
function api_replace_dangerous_char($filename, $treat_spaces_as_hyphens = true)
6865
{
6866
    // Some non-properly encoded file names can cause the whole file to be
6867
    // skipped when uploaded. Avoid this by detecting the encoding and
6868
    // converting to UTF-8, setting the source as ASCII (a reasonably
6869
    // limited characters set) if nothing could be found (BT#
6870
    $encoding = api_detect_encoding($filename);
6871
    if (empty($encoding)) {
6872
        $encoding = 'ASCII';
6873
        if (!api_is_valid_ascii($filename)) {
6874
            // try iconv and try non standard ASCII a.k.a CP437
6875
            // see BT#15022
6876
            if (function_exists('iconv')) {
6877
                $result = iconv('CP437', 'UTF-8', $filename);
6878
                if (api_is_valid_utf8($result)) {
6879
                    $filename = $result;
6880
                    $encoding = 'UTF-8';
6881
                }
6882
            }
6883
        }
6884
    }
6885
6886
    $filename = api_to_system_encoding($filename, $encoding);
6887
6888
    $url = URLify::filter(
6889
        $filename,
6890
        250,
6891
        '',
6892
        true,
6893
        false,
6894
        false,
6895
        false,
6896
        $treat_spaces_as_hyphens
6897
    );
6898
6899
    // Replace multiple dots at the end.
6900
    $regex = "/\.+$/";
6901
    $url = preg_replace($regex, '', $url);
6902
6903
    return $url;
6904
}
6905
6906
/**
6907
 * Fixes the $_SERVER['REQUEST_URI'] that is empty in IIS6.
6908
 *
6909
 * @author Ivan Tcholakov, 28-JUN-2006.
6910
 */
6911
function api_request_uri()
6912
{
6913
    if (!empty($_SERVER['REQUEST_URI'])) {
6914
        return $_SERVER['REQUEST_URI'];
6915
    }
6916
    $uri = $_SERVER['SCRIPT_NAME'];
6917
    if (!empty($_SERVER['QUERY_STRING'])) {
6918
        $uri .= '?'.$_SERVER['QUERY_STRING'];
6919
    }
6920
    $_SERVER['REQUEST_URI'] = $uri;
6921
6922
    return $uri;
6923
}
6924
6925
/**
6926
 * Gets the current access_url id of the Chamilo Platform.
6927
 *
6928
 * @return int access_url_id of the current Chamilo Installation or 1 if multiple_access_urls is not enabled
6929
 *
6930
 * @author Julio Montoya <[email protected]>
6931
 */
6932
function api_get_current_access_url_id()
6933
{
6934
    if ('cli' === PHP_SAPI) {
6935
        $accessUrlId = api_get_configuration_value('access_url');
6936
        if (!empty($accessUrlId)) {
6937
            return $accessUrlId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $accessUrlId also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
6938
        }
6939
    }
6940
6941
    static $id;
6942
    if (!empty($id)) {
6943
        return (int) $id;
6944
    }
6945
6946
    if (!api_get_multiple_access_url()) {
6947
        // If the feature is not enabled, assume 1 and return before querying
6948
        // the database
6949
        return 1;
6950
    }
6951
6952
    $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6953
    $path = Database::escape_string(api_get_path(WEB_PATH));
6954
    $sql = "SELECT id FROM $table WHERE url = '".$path."'";
6955
    $result = Database::query($sql);
6956
    if (Database::num_rows($result) > 0) {
6957
        $id = Database::result($result, 0, 0);
6958
        if ($id === false) {
6959
            return -1;
6960
        }
6961
6962
        return (int) $id;
6963
    }
6964
6965
    $id = 1;
6966
6967
    //if the url in WEB_PATH was not found, it can only mean that there is
6968
    // either a configuration problem or the first URL has not been defined yet
6969
    // (by default it is http://localhost/). Thus the more sensible thing we can
6970
    // do is return 1 (the main URL) as the user cannot hack this value anyway
6971
    return 1;
6972
}
6973
6974
/**
6975
 * Gets the registered urls from a given user id.
6976
 *
6977
 * @param int $user_id
6978
 * @param int $checkCourseId the course id to check url access
6979
 *
6980
 * @return array
6981
 *
6982
 * @author Julio Montoya <[email protected]>
6983
 */
6984
function api_get_access_url_from_user($user_id, $checkCourseId = null)
6985
{
6986
    $user_id = (int) $user_id;
6987
    $table_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
6988
    $table_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
6989
    $includeIds = "";
6990
    if (isset($checkCourseId)) {
6991
        $cid = (int) $checkCourseId;
6992
        $tblUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
6993
        $sql = "SELECT access_url_id
6994
            FROM $tblUrlCourse url_rel_course
6995
            INNER JOIN $table_url u
6996
            ON (url_rel_course.access_url_id = u.id)
6997
            WHERE c_id = $cid";
6998
        $rs = Database::query($sql);
6999
        $courseUrlIds = [];
7000
        if (Database::num_rows($rs) > 0) {
7001
            while ($rowC = Database::fetch_array($rs, 'ASSOC')) {
7002
                $courseUrlIds[] = $rowC['access_url_id'];
7003
            }
7004
        }
7005
        if (!empty($courseUrlIds)) {
7006
            $includeIds = " AND access_url_id IN (".implode(',', $courseUrlIds).")";
7007
        }
7008
    }
7009
7010
    $sql = "SELECT access_url_id
7011
            FROM $table_url_rel_user url_rel_user
7012
            INNER JOIN $table_url u
7013
            ON (url_rel_user.access_url_id = u.id)
7014
            WHERE user_id = $user_id $includeIds
7015
            ORDER BY access_url_id";
7016
    $result = Database::query($sql);
7017
    $list = [];
7018
    while ($row = Database::fetch_array($result, 'ASSOC')) {
7019
        $list[] = $row['access_url_id'];
7020
    }
7021
7022
    return $list;
7023
}
7024
7025
/**
7026
 * Gets the status of a user in a course.
7027
 *
7028
 * @param int $user_id
7029
 * @param int $courseId
7030
 *
7031
 * @return int user status
7032
 */
7033
function api_get_status_of_user_in_course($user_id, $courseId)
7034
{
7035
    $tbl_rel_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
7036
    if (!empty($user_id) && !empty($courseId)) {
7037
        $user_id = intval($user_id);
7038
        $courseId = intval($courseId);
7039
        $sql = 'SELECT status
7040
                FROM '.$tbl_rel_course_user.'
7041
                WHERE user_id='.$user_id.' AND c_id = '.$courseId;
7042
        $result = Database::query($sql);
7043
        $row_status = Database::fetch_array($result, 'ASSOC');
7044
7045
        return $row_status['status'];
7046
    } else {
7047
        return 0;
7048
    }
7049
}
7050
7051
/**
7052
 * Checks whether the curent user is in a group or not.
7053
 *
7054
 * @param string        The group id - optional (takes it from session if not given)
7055
 * @param string        The course code - optional (no additional check by course if course code is not given)
7056
 *
7057
 * @return bool
7058
 *
7059
 * @author Ivan Tcholakov
7060
 */
7061
function api_is_in_group($groupIdParam = null, $courseCodeParam = null)
7062
{
7063
    if (!empty($courseCodeParam)) {
7064
        $courseCode = api_get_course_id();
7065
        if (!empty($courseCode)) {
7066
            if ($courseCodeParam != $courseCode) {
7067
                return false;
7068
            }
7069
        } else {
7070
            return false;
7071
        }
7072
    }
7073
7074
    $groupId = api_get_group_id();
7075
7076
    if (!empty($groupId)) {
7077
        if (!empty($groupIdParam)) {
7078
            return $groupIdParam == $groupId;
7079
        } else {
7080
            return true;
7081
        }
7082
    }
7083
7084
    return false;
7085
}
7086
7087
/**
7088
 * Checks whether a secret key is valid.
7089
 *
7090
 * @param string $original_key_secret - secret key from (webservice) client
7091
 * @param string $security_key        - security key from Chamilo
7092
 *
7093
 * @return bool - true if secret key is valid, false otherwise
7094
 */
7095
function api_is_valid_secret_key($original_key_secret, $security_key)
7096
{
7097
    if (empty($original_key_secret) || empty($security_key)) {
7098
        return false;
7099
    }
7100
7101
    return (string) $original_key_secret === sha1($security_key);
7102
}
7103
7104
/**
7105
 * Checks whether a user is into course.
7106
 *
7107
 * @param int $course_id - the course id
7108
 * @param int $user_id   - the user id
7109
 *
7110
 * @return bool
7111
 */
7112
function api_is_user_of_course($course_id, $user_id)
7113
{
7114
    $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
7115
    $sql = 'SELECT user_id FROM '.$tbl_course_rel_user.'
7116
            WHERE
7117
                c_id ="'.intval($course_id).'" AND
7118
                user_id = "'.intval($user_id).'" AND
7119
                relation_type <> '.COURSE_RELATION_TYPE_RRHH.' ';
7120
    $result = Database::query($sql);
7121
7122
    return Database::num_rows($result) == 1;
7123
}
7124
7125
/**
7126
 * Checks whether the server's operating system is Windows (TM).
7127
 *
7128
 * @return bool - true if the operating system is Windows, false otherwise
7129
 */
7130
function api_is_windows_os()
7131
{
7132
    if (function_exists('php_uname')) {
7133
        // php_uname() exists as of PHP 4.0.2, according to the documentation.
7134
        // We expect that this function will always work for Chamilo 1.8.x.
7135
        $os = php_uname();
7136
    }
7137
    // The following methods are not needed, but let them stay, just in case.
7138
    elseif (isset($_ENV['OS'])) {
7139
        // Sometimes $_ENV['OS'] may not be present (bugs?)
7140
        $os = $_ENV['OS'];
7141
    } elseif (defined('PHP_OS')) {
7142
        // PHP_OS means on which OS PHP was compiled, this is why
7143
        // using PHP_OS is the last choice for detection.
7144
        $os = PHP_OS;
7145
    } else {
7146
        return false;
7147
    }
7148
7149
    return strtolower(substr((string) $os, 0, 3)) == 'win';
7150
}
7151
7152
/**
7153
 * This function informs whether the sent request is XMLHttpRequest.
7154
 */
7155
function api_is_xml_http_request()
7156
{
7157
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
7158
}
7159
7160
/**
7161
 * This wrapper function has been implemented for avoiding some known problems about the function getimagesize().
7162
 *
7163
 * @see http://php.net/manual/en/function.getimagesize.php
7164
 * @see http://www.dokeos.com/forum/viewtopic.php?t=12345
7165
 * @see http://www.dokeos.com/forum/viewtopic.php?t=16355
7166
 *
7167
 * @return int
7168
 */
7169
function api_getimagesize($path)
7170
{
7171
    $image = new Image($path);
7172
7173
    return $image->get_image_size();
7174
}
7175
7176
/**
7177
 * This function resizes an image, with preserving its proportions (or aspect ratio).
7178
 *
7179
 * @author Ivan Tcholakov, MAY-2009.
7180
 *
7181
 * @param int $image         System path or URL of the image
7182
 * @param int $target_width  Targeted width
7183
 * @param int $target_height Targeted height
7184
 *
7185
 * @return array Calculated new width and height
7186
 */
7187
function api_resize_image($image, $target_width, $target_height)
7188
{
7189
    $image_properties = api_getimagesize($image);
7190
7191
    return api_calculate_image_size(
7192
        $image_properties['width'],
7193
        $image_properties['height'],
7194
        $target_width,
7195
        $target_height
7196
    );
7197
}
7198
7199
/**
7200
 * This function calculates new image size, with preserving image's proportions (or aspect ratio).
7201
 *
7202
 * @author Ivan Tcholakov, MAY-2009.
7203
 * @author The initial idea has been taken from code by Patrick Cool, MAY-2004.
7204
 *
7205
 * @param int $image_width   Initial width
7206
 * @param int $image_height  Initial height
7207
 * @param int $target_width  Targeted width
7208
 * @param int $target_height Targeted height
7209
 *
7210
 * @return array Calculated new width and height
7211
 */
7212
function api_calculate_image_size(
7213
    $image_width,
7214
    $image_height,
7215
    $target_width,
7216
    $target_height
7217
) {
7218
    // Only maths is here.
7219
    $result = ['width' => $image_width, 'height' => $image_height];
7220
    if ($image_width <= 0 || $image_height <= 0) {
7221
        return $result;
7222
    }
7223
    $resize_factor_width = $target_width / $image_width;
7224
    $resize_factor_height = $target_height / $image_height;
7225
    $delta_width = $target_width - $image_width * $resize_factor_height;
7226
    $delta_height = $target_height - $image_height * $resize_factor_width;
7227
    if ($delta_width > $delta_height) {
7228
        $result['width'] = ceil($image_width * $resize_factor_height);
7229
        $result['height'] = ceil($image_height * $resize_factor_height);
7230
    } elseif ($delta_width < $delta_height) {
7231
        $result['width'] = ceil($image_width * $resize_factor_width);
7232
        $result['height'] = ceil($image_height * $resize_factor_width);
7233
    } else {
7234
        $result['width'] = ceil($target_width);
7235
        $result['height'] = ceil($target_height);
7236
    }
7237
7238
    return $result;
7239
}
7240
7241
/**
7242
 * Returns a list of Chamilo's tools or
7243
 * checks whether a given identificator is a valid Chamilo's tool.
7244
 *
7245
 * @author Isaac flores paz
7246
 *
7247
 * @param string The tool name to filter
7248
 *
7249
 * @return mixed Filtered string or array
7250
 */
7251
function api_get_tools_lists($my_tool = null)
7252
{
7253
    $tools_list = [
7254
        TOOL_DOCUMENT,
7255
        TOOL_THUMBNAIL,
7256
        TOOL_HOTPOTATOES,
7257
        TOOL_CALENDAR_EVENT,
7258
        TOOL_LINK,
7259
        TOOL_COURSE_DESCRIPTION,
7260
        TOOL_SEARCH,
7261
        TOOL_LEARNPATH,
7262
        TOOL_ANNOUNCEMENT,
7263
        TOOL_FORUM,
7264
        TOOL_THREAD,
7265
        TOOL_POST,
7266
        TOOL_DROPBOX,
7267
        TOOL_QUIZ,
7268
        TOOL_USER,
7269
        TOOL_GROUP,
7270
        TOOL_BLOGS,
7271
        TOOL_CHAT,
7272
        TOOL_STUDENTPUBLICATION,
7273
        TOOL_TRACKING,
7274
        TOOL_HOMEPAGE_LINK,
7275
        TOOL_COURSE_SETTING,
7276
        TOOL_BACKUP,
7277
        TOOL_COPY_COURSE_CONTENT,
7278
        TOOL_RECYCLE_COURSE,
7279
        TOOL_COURSE_HOMEPAGE,
7280
        TOOL_COURSE_RIGHTS_OVERVIEW,
7281
        TOOL_UPLOAD,
7282
        TOOL_COURSE_MAINTENANCE,
7283
        TOOL_SURVEY,
7284
        TOOL_WIKI,
7285
        TOOL_GLOSSARY,
7286
        TOOL_GRADEBOOK,
7287
        TOOL_NOTEBOOK,
7288
        TOOL_ATTENDANCE,
7289
        TOOL_COURSE_PROGRESS,
7290
    ];
7291
    if (empty($my_tool)) {
7292
        return $tools_list;
7293
    }
7294
7295
    return in_array($my_tool, $tools_list) ? $my_tool : '';
7296
}
7297
7298
/**
7299
 * Checks whether we already approved the last version term and condition.
7300
 *
7301
 * @param int user id
7302
 *
7303
 * @return bool true if we pass false otherwise
7304
 */
7305
function api_check_term_condition($userId)
7306
{
7307
    if (api_get_setting('allow_terms_conditions') === 'true') {
7308
        // Check if exists terms and conditions
7309
        if (LegalManager::count() == 0) {
7310
            return true;
7311
        }
7312
7313
        $extraFieldValue = new ExtraFieldValue('user');
7314
        $data = $extraFieldValue->get_values_by_handler_and_field_variable(
7315
            $userId,
7316
            'legal_accept'
7317
        );
7318
7319
        if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
7320
            $result = $data['value'];
7321
            $user_conditions = explode(':', $result);
7322
            $version = $user_conditions[0];
7323
            $langId = $user_conditions[1];
7324
            $realVersion = LegalManager::get_last_version($langId);
7325
7326
            return $version >= $realVersion;
7327
        }
7328
7329
        return false;
7330
    }
7331
7332
    return false;
7333
}
7334
7335
/**
7336
 * Gets all information of a tool into course.
7337
 *
7338
 * @param int The tool id
7339
 *
7340
 * @return array
7341
 */
7342
function api_get_tool_information_by_name($name)
7343
{
7344
    $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
7345
    $course_id = api_get_course_int_id();
7346
    $sql = "SELECT * FROM $t_tool
7347
            WHERE c_id = $course_id  AND name = '".Database::escape_string($name)."' ";
7348
    $rs = Database::query($sql);
7349
7350
    return Database::fetch_array($rs, 'ASSOC');
7351
}
7352
7353
/**
7354
 * Function used to protect a "global" admin script.
7355
 * The function blocks access when the user has no global platform admin rights.
7356
 * Global admins are the admins that are registered in the main.admin table
7357
 * AND the users who have access to the "principal" portal.
7358
 * That means that there is a record in the main.access_url_rel_user table
7359
 * with his user id and the access_url_id=1.
7360
 *
7361
 * @author Julio Montoya
7362
 *
7363
 * @param int $user_id
7364
 *
7365
 * @return bool
7366
 */
7367
function api_is_global_platform_admin($user_id = null)
7368
{
7369
    $user_id = (int) $user_id;
7370
    if (empty($user_id)) {
7371
        $user_id = api_get_user_id();
7372
    }
7373
    if (api_is_platform_admin_by_id($user_id)) {
7374
        $urlList = api_get_access_url_from_user($user_id);
7375
        // The admin is registered in the first "main" site with access_url_id = 1
7376
        if (in_array(1, $urlList)) {
7377
            return true;
7378
        } else {
7379
            return false;
7380
        }
7381
    }
7382
7383
    return false;
7384
}
7385
7386
/**
7387
 * @param int  $admin_id_to_check
7388
 * @param int  $my_user_id
7389
 * @param bool $allow_session_admin
7390
 *
7391
 * @return bool
7392
 */
7393
function api_global_admin_can_edit_admin(
7394
    $admin_id_to_check,
7395
    $my_user_id = null,
7396
    $allow_session_admin = false
7397
) {
7398
    if (empty($my_user_id)) {
7399
        $my_user_id = api_get_user_id();
7400
    }
7401
7402
    $iam_a_global_admin = api_is_global_platform_admin($my_user_id);
7403
    $user_is_global_admin = api_is_global_platform_admin($admin_id_to_check);
7404
7405
    if ($iam_a_global_admin) {
7406
        // Global admin can edit everything
7407
        return true;
7408
    } else {
7409
        // If i'm a simple admin
7410
        $is_platform_admin = api_is_platform_admin_by_id($my_user_id);
7411
7412
        if ($allow_session_admin) {
7413
            $is_platform_admin = api_is_platform_admin_by_id($my_user_id) || (api_get_user_status($my_user_id) == SESSIONADMIN);
7414
        }
7415
7416
        if ($is_platform_admin) {
7417
            if ($user_is_global_admin) {
7418
                return false;
7419
            } else {
7420
                return true;
7421
            }
7422
        } else {
7423
            return false;
7424
        }
7425
    }
7426
}
7427
7428
/**
7429
 * @param int  $admin_id_to_check
7430
 * @param int  $my_user_id
7431
 * @param bool $allow_session_admin
7432
 *
7433
 * @return bool|null
7434
 */
7435
function api_protect_super_admin($admin_id_to_check, $my_user_id = null, $allow_session_admin = false)
7436
{
7437
    if (api_global_admin_can_edit_admin($admin_id_to_check, $my_user_id, $allow_session_admin)) {
7438
        return true;
7439
    } else {
7440
        api_not_allowed();
7441
    }
7442
}
7443
7444
/**
7445
 * Function used to protect a global admin script.
7446
 * The function blocks access when the user has no global platform admin rights.
7447
 * See also the api_is_global_platform_admin() function wich defines who's a "global" admin.
7448
 *
7449
 * @author Julio Montoya
7450
 */
7451
function api_protect_global_admin_script()
7452
{
7453
    if (!api_is_global_platform_admin()) {
7454
        api_not_allowed();
7455
7456
        return false;
7457
    }
7458
7459
    return true;
7460
}
7461
7462
/**
7463
 * Get active template.
7464
 *
7465
 * @param string    theme type (optional: default)
7466
 * @param string    path absolute(abs) or relative(rel) (optional:rel)
7467
 *
7468
 * @return string actived template path
7469
 */
7470
function api_get_template($path_type = 'rel')
7471
{
7472
    $path_types = ['rel', 'abs'];
7473
    $template_path = '';
7474
    if (in_array($path_type, $path_types)) {
7475
        if ($path_type == 'rel') {
7476
            $template_path = api_get_path(SYS_TEMPLATE_PATH);
7477
        } else {
7478
            $template_path = api_get_path(WEB_TEMPLATE_PATH);
7479
        }
7480
    }
7481
    $actived_theme = 'default';
7482
    if (api_get_setting('active_template')) {
7483
        $actived_theme = api_get_setting('active_template');
7484
    }
7485
    $actived_theme_path = $template_path.$actived_theme.DIRECTORY_SEPARATOR;
7486
7487
    return $actived_theme_path;
7488
}
7489
7490
/**
7491
 * Check browser support for specific file types or features
7492
 * This function checks if the user's browser supports a file format or given
7493
 * feature, or returns the current browser and major version when
7494
 * $format=check_browser. Only a limited number of formats and features are
7495
 * checked by this method. Make sure you check its definition first.
7496
 *
7497
 * @param string $format Can be a file format (extension like svg, webm, ...) or a feature (like autocapitalize, ...)
7498
 *
7499
 * @return bool or return text array if $format=check_browser
7500
 *
7501
 * @author Juan Carlos Raña Trabado
7502
 */
7503
function api_browser_support($format = '')
7504
{
7505
    $browser = new Browser();
7506
    $current_browser = $browser->getBrowser();
7507
    $a_versiontemp = explode('.', $browser->getVersion());
7508
    $current_majorver = $a_versiontemp[0];
7509
7510
    static $result;
7511
7512
    if (isset($result[$format])) {
7513
        return $result[$format];
7514
    }
7515
7516
    // Native svg support
7517
    if ($format == 'svg') {
7518
        if (($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7519
            ($current_browser == 'Firefox' && $current_majorver > 1) ||
7520
            ($current_browser == 'Safari' && $current_majorver >= 4) ||
7521
            ($current_browser == 'Chrome' && $current_majorver >= 1) ||
7522
            ($current_browser == 'Opera' && $current_majorver >= 9)
7523
        ) {
7524
            $result[$format] = true;
7525
7526
            return true;
7527
        } else {
7528
            $result[$format] = false;
7529
7530
            return false;
7531
        }
7532
    } elseif ($format == 'pdf') {
7533
        // native pdf support
7534
        if (($current_browser == 'Chrome' && $current_majorver >= 6) ||
7535
            ('Firefox' === $current_browser && $current_majorver >= 15)
7536
        ) {
7537
            $result[$format] = true;
7538
7539
            return true;
7540
        } else {
7541
            $result[$format] = false;
7542
7543
            return false;
7544
        }
7545
    } elseif ($format == 'tif' || $format == 'tiff') {
7546
        //native tif support
7547
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7548
            $result[$format] = true;
7549
7550
            return true;
7551
        } else {
7552
            $result[$format] = false;
7553
7554
            return false;
7555
        }
7556
    } elseif ($format == 'ogg' || $format == 'ogx' || $format == 'ogv' || $format == 'oga') {
7557
        //native ogg, ogv,oga support
7558
        if (($current_browser == 'Firefox' && $current_majorver >= 3) ||
7559
            ($current_browser == 'Chrome' && $current_majorver >= 3) ||
7560
            ($current_browser == 'Opera' && $current_majorver >= 9)) {
7561
            $result[$format] = true;
7562
7563
            return true;
7564
        } else {
7565
            $result[$format] = false;
7566
7567
            return false;
7568
        }
7569
    } elseif ($format == 'mpg' || $format == 'mpeg') {
7570
        //native mpg support
7571
        if (($current_browser == 'Safari' && $current_majorver >= 5)) {
7572
            $result[$format] = true;
7573
7574
            return true;
7575
        } else {
7576
            $result[$format] = false;
7577
7578
            return false;
7579
        }
7580
    } elseif ($format == 'mp4') {
7581
        //native mp4 support (TODO: Android, iPhone)
7582
        if ($current_browser == 'Android' || $current_browser == 'iPhone') {
7583
            $result[$format] = true;
7584
7585
            return true;
7586
        } else {
7587
            $result[$format] = false;
7588
7589
            return false;
7590
        }
7591
    } elseif ($format == 'mov') {
7592
        //native mov support( TODO:check iPhone)
7593
        if ($current_browser == 'Safari' && $current_majorver >= 5 || $current_browser == 'iPhone') {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($current_browser == 'Sa...ent_browser == 'iPhone', Probably Intended Meaning: $current_browser == 'Saf...nt_browser == 'iPhone')
Loading history...
7594
            $result[$format] = true;
7595
7596
            return true;
7597
        } else {
7598
            $result[$format] = false;
7599
7600
            return false;
7601
        }
7602
    } elseif ($format == 'avi') {
7603
        //native avi support
7604
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7605
            $result[$format] = true;
7606
7607
            return true;
7608
        } else {
7609
            $result[$format] = false;
7610
7611
            return false;
7612
        }
7613
    } elseif ($format == 'wmv') {
7614
        //native wmv support
7615
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7616
            $result[$format] = true;
7617
7618
            return true;
7619
        } else {
7620
            $result[$format] = false;
7621
7622
            return false;
7623
        }
7624
    } elseif ($format == 'webm') {
7625
        //native webm support (TODO:check IE9, Chrome9, Android)
7626
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7627
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7628
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7629
            ($current_browser == 'Chrome' && $current_majorver >= 9) ||
7630
            $current_browser == 'Android'
7631
        ) {
7632
            $result[$format] = true;
7633
7634
            return true;
7635
        } else {
7636
            $result[$format] = false;
7637
7638
            return false;
7639
        }
7640
    } elseif ($format == 'wav') {
7641
        //native wav support (only some codecs !)
7642
        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
7643
            ($current_browser == 'Safari' && $current_majorver >= 5) ||
7644
            ($current_browser == 'Opera' && $current_majorver >= 9) ||
7645
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7646
            ($current_browser == 'Chrome' && $current_majorver > 9) ||
7647
            $current_browser == 'Android' ||
7648
            $current_browser == 'iPhone'
7649
        ) {
7650
            $result[$format] = true;
7651
7652
            return true;
7653
        } else {
7654
            $result[$format] = false;
7655
7656
            return false;
7657
        }
7658
    } elseif ($format == 'mid' || $format == 'kar') {
7659
        //native midi support (TODO:check Android)
7660
        if ($current_browser == 'Opera' && $current_majorver >= 9 || $current_browser == 'Android') {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($current_browser == 'Op...nt_browser == 'Android', Probably Intended Meaning: $current_browser == 'Ope...t_browser == 'Android')
Loading history...
7661
            $result[$format] = true;
7662
7663
            return true;
7664
        } else {
7665
            $result[$format] = false;
7666
7667
            return false;
7668
        }
7669
    } elseif ($format == 'wma') {
7670
        //native wma support
7671
        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
7672
            $result[$format] = true;
7673
7674
            return true;
7675
        } else {
7676
            $result[$format] = false;
7677
7678
            return false;
7679
        }
7680
    } elseif ($format == 'au') {
7681
        //native au support
7682
        if ($current_browser == 'Safari' && $current_majorver >= 5) {
7683
            $result[$format] = true;
7684
7685
            return true;
7686
        } else {
7687
            $result[$format] = false;
7688
7689
            return false;
7690
        }
7691
    } elseif ($format == 'mp3') {
7692
        //native mp3 support (TODO:check Android, iPhone)
7693
        if (($current_browser == 'Safari' && $current_majorver >= 5) ||
7694
            ($current_browser == 'Chrome' && $current_majorver >= 6) ||
7695
            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
7696
            $current_browser == 'Android' ||
7697
            $current_browser == 'iPhone' ||
7698
            $current_browser == 'Firefox'
7699
        ) {
7700
            $result[$format] = true;
7701
7702
            return true;
7703
        } else {
7704
            $result[$format] = false;
7705
7706
            return false;
7707
        }
7708
    } elseif ($format == 'autocapitalize') {
7709
        // Help avoiding showing the autocapitalize option if the browser doesn't
7710
        // support it: this attribute is against the HTML5 standard
7711
        if ($current_browser == 'Safari' || $current_browser == 'iPhone') {
7712
            return true;
7713
        } else {
7714
            return false;
7715
        }
7716
    } elseif ($format == "check_browser") {
7717
        $array_check_browser = [$current_browser, $current_majorver];
7718
7719
        return $array_check_browser;
7720
    } else {
7721
        $result[$format] = false;
7722
7723
        return false;
7724
    }
7725
}
7726
7727
/**
7728
 * This function checks if exist path and file browscap.ini
7729
 * In order for this to work, your browscap configuration setting in php.ini
7730
 * must point to the correct location of the browscap.ini file on your system
7731
 * http://php.net/manual/en/function.get-browser.php.
7732
 *
7733
 * @return bool
7734
 *
7735
 * @author Juan Carlos Raña Trabado
7736
 */
7737
function api_check_browscap()
7738
{
7739
    $setting = ini_get('browscap');
7740
    if ($setting) {
7741
        $browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
7742
        if (strpos($setting, 'browscap.ini') && !empty($browser)) {
7743
            return true;
7744
        }
7745
    }
7746
7747
    return false;
7748
}
7749
7750
/**
7751
 * Returns the <script> HTML tag.
7752
 */
7753
function api_get_js($file)
7754
{
7755
    return '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/'.$file.'"></script>'."\n";
7756
}
7757
7758
/**
7759
 * Returns the <script> HTML tag.
7760
 *
7761
 * @return string
7762
 */
7763
function api_get_asset($file)
7764
{
7765
    return '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
7766
}
7767
7768
/**
7769
 * Returns the <script> HTML tag.
7770
 *
7771
 * @param string $file
7772
 * @param string $media
7773
 *
7774
 * @return string
7775
 */
7776
function api_get_css_asset($file, $media = 'screen')
7777
{
7778
    return '<link href="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7779
}
7780
7781
/**
7782
 * Returns the <link> HTML tag.
7783
 *
7784
 * @param string $file
7785
 * @param string $media
7786
 */
7787
function api_get_css($file, $media = 'screen')
7788
{
7789
    return '<link href="'.$file.'" rel="stylesheet" media="'.$media.'" type="text/css" />'."\n";
7790
}
7791
7792
/**
7793
 * Returns the js header to include the jquery library.
7794
 */
7795
function api_get_jquery_js()
7796
{
7797
    return api_get_asset('jquery/dist/jquery.min.js');
7798
}
7799
7800
/**
7801
 * Returns the jquery path.
7802
 *
7803
 * @return string
7804
 */
7805
function api_get_jquery_web_path()
7806
{
7807
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery/dist/jquery.min.js';
7808
}
7809
7810
/**
7811
 * @return string
7812
 */
7813
function api_get_jquery_ui_js_web_path()
7814
{
7815
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/jquery-ui.min.js';
7816
}
7817
7818
/**
7819
 * @return string
7820
 */
7821
function api_get_jquery_ui_css_web_path()
7822
{
7823
    return api_get_path(WEB_PUBLIC_PATH).'assets/jquery-ui/themes/smoothness/jquery-ui.min.css';
7824
}
7825
7826
/**
7827
 * Returns the jquery-ui library js headers.
7828
 *
7829
 * @param   bool    add the jqgrid library
7830
 *
7831
 * @return string html tags
7832
 */
7833
function api_get_jquery_ui_js($include_jqgrid = false)
7834
{
7835
    $libraries = [];
7836
    if ($include_jqgrid) {
7837
        $libraries[] = 'jqgrid';
7838
    }
7839
7840
    return api_get_jquery_libraries_js($libraries);
7841
}
7842
7843
function api_get_jqgrid_js()
7844
{
7845
    return api_get_jquery_libraries_js(['jqgrid']);
7846
}
7847
7848
/**
7849
 * Returns the jquery library js and css headers.
7850
 *
7851
 * @param   array   list of jquery libraries supported jquery-ui, jqgrid
7852
 * @param   bool    add the jquery library
7853
 *
7854
 * @return string html tags
7855
 */
7856
function api_get_jquery_libraries_js($libraries)
7857
{
7858
    $js = '';
7859
    $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
7860
7861
    //jqgrid js and css
7862
    if (in_array('jqgrid', $libraries)) {
7863
        $languaje = 'en';
7864
        $platform_isocode = strtolower(api_get_language_isocode());
7865
7866
        //languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7867
        $jqgrid_langs = [
7868
            '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',
7869
        ];
7870
7871
        if (in_array($platform_isocode, $jqgrid_langs)) {
7872
            $languaje = $platform_isocode;
7873
        }
7874
        //$js .= '<link rel="stylesheet" href="'.$js_path.'jqgrid/css/ui.jqgrid.css" type="text/css">';
7875
        $js .= api_get_css($js_path.'jqgrid/css/ui.jqgrid.css');
7876
        $js .= api_get_js('jqgrid/js/i18n/grid.locale-'.$languaje.'.js');
7877
        $js .= api_get_js('jqgrid/js/jquery.jqGrid.min.js');
7878
    }
7879
7880
    //Document multiple upload funcionality
7881
    if (in_array('jquery-upload', $libraries)) {
7882
        $js .= api_get_asset('blueimp-load-image/js/load-image.all.min.js');
7883
        $js .= api_get_asset('blueimp-canvas-to-blob/js/canvas-to-blob.min.js');
7884
        $js .= api_get_asset('jquery-file-upload/js/jquery.iframe-transport.js');
7885
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload.js');
7886
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-process.js');
7887
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-image.js');
7888
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-audio.js');
7889
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-video.js');
7890
        $js .= api_get_asset('jquery-file-upload/js/jquery.fileupload-validate.js');
7891
7892
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload.css');
7893
        $js .= api_get_css(api_get_path(WEB_PUBLIC_PATH).'assets/jquery-file-upload/css/jquery.fileupload-ui.css');
7894
    }
7895
7896
    // jquery datepicker
7897
    if (in_array('datepicker', $libraries)) {
7898
        $languaje = 'en-GB';
7899
        $platform_isocode = strtolower(api_get_language_isocode());
7900
7901
        // languages supported by jqgrid see files in main/inc/lib/javascript/jqgrid/js/i18n
7902
        $datapicker_langs = [
7903
            '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',
7904
        ];
7905
        if (in_array($platform_isocode, $datapicker_langs)) {
7906
            $languaje = $platform_isocode;
7907
        }
7908
7909
        $js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
7910
        $script = '<script>
7911
        $(function(){
7912
            $.datepicker.setDefaults($.datepicker.regional["'.$languaje.'"]);
7913
            $.datepicker.regional["local"] = $.datepicker.regional["'.$languaje.'"];
7914
        });
7915
        </script>
7916
        ';
7917
        $js .= $script;
7918
    }
7919
7920
    return $js;
7921
}
7922
7923
/**
7924
 * Returns the URL to the course or session, removing the complexity of the URL
7925
 * building piece by piece.
7926
 *
7927
 * This function relies on api_get_course_info()
7928
 *
7929
 * @param string $courseCode The course code - optional (takes it from context if not given)
7930
 * @param int    $sessionId  The session ID  - optional (takes it from context if not given)
7931
 * @param int    $groupId    The group ID - optional (takes it from context if not given)
7932
 *
7933
 * @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
7934
 *
7935
 * @author  Julio Montoya <[email protected]>
7936
 */
7937
function api_get_course_url($courseCode = null, $sessionId = null, $groupId = null)
7938
{
7939
    $courseDirectory = '';
7940
    $url = '';
7941
    // If courseCode not set, get context or []
7942
    if (empty($courseCode)) {
7943
        $courseInfo = api_get_course_info();
7944
    } else {
7945
        $courseInfo = api_get_course_info($courseCode);
7946
    }
7947
7948
    // If course defined, get directory, otherwise keep empty string
7949
    if (!empty($courseInfo['directory'])) {
7950
        $courseDirectory = $courseInfo['directory'];
7951
    }
7952
7953
    // If sessionId not set, get context or 0
7954
    if (empty($sessionId)) {
7955
        $sessionId = api_get_session_id();
7956
    }
7957
7958
    // If groupId not set, get context or 0
7959
    if (empty($groupId)) {
7960
        $groupId = api_get_group_id();
7961
    }
7962
7963
    // Build the URL
7964
    if (!empty($courseDirectory)) {
7965
        // directory not empty, so we do have a course
7966
        $url = api_get_path(WEB_COURSE_PATH).$courseDirectory.'/index.php?id_session='.$sessionId.'&gidReq='.$groupId;
7967
    } elseif (!empty($sessionId) && api_get_configuration_value('remove_session_url') !== true) {
7968
        // if the course was unset and the session was set, send directly to the session
7969
        $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
7970
    }
7971
    // if not valid combination was found, return an empty string
7972
    return $url;
7973
}
7974
7975
/**
7976
 * Check if the current portal has the $_configuration['multiple_access_urls'] parameter on.
7977
 *
7978
 * @return bool true if multi site is enabled
7979
 */
7980
function api_get_multiple_access_url()
7981
{
7982
    global $_configuration;
7983
    if (isset($_configuration['multiple_access_urls']) && $_configuration['multiple_access_urls']) {
7984
        return true;
7985
    }
7986
7987
    return false;
7988
}
7989
7990
/**
7991
 * Just a synonym for api_get_multiple_access_url().
7992
 *
7993
 * @return bool
7994
 */
7995
function api_is_multiple_url_enabled()
7996
{
7997
    return api_get_multiple_access_url();
7998
}
7999
8000
/**
8001
 * Returns a md5 unique id.
8002
 *
8003
 * @todo add more parameters
8004
 */
8005
function api_get_unique_id()
8006
{
8007
    $id = md5(time().uniqid().api_get_user_id().api_get_course_id().api_get_session_id());
8008
8009
    return $id;
8010
}
8011
8012
/**
8013
 * Get home path.
8014
 *
8015
 * @return string
8016
 */
8017
function api_get_home_path()
8018
{
8019
    // FIX : Start the routing determination from central path definition
8020
    $home = api_get_path(SYS_HOME_PATH);
8021
    if (api_get_multiple_access_url()) {
8022
        $access_url_id = api_get_current_access_url_id();
8023
        $url_info = api_get_access_url($access_url_id);
8024
        $url = api_remove_trailing_slash(preg_replace('/https?:\/\//i', '', $url_info['url']));
8025
        $clean_url = api_replace_dangerous_char($url);
8026
        $clean_url = str_replace('/', '-', $clean_url);
8027
        $clean_url .= '/';
8028
        if ($clean_url != 'localhost/') {
8029
            // means that the multiple URL was not well configured we don't rename the $home variable
8030
            return "{$home}{$clean_url}";
8031
        }
8032
    }
8033
8034
    return $home;
8035
}
8036
8037
/**
8038
 * @param int Course id
8039
 * @param int tool id: TOOL_QUIZ, TOOL_FORUM, TOOL_STUDENTPUBLICATION, TOOL_LEARNPATH
8040
 * @param int the item id (tool id, exercise id, lp id)
8041
 *
8042
 * @return bool
8043
 */
8044
function api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code = null)
8045
{
8046
    if (api_is_platform_admin()) {
8047
        return false;
8048
    }
8049
    if (api_get_setting('gradebook_locking_enabled') == 'true') {
8050
        if (empty($course_code)) {
8051
            $course_code = api_get_course_id();
8052
        }
8053
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
8054
        $item_id = intval($item_id);
8055
        $link_type = intval($link_type);
8056
        $course_code = Database::escape_string($course_code);
8057
        $sql = "SELECT locked FROM $table
8058
                WHERE locked = 1 AND ref_id = $item_id AND type = $link_type AND course_code = '$course_code' ";
8059
        $result = Database::query($sql);
8060
        if (Database::num_rows($result)) {
8061
            return true;
8062
        }
8063
    }
8064
8065
    return false;
8066
}
8067
8068
/**
8069
 * Blocks a page if the item was added in a gradebook.
8070
 *
8071
 * @param int       exercise id, work id, thread id,
8072
 * @param int       LINK_EXERCISE, LINK_STUDENTPUBLICATION, LINK_LEARNPATH LINK_FORUM_THREAD, LINK_ATTENDANCE
8073
 * see gradebook/lib/be/linkfactory
8074
 * @param string    course code
8075
 *
8076
 * @return false|null
8077
 */
8078
function api_block_course_item_locked_by_gradebook($item_id, $link_type, $course_code = null)
8079
{
8080
    if (api_is_platform_admin()) {
8081
        return false;
8082
    }
8083
8084
    if (api_resource_is_locked_by_gradebook($item_id, $link_type, $course_code)) {
8085
        $message = Display::return_message(get_lang('ResourceLockedByGradebook'), 'warning');
8086
        api_not_allowed(true, $message);
8087
    }
8088
}
8089
8090
/**
8091
 * Checks the PHP version installed is enough to run Chamilo.
8092
 *
8093
 * @param string Include path (used to load the error page)
8094
 */
8095
function api_check_php_version($my_inc_path = null)
8096
{
8097
    if (!function_exists('version_compare') || version_compare(phpversion(), REQUIRED_PHP_VERSION, '<')) {
8098
        $global_error_code = 1;
8099
        // Incorrect PHP version
8100
        $global_page = $my_inc_path.'global_error_message.inc.php';
8101
        if (file_exists($global_page)) {
8102
            require $global_page;
8103
        }
8104
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
8105
    }
8106
}
8107
8108
/**
8109
 * Checks whether the Archive directory is present and writeable. If not,
8110
 * prints a warning message.
8111
 */
8112
function api_check_archive_dir()
8113
{
8114
    if (is_dir(api_get_path(SYS_ARCHIVE_PATH)) && !is_writable(api_get_path(SYS_ARCHIVE_PATH))) {
8115
        $message = Display::return_message(get_lang('ArchivesDirectoryNotWriteableContactAdmin'), 'warning');
8116
        api_not_allowed(true, $message);
8117
    }
8118
}
8119
8120
/**
8121
 * Returns an array of global configuration settings which should be ignored
8122
 * when printing the configuration settings screens.
8123
 *
8124
 * @return array Array of strings, each identifying one of the excluded settings
8125
 */
8126
function api_get_locked_settings()
8127
{
8128
    return [
8129
        'server_type',
8130
        'permanently_remove_deleted_files',
8131
        'account_valid_duration',
8132
        'service_ppt2lp',
8133
        'wcag_anysurfer_public_pages',
8134
        'upload_extensions_list_type',
8135
        'upload_extensions_blacklist',
8136
        'upload_extensions_whitelist',
8137
        'upload_extensions_skip',
8138
        'upload_extensions_replace_by',
8139
        'hide_dltt_markup',
8140
        'split_users_upload_directory',
8141
        'permissions_for_new_directories',
8142
        'permissions_for_new_files',
8143
        'platform_charset',
8144
        'ldap_description',
8145
        'cas_activate',
8146
        'cas_server',
8147
        'cas_server_uri',
8148
        'cas_port',
8149
        'cas_protocol',
8150
        'cas_add_user_activate',
8151
        'update_user_info_cas_with_ldap',
8152
        'languagePriority1',
8153
        'languagePriority2',
8154
        'languagePriority3',
8155
        'languagePriority4',
8156
        'login_is_email',
8157
        'chamilo_database_version',
8158
    ];
8159
}
8160
8161
/**
8162
 * Checks if the user is corrently logged in. Returns the user ID if he is, or
8163
 * false if he isn't. If the user ID is given and is an integer, then the same
8164
 * ID is simply returned.
8165
 *
8166
 * @param  int User ID
8167
 *
8168
 * @return bool Integer User ID is logged in, or false otherwise
8169
 */
8170
function api_user_is_login($user_id = null)
8171
{
8172
    $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id;
8173
8174
    return $user_id && !api_is_anonymous();
8175
}
8176
8177
/**
8178
 * Guess the real ip for register in the database, even in reverse proxy cases.
8179
 * To be recognized, the IP has to be found in either $_SERVER['REMOTE_ADDR'] or
8180
 * in $_SERVER['HTTP_X_FORWARDED_FOR'], which is in common use with rproxies.
8181
 * Note: the result of this function is not SQL-safe. Please escape it before
8182
 * inserting in a database.
8183
 *
8184
 * @return string the user's real ip (unsafe - escape it before inserting to db)
8185
 *
8186
 * @author Jorge Frisancho Jibaja <[email protected]>, USIL - Some changes to allow the use of real IP using reverse proxy
8187
 *
8188
 * @version CEV CHANGE 24APR2012
8189
 */
8190
function api_get_real_ip()
8191
{
8192
    $ip = trim($_SERVER['REMOTE_ADDR']);
8193
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
8194
        if (preg_match('/,/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
8195
            @list($ip1, $ip2) = @explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
8196
        } else {
8197
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
8198
        }
8199
        $ip = trim($ip1);
8200
    }
8201
8202
    return $ip;
8203
}
8204
8205
/**
8206
 * Checks whether an IP is included inside an IP range.
8207
 *
8208
 * @param string IP address
8209
 * @param string IP range
8210
 * @param string $ip
8211
 *
8212
 * @return bool True if IP is in the range, false otherwise
8213
 *
8214
 * @author claudiu at cnixs dot com  on http://www.php.net/manual/fr/ref.network.php#55230
8215
 * @author Yannick Warnier for improvements and managment of multiple ranges
8216
 *
8217
 * @todo check for IPv6 support
8218
 */
8219
function api_check_ip_in_range($ip, $range)
8220
{
8221
    if (empty($ip) or empty($range)) {
8222
        return false;
8223
    }
8224
    $ip_ip = ip2long($ip);
8225
    // divide range param into array of elements
8226
    if (strpos($range, ',') !== false) {
8227
        $ranges = explode(',', $range);
8228
    } else {
8229
        $ranges = [$range];
8230
    }
8231
    foreach ($ranges as $range) {
0 ignored issues
show
introduced by
$range is overwriting one of the parameters of this function.
Loading history...
8232
        $range = trim($range);
8233
        if (empty($range)) {
8234
            continue;
8235
        }
8236
        if (strpos($range, '/') === false) {
8237
            if (strcmp($ip, $range) === 0) {
8238
                return true; // there is a direct IP match, return OK
8239
            }
8240
            continue; //otherwise, get to the next range
8241
        }
8242
        // the range contains a "/", so analyse completely
8243
        list($net, $mask) = explode("/", $range);
8244
8245
        $ip_net = ip2long($net);
8246
        // mask binary magic
8247
        $ip_mask = ~((1 << (32 - $mask)) - 1);
8248
8249
        $ip_ip_net = $ip_ip & $ip_mask;
8250
        if ($ip_ip_net == $ip_net) {
8251
            return true;
8252
        }
8253
    }
8254
8255
    return false;
8256
}
8257
8258
function api_check_user_access_to_legal($courseInfo)
8259
{
8260
    if (empty($courseInfo)) {
8261
        return false;
8262
    }
8263
8264
    $visibility = (int) $courseInfo['visibility'];
8265
    $visibilityList = [COURSE_VISIBILITY_OPEN_WORLD, COURSE_VISIBILITY_OPEN_PLATFORM];
8266
8267
    return
8268
        in_array($visibility, $visibilityList) ||
8269
        api_is_drh() ||
8270
        (COURSE_VISIBILITY_REGISTERED === $visibility && 1 === (int) $courseInfo['subscribe']);
8271
}
8272
8273
/**
8274
 * Checks if the global chat is enabled or not.
8275
 *
8276
 * @return bool
8277
 */
8278
function api_is_global_chat_enabled()
8279
{
8280
    return
8281
        !api_is_anonymous() &&
8282
        api_get_setting('allow_global_chat') === 'true' &&
8283
        api_get_setting('allow_social_tool') === 'true';
8284
}
8285
8286
/**
8287
 * @todo Fix tool_visible_by_default_at_creation labels
8288
 *
8289
 * @param int   $item_id
8290
 * @param int   $tool_id
8291
 * @param int   $group_id   id
8292
 * @param array $courseInfo
8293
 * @param int   $sessionId
8294
 * @param int   $userId
8295
 */
8296
function api_set_default_visibility(
8297
    $item_id,
8298
    $tool_id,
8299
    $group_id = 0,
8300
    $courseInfo = [],
8301
    $sessionId = 0,
8302
    $userId = 0
8303
) {
8304
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
8305
    $courseId = $courseInfo['real_id'];
8306
    $courseCode = $courseInfo['code'];
8307
    $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
8308
    $userId = empty($userId) ? api_get_user_id() : $userId;
8309
8310
    // if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
8311
    if (is_null($group_id)) {
8312
        $group_id = 0;
8313
    } else {
8314
        $group_id = empty($group_id) ? api_get_group_id() : $group_id;
8315
    }
8316
8317
    $groupInfo = [];
8318
    if (!empty($group_id)) {
8319
        $groupInfo = GroupManager::get_group_properties($group_id);
8320
    }
8321
    $original_tool_id = $tool_id;
8322
8323
    switch ($tool_id) {
8324
        case TOOL_LINK:
8325
        case TOOL_LINK_CATEGORY:
8326
            $tool_id = 'links';
8327
            break;
8328
        case TOOL_DOCUMENT:
8329
            $tool_id = 'documents';
8330
            break;
8331
        case TOOL_LEARNPATH:
8332
            $tool_id = 'learning';
8333
            break;
8334
        case TOOL_ANNOUNCEMENT:
8335
            $tool_id = 'announcements';
8336
            break;
8337
        case TOOL_FORUM:
8338
        case TOOL_FORUM_CATEGORY:
8339
        case TOOL_FORUM_THREAD:
8340
            $tool_id = 'forums';
8341
            break;
8342
        case TOOL_QUIZ:
8343
            $tool_id = 'quiz';
8344
            break;
8345
    }
8346
    $setting = api_get_setting('tool_visible_by_default_at_creation');
8347
8348
    if (isset($setting[$tool_id])) {
8349
        $visibility = 'invisible';
8350
        if ($setting[$tool_id] == 'true') {
8351
            $visibility = 'visible';
8352
        }
8353
8354
        // Read the portal and course default visibility
8355
        if ($tool_id === 'documents') {
8356
            $visibility = DocumentManager::getDocumentDefaultVisibility($courseInfo);
8357
        }
8358
8359
        api_item_property_update(
8360
            $courseInfo,
8361
            $original_tool_id,
8362
            $item_id,
8363
            $visibility,
8364
            $userId,
8365
            $groupInfo,
8366
            null,
8367
            null,
8368
            null,
8369
            $sessionId
8370
        );
8371
8372
        // Fixes default visibility for tests
8373
        switch ($original_tool_id) {
8374
            case TOOL_QUIZ:
8375
                if (empty($sessionId)) {
8376
                    $objExerciseTmp = new Exercise($courseId);
8377
                    $objExerciseTmp->read($item_id);
8378
                    if ($visibility == 'visible') {
8379
                        $objExerciseTmp->enable();
8380
                        $objExerciseTmp->save();
8381
                    } else {
8382
                        $objExerciseTmp->disable();
8383
                        $objExerciseTmp->save();
8384
                    }
8385
                }
8386
                break;
8387
        }
8388
    }
8389
}
8390
8391
/**
8392
 * @return string
8393
 */
8394
function api_get_security_key()
8395
{
8396
    return api_get_configuration_value('security_key');
0 ignored issues
show
Bug Best Practice introduced by
The expression return api_get_configura...n_value('security_key') also could return the type boolean which is incompatible with the documented return type string.
Loading history...
8397
}
8398
8399
/**
8400
 * @param int $user_id
8401
 * @param int $courseId
8402
 * @param int $session_id
8403
 *
8404
 * @return array
8405
 */
8406
function api_detect_user_roles($user_id, $courseId, $session_id = 0)
8407
{
8408
    $user_roles = [];
8409
    $courseInfo = api_get_course_info_by_id($courseId);
8410
    $course_code = $courseInfo['code'];
8411
8412
    $url_id = api_get_current_access_url_id();
8413
    if (api_is_platform_admin_by_id($user_id, $url_id)) {
8414
        $user_roles[] = PLATFORM_ADMIN;
8415
    }
8416
8417
    /*if (api_is_drh()) {
8418
        $user_roles[] = DRH;
8419
    }*/
8420
8421
    if (!empty($session_id)) {
8422
        if (SessionManager::user_is_general_coach($user_id, $session_id)) {
8423
            $user_roles[] = SESSION_GENERAL_COACH;
8424
        }
8425
    }
8426
8427
    if (!empty($course_code)) {
8428
        if (empty($session_id)) {
8429
            if (CourseManager::is_course_teacher($user_id, $course_code)) {
8430
                $user_roles[] = COURSEMANAGER;
8431
            }
8432
            if (CourseManager::get_tutor_in_course_status($user_id, $courseInfo['real_id'])) {
8433
                $user_roles[] = COURSE_TUTOR;
8434
            }
8435
8436
            if (CourseManager::is_user_subscribed_in_course($user_id, $course_code)) {
8437
                $user_roles[] = COURSE_STUDENT;
8438
            }
8439
        } else {
8440
            $user_status_in_session = SessionManager::get_user_status_in_course_session(
8441
                $user_id,
8442
                $courseId,
8443
                $session_id
8444
            );
8445
8446
            if (!empty($user_status_in_session)) {
8447
                if ($user_status_in_session == 0) {
8448
                    $user_roles[] = SESSION_STUDENT;
8449
                }
8450
                if ($user_status_in_session == 2) {
8451
                    $user_roles[] = SESSION_COURSE_COACH;
8452
                }
8453
            }
8454
8455
            /*if (api_is_course_session_coach($user_id, $course_code, $session_id)) {
8456
               $user_roles[] = SESSION_COURSE_COACH;
8457
            }*/
8458
        }
8459
    }
8460
8461
    return $user_roles;
8462
}
8463
8464
/**
8465
 * @param int $courseId
8466
 * @param int $session_id
8467
 *
8468
 * @return bool
8469
 */
8470
function api_coach_can_edit_view_results($courseId = null, $session_id = null)
8471
{
8472
    if (api_is_platform_admin()) {
8473
        return true;
8474
    }
8475
8476
    $user_id = api_get_user_id();
8477
8478
    if (empty($courseId)) {
8479
        $courseId = api_get_course_int_id();
8480
    }
8481
8482
    if (empty($session_id)) {
8483
        $session_id = api_get_session_id();
8484
    }
8485
8486
    $roles = api_detect_user_roles($user_id, $courseId, $session_id);
8487
8488
    if (in_array(SESSION_COURSE_COACH, $roles)) {
8489
        //return api_get_setting('session_tutor_reports_visibility') == 'true';
8490
        return true;
8491
    } else {
8492
        if (in_array(COURSEMANAGER, $roles)) {
8493
            return true;
8494
        }
8495
8496
        return false;
8497
    }
8498
}
8499
8500
/**
8501
 * @param string $file
8502
 *
8503
 * @return string
8504
 */
8505
function api_get_js_simple($file)
8506
{
8507
    return '<script src="'.$file.'"></script>'."\n";
8508
}
8509
8510
function api_set_settings_and_plugins()
8511
{
8512
    global $_configuration;
8513
    $_setting = [];
8514
    $_plugins = [];
8515
8516
    // access_url == 1 is the default chamilo location
8517
    $settings_by_access_list = [];
8518
    $access_url_id = api_get_current_access_url_id();
8519
    if ($access_url_id != 1) {
8520
        $url_info = api_get_access_url($_configuration['access_url']);
8521
        if ($url_info['active'] == 1) {
8522
            $settings_by_access = api_get_settings(null, 'list', $_configuration['access_url'], 1);
8523
            foreach ($settings_by_access as &$row) {
8524
                if (empty($row['variable'])) {
8525
                    $row['variable'] = 0;
8526
                }
8527
                if (empty($row['subkey'])) {
8528
                    $row['subkey'] = 0;
8529
                }
8530
                if (empty($row['category'])) {
8531
                    $row['category'] = 0;
8532
                }
8533
                $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']] = $row;
8534
            }
8535
        }
8536
    }
8537
8538
    $result = api_get_settings(null, 'list', 1);
8539
8540
    foreach ($result as &$row) {
8541
        if ($access_url_id != 1) {
8542
            if ($url_info['active'] == 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $url_info does not seem to be defined for all execution paths leading up to this point.
Loading history...
8543
                $var = empty($row['variable']) ? 0 : $row['variable'];
8544
                $subkey = empty($row['subkey']) ? 0 : $row['subkey'];
8545
                $category = empty($row['category']) ? 0 : $row['category'];
8546
            }
8547
8548
            if ($row['access_url_changeable'] == 1 && $url_info['active'] == 1) {
8549
                if (isset($settings_by_access_list[$var]) &&
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $var does not seem to be defined for all execution paths leading up to this point.
Loading history...
8550
                    $settings_by_access_list[$var][$subkey][$category]['selected_value'] != '') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subkey does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $category does not seem to be defined for all execution paths leading up to this point.
Loading history...
8551
                    if ($row['subkey'] == null) {
8552
                        $_setting[$row['variable']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8553
                    } else {
8554
                        $_setting[$row['variable']][$row['subkey']] = $settings_by_access_list[$var][$subkey][$category]['selected_value'];
8555
                    }
8556
                } else {
8557
                    if ($row['subkey'] == null) {
8558
                        $_setting[$row['variable']] = $row['selected_value'];
8559
                    } else {
8560
                        $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8561
                    }
8562
                }
8563
            } else {
8564
                if ($row['subkey'] == null) {
8565
                    $_setting[$row['variable']] = $row['selected_value'];
8566
                } else {
8567
                    $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8568
                }
8569
            }
8570
        } else {
8571
            if ($row['subkey'] == null) {
8572
                $_setting[$row['variable']] = $row['selected_value'];
8573
            } else {
8574
                $_setting[$row['variable']][$row['subkey']] = $row['selected_value'];
8575
            }
8576
        }
8577
    }
8578
8579
    $result = api_get_settings('Plugins', 'list', $access_url_id);
8580
    $_plugins = [];
8581
    foreach ($result as &$row) {
8582
        $key = &$row['variable'];
8583
        if (is_string($_setting[$key])) {
8584
            $_setting[$key] = [];
8585
        }
8586
        $_setting[$key][] = $row['selected_value'];
8587
        $_plugins[$key][] = $row['selected_value'];
8588
    }
8589
8590
    $_SESSION['_setting'] = $_setting;
8591
    $_SESSION['_plugins'] = $_plugins;
8592
}
8593
8594
/**
8595
 * Modify default memory_limit and max_execution_time limits
8596
 * Needed when processing long tasks.
8597
 */
8598
function api_set_more_memory_and_time_limits()
8599
{
8600
    if (function_exists('ini_set')) {
8601
        api_set_memory_limit('2048M');
8602
        ini_set('max_execution_time', 3600);
8603
    }
8604
}
8605
8606
/**
8607
 * Tries to set memory limit, if authorized and new limit is higher than current.
8608
 *
8609
 * @param string $mem New memory limit
8610
 *
8611
 * @return bool True on success, false on failure or current is higher than suggested
8612
 * @assert (null) === false
8613
 * @assert (-1) === false
8614
 * @assert (0) === true
8615
 * @assert ('1G') === true
8616
 */
8617
function api_set_memory_limit($mem)
8618
{
8619
    //if ini_set() not available, this function is useless
8620
    if (!function_exists('ini_set') || is_null($mem) || $mem == -1) {
8621
        return false;
8622
    }
8623
8624
    $memory_limit = ini_get('memory_limit');
8625
    if (api_get_bytes_memory_limit($mem) > api_get_bytes_memory_limit($memory_limit)) {
8626
        ini_set('memory_limit', $mem);
8627
8628
        return true;
8629
    }
8630
8631
    return false;
8632
}
8633
8634
/**
8635
 * Gets memory limit in bytes.
8636
 *
8637
 * @param string The memory size (128M, 1G, 1000K, etc)
8638
 *
8639
 * @return int
8640
 * @assert (null) === false
8641
 * @assert ('1t')  === 1099511627776
8642
 * @assert ('1g')  === 1073741824
8643
 * @assert ('1m')  === 1048576
8644
 * @assert ('100k') === 102400
8645
 */
8646
function api_get_bytes_memory_limit($mem)
8647
{
8648
    $size = strtolower(substr($mem, -1));
8649
8650
    switch ($size) {
8651
        case 't':
8652
            $mem = intval(substr($mem, -1)) * 1024 * 1024 * 1024 * 1024;
8653
            break;
8654
        case 'g':
8655
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024 * 1024;
8656
            break;
8657
        case 'm':
8658
            $mem = intval(substr($mem, 0, -1)) * 1024 * 1024;
8659
            break;
8660
        case 'k':
8661
            $mem = intval(substr($mem, 0, -1)) * 1024;
8662
            break;
8663
        default:
8664
            // we assume it's integer only
8665
            $mem = intval($mem);
8666
            break;
8667
    }
8668
8669
    return $mem;
8670
}
8671
8672
/**
8673
 * Finds all the information about a user from username instead of user id.
8674
 *
8675
 * @param string $officialCode
8676
 *
8677
 * @return array $user_info user_id, lastname, firstname, username, email, ...
8678
 *
8679
 * @author Yannick Warnier <[email protected]>
8680
 */
8681
function api_get_user_info_from_official_code($officialCode)
8682
{
8683
    if (empty($officialCode)) {
8684
        return false;
8685
    }
8686
    $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_USER)."
8687
            WHERE official_code ='".Database::escape_string($officialCode)."'";
8688
    $result = Database::query($sql);
8689
    if (Database::num_rows($result) > 0) {
8690
        $result_array = Database::fetch_array($result);
8691
8692
        return _api_format_user($result_array);
8693
    }
8694
8695
    return false;
8696
}
8697
8698
/**
8699
 * @param string $usernameInputId
8700
 * @param string $passwordInputId
8701
 *
8702
 * @return string|null
8703
 */
8704
function api_get_password_checker_js($usernameInputId, $passwordInputId)
8705
{
8706
    $checkPass = api_get_setting('allow_strength_pass_checker');
8707
    $useStrengthPassChecker = $checkPass === 'true';
8708
8709
    if ($useStrengthPassChecker === false) {
8710
        return null;
8711
    }
8712
8713
    $minRequirements = Security::getPasswordRequirements()['min'];
8714
8715
    $options = [
8716
        'rules' => [],
8717
    ];
8718
8719
    if ($minRequirements['length'] > 0) {
8720
        $options['rules'][] = [
8721
            'minChar' => $minRequirements['length'],
8722
            'pattern' => '.',
8723
            'helpText' => sprintf(
8724
                get_lang('NewPasswordRequirementMinXLength'),
8725
                $minRequirements['length']
8726
            ),
8727
        ];
8728
    }
8729
8730
    if ($minRequirements['lowercase'] > 0) {
8731
        $options['rules'][] = [
8732
            'minChar' => $minRequirements['lowercase'],
8733
            'pattern' => '[a-z]',
8734
            'helpText' => sprintf(
8735
                get_lang('NewPasswordRequirementMinXLowercase'),
8736
                $minRequirements['lowercase']
8737
            ),
8738
        ];
8739
    }
8740
8741
    if ($minRequirements['uppercase'] > 0) {
8742
        $options['rules'][] = [
8743
            'minChar' => $minRequirements['uppercase'],
8744
            'pattern' => '[A-Z]',
8745
            'helpText' => sprintf(
8746
                get_lang('NewPasswordRequirementMinXUppercase'),
8747
                $minRequirements['uppercase']
8748
            ),
8749
        ];
8750
    }
8751
8752
    if ($minRequirements['numeric'] > 0) {
8753
        $options['rules'][] = [
8754
            'minChar' => $minRequirements['numeric'],
8755
            'pattern' => '[0-9]',
8756
            'helpText' => sprintf(
8757
                get_lang('NewPasswordRequirementMinXNumeric'),
8758
                $minRequirements['numeric']
8759
            ),
8760
        ];
8761
    }
8762
8763
    if ($minRequirements['specials'] > 0) {
8764
        $options['rules'][] = [
8765
            'minChar' => $minRequirements['specials'],
8766
            'pattern' => '[!"#$%&\'()*+,\-./\\\:;<=>?@[\\]^_`{|}~]',
8767
            'helpText' => sprintf(
8768
                get_lang('NewPasswordRequirementMinXSpecials'),
8769
                $minRequirements['specials']
8770
            ),
8771
        ];
8772
    }
8773
8774
    $js = api_get_js('password-checker/password-checker.js');
8775
    $js .= "<script>
8776
    $(function() {
8777
        $('".$passwordInputId."').passwordChecker(".json_encode($options).");
8778
    });
8779
    </script>";
8780
8781
    return $js;
8782
}
8783
8784
/**
8785
 * create an user extra field called 'captcha_blocked_until_date'.
8786
 *
8787
 * @param string $username
8788
 *
8789
 * @return bool
8790
 */
8791
function api_block_account_captcha($username)
8792
{
8793
    $userInfo = api_get_user_info_from_username($username);
8794
    if (empty($userInfo)) {
8795
        return false;
8796
    }
8797
    $minutesToBlock = api_get_setting('captcha_time_to_block');
8798
    $time = time() + $minutesToBlock * 60;
8799
    UserManager::update_extra_field_value(
8800
        $userInfo['user_id'],
8801
        'captcha_blocked_until_date',
8802
        api_get_utc_datetime($time)
8803
    );
8804
8805
    return true;
8806
}
8807
8808
/**
8809
 * @param string $username
8810
 *
8811
 * @return bool
8812
 */
8813
function api_clean_account_captcha($username)
8814
{
8815
    $userInfo = api_get_user_info_from_username($username);
8816
    if (empty($userInfo)) {
8817
        return false;
8818
    }
8819
    Session::erase('loginFailedCount');
8820
    UserManager::update_extra_field_value(
8821
        $userInfo['user_id'],
8822
        'captcha_blocked_until_date',
8823
        null
8824
    );
8825
8826
    return true;
8827
}
8828
8829
/**
8830
 * @param string $username
8831
 *
8832
 * @return bool
8833
 */
8834
function api_get_user_blocked_by_captcha($username)
8835
{
8836
    $userInfo = api_get_user_info_from_username($username);
8837
    if (empty($userInfo)) {
8838
        return false;
8839
    }
8840
    $data = UserManager::get_extra_user_data_by_field(
8841
        $userInfo['user_id'],
8842
        'captcha_blocked_until_date'
8843
    );
8844
    if (isset($data) && isset($data['captcha_blocked_until_date'])) {
8845
        return $data['captcha_blocked_until_date'];
8846
    }
8847
8848
    return false;
8849
}
8850
8851
/**
8852
 * Remove tags from HTML anf return the $in_number_char first non-HTML char
8853
 * Postfix the text with "..." if it has been truncated.
8854
 *
8855
 * @param string $text
8856
 * @param int    $number
8857
 *
8858
 * @return string
8859
 *
8860
 * @author hubert borderiou
8861
 */
8862
function api_get_short_text_from_html($text, $number)
8863
{
8864
    // Delete script and style tags
8865
    $text = preg_replace('/(<(script|style)\b[^>]*>).*?(<\/\2>)/is', "$1$3", $text);
8866
    $text = api_html_entity_decode($text);
8867
    $out_res = api_remove_tags_with_space($text, false);
8868
    $postfix = "...";
8869
    if (strlen($out_res) > $number) {
8870
        $out_res = substr($out_res, 0, $number).$postfix;
8871
    }
8872
8873
    return $out_res;
8874
}
8875
8876
/**
8877
 * Replace tags with a space in a text.
8878
 * If $in_double_quote_replace, replace " with '' (for HTML attribute purpose, for exemple).
8879
 *
8880
 * @return string
8881
 *
8882
 * @author hubert borderiou
8883
 */
8884
function api_remove_tags_with_space($in_html, $in_double_quote_replace = true)
8885
{
8886
    $out_res = $in_html;
8887
    if ($in_double_quote_replace) {
8888
        $out_res = str_replace('"', "''", $out_res);
8889
    }
8890
    // avoid text stuck together when tags are removed, adding a space after >
8891
    $out_res = str_replace(">", "> ", $out_res);
8892
    $out_res = strip_tags($out_res);
8893
8894
    return $out_res;
8895
}
8896
8897
/**
8898
 * If true, the drh can access all content (courses, users) inside a session.
8899
 *
8900
 * @return bool
8901
 */
8902
function api_drh_can_access_all_session_content()
8903
{
8904
    return api_get_setting('drh_can_access_all_session_content') === 'true';
8905
}
8906
8907
/**
8908
 * @param string $tool
8909
 * @param string $setting
8910
 * @param int    $defaultValue
8911
 *
8912
 * @return string
8913
 */
8914
function api_get_default_tool_setting($tool, $setting, $defaultValue)
8915
{
8916
    global $_configuration;
8917
    if (isset($_configuration[$tool]) &&
8918
        isset($_configuration[$tool]['default_settings']) &&
8919
        isset($_configuration[$tool]['default_settings'][$setting])
8920
    ) {
8921
        return $_configuration[$tool]['default_settings'][$setting];
8922
    }
8923
8924
    return $defaultValue;
8925
}
8926
8927
/**
8928
 * Checks if user can login as another user.
8929
 *
8930
 * @param int $loginAsUserId the user id to log in
8931
 * @param int $userId        my user id
8932
 *
8933
 * @return bool
8934
 */
8935
function api_can_login_as($loginAsUserId, $userId = null)
8936
{
8937
    if (empty($userId)) {
8938
        $userId = api_get_user_id();
8939
    }
8940
    if ($loginAsUserId == $userId) {
8941
        return false;
8942
    }
8943
8944
    if (empty($loginAsUserId)) {
8945
        return false;
8946
    }
8947
8948
    if ($loginAsUserId != strval(intval($loginAsUserId))) {
8949
        return false;
8950
    }
8951
8952
    // Check if the user to login is an admin
8953
    if (api_is_platform_admin_by_id($loginAsUserId)) {
8954
        // Only super admins can login to admin accounts
8955
        if (!api_global_admin_can_edit_admin($loginAsUserId)) {
8956
            return false;
8957
        }
8958
    }
8959
8960
    $userInfo = api_get_user_info($loginAsUserId);
8961
    $isDrh = function () use ($loginAsUserId) {
8962
        if (api_is_drh()) {
8963
            if (true === api_get_configuration_value('disallow_hrm_login_as')) {
8964
                return false;
8965
            }
8966
8967
            if (api_drh_can_access_all_session_content()) {
8968
                $users = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
8969
                    'drh_all',
8970
                    api_get_user_id()
8971
                );
8972
                $userList = [];
8973
                if (is_array($users)) {
8974
                    foreach ($users as $user) {
8975
                        $userList[] = $user['user_id'];
8976
                    }
8977
                }
8978
                if (in_array($loginAsUserId, $userList)) {
8979
                    return true;
8980
                }
8981
            } else {
8982
                if (api_is_drh() &&
8983
                    UserManager::is_user_followed_by_drh($loginAsUserId, api_get_user_id())
8984
                ) {
8985
                    return true;
8986
                }
8987
            }
8988
        }
8989
8990
        return false;
8991
    };
8992
8993
    $allowSessionAdmin = function () use ($userInfo) {
8994
        if (!api_is_session_admin()) {
8995
            return false;
8996
        }
8997
8998
        if (true === api_get_configuration_value('disallow_session_admin_login_as')) {
8999
            return false;
9000
        }
9001
9002
        $loginAsStatusForSessionAdmins = [STUDENT];
9003
9004
        if (api_get_configuration_value('allow_session_admin_login_as_teacher')) {
9005
            $loginAsStatusForSessionAdmins[] = COURSEMANAGER;
9006
        }
9007
9008
        return in_array($userInfo['status'], $loginAsStatusForSessionAdmins);
9009
    };
9010
9011
    return api_is_platform_admin() ||
9012
        $allowSessionAdmin() ||
9013
        $isDrh();
9014
}
9015
9016
/**
9017
 * @return bool
9018
 */
9019
function api_is_allowed_in_course()
9020
{
9021
    if (api_is_platform_admin()) {
9022
        return true;
9023
    }
9024
9025
    return Session::read('is_allowed_in_course');
9026
}
9027
9028
function api_set_cookie($name, $value, $expires = 0)
9029
{
9030
    $expires = (int) $expires;
9031
    setcookie($name, $value, $expires, '', '', api_is_https(), true);
9032
}
9033
9034
/**
9035
 * Set the cookie to go directly to the course code $in_firstpage
9036
 * after login.
9037
 *
9038
 * @param string $value is the course code of the course to go
9039
 */
9040
function api_set_firstpage_parameter($value)
9041
{
9042
    api_set_cookie('GotoCourse', $value);
9043
}
9044
9045
/**
9046
 * Delete the cookie to go directly to the course code $in_firstpage
9047
 * after login.
9048
 */
9049
function api_delete_firstpage_parameter()
9050
{
9051
    api_set_cookie('GotoCourse', '', time() - 3600);
9052
}
9053
9054
/**
9055
 * @return bool if course_code for direct course access after login is set
9056
 */
9057
function exist_firstpage_parameter()
9058
{
9059
    return isset($_COOKIE['GotoCourse']) && $_COOKIE['GotoCourse'] != '';
9060
}
9061
9062
/**
9063
 * @return string return the course_code of the course where user login
9064
 */
9065
function api_get_firstpage_parameter()
9066
{
9067
    return $_COOKIE['GotoCourse'];
9068
}
9069
9070
/**
9071
 * Return true on https install.
9072
 *
9073
 * @return bool
9074
 */
9075
function api_is_https()
9076
{
9077
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($_SERVER['HTTP_...ttps_forwarded_proto']), Probably Intended Meaning: ! empty($_SERVER['HTTP_X...tps_forwarded_proto']))
Loading history...
9078
        $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto'])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_configuration seems to never exist and therefore empty should always be true.
Loading history...
9079
    ) {
9080
        $isSecured = true;
9081
    } else {
9082
        if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
9083
            $isSecured = true;
9084
        } else {
9085
            $isSecured = false;
9086
            // last chance
9087
            if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
9088
                $isSecured = true;
9089
            }
9090
        }
9091
    }
9092
9093
    return $isSecured;
9094
}
9095
9096
/**
9097
 * Return protocol (http or https).
9098
 *
9099
 * @return string
9100
 */
9101
function api_get_protocol()
9102
{
9103
    return api_is_https() ? 'https' : 'http';
9104
}
9105
9106
/**
9107
 * Return a string where " are replaced with 2 '
9108
 * It is useful when you pass a PHP variable in a Javascript browser dialog
9109
 * e.g. : alert("<?php get_lang('Message') ?>");
9110
 * and message contains character ".
9111
 *
9112
 * @param string $in_text
9113
 *
9114
 * @return string
9115
 */
9116
function convert_double_quote_to_single($in_text)
9117
{
9118
    return api_preg_replace('/"/', "''", $in_text);
9119
}
9120
9121
/**
9122
 * Get origin.
9123
 *
9124
 * @param string
9125
 *
9126
 * @return string
9127
 */
9128
function api_get_origin()
9129
{
9130
    return isset($_REQUEST['origin']) ? urlencode(Security::remove_XSS(urlencode($_REQUEST['origin']))) : '';
9131
}
9132
9133
/**
9134
 * Warns an user that the portal reach certain limit.
9135
 *
9136
 * @param string $limitName
9137
 */
9138
function api_warn_hosting_contact($limitName)
9139
{
9140
    $hostingParams = api_get_configuration_value(1);
9141
    $email = null;
9142
9143
    if (!empty($hostingParams)) {
9144
        if (isset($hostingParams['hosting_contact_mail'])) {
9145
            $email = $hostingParams['hosting_contact_mail'];
9146
        }
9147
    }
9148
9149
    if (!empty($email)) {
9150
        $subject = get_lang('HostingWarningReached');
9151
        $body = get_lang('PortalName').': '.api_get_path(WEB_PATH)." \n ";
9152
        $body .= get_lang('PortalLimitType').': '.$limitName." \n ";
9153
        if (isset($hostingParams[$limitName])) {
9154
            $body .= get_lang('Value').': '.$hostingParams[$limitName];
9155
        }
9156
        api_mail_html(null, $email, $subject, $body);
9157
    }
9158
}
9159
9160
/**
9161
 * Gets value of a variable from app/config/configuration.php
9162
 * Variables that are not set in the configuration.php file but set elsewhere:
9163
 * - virtual_css_theme_folder (vchamilo plugin)
9164
 * - access_url (global.inc.php)
9165
 * - apc/apc_prefix (global.inc.php).
9166
 *
9167
 * @param string $variable
9168
 *
9169
 * @return bool|mixed
9170
 */
9171
function api_get_configuration_value($variable)
9172
{
9173
    global $_configuration;
9174
    // Check the current url id, id = 1 by default
9175
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
9176
9177
    $variable = trim($variable);
9178
9179
    // Check if variable exists
9180
    if (isset($_configuration[$variable])) {
9181
        if (is_array($_configuration[$variable]) && api_is_multiple_url_enabled() && is_int(array_keys($_configuration[$variable])[0])) {
9182
            // It has been configured for at least one sub URL, so we will not return the complete variable
9183
            /*
9184
             * The idea is that if the first level key of the configuration variable is an int
9185
             * then it is a multiURL configuration and if it's a string then it's a configuration that is not multiURL.
9186
             * For example if in app/config/configuration.php you have set :
9187
             * $_configuration['ticket_project_user_roles'] = [
9188
             *     'permissions' => [
9189
             *         1 => [17] // project_id = 1, STUDENT_BOSS = 17
9190
             *     ]
9191
             * ];
9192
             * You do not want to enter this block even if multiURL is activated because the option is configured globally
9193
             * and you want to return the full array.
9194
             * The is_int is to consider only the option that are array and configured for multiURL
9195
             * which means there is an int as the first level key of the array.
9196
             */
9197
            // Check if it exists for the sub portal
9198
            if (array_key_exists($urlId, $_configuration[$variable])) {
9199
                return $_configuration[$variable][$urlId];
9200
            } else {
9201
                // Try to found element with id = 1 (master portal)
9202
                if (array_key_exists(1, $_configuration[$variable])) {
9203
                    return $_configuration[$variable][1];
9204
                } else {
9205
                    // The value was there for other URLs but not the main URL nor the current URL
9206
                    return null;
9207
                }
9208
            }
9209
        }
9210
9211
        return $_configuration[$variable];
9212
    }
9213
9214
    return false;
9215
}
9216
9217
/**
9218
 * Gets value of a variable from app/config/mail.conf.php.
9219
 *
9220
 * @param string $variable
9221
 *
9222
 * @return bool|mixed
9223
 */
9224
function api_get_mail_configuration_value($variable)
9225
{
9226
    global $_configuration;
9227
    global $platform_email;
9228
9229
    // Check the current url id, id = 1 by default
9230
    $urlId = isset($_configuration['access_url']) ? (int) $_configuration['access_url'] : 1;
9231
9232
    $variable = trim($variable);
9233
9234
    // Check if variable exists for the sub portal
9235
    if (api_is_multiple_url_enabled() && isset($platform_email[$urlId]) && isset($platform_email[$urlId][$variable])) {
9236
        return $platform_email[$urlId][$variable];
9237
    } elseif (isset($platform_email[1]) && isset($platform_email[1][$variable])) {
9238
        // Try to find element with id = 1 (master portal)
9239
        return $platform_email[1][$variable];
9240
    } elseif (isset($platform_email[$variable])) {
9241
        // If variable is not found for the sub portal or master portal, try to find the default element
9242
        return $platform_email[$variable];
9243
    }
9244
9245
    return false;
9246
}
9247
9248
/**
9249
 * Retreives and returns a value in a hierarchical configuration array
9250
 * api_get_configuration_sub_value('a/b/c') returns api_get_configuration_value('a')['b']['c'].
9251
 *
9252
 * @param string $path      the successive array keys, separated by the separator
9253
 * @param mixed  $default   value to be returned if not found, null by default
9254
 * @param string $separator '/' by default
9255
 * @param array  $array     the active configuration array by default
9256
 *
9257
 * @return mixed the found value or $default
9258
 */
9259
function api_get_configuration_sub_value($path, $default = null, $separator = '/', $array = null)
9260
{
9261
    $pos = strpos($path, $separator);
9262
    if (false === $pos) {
9263
        if (is_null($array)) {
9264
            return api_get_configuration_value($path);
9265
        }
9266
        if (is_array($array) && array_key_exists($path, $array)) {
9267
            return $array[$path];
9268
        }
9269
9270
        return $default;
9271
    }
9272
    $key = substr($path, 0, $pos);
9273
    if (is_null($array)) {
9274
        $newArray = api_get_configuration_value($key);
9275
    } elseif (is_array($array) && array_key_exists($key, $array)) {
9276
        $newArray = $array[$key];
9277
    } else {
9278
        return $default;
9279
    }
9280
    if (is_array($newArray)) {
9281
        $newPath = substr($path, $pos + 1);
9282
9283
        return api_get_configuration_sub_value($newPath, $default, $separator, $newArray);
9284
    }
9285
9286
    return $default;
9287
}
9288
9289
/**
9290
 * Retrieves and returns a value in a hierarchical configuration array
9291
 * api_array_sub_value($array, 'a/b/c') returns $array['a']['b']['c'].
9292
 *
9293
 * @param array  $array     the recursive array that contains the value to be returned (or not)
9294
 * @param string $path      the successive array keys, separated by the separator
9295
 * @param mixed  $default   the value to be returned if not found
9296
 * @param string $separator the separator substring
9297
 *
9298
 * @return mixed the found value or $default
9299
 */
9300
function api_array_sub_value($array, $path, $default = null, $separator = '/')
9301
{
9302
    $pos = strpos($path, $separator);
9303
    if (false === $pos) {
9304
        if (is_array($array) && array_key_exists($path, $array)) {
9305
            return $array[$path];
9306
        }
9307
9308
        return $default;
9309
    }
9310
    $key = substr($path, 0, $pos);
9311
    if (is_array($array) && array_key_exists($key, $array)) {
9312
        $newArray = $array[$key];
9313
    } else {
9314
        return $default;
9315
    }
9316
    if (is_array($newArray)) {
9317
        $newPath = substr($path, $pos + 1);
9318
9319
        return api_array_sub_value($newArray, $newPath, $default);
9320
    }
9321
9322
    return $default;
9323
}
9324
9325
/**
9326
 * Returns supported image extensions in the portal.
9327
 *
9328
 * @param bool $supportVectors Whether vector images should also be accepted or not
9329
 *
9330
 * @return array Supported image extensions in the portal
9331
 */
9332
function api_get_supported_image_extensions($supportVectors = true)
9333
{
9334
    // jpg can also be called jpeg, jpe, jfif and jif. See https://en.wikipedia.org/wiki/JPEG#JPEG_filename_extensions
9335
    $supportedImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'jpe', 'jfif', 'jif'];
9336
    if ($supportVectors) {
9337
        array_push($supportedImageExtensions, 'svg');
9338
    }
9339
    if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
9340
        array_push($supportedImageExtensions, 'webp');
9341
    }
9342
9343
    return $supportedImageExtensions;
9344
}
9345
9346
/**
9347
 * This setting changes the registration status for the campus.
9348
 *
9349
 * @author Patrick Cool <[email protected]>, Ghent University
9350
 *
9351
 * @version August 2006
9352
 *
9353
 * @param bool $listCampus Whether we authorize
9354
 *
9355
 * @todo the $_settings should be reloaded here. => write api function for this and use this in global.inc.php also.
9356
 */
9357
function api_register_campus($listCampus = true)
9358
{
9359
    $tbl_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
9360
9361
    $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='registered'";
9362
    Database::query($sql);
9363
9364
    if (!$listCampus) {
9365
        $sql = "UPDATE $tbl_settings SET selected_value='true' WHERE variable='donotlistcampus'";
9366
        Database::query($sql);
9367
    }
9368
}
9369
9370
/**
9371
 * Checks whether current user is a student boss.
9372
 *
9373
 * @global array $_user
9374
 *
9375
 * @return bool
9376
 */
9377
function api_is_student_boss()
9378
{
9379
    $_user = api_get_user_info();
9380
9381
    return isset($_user['status']) && $_user['status'] == STUDENT_BOSS;
9382
}
9383
9384
/**
9385
 * Check whether the user type should be exclude.
9386
 * Such as invited or anonymous users.
9387
 *
9388
 * @param bool $checkDB Optional. Whether check the user status
9389
 * @param int  $userId  Options. The user id
9390
 *
9391
 * @return bool
9392
 */
9393
function api_is_excluded_user_type($checkDB = false, $userId = 0)
9394
{
9395
    if ($checkDB) {
9396
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
9397
9398
        if ($userId == 0) {
9399
            return true;
9400
        }
9401
9402
        $userInfo = api_get_user_info($userId);
9403
9404
        switch ($userInfo['status']) {
9405
            case INVITEE:
9406
            case ANONYMOUS:
9407
                return true;
9408
            default:
9409
                return false;
9410
        }
9411
    }
9412
9413
    $isInvited = api_is_invitee();
9414
    $isAnonymous = api_is_anonymous();
9415
9416
    if ($isInvited || $isAnonymous) {
9417
        return true;
9418
    }
9419
9420
    return false;
9421
}
9422
9423
/**
9424
 * Get the user status to ignore in reports.
9425
 *
9426
 * @param string $format Optional. The result type (array or string)
9427
 *
9428
 * @return array|string
9429
 */
9430
function api_get_users_status_ignored_in_reports($format = 'array')
9431
{
9432
    $excludedTypes = [
9433
        INVITEE,
9434
        ANONYMOUS,
9435
    ];
9436
9437
    if ($format == 'string') {
9438
        return implode(', ', $excludedTypes);
9439
    }
9440
9441
    return $excludedTypes;
9442
}
9443
9444
/**
9445
 * Set the Site Use Cookie Warning for 1 year.
9446
 */
9447
function api_set_site_use_cookie_warning_cookie()
9448
{
9449
    api_set_cookie('ChamiloUsesCookies', 'ok', time() + 31556926);
9450
}
9451
9452
/**
9453
 * Return true if the Site Use Cookie Warning Cookie warning exists.
9454
 *
9455
 * @return bool
9456
 */
9457
function api_site_use_cookie_warning_cookie_exist()
9458
{
9459
    return isset($_COOKIE['ChamiloUsesCookies']);
9460
}
9461
9462
/**
9463
 * Given a number of seconds, format the time to show hours, minutes and seconds.
9464
 *
9465
 * @param int    $time         The time in seconds
9466
 * @param string $originFormat Optional.
9467
 *                             PHP (used for scorm)
9468
 *                             JS (used in most cases and understood by excel)
9469
 *                             LANG (used to present unit in the user language)
9470
 *
9471
 * @return string (00h00'00")
9472
 */
9473
function api_format_time($time, $originFormat = 'php')
9474
{
9475
    $h = get_lang('h');
9476
    $hours = $time / 3600;
9477
    $mins = ($time % 3600) / 60;
9478
    $secs = ($time % 60);
9479
9480
    if ($time < 0) {
9481
        $hours = 0;
9482
        $mins = 0;
9483
        $secs = 0;
9484
    }
9485
9486
    if ($originFormat == 'js') {
9487
        $formattedTime = trim(sprintf("%02d : %02d : %02d", $hours, $mins, $secs));
9488
    } elseif ($originFormat == 'lang') {
9489
        $formattedTime = trim(sprintf(get_lang('HoursMinutesSeconds'), $hours, $mins, $secs));
9490
    } else {
9491
        $formattedTime = trim(sprintf("%02d$h%02d'%02d\"", $hours, $mins, $secs));
9492
    }
9493
9494
    return $formattedTime;
9495
}
9496
9497
/**
9498
 * Create a new empty directory with index.html file.
9499
 *
9500
 * @param string $name            The new directory name
9501
 * @param string $parentDirectory Directory parent directory name
9502
 *
9503
 * @return bool Return true if the directory was create. Otherwise return false
9504
 */
9505
function api_create_protected_dir($name, $parentDirectory)
9506
{
9507
    $isCreated = false;
9508
9509
    if (!is_writable($parentDirectory)) {
9510
        return false;
9511
    }
9512
9513
    $fullPath = $parentDirectory.api_replace_dangerous_char($name);
9514
9515
    if (mkdir($fullPath, api_get_permissions_for_new_directories(), true)) {
9516
        $fp = fopen($fullPath.'/index.html', 'w');
9517
9518
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type resource, thus it always evaluated to false.
Loading history...
9519
            if (fwrite($fp, '<html><head><title></title></head><body></body></html>')) {
9520
                $isCreated = true;
9521
            }
9522
        }
9523
9524
        fclose($fp);
9525
    }
9526
9527
    return $isCreated;
9528
}
9529
9530
/**
9531
 * Sends an HTML email using the phpmailer class (and multipart/alternative to downgrade gracefully)
9532
 * Sender name and email can be specified, if not specified
9533
 * name and email of the platform admin are used.
9534
 *
9535
 * @author Bert Vanderkimpen ICT&O UGent
9536
 * @author Yannick Warnier <[email protected]>
9537
 *
9538
 * @param string    name of recipient
9539
 * @param string    email of recipient
9540
 * @param string    email subject
9541
 * @param string    email body
9542
 * @param string    sender name
9543
 * @param string    sender e-mail
9544
 * @param array  $extra_headers        in form $headers = array($name => $value) to allow parsing
9545
 * @param array  $data_file            (path and filename)
9546
 * @param bool   $embedded_image       True for attaching a embedded file inside content html (optional)
9547
 * @param array  $additionalParameters
9548
 * @param string $sendErrorTo          If there's an error while sending the email, $sendErrorTo will receive a notification
9549
 *
9550
 * @return int true if mail was sent
9551
 *
9552
 * @see             PHPMailer.php
9553
 */
9554
function api_mail_html(
9555
    $recipient_name,
9556
    $recipient_email,
9557
    $subject,
9558
    $message,
9559
    $senderName = '',
9560
    $senderEmail = '',
9561
    $extra_headers = [],
9562
    $data_file = [],
9563
    $embedded_image = false,
9564
    $additionalParameters = [],
9565
    $sendErrorTo = ''
9566
) {
9567
    if (true === api_get_configuration_value('disable_send_mail')) {
9568
        return true;
9569
    }
9570
9571
    $mail = new PHPMailer();
9572
9573
    if (!empty(api_get_mail_configuration_value('XOAUTH2_METHOD'))) {
9574
        $provider = new GenericProvider([
9575
            'clientId' => api_get_mail_configuration_value('XOAUTH2_CLIENT_ID'),
9576
            'clientSecret' => api_get_mail_configuration_value('XOAUTH2_CLIENT_SECRET'),
9577
            'urlAuthorize' => api_get_mail_configuration_value('XOAUTH2_URL_AUTHORIZE'),
9578
            'urlAccessToken' => api_get_mail_configuration_value('XOAUTH2_URL_ACCES_TOKEN'),
9579
            'urlResourceOwnerDetails' => api_get_mail_configuration_value('XOAUTH2_URL_RESOURCE_OWNER_DETAILS'),
9580
            'scopes' => api_get_mail_configuration_value('XOAUTH2_SCOPES'),
9581
        ]);
9582
        $mail->AuthType = 'XOAUTH2';
9583
        $mail->setOAuth(
9584
            new OAuth([
9585
                'provider' => $provider,
9586
                'clientId' => api_get_mail_configuration_value('XOAUTH2_CLIENT_ID'),
9587
                'clientSecret' => api_get_mail_configuration_value('XOAUTH2_CLIENT_SECRET'),
9588
                'refreshToken' => api_get_mail_configuration_value('XOAUTH2_REFRESH_TOKEN'),
9589
                'userName' => api_get_mail_configuration_value('SMTP_USER'),
9590
            ])
9591
        );
9592
    }
9593
9594
    $mail->Mailer = api_get_mail_configuration_value('SMTP_MAILER');
9595
    $mail->Host = api_get_mail_configuration_value('SMTP_HOST');
9596
    $mail->Port = api_get_mail_configuration_value('SMTP_PORT');
9597
    $mail->CharSet = api_get_mail_configuration_value('SMTP_CHARSET') ?: 'UTF-8';
9598
    // Stay far below SMTP protocol 980 chars limit.
9599
    $mail->WordWrap = 200;
9600
    $mail->SMTPOptions = api_get_mail_configuration_value('SMTPOptions') ?: [];
9601
9602
    if (api_get_mail_configuration_value('SMTP_AUTH')) {
9603
        $mail->SMTPAuth = 1;
9604
        $mail->Username = api_get_mail_configuration_value('SMTP_USER');
9605
        $mail->Password = api_get_mail_configuration_value('SMTP_PASS');
9606
        if (api_get_mail_configuration_value('SMTP_SECURE')) {
9607
            $mail->SMTPSecure = api_get_mail_configuration_value('SMTP_SECURE');
9608
        }
9609
    }
9610
    $mail->SMTPDebug = api_get_mail_configuration_value('SMTP_DEBUG') ?: 0;
9611
9612
    // 5 = low, 1 = high
9613
    $mail->Priority = 3;
9614
    $mail->SMTPKeepAlive = true;
9615
9616
    api_set_noreply_and_from_address_to_mailer(
9617
        $mail,
9618
        ['name' => $senderName, 'email' => $senderEmail],
9619
        !empty($extra_headers['reply_to']) ? $extra_headers['reply_to'] : []
9620
    );
9621
9622
    if (!empty($sendErrorTo) && PHPMailer::ValidateAddress($sendErrorTo)) {
9623
        $mail->AddCustomHeader('Errors-To', $sendErrorTo);
9624
    }
9625
9626
    unset($extra_headers['reply_to']);
9627
9628
    $mail->Subject = $subject;
9629
    $mail->AltBody = strip_tags(
9630
        str_replace('<br />', "\n", api_html_entity_decode($message))
9631
    );
9632
9633
    $list = api_get_configuration_value('send_all_emails_to');
9634
    if (!empty($list) && isset($list['emails'])) {
9635
        foreach ($list['emails'] as $email) {
9636
            $mail->AddAddress($email);
9637
        }
9638
    }
9639
9640
    // Send embedded image.
9641
    if ($embedded_image) {
9642
        // Get all images html inside content.
9643
        preg_match_all("/<img\s+.*?src=[\"\']?([^\"\' >]*)[\"\']?[^>]*>/i", $message, $m);
9644
        // Prepare new tag images.
9645
        $new_images_html = [];
9646
        $i = 1;
9647
        if (!empty($m[1])) {
9648
            foreach ($m[1] as $image_path) {
9649
                $real_path = realpath($image_path);
9650
                $filename = basename($image_path);
9651
                $image_cid = $filename.'_'.$i;
9652
                $encoding = 'base64';
9653
                $image_type = mime_content_type($real_path);
9654
                $mail->AddEmbeddedImage(
9655
                    $real_path,
9656
                    $image_cid,
9657
                    $filename,
9658
                    $encoding,
9659
                    $image_type
9660
                );
9661
                $new_images_html[] = '<img src="cid:'.$image_cid.'" />';
9662
                $i++;
9663
            }
9664
        }
9665
9666
        // Replace origin image for new embedded image html.
9667
        $x = 0;
9668
        if (!empty($m[0])) {
9669
            foreach ($m[0] as $orig_img) {
9670
                $message = str_replace($orig_img, $new_images_html[$x], $message);
9671
                $x++;
9672
            }
9673
        }
9674
    }
9675
9676
    $extendedFooterMessageConfig = api_get_configuration_value('notifications_extended_footer_message');
9677
    if ($extendedFooterMessageConfig) {
9678
        $platformLanguage = api_get_interface_language();
9679
        $extendedFooterMessage = api_get_configuration_value('notifications_extended_footer_message')[$platformLanguage];
9680
9681
        if ($extendedFooterMessage) {
9682
            $message .= '<br /><hr><i>'.'<p>'.implode('<br/><br/>', $extendedFooterMessage['paragraphs']).'</p>';
9683
        }
9684
    }
9685
9686
    $mailView = new Template(null, false, false, false, false, false, false);
9687
9688
    $noReply = api_get_setting('noreply_email_address');
9689
    if (!empty($noReply)) {
9690
        $message .= '<br />'.get_lang('ThisIsAutomaticEmailNoReply');
9691
    }
9692
    $mailView->assign('content', $message);
9693
9694
    if (isset($additionalParameters['link'])) {
9695
        $mailView->assign('link', $additionalParameters['link']);
9696
    }
9697
    if (isset($additionalParameters['logo'])) {
9698
        $mailView->assign('logo', $additionalParameters['logo']);
9699
    } elseif (api_get_configuration_value('email_logo') == true) {
9700
        $logoSubPath = 'themes/'.api_get_visual_theme().'/images/email-logo.png';
9701
        $logoSysPath = api_get_path(SYS_PATH).'web/css/'.$logoSubPath;
9702
        if (file_exists($logoSysPath)) {
9703
            $logoWebPath = api_get_path(WEB_CSS_PATH).$logoSubPath;
9704
            $imgTag = \Display::img(
9705
                $logoWebPath,
9706
                api_get_setting('siteName'),
9707
                [
9708
                    'id' => 'header-logo',
9709
                    'class' => 'img-responsive',
9710
                ]
9711
            );
9712
            $logoTag = \Display::url($imgTag, api_get_path(WEB_PATH));
9713
            $mailView->assign('logo', $logoTag);
9714
        }
9715
    }
9716
    $mailView->assign('mail_header_style', api_get_configuration_value('mail_header_style'));
9717
    $mailView->assign('mail_content_style', api_get_configuration_value('mail_content_style'));
9718
    $mailView->assign('include_ldjson', (empty(api_get_mail_configuration_value('EXCLUDE_JSON')) ? true : false));
9719
    $layout = $mailView->get_template('mail/mail.tpl');
9720
    $mail->Body = $mailView->fetch($layout);
9721
9722
    if (isset($additionalParameters['checkUrls'])) {
9723
        $useMultipleUrl = api_get_configuration_value('multiple_access_urls');
9724
        if ($useMultipleUrl) {
9725
            $accessConfig = [];
9726
            $accessUrls = api_get_access_url_from_user($additionalParameters['userId'], $additionalParameters['courseId']);
9727
            if (!empty($accessUrls)) {
9728
                $accessConfig['multiple_access_urls'] = true;
9729
                $accessConfig['access_url'] = (int) $accessUrls[0];
9730
                $params = ['variable = ? AND access_url = ?' => ['stylesheets', $accessConfig['access_url']]];
9731
                $settings = api_get_settings_params_simple($params);
9732
                if (!empty($settings['selected_value'])) {
9733
                    $accessConfig['theme_dir'] = \Template::getThemeDir($settings['selected_value']);
9734
                }
9735
            }
9736
            // To replace the current urls by access url user
9737
            $mail->Body = str_replace(api_get_path(WEB_PATH), api_get_path(WEB_PATH, $accessConfig), $mail->Body);
9738
            if (!empty($accessConfig['theme_dir'])) {
9739
                $mail->Body = str_replace('themes/chamilo/', $accessConfig['theme_dir'], $mail->Body);
9740
            }
9741
        }
9742
    }
9743
    // Attachment.
9744
    if (!empty($data_file)) {
9745
        foreach ($data_file as $file_attach) {
9746
            if (!empty($file_attach['path']) && !empty($file_attach['filename'])) {
9747
                $mail->AddAttachment($file_attach['path'], $file_attach['filename']);
9748
            }
9749
        }
9750
    }
9751
9752
    // Only valid addresses are accepted.
9753
    if (is_array($recipient_email)) {
9754
        foreach ($recipient_email as $dest) {
9755
            if (api_valid_email($dest)) {
9756
                if (UserManager::isEmailingAllowed($dest)) {
9757
                    // Do not send if user is not active = 1
9758
                    $mail->AddAddress($dest, $recipient_name);
9759
                }
9760
            } else {
9761
                // error_log('e-mail recipient '.$dest.' is not valid.');
9762
                return 0;
9763
            }
9764
        }
9765
    } else {
9766
        if (api_valid_email($recipient_email)) {
9767
            if (UserManager::isEmailingAllowed($recipient_email)) {
9768
                // Do not send if user is not active = 1
9769
                $mail->AddAddress($recipient_email, $recipient_name);
9770
            }
9771
        } else {
9772
            // error_log('e-mail recipient '.$recipient_email.' is not valid.');
9773
            return 0;
9774
        }
9775
    }
9776
    if (empty($mail->getAllRecipientAddresses())) {
9777
        // error_log('No valid and active destination e-mail in api_mail_html() with address '.print_r($recipient_email, 1).'. Not sending.');
9778
        return 0;
9779
    }
9780
9781
    if (is_array($extra_headers) && count($extra_headers) > 0) {
9782
        foreach ($extra_headers as $key => $value) {
9783
            switch (strtolower($key)) {
9784
                case 'encoding':
9785
                case 'content-transfer-encoding':
9786
                    $mail->Encoding = $value;
9787
                    break;
9788
                case 'charset':
9789
                    $mail->CharSet = $value;
9790
                    break;
9791
                case 'contenttype':
9792
                case 'content-type':
9793
                    $mail->ContentType = $value;
9794
                    break;
9795
                default:
9796
                    $mail->AddCustomHeader($key, $value);
9797
                    break;
9798
            }
9799
        }
9800
    } else {
9801
        if (!empty($extra_headers)) {
9802
            $mail->AddCustomHeader($extra_headers);
9803
        }
9804
    }
9805
9806
    // WordWrap the html body (phpMailer only fixes AltBody) FS#2988
9807
    $mail->Body = $mail->WrapText($mail->Body, $mail->WordWrap);
9808
9809
    if (!empty(api_get_mail_configuration_value('DKIM')) &&
9810
        !empty(api_get_mail_configuration_value('DKIM_SELECTOR')) &&
9811
        !empty(api_get_mail_configuration_value('DKIM_DOMAIN')) &&
9812
        (!empty(api_get_mail_configuration_value('DKIM_PRIVATE_KEY_STRING')) || !empty(api_get_mail_configuration_value('DKIM_PRIVATE_KEY')))) {
9813
        $mail->DKIM_selector = api_get_mail_configuration_value('DKIM_SELECTOR');
9814
        $mail->DKIM_domain = api_get_mail_configuration_value('DKIM_DOMAIN');
9815
        if (!empty(api_get_mail_configuration_value('SMTP_UNIQUE_SENDER'))) {
9816
            $mail->DKIM_identity = api_get_mail_configuration_value('SMTP_FROM_EMAIL');
9817
        }
9818
        $mail->DKIM_private_string = api_get_mail_configuration_value('DKIM_PRIVATE_KEY_STRING');
9819
        $mail->DKIM_private = api_get_mail_configuration_value('DKIM_PRIVATE_KEY');
9820
        if (!empty(api_get_mail_configuration_value['DKIM_PASSPHRASE'])) {
0 ignored issues
show
Bug introduced by
The constant api_get_mail_configuration_value was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
9821
            $mail->DKIM_passphrase = api_get_mail_configuration_value['DKIM_PASSPHRASE'];
9822
        }
9823
    }
9824
9825
    // Send the mail message.
9826
    $sent = $mail->Send();
9827
    if (!$sent) {
9828
        error_log('ERROR: mail not sent to '.$recipient_name.' ('.$recipient_email.') because of '.$mail->ErrorInfo.'<br />');
9829
    }
9830
9831
    if ($mail->SMTPDebug >= 1) {
9832
        error_log(
9833
            "Mail debug:: ".
9834
            "Protocol: ".$mail->Mailer.' :: '.
9835
            "Host/Port: ".$mail->Host.':'.$mail->Port.' :: '.
9836
            "Authent/Open: ".($mail->SMTPAuth ? 'Authent' : 'Open').' :: '.
9837
            ($mail->SMTPAuth ? "  User/Pass: ".$mail->Username.':'.$mail->Password : '').' :: '.
9838
            "Sender: ".$mail->Sender.
9839
            "Recipient email: ".$recipient_email.
9840
            "Subject: ".$subject
9841
        );
9842
    }
9843
9844
    if (!$sent) {
9845
        return 0;
9846
    }
9847
9848
    if (!empty($additionalParameters)) {
9849
        $plugin = new AppPlugin();
9850
        $smsPlugin = $plugin->getSMSPluginLibrary();
9851
        if ($smsPlugin) {
0 ignored issues
show
introduced by
$smsPlugin is of type SmsPluginLibraryInterface, thus it always evaluated to true.
Loading history...
9852
            $smsPlugin->send($additionalParameters);
9853
        }
9854
    }
9855
9856
    // Clear all the addresses.
9857
    $mail->ClearAddresses();
9858
9859
    // Clear all attachments
9860
    $mail->ClearAttachments();
9861
9862
    return 1;
9863
}
9864
9865
/**
9866
 * Checks access to a course group.
9867
 *
9868
 * @param string $tool       Possible values: GroupManager::GROUP_TOOL_*
9869
 * @param bool   $showHeader
9870
 */
9871
function api_protect_course_group($tool, $showHeader = true)
9872
{
9873
    $groupId = api_get_group_id();
9874
    if (!empty($groupId)) {
9875
        if (api_is_platform_admin()) {
9876
            return true;
9877
        }
9878
9879
        if (api_is_allowed_to_edit(false, true, true)) {
9880
            return true;
9881
        }
9882
9883
        $userId = api_get_user_id();
9884
        $sessionId = api_get_session_id();
9885
        if (!empty($sessionId)) {
9886
            if (api_is_coach($sessionId, api_get_course_int_id())) {
9887
                return true;
9888
            }
9889
9890
            if (api_is_drh()) {
9891
                if (SessionManager::isUserSubscribedAsHRM($sessionId, $userId)) {
9892
                    return true;
9893
                }
9894
            }
9895
        }
9896
9897
        $groupInfo = GroupManager::get_group_properties($groupId);
9898
9899
        // Group doesn't exists
9900
        if (empty($groupInfo)) {
9901
            api_not_allowed($showHeader);
9902
        }
9903
9904
        // Check group access
9905
        $allow = GroupManager::user_has_access(
9906
            $userId,
9907
            $groupInfo['iid'],
9908
            $tool
9909
        );
9910
9911
        if (!$allow) {
9912
            api_not_allowed($showHeader);
9913
        }
9914
    }
9915
9916
    return false;
9917
}
9918
9919
/**
9920
 * Check if a date is in a date range.
9921
 *
9922
 * @param datetime $startDate
9923
 * @param datetime $endDate
9924
 * @param datetime $currentDate
9925
 *
9926
 * @return bool true if date is in rage, false otherwise
9927
 */
9928
function api_is_date_in_date_range($startDate, $endDate, $currentDate = null)
9929
{
9930
    $startDate = strtotime(api_get_local_time($startDate));
9931
    $endDate = strtotime(api_get_local_time($endDate));
9932
    $currentDate = strtotime(api_get_local_time($currentDate));
9933
9934
    if ($currentDate >= $startDate && $currentDate <= $endDate) {
9935
        return true;
9936
    }
9937
9938
    return false;
9939
}
9940
9941
/**
9942
 * Eliminate the duplicates of a multidimensional array by sending the key.
9943
 *
9944
 * @param array $array multidimensional array
9945
 * @param int   $key   key to find to compare
9946
 *
9947
 * @return array
9948
 */
9949
function api_unique_multidim_array($array, $key)
9950
{
9951
    $temp_array = [];
9952
    $i = 0;
9953
    $key_array = [];
9954
9955
    foreach ($array as $val) {
9956
        if (!in_array($val[$key], $key_array)) {
9957
            $key_array[$i] = $val[$key];
9958
            $temp_array[$i] = $val;
9959
        }
9960
        $i++;
9961
    }
9962
9963
    return $temp_array;
9964
}
9965
9966
/**
9967
 * Limit the access to Session Admins when the limit_session_admin_role
9968
 * configuration variable is set to true.
9969
 */
9970
function api_protect_limit_for_session_admin()
9971
{
9972
    $limitAdmin = api_get_setting('limit_session_admin_role');
9973
    if (api_is_session_admin() && $limitAdmin === 'true') {
9974
        api_not_allowed(true);
9975
    }
9976
}
9977
9978
/**
9979
 * Limits that a session admin has access to list users.
9980
 * When limit_session_admin_list_users configuration variable is set to true.
9981
 */
9982
function api_protect_session_admin_list_users()
9983
{
9984
    $limitAdmin = api_get_configuration_value('limit_session_admin_list_users');
9985
9986
    if (api_is_session_admin() && true === $limitAdmin) {
9987
        api_not_allowed(true);
9988
    }
9989
}
9990
9991
/**
9992
 * @return bool
9993
 */
9994
function api_is_student_view_active()
9995
{
9996
    $studentView = Session::read('studentview');
9997
9998
    return $studentView == 'studentview';
9999
}
10000
10001
/**
10002
 * Adds a file inside the upload/$type/id.
10003
 *
10004
 * @param string $type
10005
 * @param array  $file
10006
 * @param int    $itemId
10007
 * @param string $cropParameters
10008
 *
10009
 * @return array|bool
10010
 */
10011
function api_upload_file($type, $file, $itemId, $cropParameters = '')
10012
{
10013
    $upload = process_uploaded_file($file);
10014
    if ($upload) {
10015
        $name = api_replace_dangerous_char($file['name']);
10016
10017
        // No "dangerous" files
10018
        $name = disable_dangerous_file($name);
10019
10020
        $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
10021
        $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
10022
10023
        if (!is_dir($path)) {
10024
            mkdir($path, api_get_permissions_for_new_directories(), true);
10025
        }
10026
10027
        $pathToSave = $path.$name;
10028
        $result = moveUploadedFile($file, $pathToSave);
10029
10030
        if ($result) {
10031
            if (!empty($cropParameters)) {
10032
                $image = new Image($pathToSave);
10033
                $image->crop($cropParameters);
10034
            }
10035
10036
            return ['path_to_save' => $pathId.$name];
10037
        }
10038
    }
10039
10040
    return false;
10041
}
10042
10043
/**
10044
 * @param string $type
10045
 * @param int    $itemId
10046
 * @param string $file
10047
 *
10048
 * @return bool
10049
 */
10050
function api_get_uploaded_web_url($type, $itemId, $file)
10051
{
10052
    return api_get_uploaded_file($type, $itemId, $file, true);
10053
}
10054
10055
/**
10056
 * @param string $type
10057
 * @param int    $itemId
10058
 * @param string $file
10059
 * @param bool   $getUrl
10060
 *
10061
 * @return bool
10062
 */
10063
function api_get_uploaded_file($type, $itemId, $file, $getUrl = false)
10064
{
10065
    $itemId = (int) $itemId;
10066
    $pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
10067
    $path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
10068
    $file = basename($file);
10069
    $file = $path.'/'.$file;
10070
    if (Security::check_abs_path($file, $path) && is_file($file) && file_exists($file)) {
10071
        if ($getUrl) {
10072
            return str_replace(api_get_path(SYS_UPLOAD_PATH), api_get_path(WEB_UPLOAD_PATH), $file);
10073
        }
10074
10075
        return $file;
10076
    }
10077
10078
    return false;
10079
}
10080
10081
/**
10082
 * @param string $type
10083
 * @param int    $itemId
10084
 * @param string $file
10085
 * @param string $title
10086
 */
10087
function api_download_uploaded_file($type, $itemId, $file, $title = '')
10088
{
10089
    $file = api_get_uploaded_file($type, $itemId, $file);
10090
    if ($file) {
10091
        if (Security::check_abs_path($file, api_get_path(SYS_UPLOAD_PATH).$type)) {
10092
            DocumentManager::file_send_for_download($file, true, $title);
10093
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
10094
        }
10095
    }
10096
    api_not_allowed(true);
10097
}
10098
10099
/**
10100
 * @param string $type
10101
 * @param string $file
10102
 */
10103
function api_remove_uploaded_file($type, $file)
10104
{
10105
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
10106
    $path = $typePath.'/'.$file;
10107
    if (Security::check_abs_path($path, $typePath) && file_exists($path) && is_file($path)) {
10108
        unlink($path);
10109
    }
10110
}
10111
10112
/**
10113
 * @param string $type
10114
 * @param int    $itemId
10115
 * @param string $file
10116
 *
10117
 * @return bool
10118
 */
10119
function api_remove_uploaded_file_by_id($type, $itemId, $file)
10120
{
10121
    $file = api_get_uploaded_file($type, $itemId, $file, false);
10122
    $typePath = api_get_path(SYS_UPLOAD_PATH).$type;
10123
    if (Security::check_abs_path($file, $typePath) && file_exists($file) && is_file($file)) {
10124
        unlink($file);
10125
10126
        return true;
10127
    }
10128
10129
    return false;
10130
}
10131
10132
/**
10133
 * Converts string value to float value.
10134
 *
10135
 * 3.141516 => 3.141516
10136
 * 3,141516 => 3.141516
10137
 *
10138
 * @todo WIP
10139
 *
10140
 * @param string $number
10141
 *
10142
 * @return float
10143
 */
10144
function api_float_val($number)
10145
{
10146
    $number = (float) str_replace(',', '.', trim($number));
10147
10148
    return $number;
10149
}
10150
10151
/**
10152
 * Converts float values
10153
 * Example if $decimals = 2.
10154
 *
10155
 * 3.141516 => 3.14
10156
 * 3,141516 => 3,14
10157
 *
10158
 * @param string $number            number in iso code
10159
 * @param int    $decimals
10160
 * @param string $decimalSeparator
10161
 * @param string $thousandSeparator
10162
 *
10163
 * @return bool|string
10164
 */
10165
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
10166
{
10167
    $number = api_float_val($number);
10168
10169
    return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
10170
}
10171
10172
/**
10173
 * Set location url with a exit break by default.
10174
 *
10175
 * @param string $url
10176
 * @param bool   $exit
10177
 */
10178
function api_location($url, $exit = true)
10179
{
10180
    header('Location: '.$url);
10181
10182
    if ($exit) {
10183
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
10184
    }
10185
}
10186
10187
/**
10188
 * @return string
10189
 */
10190
function api_get_web_url()
10191
{
10192
    if (api_get_setting('server_type') === 'test') {
10193
        return api_get_path(WEB_PATH).'web/app_dev.php/';
10194
    } else {
10195
        return api_get_path(WEB_PATH).'web/';
10196
    }
10197
}
10198
10199
/**
10200
 * @param string $from
10201
 * @param string $to
10202
 *
10203
 * @return string
10204
 */
10205
function api_get_relative_path($from, $to)
10206
{
10207
    // some compatibility fixes for Windows paths
10208
    $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
10209
    $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
10210
    $from = str_replace('\\', '/', $from);
10211
    $to = str_replace('\\', '/', $to);
10212
10213
    $from = explode('/', $from);
10214
    $to = explode('/', $to);
10215
    $relPath = $to;
10216
10217
    foreach ($from as $depth => $dir) {
10218
        // find first non-matching dir
10219
        if ($dir === $to[$depth]) {
10220
            // ignore this directory
10221
            array_shift($relPath);
10222
        } else {
10223
            // get number of remaining dirs to $from
10224
            $remaining = count($from) - $depth;
10225
            if ($remaining > 1) {
10226
                // add traversals up to first matching dir
10227
                $padLength = (count($relPath) + $remaining - 1) * -1;
10228
                $relPath = array_pad($relPath, $padLength, '..');
10229
                break;
10230
            } else {
10231
                $relPath[0] = './'.$relPath[0];
10232
            }
10233
        }
10234
    }
10235
10236
    return implode('/', $relPath);
10237
}
10238
10239
/**
10240
 * Unserialize content using Brummann\Polyfill\Unserialize.
10241
 *
10242
 * @param string $type
10243
 * @param string $serialized
10244
 *
10245
 * @return mixed
10246
 */
10247
function api_unserialize_content($type, $serialized, $ignoreErrors = false)
10248
{
10249
    switch ($type) {
10250
        case 'career':
10251
        case 'sequence_graph':
10252
            $allowedClasses = [Graph::class, VerticesMap::class, Vertices::class, Edges::class];
10253
            break;
10254
        case 'lp':
10255
            $allowedClasses = [
10256
                learnpath::class,
10257
                learnpathItem::class,
10258
                aicc::class,
10259
                aiccBlock::class,
10260
                aiccItem::class,
10261
                aiccObjective::class,
10262
                aiccResource::class,
10263
                scorm::class,
10264
                scormItem::class,
10265
                scormMetadata::class,
10266
                scormOrganization::class,
10267
                scormResource::class,
10268
                Link::class,
10269
                LpItem::class,
10270
            ];
10271
            break;
10272
        case 'course':
10273
            $allowedClasses = [
10274
                Course::class,
10275
                Announcement::class,
10276
                Attendance::class,
10277
                CalendarEvent::class,
10278
                CourseCopyLearnpath::class,
10279
                CourseCopyTestCategory::class,
10280
                CourseDescription::class,
10281
                CourseSession::class,
10282
                Document::class,
10283
                Forum::class,
10284
                ForumCategory::class,
10285
                ForumPost::class,
10286
                ForumTopic::class,
10287
                Glossary::class,
10288
                GradeBookBackup::class,
10289
                Link::class,
10290
                LinkCategory::class,
10291
                Quiz::class,
10292
                QuizQuestion::class,
10293
                QuizQuestionOption::class,
10294
                ScormDocument::class,
10295
                Survey::class,
10296
                SurveyInvitation::class,
10297
                SurveyQuestion::class,
10298
                Thematic::class,
10299
                ToolIntro::class,
10300
                Wiki::class,
10301
                Work::class,
10302
                stdClass::class,
10303
            ];
10304
            break;
10305
        case 'not_allowed_classes':
10306
        default:
10307
            $allowedClasses = false;
10308
    }
10309
10310
    if ($ignoreErrors) {
10311
        return @Unserialize::unserialize(
10312
            $serialized,
10313
            ['allowed_classes' => $allowedClasses]
10314
        );
10315
    }
10316
10317
    return Unserialize::unserialize(
10318
        $serialized,
10319
        ['allowed_classes' => $allowedClasses]
10320
    );
10321
}
10322
10323
/**
10324
 * Set the From and ReplyTo properties to PHPMailer instance.
10325
 *
10326
 * @throws \PHPMailer\PHPMailer\Exception
10327
 */
10328
function api_set_noreply_and_from_address_to_mailer(PHPMailer $mailer, array $sender, array $replyToAddress = [])
10329
{
10330
    $noReplyAddress = api_get_setting('noreply_email_address');
10331
    $avoidReplyToAddress = false;
10332
10333
    if (!empty($noReplyAddress)) {
10334
        $avoidReplyToAddress = api_get_configuration_value('mail_no_reply_avoid_reply_to');
10335
    }
10336
10337
    $notification = new Notification();
10338
    // If the parameter is set don't use the admin.
10339
    $senderName = !empty($sender['name']) ? $sender['name'] : $notification->getDefaultPlatformSenderName();
10340
    $senderEmail = !empty($sender['email']) ? $sender['email'] : $notification->getDefaultPlatformSenderEmail();
10341
10342
    // Send errors to the platform admin
10343
    $adminEmail = api_get_setting('emailAdministrator');
10344
    if (PHPMailer::ValidateAddress($adminEmail)) {
10345
        $mailer->AddCustomHeader('Errors-To: '.$adminEmail);
10346
    }
10347
10348
    // Reply to first
10349
    if (!$avoidReplyToAddress) {
10350
        if (
10351
            !empty($replyToAddress) &&
10352
            PHPMailer::ValidateAddress($replyToAddress['mail'])
10353
        ) {
10354
            $mailer->AddReplyTo($replyToAddress['mail'], $replyToAddress['name']);
10355
            //$mailer->Sender = $replyToAddress['mail'];
10356
        }
10357
    }
10358
10359
    //If the SMTP configuration only accept one sender
10360
    if (
10361
        !empty(api_get_mail_configuration_value('SMTP_UNIQUE_SENDER')) &&
10362
        api_get_mail_configuration_value('SMTP_UNIQUE_SENDER')
10363
    ) {
10364
        $senderName = $notification->getDefaultPlatformSenderName();
10365
        $senderEmail = $notification->getDefaultPlatformSenderEmail();
10366
10367
        if (PHPMailer::ValidateAddress($senderEmail)) {
10368
            //force-set Sender to $senderEmail, otherwise SetFrom only does it if it is currently empty
10369
            $mailer->Sender = $senderEmail;
10370
        }
10371
    }
10372
10373
    $mailer->SetFrom($senderEmail, $senderName, !$avoidReplyToAddress);
10374
}
10375
10376
/**
10377
 * @param string $template
10378
 *
10379
 * @return string
10380
 */
10381
function api_find_template($template)
10382
{
10383
    return Template::findTemplateFilePath($template);
10384
}
10385
10386
/**
10387
 * Returns an array of languages (English names like "english", "french", etc)
10388
 * to ISO 639-1 codes (fr, es, etc) for use (for example) to show flags
10389
 * Note: 'english' is returned as 'gb'.
10390
 *
10391
 * @return array
10392
 */
10393
function api_get_language_list_for_flag()
10394
{
10395
    $table = Database::get_main_table(TABLE_MAIN_LANGUAGE);
10396
    $sql = "SELECT english_name, isocode FROM $table
10397
            ORDER BY original_name ASC";
10398
    static $languages = [];
10399
    if (empty($languages)) {
10400
        $result = Database::query($sql);
10401
        while ($row = Database::fetch_array($result)) {
10402
            $languages[$row['english_name']] = $row['isocode'];
10403
        }
10404
        $languages['english'] = 'gb';
10405
    }
10406
10407
    return $languages;
10408
}
10409
10410
/**
10411
 * Generate the Javascript required for the on-page translation of
10412
 * multi-language strings.
10413
 *
10414
 * @throws Exception
10415
 *
10416
 * @return string
10417
 */
10418
function api_get_language_translate_html()
10419
{
10420
    $translate = api_get_configuration_value('translate_html');
10421
10422
    if (!$translate) {
10423
        return '';
10424
    }
10425
10426
    $languageList = api_get_languages();
10427
    $hideAll = '';
10428
    foreach ($languageList['all'] as $language) {
10429
        $hideAll .= '
10430
        $("span:lang('.$language['isocode'].')").filter(
10431
            function(e, val) {
10432
                // Only find the spans if they have set the lang
10433
                if ($(this).attr("lang") == null) {
10434
                    return false;
10435
                }
10436
10437
                // Ignore ckeditor classes
10438
                return !this.className.match(/cke(.*)/);
10439
        }).hide();'."\n";
10440
    }
10441
10442
    $userInfo = api_get_user_info();
10443
    $languageId = 0;
10444
    if (!empty($userInfo['language'])) {
10445
        $languageId = api_get_language_id($userInfo['language']);
10446
    } elseif (!empty($_GET['language'])) {
10447
        $languageId = api_get_language_id($_GET['language']);
10448
    }
10449
    $languageInfo = api_get_language_info($languageId);
10450
    $isoCode = 'en';
10451
10452
    if (!empty($languageInfo)) {
10453
        $isoCode = $languageInfo['isocode'];
10454
    }
10455
10456
    return '
10457
            $(function() {
10458
                '.$hideAll.'
10459
                var defaultLanguageFromUser = "'.$isoCode.'";
10460
10461
                $("span:lang('.$isoCode.')").filter(
10462
                    function() {
10463
                        // Ignore ckeditor classes
10464
                        return !this.className.match(/cke(.*)/);
10465
                }).show();
10466
10467
                var defaultLanguage = "";
10468
                var langFromUserFound = false;
10469
10470
                $(this).find("span").filter(
10471
                    function() {
10472
                        // Ignore ckeditor classes
10473
                        return !this.className.match(/cke(.*)/);
10474
                }).each(function() {
10475
                    defaultLanguage = $(this).attr("lang");
10476
                    if (defaultLanguage) {
10477
                        $(this).before().next("br").remove();
10478
                        if (defaultLanguageFromUser == defaultLanguage) {
10479
                            langFromUserFound = true;
10480
                        }
10481
                    }
10482
                });
10483
10484
                // Show default language
10485
                if (langFromUserFound == false && defaultLanguage) {
10486
                    $("span:lang("+defaultLanguage+")").filter(
10487
                    function() {
10488
                            // Ignore ckeditor classes
10489
                            return !this.className.match(/cke(.*)/);
10490
                    }).show();
10491
                }
10492
            });
10493
    ';
10494
}
10495
10496
/**
10497
 * Filter a multi-language HTML string (for the multi-language HTML
10498
 * feature) into the given language (strip the rest).
10499
 *
10500
 * @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>
10501
 * @param string $language   The language in which we want to get the
10502
 *
10503
 * @throws Exception
10504
 *
10505
 * @return string The filtered string in the given language, or the full string if no translated string was identified
10506
 */
10507
function api_get_filtered_multilingual_HTML_string($htmlString, $language = null)
10508
{
10509
    if (api_get_configuration_value('translate_html') != true) {
10510
        return $htmlString;
10511
    }
10512
    $userInfo = api_get_user_info();
10513
    $languageId = 0;
10514
    if (!empty($language)) {
10515
        $languageId = api_get_language_id($language);
10516
    } elseif (!empty($userInfo['language'])) {
10517
        $languageId = api_get_language_id($userInfo['language']);
10518
    }
10519
    $languageInfo = api_get_language_info($languageId);
10520
    $isoCode = 'en';
10521
10522
    if (!empty($languageInfo)) {
10523
        $isoCode = $languageInfo['isocode'];
10524
    }
10525
10526
    // Split HTML in the separate language strings
10527
    // Note: some strings might look like <p><span ..>...</span></p> but others might be like combine 2 <span> in 1 <p>
10528
    if (!preg_match('/<span.*?lang="(\w\w)">/is', $htmlString)) {
10529
        return $htmlString;
10530
    }
10531
    $matches = [];
10532
    preg_match_all('/<span.*?lang="(\w\w)">(.*?)<\/span>/is', $htmlString, $matches);
10533
    if (!empty($matches)) {
10534
        // matches[0] are the full string
10535
        // matches[1] are the languages
10536
        // matches[2] are the strings
10537
        foreach ($matches[1] as $id => $match) {
10538
            if ($match == $isoCode) {
10539
                return $matches[2][$id];
10540
            }
10541
        }
10542
        // Could find the pattern but could not find our language. Return the first language found.
10543
        return $matches[2][0];
10544
    }
10545
    // Could not find pattern. Just return the whole string. We shouldn't get here.
10546
    return $htmlString;
10547
}
10548
10549
/**
10550
 * Get the print.css file for current theme.
10551
 * Only the file path or the file contents when $getFileContents is true.
10552
 */
10553
function api_get_print_css(bool $getFileContents = true, bool $useWebPath = false): string
10554
{
10555
    $sysCssPath = api_get_path(SYS_CSS_PATH);
10556
    $cssFile = $sysCssPath.'themes/'.api_get_visual_theme().'/print.css';
10557
10558
    if (!file_exists($cssFile)) {
10559
        $cssFile = $sysCssPath.'print.css';
10560
    }
10561
10562
    if ($getFileContents) {
10563
        return file_get_contents($cssFile);
10564
    }
10565
10566
    if ($useWebPath) {
10567
        return str_replace($sysCssPath, api_get_path(WEB_CSS_PATH), $cssFile);
10568
    }
10569
10570
    return $cssFile;
10571
}
10572
10573
function api_protect_webservices()
10574
{
10575
    if (api_get_configuration_value('disable_webservices')) {
10576
        echo "Webservices are disabled. \n";
10577
        echo "To enable, add \$_configuration['disable_webservices'] = false; in configuration.php";
10578
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
10579
    }
10580
}
10581
10582
function api_filename_has_blacklisted_stream_wrapper(string $filename)
10583
{
10584
    if (strpos($filename, '://') > 0) {
10585
        $wrappers = stream_get_wrappers();
10586
        $allowedWrappers = ['http', 'https', 'file'];
10587
10588
        foreach ($wrappers as $wrapper) {
10589
            if (in_array($wrapper, $allowedWrappers)) {
10590
                continue;
10591
            }
10592
10593
            if (stripos($filename, $wrapper.'://') === 0) {
10594
                return true;
10595
            }
10596
        }
10597
    }
10598
10599
    return false;
10600
}
10601
10602
/**
10603
 * Calculate the percent between two numbers.
10604
 *
10605
 * @return string
10606
 */
10607
function api_calculate_increment_percent(int $newValue, int $oldValue)
10608
{
10609
    if ($oldValue <= 0) {
10610
        $result = " - ";
10611
    } else {
10612
        $result = ' '.round(100 * (($newValue / $oldValue) - 1), 2).' %';
10613
    }
10614
10615
    return $result;
10616
}
10617
10618
/**
10619
 * Erase settings from cache (because of some update) if applicable.
10620
 *
10621
 * @param int $url_id The ID of the present URL
10622
 */
10623
function api_flush_settings_cache(int $url_id): bool
10624
{
10625
    global $_configuration;
10626
    $cacheAvailable = api_get_configuration_value('apc');
10627
    if (!$cacheAvailable) {
10628
        return false;
10629
    }
10630
    $apcRootVarName = api_get_configuration_value('apc_prefix');
10631
    // Delete the APCu-stored settings array, if present
10632
    $apcVarName = $apcRootVarName.'settings';
10633
    apcu_delete($apcVarName);
10634
    if (api_is_multiple_url_enabled() && $url_id === 1) {
10635
        // if we are on the main URL of a multi-url portal, we must
10636
        // invalidate the cache for all other URLs as well as some
10637
        // main settings span multiple URLs
10638
        $urls = api_get_access_urls();
10639
        foreach ($urls as $i => $row) {
10640
            if ($row['id'] == 1) {
10641
                continue;
10642
            }
10643
            $apcVarName = $_configuration['main_database'].'_'.$row['id'].'_settings';
10644
            apcu_delete($apcVarName);
10645
        }
10646
    }
10647
10648
    return true;
10649
}
10650
10651
/**
10652
 * Decrypt sent data with encoded secret defined in app/config/configuration.php
10653
 * in the variable $_configuration['ldap_admin_password_salt'].
10654
 *
10655
 * @param $encryptedText The text to be decrypted
10656
 */
10657
function api_decrypt_ldap_password(string $encryptedText): string
10658
{
10659
    if (!empty(api_get_configuration_value('ldap_admin_password_salt'))) {
10660
        $secret = api_get_configuration_value('ldap_admin_password_salt');
10661
    } else {
10662
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
10663
    }
10664
10665
    return api_decrypt_hash($encryptedText, $secret);
10666
}
10667
10668
/**
10669
 * Decrypt sent hash encoded with secret.
10670
 *
10671
 * @param $encryptedText The hash text to be decrypted
10672
 * @param $secret        The secret used to encoded the hash
10673
 *
10674
 * @return string The decrypted text or false
10675
 */
10676
function api_decrypt_hash(string $encryptedHash, string $secret): string
10677
{
10678
    $iv = base64_decode(substr($encryptedHash, 0, 16), true);
10679
    $data = base64_decode(substr($encryptedHash, 16), true);
10680
    $tag = substr($data, strlen($data) - 16);
10681
    $data = substr($data, 0, strlen($data) - 16);
10682
10683
    try {
10684
        return openssl_decrypt(
10685
        $data,
10686
        'aes-256-gcm',
10687
        $secret,
10688
        OPENSSL_RAW_DATA,
10689
        $iv,
10690
        $tag
10691
      );
10692
    } catch (\Exception $e) {
10693
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
10694
    }
10695
}
10696
10697
/**
10698
 * Encrypt sent data with secret.
10699
 *
10700
 * @param $data   The text to be encrypted
10701
 * @param $secret The secret to use encode data
10702
 *
10703
 * @return string The encrypted text or false
10704
 */
10705
function api_encrypt_hash($data, $secret)
10706
{
10707
    $iv = random_bytes(12);
10708
    $tag = '';
10709
10710
    $encrypted = openssl_encrypt(
10711
    $data,
10712
    'aes-256-gcm',
10713
    $secret,
10714
    OPENSSL_RAW_DATA,
10715
    $iv,
10716
    $tag,
10717
    '',
10718
    16
10719
  );
10720
10721
    return base64_encode($iv).base64_encode($encrypted.$tag);
10722
}
10723
10724
/**
10725
 * Replace a specific term by another in all course-related text elements in the database.
10726
 * Does not rename directories or replace content of files on disk. Check tests/scripts/replace_course_code.php if
10727
 * you are looking for this.
10728
 * The replacement can replace bits in larger strings, requiring the search string to be very specific to avoid
10729
 * excess replacements.
10730
 *
10731
 * @return array The number of changes executed in each table
10732
 */
10733
function api_replace_terms_in_content(string $search, string $replace): array
10734
{
10735
    $replacements = [
10736
        Database::get_course_table(TABLE_QUIZ_TEST) => [
10737
            'iid' => ['title', 'description', 'sound'],
10738
        ],
10739
        Database::get_course_table(TABLE_QUIZ_QUESTION) => [
10740
            'iid' => ['question', 'description'],
10741
        ],
10742
        Database::get_course_table(TABLE_QUIZ_ANSWER) => [
10743
            'iid' => ['answer', 'comment'],
10744
        ],
10745
        Database::get_course_table(TABLE_ANNOUNCEMENT) => [
10746
            'iid' => ['title', 'content'],
10747
        ],
10748
        Database::get_course_table(TABLE_ANNOUNCEMENT_ATTACHMENT) => [
10749
            'iid' => ['path', 'comment'],
10750
        ],
10751
        Database::get_course_table(TABLE_ATTENDANCE) => [
10752
            'iid' => ['name', 'description', 'attendance_qualify_title'],
10753
        ],
10754
        Database::get_course_table(TABLE_BLOGS) => [
10755
            'iid' => ['blog_name', 'blog_subtitle'],
10756
        ],
10757
        Database::get_course_table(TABLE_BLOGS_ATTACHMENT) => [
10758
            'iid' => ['path', 'comment'],
10759
        ],
10760
        Database::get_course_table(TABLE_BLOGS_COMMENTS) => [
10761
            'iid' => ['title', 'comment'],
10762
        ],
10763
        Database::get_course_table(TABLE_BLOGS_POSTS) => [
10764
            'iid' => ['title', 'full_text'],
10765
        ],
10766
        Database::get_course_table(TABLE_BLOGS_TASKS) => [
10767
            'iid' => ['title', 'description'],
10768
        ],
10769
        Database::get_course_table(TABLE_AGENDA) => [
10770
            'iid' => ['title', 'content', 'comment'],
10771
        ],
10772
        Database::get_course_table(TABLE_AGENDA_ATTACHMENT) => [
10773
            'iid' => ['path', 'comment', 'filename'],
10774
        ],
10775
        Database::get_course_table(TABLE_COURSE_DESCRIPTION) => [
10776
            'iid' => ['title', 'content'],
10777
        ],
10778
        Database::get_course_table(TABLE_DOCUMENT) => [
10779
            'iid' => ['path', 'comment'],
10780
        ],
10781
        Database::get_course_table(TABLE_DROPBOX_FEEDBACK) => [
10782
            'iid' => ['feedback'],
10783
        ],
10784
        Database::get_course_table(TABLE_DROPBOX_FILE) => [
10785
            'iid' => ['title', 'description'],
10786
        ],
10787
        Database::get_course_table(TABLE_DROPBOX_POST) => [
10788
            'iid' => ['feedback'],
10789
        ],
10790
        Database::get_course_table(TABLE_FORUM_ATTACHMENT) => [
10791
            'iid' => ['path', 'comment', 'filename'],
10792
        ],
10793
        Database::get_course_table(TABLE_FORUM_CATEGORY) => [
10794
            'iid' => ['cat_title', 'cat_comment'],
10795
        ],
10796
        Database::get_course_table(TABLE_FORUM) => [
10797
            'iid' => ['forum_title', 'forum_comment', 'forum_image'],
10798
        ],
10799
        Database::get_course_table(TABLE_FORUM_POST) => [
10800
            'iid' => ['post_title', 'post_text', 'poster_name'],
10801
        ],
10802
        Database::get_course_table(TABLE_FORUM_THREAD) => [
10803
            'iid' => ['thread_title', 'thread_poster_name', 'thread_title_qualify'],
10804
        ],
10805
        Database::get_course_table(TABLE_GLOSSARY) => [
10806
            'iid' => ['name', 'description'],
10807
        ],
10808
        Database::get_course_table(TABLE_GROUP_CATEGORY) => [
10809
            'iid' => ['title', 'description'],
10810
        ],
10811
        Database::get_course_table(TABLE_GROUP) => [
10812
            'iid' => ['name', 'description', 'secret_directory'],
10813
        ],
10814
        Database::get_course_table(TABLE_LINK) => [
10815
            'iid' => ['description'],
10816
        ],
10817
        Database::get_course_table(TABLE_LINK_CATEGORY) => [
10818
            'iid' => ['category_title', 'description'],
10819
        ],
10820
        Database::get_course_table(TABLE_LP_MAIN) => [
10821
            'iid' => ['name', 'ref', 'description', 'path', 'content_license', 'preview_image', 'theme'],
10822
        ],
10823
        Database::get_course_table(TABLE_LP_CATEGORY) => [
10824
            'iid' => ['name'],
10825
        ],
10826
        Database::get_course_table(TABLE_LP_ITEM) => [
10827
            'iid' => ['prerequisite', 'description', 'title', 'parameters', 'launch_data', 'terms'],
10828
        ],
10829
        Database::get_course_table(TABLE_LP_ITEM_VIEW) => [
10830
            'iid' => ['suspend_data', 'lesson_location'],
10831
        ],
10832
        Database::get_course_table(TABLE_NOTEBOOK) => [
10833
            'iid' => ['title', 'description'],
10834
        ],
10835
        Database::get_course_table(TABLE_ONLINE_LINK) => [
10836
            'iid' => ['name'],
10837
        ],
10838
        Database::get_course_table(TABLE_QUIZ_QUESTION_CATEGORY) => [
10839
            'iid' => ['title', 'description'],
10840
        ],
10841
        Database::get_course_table(TABLE_ROLE) => [
10842
            'iid' => ['role_name', 'role_comment'],
10843
        ],
10844
        Database::get_course_table(TABLE_STUDENT_PUBLICATION) => [
10845
            'iid' => ['title', 'title_correction', 'description'],
10846
        ],
10847
        Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT_COMMENT) => [
10848
            'iid' => ['comment', 'file'],
10849
        ],
10850
        Database::get_course_table(TABLE_SURVEY) => [
10851
            'iid' => ['title', 'subtitle', 'surveythanks', 'invite_mail', 'reminder_mail', 'mail_subject', 'access_condition', 'form_fields'],
10852
        ],
10853
        Database::get_course_table(TABLE_SURVEY_QUESTION_GROUP) => [
10854
            'iid' => ['name', 'description'],
10855
        ],
10856
        Database::get_course_table(TABLE_SURVEY_QUESTION) => [
10857
            'iid' => ['survey_question', 'survey_question_comment'],
10858
        ],
10859
        Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION) => [
10860
            'iid' => ['option_text'],
10861
        ],
10862
        Database::get_course_table(TABLE_THEMATIC) => [
10863
            'iid' => ['content', 'title'],
10864
        ],
10865
        Database::get_course_table(TABLE_THEMATIC_ADVANCE) => [
10866
            'iid' => ['content'],
10867
        ],
10868
        Database::get_course_table(TABLE_THEMATIC_PLAN) => [
10869
            'iid' => ['description'],
10870
        ],
10871
        Database::get_course_table(TABLE_TOOL_LIST) => [
10872
            'iid' => ['description'],
10873
        ],
10874
        Database::get_course_table(TABLE_TOOL_INTRO) => [
10875
            'iid' => ['intro_text'],
10876
        ],
10877
        Database::get_course_table(TABLE_USER_INFO_DEF) => [
10878
            'iid' => ['comment'],
10879
        ],
10880
        Database::get_course_table(TABLE_WIKI) => [
10881
            'iid' => ['title', 'content', 'comment', 'progress', 'linksto'],
10882
        ],
10883
        Database::get_course_table(TABLE_WIKI_CONF) => [
10884
            'iid' => ['feedback1', 'feedback2', 'feedback3'],
10885
        ],
10886
        Database::get_course_table(TABLE_WIKI_DISCUSS) => [
10887
            'iid' => ['comment'],
10888
        ],
10889
        Database::get_main_table(TABLE_CAREER) => [
10890
            'id' => ['name', 'description'],
10891
        ],
10892
        Database::get_main_table(TABLE_MAIN_CHAT) => [
10893
            'id' => ['message'],
10894
        ],
10895
        Database::get_main_table(TABLE_MAIN_CLASS) => [
10896
            'id' => ['name'],
10897
        ],
10898
        Database::get_main_table(TABLE_MAIN_COURSE_REQUEST) => [
10899
            'id' => ['description', 'title', 'objetives', 'target_audience'],
10900
        ],
10901
        'course_type' => [
10902
            'id' => ['description'],
10903
        ],
10904
        Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE) => [
10905
            'id' => ['message', 'subject', 'event_type_name'],
10906
        ],
10907
        Database::get_main_table(TABLE_GRADE_MODEL) => [
10908
            'id' => ['name', 'description'],
10909
        ],
10910
        Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY) => [
10911
            'id' => ['name', 'description'],
10912
        ],
10913
        Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE) => [
10914
            'id' => ['path_certificate'],
10915
        ],
10916
        Database::get_main_table(TABLE_MAIN_GRADEBOOK_EVALUATION) => [
10917
            'id' => ['description', 'name'],
10918
        ],
10919
        Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINKEVAL_LOG) => [
10920
            'id' => ['name', 'description'],
10921
        ],
10922
        Database::get_main_table(TABLE_MAIN_LEGAL) => [
10923
            'id' => ['content', 'changes'],
10924
        ],
10925
        Database::get_main_table(TABLE_MESSAGE) => [
10926
            'id' => ['content'],
10927
        ],
10928
        Database::get_main_table(TABLE_MESSAGE_ATTACHMENT) => [
10929
            'id' => ['path', 'comment', 'filename'],
10930
        ],
10931
        Database::get_main_table(TABLE_NOTIFICATION) => [
10932
            'id' => ['content'],
10933
        ],
10934
        Database::get_main_table(TABLE_PERSONAL_AGENDA) => [
10935
            'id' => ['title', 'text'],
10936
        ],
10937
        Database::get_main_table(TABLE_PROMOTION) => [
10938
            'id' => ['description'],
10939
        ],
10940
        'room' => [
10941
            'id' => ['description'],
10942
        ],
10943
        'sequence_condition' => [
10944
            'id' => ['description'],
10945
        ],
10946
        'sequence_method' => [
10947
            'id' => ['description', 'formula'],
10948
        ],
10949
        'sequence_rule' => [
10950
            'id' => ['description'],
10951
        ],
10952
        'sequence_type_entity' => [
10953
            'id' => ['description'],
10954
        ],
10955
        'sequence_variable' => [
10956
            'id' => ['description'],
10957
        ],
10958
        Database::get_main_table(TABLE_MAIN_SESSION) => [
10959
            'id' => ['description'],
10960
        ],
10961
        Database::get_main_table(TABLE_MAIN_SHARED_SURVEY) => [
10962
            'survey_id' => ['subtitle', 'surveythanks', 'intro'],
10963
        ],
10964
        Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION) => [
10965
            'question_id' => ['survey_question', 'survey_question_comment'],
10966
        ],
10967
        Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION_OPTION) => [
10968
            'question_option_id' => ['option_text'],
10969
        ],
10970
        Database::get_main_table(TABLE_MAIN_SKILL) => [
10971
            'id' => ['name', 'description', 'criteria'],
10972
        ],
10973
        Database::get_main_table(TABLE_MAIN_SKILL_PROFILE) => [
10974
            'id' => ['description'],
10975
        ],
10976
        Database::get_main_table(TABLE_MAIN_SKILL_REL_USER) => [
10977
            'id' => ['argumentation'],
10978
        ],
10979
        'skill_rel_user_comment' => [
10980
            'id' => ['feedback_text'],
10981
        ],
10982
        Database::get_main_table(TABLE_MAIN_SYSTEM_ANNOUNCEMENTS) => [
10983
            'id' => ['content'],
10984
        ],
10985
        Database::get_main_table(TABLE_MAIN_SYSTEM_CALENDAR) => [
10986
            'id' => ['content'],
10987
        ],
10988
        Database::get_main_table(TABLE_MAIN_SYSTEM_TEMPLATE) => [
10989
            'id' => ['comment', 'content'],
10990
        ],
10991
        Database::get_main_table(TABLE_MAIN_TEMPLATES) => [
10992
            'id' => ['description', 'image'],
10993
        ],
10994
        Database::get_main_table(TABLE_TICKET_CATEGORY) => [
10995
            'id' => ['description'],
10996
        ],
10997
        Database::get_main_table(TABLE_TICKET_MESSAGE) => [
10998
            'id' => ['message'],
10999
        ],
11000
        Database::get_main_table(TABLE_TICKET_MESSAGE_ATTACHMENTS) => [
11001
            'id' => ['filename', 'path'],
11002
        ],
11003
        Database::get_main_table(TABLE_TICKET_PRIORITY) => [
11004
            'id' => ['description'],
11005
        ],
11006
        Database::get_main_table(TABLE_TICKET_PROJECT) => [
11007
            'id' => ['description'],
11008
        ],
11009
        Database::get_main_table(TABLE_TICKET_STATUS) => [
11010
            'id' => ['description'],
11011
        ],
11012
        Database::get_main_table(TABLE_TICKET_TICKET) => [
11013
            'id' => ['message'],
11014
        ],
11015
        Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT) => [
11016
            'id' => ['answer', 'teacher_comment', 'filename'],
11017
        ],
11018
        Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING) => [
11019
            'id' => ['teacher_comment'],
11020
        ],
11021
        Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT) => [
11022
            'default_id' => ['default_value'],
11023
        ],
11024
        Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES) => [
11025
            'exe_id' => ['data_tracking', 'questions_to_check'],
11026
        ],
11027
        Database::get_main_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY) => [
11028
            'id' => ['content'],
11029
        ],
11030
        'track_e_open' => [
11031
            'open_id' => ['open_remote_host', 'open_agent', 'open_referer'],
11032
        ],
11033
        Database::get_main_table(TABLE_TRACK_STORED_VALUES) => [
11034
            'id' => ['sv_value'],
11035
        ],
11036
        Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK) => [
11037
            'id' => ['sv_value'],
11038
        ],
11039
        Database::get_main_table(TABLE_MAIN_USER_API_KEY) => [
11040
            'id' => ['description'],
11041
        ],
11042
        Database::get_main_table(TABLE_USERGROUP) => [
11043
            'id' => ['name', 'description', 'picture', 'url'],
11044
        ],
11045
        Database::get_main_table(TABLE_MAIN_BLOCK) => [
11046
            'id' => ['name', 'description', 'path'],
11047
        ],
11048
    ];
11049
11050
    if (api_get_configuration_value('attendance_allow_comments')) {
11051
        $replacements['c_attendance_result_comment'] = [
11052
            'iid' => ['comment'],
11053
        ];
11054
    }
11055
11056
    if (api_get_configuration_value('exercise_text_when_finished_failure')) {
11057
        $replacements[Database::get_course_table(TABLE_QUIZ_TEST)]['iid'][] = 'text_when_finished_failure';
11058
    }
11059
11060
    $changes = array_map(
11061
        fn ($table) => 0,
11062
        $replacements
11063
    );
11064
11065
    foreach ($replacements as $table => $replacement) {
11066
        foreach ($replacement as $idColumn => $columns) {
11067
            $keys = array_map(fn ($column) => "$column LIKE %?%", $columns);
11068
            $values = array_fill(0, count($columns), $search);
11069
11070
            $result = Database::select(
11071
                [$idColumn, ...$columns],
11072
                $table,
11073
                [
11074
                    'where' => [
11075
                        implode(' OR ', $keys) => $values,
11076
                    ],
11077
                    'order' => "$idColumn ASC",
11078
                ]
11079
            );
11080
11081
            foreach ($result as $row) {
11082
                $attributes = array_combine(
11083
                    $columns,
11084
                    array_map(
11085
                        fn ($column) => preg_replace('#'.$search.'#', $replace, $row[$column]),
11086
                        $columns
11087
                    )
11088
                );
11089
11090
                try {
11091
                    Database::update(
11092
                        $table,
11093
                        $attributes,
11094
                        ["$idColumn = ?" => $row[$idColumn]]
11095
                    );
11096
                } catch (Exception $e) {
11097
                    Database::handleError($e);
11098
                }
11099
11100
                $changes[$table]++;
11101
            }
11102
        }
11103
    }
11104
11105
    return $changes;
11106
}
11107