1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* @title Import Users; Process Class |
4
|
|
|
* @desc Import new Users from CSV data file. |
5
|
|
|
* |
6
|
|
|
* @author Pierre-Henry Soria <[email protected]> |
7
|
|
|
* @copyright (c) 2015-2019, Pierre-Henry Soria. All Rights Reserved. |
8
|
|
|
* @license GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory. |
9
|
|
|
* @package PH7 / App / System / Module / Admin / Inc / Class |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace PH7; |
13
|
|
|
|
14
|
|
|
defined('PH7') or exit('Restricted access'); |
15
|
|
|
|
16
|
|
|
use PH7\Framework\Ip\Ip; |
17
|
|
|
use PH7\Framework\Security\Validate\Validate; |
18
|
|
|
use PH7\Framework\Util\Various; |
19
|
|
|
|
20
|
|
|
/** Reset the time limit and increase the memory **/ |
21
|
|
|
@set_time_limit(0); |
|
|
|
|
22
|
|
|
@ini_set('memory_limit', '528M'); |
|
|
|
|
23
|
|
|
|
24
|
|
|
class ImportUser extends Core |
25
|
|
|
{ |
26
|
|
|
const NO_ERROR = 0; |
27
|
|
|
const ERR_BAD_FILE = 1; |
28
|
|
|
const ERR_TOO_LARGE = 2; |
29
|
|
|
const ERR_INVALID = 3; |
30
|
|
|
|
31
|
|
|
const IMPORT_FILE_EXTENSION = 'csv'; |
32
|
|
|
|
33
|
|
|
/* |
34
|
|
|
* @var array Array containing the DB data types. |
35
|
|
|
*/ |
36
|
|
|
const DB_TYPES = [ |
37
|
|
|
'first_name', |
38
|
|
|
'last_name', |
39
|
|
|
'username', |
40
|
|
|
'email', |
41
|
|
|
'password', |
42
|
|
|
'sex', |
43
|
|
|
'match_sex', |
44
|
|
|
'birth_date', |
45
|
|
|
'description', |
46
|
|
|
'country', |
47
|
|
|
'city', |
48
|
|
|
'state', |
49
|
|
|
'zip_code', |
50
|
|
|
'website', |
51
|
|
|
'social_network_site', |
52
|
|
|
'ip' |
53
|
|
|
]; |
54
|
|
|
|
55
|
|
|
/** @var bool|resource */ |
56
|
|
|
private $rHandler; |
57
|
|
|
|
58
|
|
|
/** @var array */ |
59
|
|
|
private $aFile; |
60
|
|
|
|
61
|
|
|
/** @var array */ |
62
|
|
|
private $aData = []; |
63
|
|
|
|
64
|
|
|
/** @var array */ |
65
|
|
|
private $aTmpData; |
66
|
|
|
|
67
|
|
|
/** @var array */ |
68
|
|
|
private $aFileData; |
69
|
|
|
|
70
|
|
|
/** @var array */ |
71
|
|
|
private $aRes; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @param array $aFile |
75
|
|
|
* @param string $sDelimiter Delimiter Field delimiter (one character). |
76
|
|
|
* @param string $sEnclosure Enclosure Field enclosure (one character). |
77
|
|
|
*/ |
78
|
|
|
public function __construct(array $aFile, $sDelimiter, $sEnclosure) |
79
|
|
|
{ |
80
|
|
|
parent::__construct(); |
81
|
|
|
|
82
|
|
|
// Initialize necessary attributes |
83
|
|
|
$this->aFile = $aFile; |
84
|
|
|
$this->rHandler = @fopen($this->aFile['tmp_name'], 'rb'); |
85
|
|
|
$this->aFileData = @fgetcsv($this->rHandler, 0, $sDelimiter, $sEnclosure); |
86
|
|
|
$this->aRes = $this->run(); |
|
|
|
|
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @return array (boolean | string) ['status', 'msg'] |
91
|
|
|
*/ |
92
|
|
|
public function getResponse() |
93
|
|
|
{ |
94
|
|
|
return $this->aRes; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Check and set the data from the CSV file. |
99
|
|
|
* |
100
|
|
|
* @param int $iRow Number of row of the CSV file |
101
|
|
|
* |
102
|
|
|
* @return void |
103
|
|
|
*/ |
104
|
|
|
private function setData($iRow) |
105
|
|
|
{ |
106
|
|
|
$oUser = new UserCore; |
107
|
|
|
|
108
|
|
|
foreach (self::DB_TYPES as $sType) { |
109
|
|
|
$sData = !empty($this->aFileData[$this->aTmpData[$sType]]) ? trim($this->aFileData[$this->aTmpData[$sType]]) : $this->aTmpData[$sType]; |
110
|
|
|
|
111
|
|
|
if ($sType === 'username') { |
112
|
|
|
$this->aData[$iRow][$sType] = $oUser->findUsername($sData, $this->aData[$iRow]['first_name'], $this->aData[$iRow]['last_name']); |
113
|
|
|
} elseif ($sType === 'sex') { |
114
|
|
|
$this->aData[$iRow][$sType] = $this->fixGender($sData); |
115
|
|
|
} elseif ($sType === 'match_sex') { |
116
|
|
|
$this->aData[$iRow][$sType] = [$this->fixGender($sData)]; |
117
|
|
|
} elseif ($sType === 'birth_date') { |
118
|
|
|
$this->aData[$iRow][$sType] = $this->dateTime->get($sData)->date('Y-m-d'); |
119
|
|
|
} else { |
120
|
|
|
$this->aData[$iRow][$sType] = $sData; |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
unset($oUser); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Set default values for the "ImportUser::$aTmpData" array. |
129
|
|
|
* |
130
|
|
|
* @return void |
131
|
|
|
*/ |
132
|
|
|
private function setDefVals() |
133
|
|
|
{ |
134
|
|
|
$sFiveChars = Various::genRnd($this->aFile['name'], 5); |
135
|
|
|
|
136
|
|
|
$this->aTmpData = [ |
137
|
|
|
'email' => 'pierrehenrysoriasanz' . $sFiveChars . '@ph7cms' . $sFiveChars . '.com', |
138
|
|
|
'username' => 'pH7CMS' . $sFiveChars, |
139
|
|
|
'password' => Various::genRnd(), |
140
|
|
|
'first_name' => 'Alex' . $sFiveChars, |
141
|
|
|
'last_name' => 'Rolli' . $sFiveChars, |
142
|
|
|
'sex' => GenderTypeUserCore::GENDERS[array_rand(GenderTypeUserCore::GENDERS)], // Generate gender randomly |
143
|
|
|
'match_sex' => GenderTypeUserCore::GENDERS[array_rand(GenderTypeUserCore::GENDERS)], // Generate one randomly |
144
|
|
|
'birth_date' => $this->getRandomDate(), |
145
|
|
|
'country' => 'US', |
146
|
|
|
'city' => 'Virginia', |
147
|
|
|
'state' => 'Doswell', |
148
|
|
|
'zip_code' => '23047', |
149
|
|
|
'description' => 'Hi all!<br />How are you today?<br /> Bye ;)', |
150
|
|
|
'website' => '', |
151
|
|
|
'social_network_site' => '', |
152
|
|
|
'ip' => Ip::get() |
153
|
|
|
]; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
private function setTmpData() |
157
|
|
|
{ |
158
|
|
|
foreach ($this->aFileData as $sKey => $sVal) { |
159
|
|
|
$sVal = $this->cleanValue($sVal); |
160
|
|
|
|
161
|
|
|
// Test comparisons of strings and adding values in an array "ImportUser::$aTmpData" |
162
|
|
|
if ($sVal === 'username' || $sVal === 'login' || $sVal === 'user' || $sVal === 'nickname') { |
163
|
|
|
$this->aTmpData['username'] = $sKey; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
if ($sVal === 'name' || $sVal === 'firstname' || $sVal === 'givenname' || $sVal === 'forname') { |
167
|
|
|
$this->aTmpData['first_name'] = $sKey; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
if ($sVal === 'lastname' || $sVal === 'surname' || $sVal === 'familyname') { |
171
|
|
|
$this->aTmpData['last_name'] = $sKey; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
if ($sVal === 'matchsex' || $sVal === 'looking' || $sVal === 'lookingfor') { |
175
|
|
|
$this->aTmpData['match_sex'] = $sKey; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
if ($sVal === 'sex' || $sVal === 'gender') { |
179
|
|
|
$this->aTmpData['sex'] = $sKey; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
if ($sVal === 'email' || $sVal === 'mail' || $sVal === 'emailid') { |
183
|
|
|
$this->aTmpData['email'] = $sKey; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
if ($sVal === 'desc' || $sVal === 'description' || $sVal === 'descriptionme' || |
187
|
|
|
$sVal === 'generaldescription' || $sVal === 'about' || $sVal === 'aboutme' || |
188
|
|
|
$sVal === 'bio' || $sVal === 'biography' || $sVal === 'comment') { |
189
|
|
|
$this->aTmpData['description'] = $sKey; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
if ($sVal === 'country' || $sVal === 'countryid') { |
193
|
|
|
$this->aTmpData['country'] = $sKey; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
if ($sVal === 'city' || $sVal === 'town') { |
197
|
|
|
$this->aTmpData['city'] = $sKey; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
if ($sVal === 'state' || $sVal === 'district' || $sVal === 'province' || $sVal === 'region') { |
201
|
|
|
$this->aTmpData['state'] = $sKey; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
if ( |
205
|
|
|
$sVal === 'zip' || $sVal === 'zipcode' || $sVal === 'postal' || $sVal === 'postcode' || |
206
|
|
|
$sVal === 'postalcode' || $sVal === 'pin' || $sVal === 'pincode' || $sVal === 'eircode' |
207
|
|
|
) { |
208
|
|
|
$this->aTmpData['zip_code'] = $sKey; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
if ($sVal === 'website' || $sVal === 'site' || $sVal === 'url') { |
212
|
|
|
$this->aTmpData['website'] = $sKey; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if ($sVal === 'birthday' || $sVal === 'birthdate' || $sVal === 'dateofbirth' || $sVal === 'dob') { |
216
|
|
|
$this->aTmpData['birth_date'] = $sKey; |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Returns the error message for the form. |
223
|
|
|
* |
224
|
|
|
* @param int $iErrType |
225
|
|
|
* |
226
|
|
|
* @return string The error message. |
227
|
|
|
*/ |
228
|
|
|
private function getErrMsg($iErrType) |
229
|
|
|
{ |
230
|
|
|
switch ($iErrType) { |
231
|
|
|
case static::ERR_BAD_FILE: |
232
|
|
|
$sErrMsg = t('Invalid File Format! Please select a valid CSV file containing the member data.'); |
233
|
|
|
break; |
234
|
|
|
|
235
|
|
|
case static::ERR_TOO_LARGE: |
236
|
|
|
$sErrMsg = t('The file is too large. Please select a smaller file or change your server PHP settings. Especially "upload_max_filesize" and "post_max_size" directives in the php.ini file.'); |
237
|
|
|
break; |
238
|
|
|
|
239
|
|
|
case static::ERR_INVALID: |
240
|
|
|
$sErrMsg = t('The file is Invalid/Empty or has incorrect Delimiter/Enclosure set.'); |
241
|
|
|
break; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
return $sErrMsg; |
|
|
|
|
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Check (and modify if incorrect) the gender type. |
249
|
|
|
* |
250
|
|
|
* @param string $sSex |
251
|
|
|
* |
252
|
|
|
* @return string |
253
|
|
|
*/ |
254
|
|
|
private function fixGender($sSex) |
255
|
|
|
{ |
256
|
|
|
$sSex = strtolower($sSex); |
257
|
|
|
|
258
|
|
|
if (!GenderTypeUserCore::isGenderValid($sSex)) { |
259
|
|
|
$sSex = GenderTypeUserCore::GENDERS[array_rand(GenderTypeUserCore::GENDERS)]; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
return $sSex; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Remove the temporary file. |
267
|
|
|
* |
268
|
|
|
* @return void |
269
|
|
|
*/ |
270
|
|
|
private function removeTmpFile() |
271
|
|
|
{ |
272
|
|
|
$this->file->deleteFile($this->aFile['tmp_name']); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* @return array |
277
|
|
|
*/ |
278
|
|
|
private function run() |
279
|
|
|
{ |
280
|
|
|
$iErrType = $this->hasError(); |
281
|
|
|
|
282
|
|
|
if ($iErrType !== static::NO_ERROR) { |
283
|
|
|
$this->removeTmpFile(); |
284
|
|
|
$this->aRes = ['status' => false, 'msg' => $this->getErrMsg($iErrType)]; |
285
|
|
|
} else { |
286
|
|
|
$this->setDefVals(); |
287
|
|
|
$this->setTmpData(); |
288
|
|
|
|
289
|
|
|
$iRow = 0; |
290
|
|
|
$oUserModel = new UserCoreModel; |
291
|
|
|
$oExistsModel = new ExistsCoreModel; |
292
|
|
|
$oValidate = new Validate; |
293
|
|
|
|
294
|
|
|
while ($this->aFileData !== false) { |
295
|
|
|
$sEmail = trim($this->aFileData[$this->aTmpData['email']]); |
296
|
|
|
if ($oValidate->email($sEmail) && !$oExistsModel->email($sEmail)) { |
297
|
|
|
$this->setData($iRow); |
298
|
|
|
$oUserModel->add(escape($this->aData[$iRow], true)); |
|
|
|
|
299
|
|
|
$iRow++; |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$this->removeTmpFile(); |
304
|
|
|
fclose($this->rHandler); |
305
|
|
|
unset($this->rHandler, $oUserModel, $oExistsModel, $oValidate, $this->aTmpData, $this->aFileData, $this->aData); |
306
|
|
|
|
307
|
|
|
return [ |
308
|
|
|
'status' => true, |
309
|
|
|
'msg' => nt('%n% user has been successfully added.', '%n% users has been successfully added.', $iRow) |
310
|
|
|
]; |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Generates a random (birth) date. |
316
|
|
|
* |
317
|
|
|
* @return string |
318
|
|
|
*/ |
319
|
|
|
private function getRandomDate() |
320
|
|
|
{ |
321
|
|
|
return date('Y') - mt_rand(20, 50) . '-' . mt_rand(1, 12) . '-' . mt_rand(1, 28); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @return int |
326
|
|
|
*/ |
327
|
|
|
private function hasError() |
328
|
|
|
{ |
329
|
|
|
$sExtFile = $this->file->getFileExt($this->aFile['name']); |
330
|
|
|
|
331
|
|
|
if ($sExtFile !== self::IMPORT_FILE_EXTENSION) { |
332
|
|
|
return static::ERR_BAD_FILE; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
if ($this->aFile['error'] === UPLOAD_ERR_INI_SIZE) { |
336
|
|
|
return static::ERR_TOO_LARGE; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
if (!$this->rHandler || !$this->aFileData || !is_array($this->aFileData)) { |
|
|
|
|
340
|
|
|
return static::ERR_INVALID; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
return static::NO_ERROR; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Clean the text to make comparisons easier... |
348
|
|
|
* |
349
|
|
|
* @param $sValue |
350
|
|
|
* |
351
|
|
|
* @return string |
352
|
|
|
*/ |
353
|
|
|
private function cleanValue($sValue) |
354
|
|
|
{ |
355
|
|
|
return strtolower(trim(str_replace(['-', '_', ' '], '', $sValue))); |
356
|
|
|
} |
357
|
|
|
} |
358
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.