2016-12-26 15:16:42 -08:00
|
|
|
<?php
|
|
|
|
namespace App\Importer;
|
|
|
|
|
2017-01-25 21:29:23 -08:00
|
|
|
|
2016-12-26 15:16:42 -08:00
|
|
|
use App\Models\CustomField;
|
|
|
|
use App\Models\Setting;
|
|
|
|
use App\Models\User;
|
|
|
|
use ForceUTF8\Encoding;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
use League\Csv\Reader;
|
|
|
|
|
|
|
|
abstract class Importer
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $filename;
|
|
|
|
private $csv;
|
|
|
|
/**
|
|
|
|
* Should we persist to database?
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $testRun;
|
|
|
|
/**
|
|
|
|
* Id of User performing import
|
|
|
|
* @var
|
|
|
|
*/
|
|
|
|
protected $user_id;
|
|
|
|
/**
|
|
|
|
* Are we updating items in the import
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $updating;
|
|
|
|
/**
|
|
|
|
* @var callable
|
|
|
|
*/
|
|
|
|
protected $logCallback;
|
|
|
|
protected $tempPassword;
|
|
|
|
/**
|
|
|
|
* @var callable
|
|
|
|
*/
|
|
|
|
protected $progressCallback;
|
|
|
|
/**
|
|
|
|
* @var null
|
|
|
|
*/
|
|
|
|
protected $usernameFormat;
|
|
|
|
/**
|
|
|
|
* @var callable
|
|
|
|
*/
|
|
|
|
protected $errorCallback;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ObjectImporter constructor.
|
|
|
|
* @param string $filename
|
|
|
|
*/
|
2017-01-25 21:29:23 -08:00
|
|
|
public function __construct(string $filename)
|
|
|
|
{
|
2017-01-05 15:45:12 -08:00
|
|
|
|
2016-12-26 15:16:42 -08:00
|
|
|
$this->filename = $filename;
|
|
|
|
$this->csv = Reader::createFromPath($filename);
|
|
|
|
$this->csv->setNewLine('\r\n');
|
|
|
|
if (! ini_get("auto_detect_line_endings")) {
|
|
|
|
ini_set("auto_detect_line_endings", '1');
|
|
|
|
}
|
|
|
|
$this->tempPassword = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
|
|
|
}
|
|
|
|
// Cached Values for import lookups
|
|
|
|
protected $customFields;
|
|
|
|
|
|
|
|
public function import()
|
|
|
|
{
|
|
|
|
$results = $this->normalizeInputArray($this->csv->fetchAssoc());
|
2017-01-25 21:29:23 -08:00
|
|
|
$this->customFields = CustomField::All(['name']);
|
2016-12-26 15:16:42 -08:00
|
|
|
DB::transaction(function () use (&$results) {
|
|
|
|
Model::unguard();
|
|
|
|
$resultsCount = sizeof($results);
|
|
|
|
foreach ($results as $row) {
|
|
|
|
$this->handle($row);
|
|
|
|
call_user_func($this->progressCallback, $resultsCount);
|
|
|
|
|
|
|
|
$this->log('------------- Action Summary ----------------');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract protected function handle($row);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $results
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function normalizeInputArray($results): array
|
|
|
|
{
|
|
|
|
$newArray = [];
|
|
|
|
|
|
|
|
foreach ($results as $index => $arrayToNormalize) {
|
|
|
|
$newArray[$index] = array_change_key_case($arrayToNormalize);
|
|
|
|
}
|
|
|
|
return $newArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check to see if the given key exists in the array, and trim excess white space before returning it
|
|
|
|
*
|
|
|
|
* @author Daniel Melzter
|
|
|
|
* @since 3.0
|
|
|
|
* @param $array array
|
|
|
|
* @param $key string
|
|
|
|
* @param $default string
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function array_smart_fetch(array $array, $key, $default = '')
|
|
|
|
{
|
2017-01-10 16:19:18 -08:00
|
|
|
$val = $default;
|
|
|
|
if (array_key_exists(trim($key), $array)) {
|
2017-01-25 03:10:18 -08:00
|
|
|
$val = e(Encoding::toUTF8(trim($array[ $key ])));
|
2017-01-10 16:19:18 -08:00
|
|
|
}
|
|
|
|
$key = title_case($key);
|
2017-01-25 21:29:23 -08:00
|
|
|
// $this->log("${key}: ${val}");
|
2017-01-10 16:19:18 -08:00
|
|
|
return $val;
|
2016-12-26 15:16:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Figure out the fieldname of the custom field
|
|
|
|
*
|
|
|
|
* @author A. Gianotto <snipe@snipe.net>
|
|
|
|
* @since 3.0
|
|
|
|
* @param $array array
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function array_smart_custom_field_fetch(array $array, $key)
|
|
|
|
{
|
|
|
|
$index_name = strtolower($key->name);
|
|
|
|
return array_key_exists($index_name, $array) ? e(trim($array[$index_name])) : '';
|
|
|
|
}
|
|
|
|
|
2016-12-29 14:02:18 -08:00
|
|
|
protected function log($string)
|
|
|
|
{
|
2016-12-26 15:16:42 -08:00
|
|
|
call_user_func($this->logCallback, $string);
|
|
|
|
}
|
|
|
|
|
2017-01-10 16:19:18 -08:00
|
|
|
protected function jsonError($item, $field)
|
2016-12-29 14:02:18 -08:00
|
|
|
{
|
2017-01-10 16:19:18 -08:00
|
|
|
call_user_func($this->errorCallback, $item, $field, $item->getErrors());
|
2016-12-26 15:16:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds the user matching given data, or creates a new one if there is no match
|
|
|
|
*
|
|
|
|
* @author Daniel Melzter
|
|
|
|
* @since 3.0
|
|
|
|
* @param $row array
|
|
|
|
* @return User Model w/ matching name
|
|
|
|
* @internal param string $user_username Username extracted from CSV
|
|
|
|
* @internal param string $user_email Email extracted from CSV
|
|
|
|
* @internal param string $first_name
|
|
|
|
* @internal param string $last_name
|
|
|
|
*/
|
|
|
|
protected function createOrFetchUser($row)
|
|
|
|
{
|
|
|
|
$user_name = $this->array_smart_fetch($row, "name");
|
|
|
|
$user_email = $this->array_smart_fetch($row, "email");
|
|
|
|
$user_username = $this->array_smart_fetch($row, "username");
|
|
|
|
$first_name = '';
|
|
|
|
$last_name = '';
|
|
|
|
// A number was given instead of a name
|
|
|
|
if (is_numeric($user_name)) {
|
|
|
|
$this->log('User '.$user_name.' is not a name - assume this user already exists');
|
|
|
|
$user_username = '';
|
|
|
|
// No name was given
|
|
|
|
} elseif (empty($user_name)) {
|
|
|
|
$this->log('No user data provided - skipping user creation, just adding asset');
|
|
|
|
//$user_username = '';
|
|
|
|
} else {
|
|
|
|
$user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
|
|
|
|
$first_name = $user_email_array['first_name'];
|
|
|
|
$last_name = $user_email_array['last_name'];
|
|
|
|
|
|
|
|
if ($user_email=='') {
|
|
|
|
if (Setting::getSettings()->email_domain) {
|
|
|
|
$user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($user_username=='') {
|
|
|
|
if ($this->usernameFormat =='email') {
|
|
|
|
$user_username = $user_email;
|
|
|
|
} else {
|
|
|
|
$user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
|
|
|
|
$user_username = $user_name_array['username'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$user = new User;
|
|
|
|
if ($this->testRun) {
|
|
|
|
return $user;
|
|
|
|
}
|
|
|
|
if (!empty($user_username)) {
|
|
|
|
if ($user = User::MatchEmailOrUsername($user_username, $user_email)
|
|
|
|
->whereNotNull('username')->first()) {
|
|
|
|
$this->log('User '.$user_username.' already exists');
|
|
|
|
} elseif (( $first_name != '') && ($last_name != '') && ($user_username != '')) {
|
|
|
|
$user = new User;
|
|
|
|
$user->first_name = $first_name;
|
|
|
|
$user->last_name = $last_name;
|
|
|
|
$user->username = $user_username;
|
|
|
|
$user->email = $user_email;
|
|
|
|
$user->activated = 1;
|
|
|
|
$user->password = $this->tempPassword;
|
|
|
|
|
|
|
|
if ($user->save()) {
|
|
|
|
$this->log('User '.$first_name.' created');
|
|
|
|
} else {
|
2017-01-10 16:19:18 -08:00
|
|
|
$this->jsonError($user, 'User "' . $first_name . '"');
|
2016-12-26 15:16:42 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $user;
|
|
|
|
}
|
2017-01-10 16:19:18 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the value of filename.
|
|
|
|
*
|
|
|
|
* @param string $filename the filename
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setFilename($filename)
|
|
|
|
{
|
|
|
|
$this->filename = $filename;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Should we persist to database?.
|
|
|
|
*
|
|
|
|
* @param bool $testRun the test run
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setTestRun($testRun)
|
|
|
|
{
|
|
|
|
$this->testRun = $testRun;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Id of User performing import.
|
|
|
|
*
|
|
|
|
* @param mixed $user_id the user id
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setUserId($user_id)
|
|
|
|
{
|
|
|
|
$this->user_id = $user_id;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Are we updating items in the import.
|
|
|
|
*
|
|
|
|
* @param bool $updating the updating
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setUpdating($updating)
|
|
|
|
{
|
|
|
|
$this->updating = $updating;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the callbacks for the import
|
|
|
|
*
|
|
|
|
* @param callable $logCallback Function to call when we have data to log
|
|
|
|
* @param callable $progressCallback Function to call to display progress
|
|
|
|
* @param callable $errorCallback Function to call when we have errors
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setCallbacks(callable $logCallback, callable $progressCallback, callable $errorCallback)
|
|
|
|
{
|
|
|
|
$this->logCallback = $logCallback;
|
|
|
|
$this->progressCallback = $progressCallback;
|
|
|
|
$this->errorCallback = $errorCallback;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Sets the value of usernameFormat.
|
|
|
|
*
|
|
|
|
* @param string $usernameFormat the username format
|
|
|
|
*
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setUsernameFormat($usernameFormat)
|
|
|
|
{
|
|
|
|
$this->usernameFormat = $usernameFormat;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
2016-12-29 14:02:18 -08:00
|
|
|
}
|