369 lines
11 KiB
PHP
369 lines
11 KiB
PHP
|
<?php
|
||
|
/*********************************************************************************
|
||
|
*
|
||
|
* TimeTrex is a Workforce Management program developed by
|
||
|
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it under
|
||
|
* the terms of the GNU Affero General Public License version 3 as published by
|
||
|
* the Free Software Foundation with the addition of the following permission
|
||
|
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||
|
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||
|
* details.
|
||
|
*
|
||
|
*
|
||
|
* You should have received a copy of the GNU Affero General Public License along
|
||
|
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
* 02110-1301 USA.
|
||
|
*
|
||
|
*
|
||
|
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||
|
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||
|
*
|
||
|
*
|
||
|
* The interactive user interfaces in modified source and object code versions
|
||
|
* of this program must display Appropriate Legal Notices, as required under
|
||
|
* Section 5 of the GNU Affero General Public License version 3.
|
||
|
*
|
||
|
*
|
||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||
|
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||
|
* the words "Powered by TimeTrex".
|
||
|
*
|
||
|
********************************************************************************/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @package Modules\Users
|
||
|
*/
|
||
|
class UserIdentificationFactory extends Factory {
|
||
|
protected $table = 'user_identification';
|
||
|
protected $pk_sequence_name = 'user_identification_id_seq'; //PK Sequence name
|
||
|
|
||
|
var $user_obj = null;
|
||
|
|
||
|
/**
|
||
|
* @param $name
|
||
|
* @param null $parent
|
||
|
* @return array|null
|
||
|
*/
|
||
|
function _getFactoryOptions( $name, $parent = null ) {
|
||
|
|
||
|
$retval = null;
|
||
|
switch ( $name ) {
|
||
|
case 'type':
|
||
|
$retval = [
|
||
|
1 => TTi18n::gettext( 'Employee Sequence' ), //Company specific employee sequence number, primarily for timeclocks. Should be less than 65535.
|
||
|
5 => TTi18n::gettext( 'Password History' ), //Web interface password history
|
||
|
10 => TTi18n::gettext( 'iButton' ),
|
||
|
20 => TTi18n::gettext( 'USB Fingerprint' ), //Biometric Data -- This is purged when employees are terminated.
|
||
|
30 => TTi18n::gettext( 'Barcode' ), //For barcode readers and USB proximity card readers.
|
||
|
35 => TTi18n::gettext( 'QRcode' ), //For cameras to read QR code badges.
|
||
|
40 => TTi18n::gettext( 'Proximity Card' ), //Mainly for proximity cards on timeclocks.
|
||
|
|
||
|
//
|
||
|
//Biometric data -- This is purged when employees are terminated.
|
||
|
//
|
||
|
70 => TTi18n::gettext( 'Face Image (v1)' ), //Raw image of cropped face in as high of quality as possible, and cropped 10-20% larger than the face itself.
|
||
|
71 => TTi18n::gettext( 'Face Image (v2)' ), //Raw image of cropped face in as high of quality as possible, and cropped 10-20% larger than the face itself.
|
||
|
75 => TTi18n::gettext( 'Facial Recognition' ), //Luxand v5 SDK templates.
|
||
|
76 => TTi18n::gettext( 'Facial Recognition (v2)' ), //Luxand v6.1 SDK templates, App v4.0+
|
||
|
77 => TTi18n::gettext( 'Facial Recognition (v3)' ), //Luxand v7 SDK templates, App v4.5+
|
||
|
//78 => TTi18n::gettext( 'Facial Recognition (v4)' ), //Luxand vX SDK templates, App vX.X+ -- Future use.
|
||
|
//79-90 -- Luxand SDK versions.
|
||
|
|
||
|
100 => TTi18n::gettext( 'TimeClock FingerPrint (v9)' ), //TimeClocks v9 algo
|
||
|
101 => TTi18n::gettext( 'TimeClock FingerPrint (v10)' ), //TimeClocks v10 algo
|
||
|
//
|
||
|
//Biometric data -- This is purged when employees are terminated.
|
||
|
//
|
||
|
|
||
|
];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return null
|
||
|
*/
|
||
|
function getUserObject() {
|
||
|
if ( is_object( $this->user_obj ) ) {
|
||
|
return $this->user_obj;
|
||
|
} else {
|
||
|
$ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */
|
||
|
$this->user_obj = $ulf->getById( $this->getUser() )->getCurrent();
|
||
|
|
||
|
return $this->user_obj;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|mixed
|
||
|
*/
|
||
|
function getUser() {
|
||
|
return $this->getGenericDataValue( 'user_id' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $value UUID
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setUser( $value ) {
|
||
|
$value = TTUUID::castUUID( $value );
|
||
|
|
||
|
return $this->setGenericDataValue( 'user_id', $value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|int
|
||
|
*/
|
||
|
function getType() {
|
||
|
return $this->getGenericDataValue( 'type_id' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $value
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setType( $value ) {
|
||
|
$value = (int)trim( $value );
|
||
|
//This needs to be stay as TimeTrex Client application still uses names rather than IDs.
|
||
|
$key = Option::getByValue( $value, $this->getOptions( 'type' ) );
|
||
|
if ( $key !== false ) {
|
||
|
$value = $key;
|
||
|
}
|
||
|
|
||
|
return $this->setGenericDataValue( 'type_id', $value );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
For fingerprints,
|
||
|
10 = Fingerprint 1 Pass 0.
|
||
|
11 = Fingerprint 1 Pass 1.
|
||
|
12 = Fingerprint 1 Pass 2.
|
||
|
|
||
|
20 = Fingerprint 2 Pass 0.
|
||
|
21 = Fingerprint 2 Pass 1.
|
||
|
...
|
||
|
*/
|
||
|
/**
|
||
|
* @return bool|mixed
|
||
|
*/
|
||
|
function getNumber() {
|
||
|
return $this->getGenericDataValue( 'number' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $value
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setNumber( $value ) {
|
||
|
$value = trim( $value );
|
||
|
//Pull out only digits
|
||
|
$value = $this->Validator->stripNonNumeric( $value );
|
||
|
|
||
|
return $this->setGenericDataValue( 'number', $value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $user_id UUID
|
||
|
* @param int $type_id
|
||
|
* @param $value
|
||
|
* @return bool
|
||
|
*/
|
||
|
function isUniqueValue( $user_id, $type_id, $value ) {
|
||
|
$ph = [
|
||
|
'user_id' => TTUUID::castUUID( $user_id ),
|
||
|
'type_id' => (int)$type_id,
|
||
|
'value' => (string)$value,
|
||
|
];
|
||
|
|
||
|
$uf = TTnew( 'UserFactory' ); /** @var UserFactory $uf */
|
||
|
|
||
|
$query = 'select a.id
|
||
|
from ' . $this->getTable() . ' as a,
|
||
|
' . $uf->getTable() . ' as b
|
||
|
where a.user_id = b.id
|
||
|
AND b.company_id = ( select z.company_id from ' . $uf->getTable() . ' as z where z.id = ? and z.deleted = 0 )
|
||
|
AND a.type_id = ?
|
||
|
AND a.value = ?
|
||
|
AND ( a.deleted = 0 AND b.deleted = 0 )';
|
||
|
$id = $this->db->GetOne( $query, $ph );
|
||
|
//Debug::Arr($id, 'Unique Value: '. $value, __FILE__, __LINE__, __METHOD__, 10);
|
||
|
|
||
|
if ( $id === false ) {
|
||
|
return true;
|
||
|
} else {
|
||
|
if ( $id == $this->getId() ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|mixed
|
||
|
*/
|
||
|
function getValue() {
|
||
|
return $this->getGenericDataValue( 'value' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $value
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setValue( $value ) {
|
||
|
$value = trim( $value );
|
||
|
|
||
|
return $this->setGenericDataValue( 'value', $value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|mixed
|
||
|
*/
|
||
|
function getExtraValue() {
|
||
|
return $this->getGenericDataValue( 'extra_value' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $value
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setExtraValue( $value ) {
|
||
|
$value = trim( $value );
|
||
|
|
||
|
return $this->setGenericDataValue( 'extra_value', $value );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param bool $ignore_warning
|
||
|
* @return bool
|
||
|
*/
|
||
|
function Validate( $ignore_warning = true ) {
|
||
|
//
|
||
|
// BELOW: Validation code moved from set*() functions.
|
||
|
//
|
||
|
// User
|
||
|
if ( $this->getUser() != TTUUID::getZeroID() ) {
|
||
|
$ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */
|
||
|
$this->Validator->isResultSetWithRows( 'user',
|
||
|
$ulf->getByID( $this->getUser() ),
|
||
|
TTi18n::gettext( 'Invalid Employee' )
|
||
|
);
|
||
|
}
|
||
|
// Type
|
||
|
$this->Validator->inArrayKey( 'type',
|
||
|
$this->getType(),
|
||
|
TTi18n::gettext( 'Incorrect Type' ),
|
||
|
$this->getOptions( 'type' )
|
||
|
);
|
||
|
// Number
|
||
|
$this->Validator->isFloat( 'number',
|
||
|
$this->getNumber(),
|
||
|
TTi18n::gettext( 'Incorrect Number' )
|
||
|
);
|
||
|
// Value
|
||
|
$this->Validator->isLength( 'value',
|
||
|
$this->getValue(),
|
||
|
TTi18n::gettext( 'Value is too short or too long' ),
|
||
|
1,
|
||
|
1024000 ); //Need relatively large face images.
|
||
|
// Extra Value
|
||
|
if ( $this->getExtraValue() !== false ) {
|
||
|
$this->Validator->isLength( 'extra_value',
|
||
|
$this->getExtraValue(),
|
||
|
TTi18n::gettext( 'Extra Value is too long' ),
|
||
|
1,
|
||
|
1024000
|
||
|
);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// ABOVE: Validation code moved from set*() functions.
|
||
|
//
|
||
|
if ( $this->getValue() == false ) {
|
||
|
$this->Validator->isTRUE( 'value',
|
||
|
false,
|
||
|
TTi18n::gettext( 'Value is not defined' ) );
|
||
|
} else {
|
||
|
$this->Validator->isTrue( 'value',
|
||
|
$this->isUniqueValue( $this->getUser(), $this->getType(), $this->getValue() ),
|
||
|
TTi18n::gettext( 'Value is already in use, please enter a different one' ) );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
function preSave() {
|
||
|
if ( $this->getNumber() == '' ) {
|
||
|
$this->setNumber( 0 );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
function postSave() {
|
||
|
$this->removeCache( $this->getId() );
|
||
|
|
||
|
if ( $this->getDeleted() == false && ( $this->getType() == 70 || $this->getType() == 71 ) ) { //Face Images
|
||
|
if ( ( $this->getType() == 71 && $this->getNumber() == 4 ) || ( $this->getType() == 70 && $this->getNumber() == 0 ) ) { //Center image.
|
||
|
$u_obj = $this->getUserObject(); /** @var UserListFactory $u_obj */
|
||
|
if ( $u_obj->isPhotoExists() == false ) {
|
||
|
Debug::Text( 'Photo does not exist, using enrolled image for this user: ' . $u_obj->getId(), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$dir = $u_obj->getStoragePath( $u_obj->getCompany(), $u_obj->getId() );
|
||
|
Debug::Text( ' Storage Path: ' . $dir, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
if ( isset( $dir ) ) {
|
||
|
@mkdir( $dir, 0700, true );
|
||
|
if ( @disk_free_space( $dir ) > ( strlen( $this->getValue() ) * 2 ) ) {
|
||
|
$file_name = $dir . DIRECTORY_SEPARATOR . TTUUID::castUUID( $u_obj->getId() ) . '.jpg';
|
||
|
$file_data = base64_decode( $this->getValue() );
|
||
|
|
||
|
$success = file_put_contents( $file_name, $file_data );
|
||
|
if ( $success == true ) {
|
||
|
TTLog::addEntry( $u_obj->getId(), 10, TTi18n::getText( 'Photo - Source: Face Enrollment' ), null, $u_obj->getTable() );
|
||
|
Debug::Text( 'User profile photo updated successfully!', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
} else {
|
||
|
Debug::Text( 'ERROR: Unable to write data to: ' . $file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
} else {
|
||
|
Debug::Text( 'ERROR: Not enough disk space free, unable to save photo to user profile!', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Debug::Text( 'User profile already has a photo, not overwriting with enrolled photo...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $log_action
|
||
|
* @return bool
|
||
|
*/
|
||
|
function addLog( $log_action ) {
|
||
|
//Don't do detail logging for this, as it will store entire figerprints in the log table.
|
||
|
return TTLog::addEntry( $this->getId(), $log_action, TTi18n::getText( 'Employee Identification - Employee' ) . ': ' . UserListFactory::getFullNameById( $this->getUser() ) . ' ' . TTi18n::getText( 'Type' ) . ': ' . Option::getByKey( $this->getType(), $this->getOptions( 'type' ) ), null, $this->getTable() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
?>
|