1
|
|
|
<?php |
2
|
|
|
/* For licensing terms, see /license.txt */ |
3
|
|
|
/** |
4
|
|
|
* This tool allows platform admins to add users by uploading a CSV or XML file |
5
|
|
|
* @package chamilo.admin |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Validate the imported data. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
$cidReset = true; |
13
|
|
|
require_once '../inc/global.inc.php'; |
14
|
|
|
|
15
|
|
|
// Set this option to true to enforce strict purification for usenames. |
16
|
|
|
$purification_option_for_usernames = false; |
17
|
|
|
|
18
|
|
|
function validate_data($users) |
|
|
|
|
19
|
|
|
{ |
20
|
|
|
global $defined_auth_sources; |
21
|
|
|
$errors = array(); |
22
|
|
|
$usernames = array(); |
23
|
|
|
|
24
|
|
|
// 1. Check if mandatory fields are set. |
25
|
|
|
$mandatory_fields = array('LastName', 'FirstName'); |
26
|
|
|
|
27
|
|
|
if (api_get_setting('registration', 'email') == 'true') { |
28
|
|
|
$mandatory_fields[] = 'Email'; |
29
|
|
|
} |
30
|
|
|
$classExistList = array(); |
31
|
|
|
$usergroup = new UserGroup(); |
32
|
|
|
|
33
|
|
|
foreach ($users as $user) { |
34
|
|
View Code Duplication |
foreach ($mandatory_fields as $field) { |
35
|
|
|
if (isset($user[$field])) { |
36
|
|
|
if (empty($user[$field])) { |
37
|
|
|
$user['error'] = get_lang($field.'Mandatory'); |
38
|
|
|
$errors[] = $user; |
39
|
|
|
} |
40
|
|
|
} |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
// 2. Check username, first, check whether it is empty. |
44
|
|
|
|
45
|
|
|
if (isset($user['NewUserName'])) { |
46
|
|
|
if (!UserManager::is_username_empty($user['NewUserName'])) { |
47
|
|
|
// 2.1. Check whether username is too long. |
48
|
|
View Code Duplication |
if (UserManager::is_username_too_long($user['NewUserName'])) { |
49
|
|
|
$user['error'] = get_lang('UserNameTooLong'); |
50
|
|
|
$errors[] = $user; |
51
|
|
|
} |
52
|
|
|
// 2.2. Check whether the username was used twice in import file. |
53
|
|
View Code Duplication |
if (isset($usernames[$user['NewUserName']])) { |
54
|
|
|
$user['error'] = get_lang('UserNameUsedTwice'); |
55
|
|
|
$errors[] = $user; |
56
|
|
|
} |
57
|
|
|
$usernames[$user['UserName']] = 1; |
58
|
|
|
// 2.3. Check whether username is allready occupied. |
59
|
|
|
if (!UserManager::is_username_available($user['NewUserName']) && $user['NewUserName'] != $user['UserName']) { |
60
|
|
|
$user['error'] = get_lang('UserNameNotAvailable'); |
61
|
|
|
$errors[] = $user; |
62
|
|
|
} |
63
|
|
|
} |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
// 3. Check status. |
67
|
|
View Code Duplication |
if (isset($user['Status']) && !api_status_exists($user['Status'])) { |
68
|
|
|
$user['error'] = get_lang('WrongStatus'); |
69
|
|
|
$errors[] = $user; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
// 4. Check ClassId |
73
|
|
View Code Duplication |
if (!empty($user['ClassId'])) { |
74
|
|
|
$classId = explode('|', trim($user['ClassId'])); |
75
|
|
|
foreach ($classId as $id) { |
76
|
|
|
if (in_array($id, $classExistList)) { |
77
|
|
|
continue; |
78
|
|
|
} |
79
|
|
|
$info = $usergroup->get($id); |
80
|
|
|
if (empty($info)) { |
81
|
|
|
$user['error'] = sprintf(get_lang('ClassIdDoesntExists'), $id); |
82
|
|
|
$errors[] = $user; |
83
|
|
|
} else { |
84
|
|
|
$classExistList[] = $info['id']; |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
// 5. Check authentication source |
90
|
|
View Code Duplication |
if (!empty($user['AuthSource'])) { |
91
|
|
|
if (!in_array($user['AuthSource'], $defined_auth_sources)) { |
92
|
|
|
$user['error'] = get_lang('AuthSourceNotAvailable'); |
93
|
|
|
$errors[] = $user; |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return $errors; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Add missing user-information (which isn't required, like password, username etc). |
103
|
|
|
*/ |
104
|
|
|
function complete_missing_data($user) |
|
|
|
|
105
|
|
|
{ |
106
|
|
|
global $purification_option_for_usernames; |
107
|
|
|
// 1. Create a username if necessary. |
108
|
|
View Code Duplication |
if (UserManager::is_username_empty($user['UserName'])) { |
109
|
|
|
$user['UserName'] = UserManager::create_unique_username($user['FirstName'], $user['LastName']); |
110
|
|
|
} else { |
111
|
|
|
$user['UserName'] = UserManager::purify_username($user['UserName'], $purification_option_for_usernames); |
112
|
|
|
} |
113
|
|
|
// 2. Generate a password if necessary. |
114
|
|
|
if (empty($user['Password'])) { |
115
|
|
|
$user['Password'] = api_generate_password(); |
116
|
|
|
} |
117
|
|
|
// 3. Set status if not allready set. |
118
|
|
|
if (empty($user['Status'])) { |
119
|
|
|
$user['Status'] = 'user'; |
120
|
|
|
} |
121
|
|
|
// 4. Set authsource if not allready set. |
122
|
|
|
if (empty($user['AuthSource'])) { |
123
|
|
|
$user['AuthSource'] = PLATFORM_AUTH_SOURCE; |
124
|
|
|
} |
125
|
|
|
return $user; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Update users from the imported data |
130
|
|
|
* @param array $users List of users |
131
|
|
|
* @return void |
132
|
|
|
* @uses global variable $inserted_in_course, which returns the list of courses the user was inserted in |
133
|
|
|
*/ |
134
|
|
|
|
135
|
|
|
function updateUsers($users) |
136
|
|
|
{ |
137
|
|
|
global $insertedIn_course; |
138
|
|
|
// Not all scripts declare the $inserted_in_course array (although they should). |
139
|
|
|
if (!isset($inserted_in_course)) { |
|
|
|
|
140
|
|
|
$inserted_in_course = array(); |
141
|
|
|
} |
142
|
|
|
$usergroup = new UserGroup(); |
143
|
|
|
$send_mail = $_POST['sendMail'] ? true : false; |
144
|
|
|
if (is_array($users)) { |
145
|
|
|
foreach ($users as $user) { |
146
|
|
|
$user = complete_missing_data($user); |
147
|
|
|
$user['Status'] = api_status_key($user['Status']); |
148
|
|
|
$userName = $user['UserName']; |
149
|
|
|
$userInfo = api_get_user_info_from_username($userName); |
150
|
|
|
$user_id = $userInfo['user_id']; |
151
|
|
|
if ($user_id == 0) { |
152
|
|
|
return false; |
153
|
|
|
} |
154
|
|
|
$firstName = isset($user['FirstName']) ? $user['FirstName'] : $userInfo['firstname']; |
155
|
|
|
$lastName = isset($user['LastName']) ? $user['LastName'] : $userInfo['lastname']; |
156
|
|
|
$userName = isset($user['NewUserName']) ? $user['NewUserName'] : $userInfo['username']; |
157
|
|
|
$password = isset($user['Password']) ? $user['Password'] : $userInfo['password']; |
158
|
|
|
$authSource = isset($user['AuthSource']) ? $user['AuthSource'] : $userInfo['auth_source']; |
159
|
|
|
$email = isset($user['Email']) ? $user['Email'] : $userInfo['email']; |
160
|
|
|
$status = isset($user['Status']) ? $user['Status'] : $userInfo['status']; |
161
|
|
|
$officialCode = isset($user['OfficialCode']) ? $user['OfficialCode'] : $userInfo['official_code']; |
162
|
|
|
$phone = isset($user['PhoneNumber']) ? $user['PhoneNumber'] : $userInfo['phone']; |
163
|
|
|
$pictureUrl = isset($user['PictureUri']) ? $user['PictureUri'] : $userInfo['picture_uri']; |
164
|
|
|
$expirationDate = isset($user['ExpiryDate']) ? $user['ExpiryDate'] : $userInfo['expiration_date']; |
165
|
|
|
$active = isset($user['Active']) ? $user['Active'] : $userInfo['active']; |
166
|
|
|
$creatorId = $userInfo['creator_id']; |
167
|
|
|
$hrDeptId = $userInfo['hr_dept_id']; |
168
|
|
|
$language = isset($user['Language']) ? $user['Language'] : $userInfo['language']; |
169
|
|
|
$sendEmail = isset($user['SendEmail']) ? $user['SendEmail'] : $userInfo['language']; |
170
|
|
|
$userUpdated = UserManager :: update_user( |
171
|
|
|
$user_id, |
172
|
|
|
$firstName, |
173
|
|
|
$lastName, |
174
|
|
|
$userName, |
175
|
|
|
$password, |
176
|
|
|
$authSource, |
177
|
|
|
$email, |
178
|
|
|
$status, |
179
|
|
|
$officialCode, |
180
|
|
|
$phone, |
181
|
|
|
$pictureUrl, |
182
|
|
|
$expirationDate, |
183
|
|
|
$active, |
184
|
|
|
$creatorId, |
185
|
|
|
$hrDeptId, |
186
|
|
|
null, |
187
|
|
|
$language, |
188
|
|
|
'', |
189
|
|
|
'', |
190
|
|
|
'' |
191
|
|
|
|
192
|
|
|
); |
193
|
|
View Code Duplication |
if (!is_array($user['Courses']) && !empty($user['Courses'])) { |
194
|
|
|
$user['Courses'] = array($user['Courses']); |
195
|
|
|
} |
196
|
|
|
if (is_array($user['Courses'])) { |
197
|
|
|
foreach ($user['Courses'] as $course) { |
198
|
|
|
if (CourseManager::course_exists($course)) { |
199
|
|
|
CourseManager::subscribe_user($user_id, $course, $user['Status']); |
200
|
|
|
$course_info = CourseManager::get_course_information($course); |
201
|
|
|
$inserted_in_course[$course] = $course_info['title']; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
View Code Duplication |
if (!empty($user['ClassId'])) { |
206
|
|
|
$classId = explode('|', trim($user['ClassId'])); |
207
|
|
|
foreach ($classId as $id) { |
208
|
|
|
$usergroup->subscribe_users_to_usergroup($id, array($user_id), false); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
// Saving extra fields. |
213
|
|
|
global $extra_fields; |
214
|
|
|
|
215
|
|
|
// We are sure that the extra field exists. |
216
|
|
View Code Duplication |
foreach ($extra_fields as $extras) { |
217
|
|
|
if (isset($user[$extras[1]])) { |
218
|
|
|
$key = $extras[1]; |
219
|
|
|
$value = $user[$extras[1]]; |
220
|
|
|
UserManager::update_extra_field_value($user_id, $key, $value); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Read the CSV-file |
230
|
|
|
* @param string $file Path to the CSV-file |
231
|
|
|
* @return array All userinformation read from the file |
232
|
|
|
*/ |
233
|
|
View Code Duplication |
function parse_csv_data($file) |
|
|
|
|
234
|
|
|
{ |
235
|
|
|
$users = Import :: csvToArray($file); |
|
|
|
|
236
|
|
|
foreach ($users as $index => $user) { |
237
|
|
|
if (isset ($user['Courses'])) { |
238
|
|
|
$user['Courses'] = explode('|', trim($user['Courses'])); |
239
|
|
|
} |
240
|
|
|
$users[$index] = $user; |
241
|
|
|
} |
242
|
|
|
return $users; |
243
|
|
|
} |
244
|
|
|
/** |
245
|
|
|
* XML-parser: handle start of element |
246
|
|
|
* @param string $parser Deprecated? |
247
|
|
|
* @param string $data The data to be parsed |
248
|
|
|
*/ |
249
|
|
View Code Duplication |
function element_start($parser, $data) |
|
|
|
|
250
|
|
|
{ |
251
|
|
|
$data = api_utf8_decode($data); |
252
|
|
|
global $user; |
253
|
|
|
global $current_tag; |
254
|
|
|
switch ($data) { |
255
|
|
|
case 'Contact': |
256
|
|
|
$user = array (); |
257
|
|
|
break; |
258
|
|
|
default: |
259
|
|
|
$current_tag = $data; |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* XML-parser: handle end of element |
265
|
|
|
* @param string $parser Deprecated? |
266
|
|
|
* @param string $data The data to be parsed |
267
|
|
|
*/ |
268
|
|
View Code Duplication |
function element_end($parser, $data) |
|
|
|
|
269
|
|
|
{ |
270
|
|
|
$data = api_utf8_decode($data); |
271
|
|
|
global $user; |
272
|
|
|
global $users; |
273
|
|
|
global $current_value; |
274
|
|
|
switch ($data) { |
275
|
|
|
case 'Contact': |
276
|
|
|
if ($user['Status'] == '5') { |
277
|
|
|
$user['Status'] = STUDENT; |
278
|
|
|
} |
279
|
|
|
if ($user['Status'] == '1') { |
280
|
|
|
$user['Status'] = COURSEMANAGER; |
281
|
|
|
} |
282
|
|
|
$users[] = $user; |
283
|
|
|
break; |
284
|
|
|
default: |
285
|
|
|
$user[$data] = $current_value; |
286
|
|
|
break; |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* XML-parser: handle character data |
292
|
|
|
* @param string $parser Parser (deprecated?) |
293
|
|
|
* @param string $data The data to be parsed |
294
|
|
|
* @return void |
295
|
|
|
*/ |
296
|
|
|
function character_data($parser, $data) |
|
|
|
|
297
|
|
|
{ |
298
|
|
|
$data = trim(api_utf8_decode($data)); |
299
|
|
|
global $current_value; |
300
|
|
|
$current_value = $data; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Read the XML-file |
305
|
|
|
* @param string $file Path to the XML-file |
306
|
|
|
* @return array All user information read from the file |
307
|
|
|
*/ |
308
|
|
View Code Duplication |
function parse_xml_data($file) |
|
|
|
|
309
|
|
|
{ |
310
|
|
|
global $users; |
311
|
|
|
$users = array(); |
312
|
|
|
$parser = xml_parser_create('UTF-8'); |
313
|
|
|
xml_set_element_handler($parser, 'element_start', 'element_end'); |
314
|
|
|
xml_set_character_data_handler($parser, 'character_data'); |
315
|
|
|
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); |
316
|
|
|
xml_parse($parser, api_utf8_encode_xml(file_get_contents($file))); |
317
|
|
|
xml_parser_free($parser); |
318
|
|
|
return $users; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
$this_section = SECTION_PLATFORM_ADMIN; |
322
|
|
|
api_protect_admin_script(true, null, 'login'); |
323
|
|
|
|
324
|
|
|
|
325
|
|
|
$defined_auth_sources[] = PLATFORM_AUTH_SOURCE; |
326
|
|
|
|
327
|
|
|
if (isset($extAuthSource) && is_array($extAuthSource)) { |
328
|
|
|
$defined_auth_sources = array_merge($defined_auth_sources, array_keys($extAuthSource)); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
$tool_name = get_lang('ImportUserListXMLCSV'); |
332
|
|
|
$interbreadcrumb[] = array("url" => 'index.php', "name" => get_lang('PlatformAdmin')); |
333
|
|
|
|
334
|
|
|
set_time_limit(0); |
335
|
|
|
$extra_fields = UserManager::get_extra_fields(0, 0, 5, 'ASC', true); |
336
|
|
|
$user_id_error = array(); |
337
|
|
|
$error_message = ''; |
338
|
|
|
|
339
|
|
|
if (isset($_POST['formSent']) && $_POST['formSent'] AND $_FILES['import_file']['size'] !== 0) { |
340
|
|
|
|
341
|
|
|
$file_type = 'csv'; |
342
|
|
|
|
343
|
|
|
Security::clear_token(); |
344
|
|
|
$tok = Security::get_token(); |
345
|
|
|
$allowed_file_mimetype = array('csv', 'xml'); |
346
|
|
|
$error_kind_file = false; |
347
|
|
|
|
348
|
|
|
$uploadInfo = pathinfo($_FILES['import_file']['name']); |
349
|
|
|
$ext_import_file = $uploadInfo['extension']; |
350
|
|
|
|
351
|
|
View Code Duplication |
if (in_array($ext_import_file, $allowed_file_mimetype)) { |
352
|
|
|
if (strcmp($file_type, 'csv') === 0 && $ext_import_file == $allowed_file_mimetype[0]) { |
353
|
|
|
$users = parse_csv_data($_FILES['import_file']['tmp_name']); |
354
|
|
|
$errors = validate_data($users); |
355
|
|
|
$error_kind_file = false; |
356
|
|
|
} elseif (strcmp($file_type, 'xml') === 0 && $ext_import_file == $allowed_file_mimetype[1]) { |
357
|
|
|
$users = parse_xml_data($_FILES['import_file']['tmp_name']); |
358
|
|
|
$errors = validate_data($users); |
359
|
|
|
$error_kind_file = false; |
360
|
|
|
} else { |
361
|
|
|
|
362
|
|
|
$error_kind_file = true; |
363
|
|
|
} |
364
|
|
|
} else { |
365
|
|
|
$error_kind_file = true; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
// List user id with error. |
369
|
|
|
$users_to_insert = $user_id_error = array(); |
370
|
|
|
|
371
|
|
|
if (is_array($errors)) { |
372
|
|
|
foreach ($errors as $my_errors) { |
373
|
|
|
$user_id_error[] = $my_errors['UserName']; |
374
|
|
|
} |
375
|
|
|
} |
376
|
|
|
|
377
|
|
View Code Duplication |
if (is_array($users)) { |
378
|
|
|
foreach ($users as $my_user) { |
379
|
|
|
if (!in_array($my_user['UserName'], $user_id_error)) { |
380
|
|
|
$users_to_insert[] = $my_user; |
381
|
|
|
} |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
$inserted_in_course = array(); |
386
|
|
|
if (strcmp($file_type, 'csv') === 0) { |
387
|
|
|
updateUsers($users_to_insert); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
View Code Duplication |
if (count($errors) > 0) { |
391
|
|
|
$see_message_import = get_lang('FileImportedJustUsersThatAreNotRegistered'); |
392
|
|
|
} else { |
393
|
|
|
$see_message_import = get_lang('FileImported'); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
View Code Duplication |
if (count($errors) != 0) { |
397
|
|
|
$warning_message = '<ul>'; |
398
|
|
|
foreach ($errors as $index => $error_user) { |
399
|
|
|
$warning_message .= '<li><b>'.$error_user['error'].'</b>: '; |
400
|
|
|
$warning_message .= |
401
|
|
|
'<strong>'.$error_user['UserName'].'</strong> ('. |
402
|
|
|
api_get_person_name($error_user['FirstName'], $error_user['LastName']).')'; |
403
|
|
|
$warning_message .= '</li>'; |
404
|
|
|
} |
405
|
|
|
$warning_message .= '</ul>'; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
// if the warning message is too long then we display the warning message trough a session |
409
|
|
|
Display::addFlash(Display::return_message($warning_message, 'warning', false)); |
410
|
|
|
|
411
|
|
View Code Duplication |
if ($error_kind_file) { |
412
|
|
|
Display::addFlash(Display::return_message(get_lang('YouMustImportAFileAccordingToSelectedOption'), 'error', false)); |
413
|
|
|
} else { |
414
|
|
|
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/user_list.php?sec_token='.$tok); |
415
|
|
|
exit; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
} |
419
|
|
|
Display :: display_header($tool_name); |
420
|
|
|
|
421
|
|
|
if (!empty($error_message)) { |
422
|
|
|
Display::display_error_message($error_message); |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
$form = new FormValidator('user_update_import', 'post', api_get_self()); |
426
|
|
|
$form->addElement('header', $tool_name); |
427
|
|
|
$form->addElement('hidden', 'formSent'); |
428
|
|
|
$form->addElement('file', 'import_file', get_lang('ImportFileLocation')); |
429
|
|
|
|
430
|
|
|
$group = array(); |
431
|
|
|
|
432
|
|
|
$form->addButtonImport(get_lang('Import')); |
433
|
|
|
$defaults['formSent'] = 1; |
434
|
|
|
$defaults['sendMail'] = 0; |
435
|
|
|
$defaults['file_type'] = 'csv'; |
436
|
|
|
$form->setDefaults($defaults); |
437
|
|
|
$form->display(); |
438
|
|
|
|
439
|
|
|
$list = array(); |
440
|
|
|
$list_reponse = array(); |
441
|
|
|
$result_xml = ''; |
442
|
|
|
$i = 0; |
443
|
|
|
$count_fields = count($extra_fields); |
444
|
|
View Code Duplication |
if ($count_fields > 0) { |
445
|
|
|
foreach ($extra_fields as $extra) { |
446
|
|
|
$list[] = $extra[1]; |
447
|
|
|
$list_reponse[] = 'xxx'; |
448
|
|
|
$spaces = ' '; |
449
|
|
|
$result_xml .= $spaces.'<'.$extra[1].'>xxx</'.$extra[1].'>'; |
450
|
|
|
if ($i != $count_fields - 1) { |
451
|
|
|
$result_xml .= '<br/>'; |
452
|
|
|
} |
453
|
|
|
$i++; |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
?> |
458
|
|
|
<p><?php echo get_lang('CSVMustLookLike').' ('.get_lang('MandatoryFields').')'; ?> :</p> |
459
|
|
|
|
460
|
|
|
<blockquote> |
461
|
|
|
<pre> |
462
|
|
|
<b>UserName</b>;LastName;FirstName;Email;NewUserName;Password;AuthSource;OfficialCode;PhoneNumber;Status;ExpiryDate;Active;Language;Courses;ClassId; |
463
|
|
|
xxx;xxx;xxx;xxx;xxx;xxx;xxx;xxx;xxx;user/teacher/drh;0000-00-00 00:00:00;0/1;xxx;<span style="color:red;"><?php if (count($list_reponse) > 0) echo implode(';', $list_reponse).';'; ?></span>xxx1|xxx2|xxx3;1;<br /> |
464
|
|
|
</pre> |
465
|
|
|
</blockquote> |
466
|
|
|
<p><?php |
467
|
|
|
|
468
|
|
|
Display :: display_footer(); |
469
|
|
|
|
This check looks for functions that have already been defined in other files.
Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the
@ignore
annotation.See also the PhpDoc documentation for @ignore.