2016-12-26 15:16:42 -08:00
< ? php
2021-06-10 13:15:52 -07:00
2016-12-26 15:16:42 -08:00
namespace App\Importer ;
use App\Models\CustomField ;
2019-03-13 20:12:03 -07:00
use App\Models\Department ;
2016-12-26 15:16:42 -08:00
use App\Models\Setting ;
use App\Models\User ;
2024-05-30 18:14:50 -07:00
use Carbon\CarbonImmutable ;
2016-12-26 15:16:42 -08:00
use ForceUTF8\Encoding ;
use Illuminate\Database\Eloquent\Model ;
2021-06-10 13:15:52 -07:00
use Illuminate\Support\Facades\Auth ;
2016-12-26 15:16:42 -08:00
use Illuminate\Support\Facades\DB ;
use League\Csv\Reader ;
2024-05-29 04:38:15 -07:00
use Illuminate\Support\Facades\Log ;
2016-12-26 15:16:42 -08:00
abstract class Importer
{
2017-07-11 20:37:02 -07:00
protected $csv ;
2016-12-26 15:16:42 -08:00
/**
* Id of User performing import
* @ var
*/
2023-11-14 06:37:48 -08:00
2016-12-26 15:16:42 -08:00
protected $user_id ;
/**
* Are we updating items in the import
* @ var bool
*/
2023-11-14 06:37:48 -08:00
2016-12-26 15:16:42 -08:00
protected $updating ;
2023-11-14 06:37:48 -08:00
2017-07-11 20:37:02 -07:00
/**
* Default Map of item fields -> csv names
2023-05-23 12:57:40 -07:00
*
2023-09-28 06:24:07 -07:00
* This has been moved into app / Http / Livewire / Importer . php to be more granular .
2023-11-14 06:37:48 -08:00
* This private variable is ONLY used for the cli - importer .
2023-05-23 12:57:40 -07:00
*
2023-11-14 06:37:48 -08:00
* @ todo - find a way to make this less duplicative
2017-07-11 20:37:02 -07:00
* @ var array
*/
private $defaultFieldMap = [
2023-11-14 06:37:48 -08:00
'asset_tag' => 'asset tag' ,
'activated' => 'activated' ,
'category' => 'category' ,
'checkout_class' => 'checkout type' , // Supports Location or User for assets. Using checkout_class instead of checkout_type because type exists on asset already.
'checkout_location' => 'checkout location' ,
'company' => 'company' ,
'item_name' => 'item name' ,
'item_number' => 'item number' ,
'image' => 'image' ,
'expiration_date' => 'expiration date' ,
'location' => 'location' ,
'notes' => 'notes' ,
'license_email' => 'licensed to email' ,
'license_name' => 'licensed to name' ,
'maintained' => 'maintained' ,
'manufacturer' => 'manufacturer' ,
'asset_model' => 'model name' ,
'model_number' => 'model number' ,
'order_number' => 'order number' ,
'purchase_cost' => 'purchase cost' ,
'purchase_date' => 'purchase date' ,
'purchase_order' => 'purchase order' ,
'qty' => 'quantity' ,
'reassignable' => 'reassignable' ,
'requestable' => 'requestable' ,
'seats' => 'seats' ,
'serial' => 'serial number' ,
'status' => 'status' ,
'supplier' => 'supplier' ,
'termination_date' => 'termination date' ,
'warranty_months' => 'warranty' ,
'full_name' => 'full name' ,
'email' => 'email' ,
'username' => 'username' ,
'address' => 'address' ,
'address2' => 'address2' ,
'city' => 'city' ,
'state' => 'state' ,
'country' => 'country' ,
'zip' => 'zip' ,
'jobtitle' => 'job title' ,
'employee_num' => 'employee number' ,
'phone_number' => 'phone number' ,
'first_name' => 'first name' ,
'last_name' => 'last name' ,
'department' => 'department' ,
'manager_name' => 'manager full name' ,
'manager_username' => 'manager username' ,
'min_amt' => 'minimum quantity' ,
'remote' => 'remote' ,
'vip' => 'vip' ,
2017-07-11 20:37:02 -07:00
];
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
/**
* Map of item fields -> csv names
* @ var array
*/
protected $fieldMap = [];
2016-12-26 15:16:42 -08:00
/**
* @ var callable
*/
protected $logCallback ;
protected $tempPassword ;
/**
* @ var callable
*/
protected $progressCallback ;
/**
* @ var null
*/
protected $usernameFormat ;
/**
* @ var callable
*/
protected $errorCallback ;
/**
* ObjectImporter constructor .
2017-07-11 20:37:02 -07:00
* @ param string $file
2016-12-26 15:16:42 -08:00
*/
2017-07-11 20:37:02 -07:00
public function __construct ( $file )
2017-01-25 21:29:23 -08:00
{
2017-07-11 20:37:02 -07:00
$this -> fieldMap = $this -> defaultFieldMap ;
2021-06-10 13:15:52 -07:00
if ( ! ini_get ( 'auto_detect_line_endings' )) {
ini_set ( 'auto_detect_line_endings' , '1' );
2018-03-13 20:06:53 -07:00
}
2018-07-05 12:22:24 -07:00
// By default the importer passes a url to the file.
// However, for testing we also support passing a string directly
2017-07-11 20:37:02 -07:00
if ( is_file ( $file )) {
$this -> csv = Reader :: createFromPath ( $file );
} else {
$this -> csv = Reader :: createFromString ( $file );
}
2023-04-16 07:47:19 -07:00
$this -> tempPassword = substr ( str_shuffle ( '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ), 0 , 40 );
2016-12-26 15:16:42 -08:00
}
2021-06-10 13:15:52 -07:00
2016-12-26 15:16:42 -08:00
// Cached Values for import lookups
protected $customFields ;
2018-08-01 00:06:41 -07:00
/**
* Sets up the database transaction and logging for the importer
*
* @ return void
* @ author Daniel Meltzer
* @ since 5.0
*/
2016-12-26 15:16:42 -08:00
public function import ()
{
2017-07-11 20:37:02 -07:00
$headerRow = $this -> csv -> fetchOne ();
2019-05-24 11:44:57 -07:00
$this -> csv -> setHeaderOffset ( 0 ); //explicitly sets the CSV document header record
$results = $this -> normalizeInputArray ( $this -> csv -> getRecords ( $headerRow ));
2017-09-18 12:29:08 -07:00
2018-07-16 17:44:03 -07:00
$this -> populateCustomFields ( $headerRow );
2017-09-18 12:29:08 -07:00
2016-12-26 15:16:42 -08:00
DB :: transaction ( function () use ( & $results ) {
Model :: unguard ();
2021-06-10 13:15:52 -07:00
$resultsCount = count ( $results );
2016-12-26 15:16:42 -08:00
foreach ( $results as $row ) {
$this -> handle ( $row );
2017-07-11 20:37:02 -07:00
if ( $this -> progressCallback ) {
call_user_func ( $this -> progressCallback , $resultsCount );
}
2016-12-26 15:16:42 -08:00
$this -> log ( '------------- Action Summary ----------------' );
}
});
}
abstract protected function handle ( $row );
2018-07-16 17:44:03 -07:00
/**
* Fetch custom fields from database and translate / parse them into a format
* appropriate for use in the importer .
* @ return void
* @ author Daniel Meltzer
* @ since 5.0
*/
protected function populateCustomFields ( $headerRow )
{
// Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/
// This 'inverts' the fields such that we have a collection of fields indexed by name.
$this -> customFields = CustomField :: All () -> reduce ( function ( $nameLookup , $field ) {
$nameLookup [ $field [ 'name' ]] = $field ;
2021-06-10 13:15:52 -07:00
2018-07-27 12:02:18 -07:00
return $nameLookup ;
2018-07-16 17:44:03 -07:00
});
// Remove any custom fields that do not exist in the header row. This prevents nulling out values that shouldn't exist.
// In detail, we compare the lower case name of custom fields (indexed by name) to the keys in the header row. This
// results in an array with only custom fields that are in the file.
if ( $this -> customFields ) {
$this -> customFields = array_intersect_key (
array_change_key_case ( $this -> customFields ),
array_change_key_case ( array_flip ( $headerRow ))
);
}
}
2021-06-10 13:15:52 -07:00
2016-12-26 15:16:42 -08:00
/**
* 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
*/
2017-08-31 11:14:21 -07:00
public function findCsvMatch ( array $array , $key , $default = null )
2016-12-26 15:16:42 -08:00
{
2017-01-10 16:19:18 -08:00
$val = $default ;
2017-08-31 11:14:21 -07:00
$key = $this -> lookupCustomKey ( $key );
2017-07-11 20:37:02 -07:00
2023-04-16 07:47:26 -07:00
// $this->log("Custom Key: ${key}");
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
if ( array_key_exists ( $key , $array )) {
2021-06-10 13:15:52 -07:00
$val = Encoding :: toUTF8 ( trim ( $array [ $key ]));
2017-01-10 16:19:18 -08:00
}
2023-04-16 07:47:26 -07:00
//$this->log("${key}: ${val}");
2017-01-10 16:19:18 -08:00
return $val ;
2016-12-26 15:16:42 -08:00
}
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
/**
* Looks up A custom key in the custom field map
*
* @ author Daniel Melzter
* @ since 4.0
* @ param $key string
* @ return string | null
*/
public function lookupCustomKey ( $key )
{
if ( array_key_exists ( $key , $this -> fieldMap )) {
2017-07-11 20:37:02 -07:00
return $this -> fieldMap [ $key ];
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
}
2017-08-31 11:14:21 -07:00
// Otherwise no custom key, return original.
return $key ;
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
}
2017-07-11 20:37:02 -07:00
/**
2018-07-05 12:22:24 -07:00
* Used to lowercase header values to ensure we ' re comparing values properly .
2021-06-10 13:15:52 -07:00
*
2017-07-11 20:37:02 -07:00
* @ param $results
* @ return array
*/
public function normalizeInputArray ( $results )
{
$newArray = [];
foreach ( $results as $index => $arrayToNormalize ) {
$newArray [ $index ] = array_change_key_case ( $arrayToNormalize );
}
2021-06-10 13:15:52 -07:00
2017-07-11 20:37:02 -07:00
return $newArray ;
}
2021-06-10 13:15:52 -07:00
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 );
2021-06-10 13:15:52 -07:00
2018-03-22 14:36:36 -07:00
return array_key_exists ( $index_name , $array ) ? trim ( $array [ $index_name ]) : false ;
2016-12-26 15:16:42 -08:00
}
2016-12-29 14:02:18 -08:00
protected function log ( $string )
{
2017-07-11 20:37:02 -07:00
if ( $this -> logCallback ) {
call_user_func ( $this -> logCallback , $string );
}
2016-12-26 15:16:42 -08:00
}
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
protected function logError ( $item , $field )
2016-12-29 14:02:18 -08:00
{
2017-07-11 20:37:02 -07:00
if ( $this -> errorCallback ) {
call_user_func ( $this -> errorCallback , $item , $field , $item -> getErrors ());
}
2016-12-26 15:16:42 -08:00
}
/**
2018-10-19 00:23:12 -07:00
* Finds the user matching given data , or creates a new one if there is no match .
* This is NOT used by the User Import , only for Asset / Accessory / etc where
* there are users listed and we have to create them and associate them at
* the same time . [ ALG ]
2016-12-26 15:16:42 -08:00
*
* @ author Daniel Melzter
* @ since 3.0
* @ param $row array
* @ return User Model w / matching name
2018-07-17 16:46:08 -07:00
* @ internal param array $user_array User details parsed from csv
2016-12-26 15:16:42 -08:00
*/
2023-04-18 16:34:10 -07:00
protected function createOrFetchUser ( $row , $type = 'user' )
2016-12-26 15:16:42 -08:00
{
2023-04-16 07:47:26 -07:00
2018-07-17 16:46:08 -07:00
$user_array = [
2021-06-10 13:15:52 -07:00
'full_name' => $this -> findCsvMatch ( $row , 'full_name' ),
2023-05-23 19:21:31 -07:00
'first_name' => $this -> findCsvMatch ( $row , 'first_name' ),
'last_name' => $this -> findCsvMatch ( $row , 'last_name' ),
2021-06-10 13:15:52 -07:00
'email' => $this -> findCsvMatch ( $row , 'email' ),
2018-11-07 18:33:43 -08:00
'manager_id' => '' ,
'department_id' => '' ,
2021-06-10 13:15:52 -07:00
'username' => $this -> findCsvMatch ( $row , 'username' ),
2018-10-19 00:23:12 -07:00
'activated' => $this -> fetchHumanBoolean ( $this -> findCsvMatch ( $row , 'activated' )),
2022-12-06 11:13:24 -08:00
'remote' => $this -> fetchHumanBoolean (( $this -> findCsvMatch ( $row , 'remote' ))),
2018-07-17 16:46:08 -07:00
];
2023-05-23 19:28:42 -07:00
2023-04-16 07:47:26 -07:00
if ( $type == 'manager' ) {
2023-04-18 16:34:10 -07:00
$user_array [ 'full_name' ] = $this -> findCsvMatch ( $row , 'manager' );
$user_array [ 'username' ] = $this -> findCsvMatch ( $row , 'manager_username' );
2023-04-16 07:47:26 -07:00
}
2023-04-18 16:34:10 -07:00
// Maybe we're lucky and the username was passed and it already exists.
if ( ! empty ( $user_array [ 'username' ])) {
if ( $user = User :: where ( 'username' , $user_array [ 'username' ]) -> first ()) {
$this -> log ( 'User ' . $user_array [ 'username' ] . ' already exists' );
return $user ;
}
2018-08-03 16:35:13 -07:00
}
2018-10-19 00:23:12 -07:00
2023-04-18 16:34:10 -07:00
// If the full name and username is empty, bail out--we need this to extract first name (at the very least)
2023-05-23 19:21:31 -07:00
if (( empty ( $user_array [ 'username' ])) && ( empty ( $user_array [ 'full_name' ])) && ( empty ( $user_array [ 'first_name' ]))) {
$this -> log ( 'Insufficient user data provided (Full name, first name or username is required) - skipping user creation.' );
2024-05-29 04:38:15 -07:00
Log :: debug ( 'User array: ' );
Log :: debug ( print_r ( $user_array , true ));
Log :: debug ( print_r ( $row , true ));
2018-04-18 07:58:26 -07:00
return false ;
}
2018-07-05 12:22:24 -07:00
2018-07-17 16:46:08 -07:00
// Populate email if it does not exist.
2021-06-10 13:15:52 -07:00
if ( empty ( $user_array [ 'email' ])) {
2018-07-17 16:46:08 -07:00
$user_array [ 'email' ] = User :: generateEmailFromFullName ( $user_array [ 'full_name' ]);
2018-04-18 07:58:26 -07:00
}
2016-12-26 15:16:42 -08:00
2023-10-17 14:33:56 -07:00
// Get some variables for $user_formatted_array in case we need them later
$user_formatted_array = User :: generateFormattedNameFromFullName ( $user_array [ 'full_name' ], Setting :: getSettings () -> username_format );
2023-05-23 19:21:31 -07:00
if ( empty ( $user_array [ 'first_name' ])) {
// Get some fields for first name and last name based off of full name
$user_array [ 'first_name' ] = $user_formatted_array [ 'first_name' ];
$user_array [ 'last_name' ] = $user_formatted_array [ 'last_name' ];
}
2018-11-07 18:05:53 -08:00
2018-07-17 16:46:08 -07:00
if ( empty ( $user_array [ 'username' ])) {
$user_array [ 'username' ] = $user_formatted_array [ 'username' ];
2021-06-10 13:15:52 -07:00
if ( $this -> usernameFormat == 'email' ) {
2018-07-17 16:46:08 -07:00
$user_array [ 'username' ] = $user_array [ 'email' ];
2016-12-26 15:16:42 -08:00
}
2021-06-10 13:15:52 -07:00
2023-04-18 16:34:10 -07:00
// Check for a matching username one more time after trying to guess username.
if ( $user = User :: where ( 'username' , $user_array [ 'username' ]) -> first ()) {
$this -> log ( 'User ' . $user_array [ 'username' ] . ' already exists' );
return $user ;
}
2018-07-17 16:46:08 -07:00
}
2018-07-18 19:10:36 -07:00
// If at this point we have not found a username or first name, bail out in shame.
2021-06-10 13:15:52 -07:00
if ( empty ( $user_array [ 'username' ]) || empty ( $user_array [ 'first_name' ])) {
2018-07-18 19:10:36 -07:00
return false ;
}
2018-07-17 16:46:08 -07:00
2023-04-18 16:34:10 -07:00
// No luck finding a user on username or first name, let's create one.
2016-12-26 15:16:42 -08:00
$user = new User ;
2021-06-10 13:15:52 -07:00
$user -> first_name = $user_array [ 'first_name' ];
$user -> last_name = $user_array [ 'last_name' ];
$user -> username = $user_array [ 'username' ];
$user -> email = $user_array [ 'email' ];
$user -> manager_id = $user_array [ 'manager_id' ] ? ? null ;
2018-10-17 13:38:13 -07:00
$user -> department_id = $user_array [ 'department_id' ] ? ? null ;
2021-06-10 13:15:52 -07:00
$user -> activated = 1 ;
$user -> password = $this -> tempPassword ;
2017-07-11 20:37:02 -07:00
2024-05-29 04:38:15 -07:00
Log :: debug ( 'Creating a user with the following attributes: ' . print_r ( $user_array , true ));
2018-10-19 00:23:12 -07:00
2018-07-17 16:46:08 -07:00
if ( $user -> save ()) {
$this -> log ( 'User ' . $user_array [ 'username' ] . ' created' );
return $user ;
}
2023-04-18 16:34:10 -07:00
2021-06-10 13:15:52 -07:00
$this -> logError ( $user , 'User "' . $user_array [ 'username' ] . '" was not able to be created.' );
2018-07-17 16:46:08 -07:00
return false ;
}
/**
* Matches a user by user_id if user_name provided is a number
* @ param string $user_name users full name from csv
* @ return User User Matching ID
*/
protected function findUserByNumber ( $user_name )
{
// A number was given instead of a name
if ( is_numeric ( $user_name )) {
$this -> log ( 'User ' . $user_name . ' is a number - lets see if it matches a user id' );
2021-06-10 13:15:52 -07:00
2018-07-17 16:46:08 -07:00
return User :: find ( $user_name );
2016-12-26 15:16:42 -08:00
}
}
2017-01-10 16:19:18 -08:00
/**
* 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 ;
}
2018-10-02 15:43:54 -07:00
/**
2018-10-03 00:52:29 -07:00
* Sets whether or not we should notify the user with a welcome email
2018-10-02 15:43:54 -07:00
*
2018-10-03 00:52:29 -07:00
* @ param bool $send_welcome the send - welcome flag
2018-10-02 15:43:54 -07:00
*
* @ return self
*/
public function setShouldNotify ( $send_welcome )
{
$this -> send_welcome = $send_welcome ;
return $this ;
}
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
/**
* Defines mappings of csv fields
*
* @ param bool $updating the updating
*
* @ return self
*/
public function setFieldMappings ( $fields )
{
// Some initial sanitization.
2017-07-11 20:37:02 -07:00
$fields = array_map ( 'strtolower' , $fields );
$this -> fieldMap = array_merge ( $this -> defaultFieldMap , $fields );
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
2017-07-11 20:37:02 -07:00
// $this->log($this->fieldMap);
Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.
* Pull the modal out into a dedicated partial, move importer to views/importer.
* Add document of CSV->importer mappings. Reorganize some code.
Progress.
* Add header_row and first_row to imports table, and process upon uploading a file
* Use an expandable table row instead of a modal for import processing. This should allow for field mapping interaction easier.
* Fix import processing after moving method.
* Frontend importer mapping improvements.
Invert display so we show found columns and allow users to select an
importer field to map to. Also implement sample data based on first row
of csv.
* Update select2. Maintain selected items properly.
* Backend support for importing. Only works on the web importer currently. Definitely needs testing and polish.
* We no longer use vue-modal plugin.
* Add a column to track field mappings to the imports table.
* Cleanup/rename methods+refactor
* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.
* Update debugbar to fix a bug in the debugbar code.
* Fix asset tag detection.
Also rename findMatch to be a bit clearer as to what it does.
Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.
This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.
* Dynamically limit options for select2 based on import type selected, and group them by item type.
* Add user importer.
Still need to implement emailing of passwords to new users, and probably
test a bit more.
This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.
* Query the db to find user matches rather than search the array. Performance is much much better.
* Speed/memory improvements in importers.
Move to querying the db rather than maintaining an array for all
importers. Also only store the id of items when we import, rather than
the full model. It saves a decent amount of memory.
* Remove grouping of items in select2
With the values being set dynamically, the grouping is redundant. It
also caused a regression with automatically guessing/matching field
names. This is starting to get close.
* Remove debug line on every create.
* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb
* Fix asset import regression matching email address.
* Rearrange travis order in attempt to fix null settings.
* Use auth::id instead of fetching it off the user. Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
return $this ;
}
2017-01-10 16:19:18 -08:00
/**
* 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 ;
}
2021-06-10 13:15:52 -07:00
2017-01-10 16:19:18 -08:00
/**
* Sets the value of usernameFormat .
*
* @ param string $usernameFormat the username format
*
* @ return self
*/
public function setUsernameFormat ( $usernameFormat )
{
$this -> usernameFormat = $usernameFormat ;
return $this ;
}
2018-10-19 00:23:12 -07:00
public function fetchHumanBoolean ( $value )
{
2021-06-10 13:15:52 -07:00
return ( int ) filter_var ( $value , FILTER_VALIDATE_BOOLEAN );
2018-10-19 00:23:12 -07:00
}
2018-11-07 18:05:53 -08:00
/**
* Fetch an existing department , or create new if it doesn ' t exist
*
* @ author A . Gianotto
* @ since 4.6 . 5
* @ param $user_department string
* @ return int id of company created / found
*/
public function createOrFetchDepartment ( $user_department_name )
{
2021-06-10 13:15:52 -07:00
if ( $user_department_name != '' ) {
2018-11-07 18:05:53 -08:00
$department = Department :: where ( 'name' , '=' , $user_department_name ) -> first ();
if ( $department ) {
2021-06-10 13:15:52 -07:00
$this -> log ( 'A matching Department ' . $user_department_name . ' already exists' );
2018-11-07 18:05:53 -08:00
return $department -> id ;
}
$department = new Department ();
$department -> name = $user_department_name ;
if ( $department -> save ()) {
2021-06-10 13:15:52 -07:00
$this -> log ( 'Department ' . $user_department_name . ' was created' );
2018-11-07 18:05:53 -08:00
return $department -> id ;
}
$this -> logError ( $department , 'Department' );
}
return null ;
}
/**
* Fetch an existing manager
*
* @ author A . Gianotto
* @ since 4.6 . 5
* @ param $user_manager string
* @ return int id of company created / found
*/
public function fetchManager ( $user_manager_first_name , $user_manager_last_name )
{
$manager = User :: where ( 'first_name' , '=' , $user_manager_first_name )
-> where ( 'last_name' , '=' , $user_manager_last_name ) -> first ();
if ( $manager ) {
2021-06-10 13:15:52 -07:00
$this -> log ( 'A matching Manager ' . $user_manager_first_name . ' ' . $user_manager_last_name . ' already exists' );
2018-11-07 18:05:53 -08:00
return $manager -> id ;
}
2021-06-10 13:15:52 -07:00
$this -> log ( 'No matching Manager ' . $user_manager_first_name . ' ' . $user_manager_last_name . ' found. If their user account is being created through this import, you should re-process this file again. ' );
2018-11-07 18:05:53 -08:00
return null ;
}
2024-05-30 18:14:50 -07:00
/**
* Parse a date or return null
*
* @ author A . Gianotto
* @ since 7.0 . 0
* @ param $field
* @ param $format
* @ return string | null
*/
public function parseOrNullDate ( $field , $format = 'date' ) {
2024-06-05 02:10:57 -07:00
$date_format = 'Y-m-d' ;
2024-05-30 18:14:50 -07:00
if ( $format == 'datetime' ) {
$date_format = 'Y-m-d H:i:s' ;
}
if ( array_key_exists ( $field , $this -> item ) && $this -> item [ $field ] != '' ) {
try {
$value = CarbonImmutable :: parse ( $this -> item [ $field ]) -> format ( $date_format );
return $value ;
} catch ( \Exception $e ) {
2024-06-05 02:10:57 -07:00
$this -> log ( 'Unable to parse date: ' . $this -> item [ $field ]);
2024-05-30 18:14:50 -07:00
return null ;
}
}
return null ;
}
2016-12-29 14:02:18 -08:00
}