From f003a1c558b324b8df82826a001e5b2efb0034ca Mon Sep 17 00:00:00 2001 From: Sandra Kuipers Date: Tue, 1 Feb 2022 16:17:13 +0800 Subject: [PATCH] v1.6.00 --- Data Admin/CHANGEDB.php | 5 + Data Admin/CHANGELOG.md | 4 + Data Admin/export_run.php | 5 +- Data Admin/imports/usersBasic.yml | 2 +- Data Admin/imports/usersFull.yml | 2 +- Data Admin/imports/usersParents.yml | 2 +- Data Admin/imports/usersPasswords.yml | 2 +- Data Admin/imports/usersStaff.yml | 2 +- Data Admin/manifest.php | 2 +- Data Admin/records_duplicates.php | 3 +- Data Admin/records_manage.php | 8 +- Data Admin/records_orphaned.php | 3 +- Data Admin/settings.php | 11 +- Data Admin/snapshot_manage.php | 4 +- Data Admin/snapshot_manage_addProcess.php | 4 +- Data Admin/snapshot_manage_delete.php | 3 +- Data Admin/snapshot_manage_deleteProcess.php | 4 +- Data Admin/snapshot_manage_load.php | 3 +- Data Admin/snapshot_manage_loadProcess.php | 4 +- Data Admin/src/DatabaseTools.php | 3 +- Data Admin/src/ImportType.php | 1231 ------------------ Data Admin/src/Importer.php | 967 -------------- Data Admin/tools_findUsernamesProcess.php | 3 +- Data Admin/version.php | 10 +- 24 files changed, 59 insertions(+), 2228 deletions(-) delete mode 100644 Data Admin/src/ImportType.php delete mode 100644 Data Admin/src/Importer.php diff --git a/Data Admin/CHANGEDB.php b/Data Admin/CHANGEDB.php index cb1d331..a9327a7 100644 --- a/Data Admin/CHANGEDB.php +++ b/Data Admin/CHANGEDB.php @@ -131,3 +131,8 @@ $sql[$count][0]="1.5.01" ; $sql[$count][1]=""; +//v1.6.00 +$count++; +$sql[$count][0]="1.6.00" ; +$sql[$count][1]=""; + diff --git a/Data Admin/CHANGELOG.md b/Data Admin/CHANGELOG.md index ee3614c..141d9fa 100644 --- a/Data Admin/CHANGELOG.md +++ b/Data Admin/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +## [1.6.00] 2022-02-01 +### Fixed +- Fixed deprecated functions for v23 compatibility. Not backwards compatible. + ## [1.5.01] 2021-09-30 ### Fixed - Removed PHPExcel class, fixed Combine Similar Fields layout diff --git a/Data Admin/export_run.php b/Data Admin/export_run.php index 391521e..36f27fd 100644 --- a/Data Admin/export_run.php +++ b/Data Admin/export_run.php @@ -18,6 +18,7 @@ */ use Gibbon\Data\ImportType; +use Gibbon\Domain\System\SettingGateway; // Increase max execution time, as this stuff gets big ini_set('max_execution_time', 7200); @@ -45,7 +46,7 @@ $type = (isset($_GET['type']))? $_GET['type'] : ''; $importType = ImportType::loadImportType($type, $pdo); - $checkUserPermissions = getSettingByScope($connection2, 'Data Admin', 'enableUserLevelPermissions'); + $checkUserPermissions = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'enableUserLevelPermissions'); if ($checkUserPermissions == 'Y' && $importType->isImportAccessible($guid, $connection2) == false) { echo "
" ; @@ -236,7 +237,7 @@ $filename = ($dataExport) ? 'DataExport'.'-'.$type : 'DataStructure'.'-'.$type; - $exportFileType = getSettingByScope($connection2, 'Data Admin', 'exportDefaultFileType'); + $exportFileType = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportDefaultFileType'); if (empty($exportFileType)) { $exportFileType = 'Excel2007'; } diff --git a/Data Admin/imports/usersBasic.yml b/Data Admin/imports/usersBasic.yml index a817545..f6d2e3a 100644 --- a/Data Admin/imports/usersBasic.yml +++ b/Data Admin/imports/usersBasic.yml @@ -60,7 +60,7 @@ table: name: "Username" desc: "User account name & login" args: {filter: nospaces, required: true} - password: + passwordStrong: name: "Password" desc: "At least one lowercase and one number, from 8 to 20 chars" args: {filter: nospaces, required: true, custom: true, function: generatePassword} diff --git a/Data Admin/imports/usersFull.yml b/Data Admin/imports/usersFull.yml index 9fcc118..d1fea3d 100644 --- a/Data Admin/imports/usersFull.yml +++ b/Data Admin/imports/usersFull.yml @@ -51,7 +51,7 @@ table: name: "Username" desc: "User account name & login" args: {filter: nospaces, required: true} - password: + passwordStrong: name: "Password" desc: "At least one lowercase and one number, from 8 to 20 chars" args: {filter: nospaces, required: true, custom: true, function: generatePassword} diff --git a/Data Admin/imports/usersParents.yml b/Data Admin/imports/usersParents.yml index 71f137c..4c0e780 100644 --- a/Data Admin/imports/usersParents.yml +++ b/Data Admin/imports/usersParents.yml @@ -49,7 +49,7 @@ table: name: "Username" desc: "User account name & login" args: {filter: nospaces, required: true} - password: + passwordStrong: name: "Password" desc: "At least one lowercase and one number, from 8 to 20 chars" args: {filter: nospaces, required: true, custom: true, function: generatePassword} diff --git a/Data Admin/imports/usersPasswords.yml b/Data Admin/imports/usersPasswords.yml index ff567b3..1d99b5a 100644 --- a/Data Admin/imports/usersPasswords.yml +++ b/Data Admin/imports/usersPasswords.yml @@ -17,7 +17,7 @@ table: name: "Username" desc: "User account name & login" args: {filter: nospaces, required: true} - password: + passwordStrong: name: "Password" desc: "At least one lowercase and one number, from 8 to 20 chars" args: {filter: nospaces, required: true, custom: true, function: generatePassword} diff --git a/Data Admin/imports/usersStaff.yml b/Data Admin/imports/usersStaff.yml index 6e6e1ab..2fec875 100644 --- a/Data Admin/imports/usersStaff.yml +++ b/Data Admin/imports/usersStaff.yml @@ -49,7 +49,7 @@ table: name: "Username" desc: "User account name & login" args: {filter: nospaces, required: true} - password: + passwordStrong: name: "Password" desc: "At least one lowercase and one number, from 8 to 20 chars" args: {filter: nospaces, required: true, custom: true, function: generatePassword} diff --git a/Data Admin/manifest.php b/Data Admin/manifest.php index 0b06383..fd1f6ff 100644 --- a/Data Admin/manifest.php +++ b/Data Admin/manifest.php @@ -25,7 +25,7 @@ $entryURL="import_manage.php" ; //The landing page for the unit, used in the main menu $type="Additional" ; //Do not change. $category="Admin" ; //The main menu area to place the module in -$version="1.5.01" ; //Version number +$version="1.6.00" ; //Version number $author="Sandra Kuipers" ; //Your name $url="https://github.com/SKuipers/" ; //Your URL diff --git a/Data Admin/records_duplicates.php b/Data Admin/records_duplicates.php index 545cdb3..82dffc6 100644 --- a/Data Admin/records_duplicates.php +++ b/Data Admin/records_duplicates.php @@ -19,6 +19,7 @@ use Gibbon\Data\ImportType; use Gibbon\Module\DataAdmin\DatabaseTools; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -88,7 +89,7 @@ echo "" ; echo "" ; - $checkUserPermissions = getSettingByScope($connection2, 'Data Admin', 'enableUserLevelPermissions'); + $checkUserPermissions = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'enableUserLevelPermissions'); $isImportAccessible = ($checkUserPermissions == 'Y' && $importType->isImportAccessible($guid, $connection2) != false); foreach ($duplicateRecords as $row) { diff --git a/Data Admin/records_manage.php b/Data Admin/records_manage.php index 42d1aad..55d103e 100644 --- a/Data Admin/records_manage.php +++ b/Data Admin/records_manage.php @@ -19,6 +19,7 @@ use Gibbon\Data\ImportType; use Gibbon\Module\DataAdmin\DatabaseTools; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -36,10 +37,11 @@ echo __('The following Gibbon tables can be exported to Excel. The full table export is still a beta feature, at this time it should not be relied upon as a backup method. Note: This list does not represent the entire Gibbon database, only tables with an existing import/export structure.', 'Data Admin'); echo "
" ; - $databaseTools = new DatabaseTools($gibbon->session, $pdo); + $databaseTools = new DatabaseTools($session, $pdo); // Get a list of available import options - $importTypeList = ImportType::loadImportTypeList($pdo, false); + $settingGateway = $container->get(SettingGateway::class); + $importTypeList = ImportType::loadImportTypeList($settingGateway, $pdo, false); // Get the unique tables used @@ -57,7 +59,7 @@ echo __("There are no records to display.") ; echo "" ; } else { - $checkUserPermissions = getSettingByScope($connection2, 'Data Admin', 'enableUserLevelPermissions'); + $checkUserPermissions = $settingGateway->getSettingByScope('Data Admin', 'enableUserLevelPermissions'); $grouping = ''; foreach ($importTables as $importType) { diff --git a/Data Admin/records_orphaned.php b/Data Admin/records_orphaned.php index e916043..003fde5 100644 --- a/Data Admin/records_orphaned.php +++ b/Data Admin/records_orphaned.php @@ -19,6 +19,7 @@ use Gibbon\Data\ImportType; use Gibbon\Module\DataAdmin\DatabaseTools; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -84,7 +85,7 @@ echo "" ; echo "" ; - $checkUserPermissions = getSettingByScope($connection2, 'Data Admin', 'enableUserLevelPermissions'); + $checkUserPermissions = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'enableUserLevelPermissions'); $isImportAccessible = ($checkUserPermissions == 'Y' && $importType->isImportAccessible($guid, $connection2) != false); foreach ($orphanedRecords as $row) { diff --git a/Data Admin/settings.php b/Data Admin/settings.php index 186f1f5..615a879 100644 --- a/Data Admin/settings.php +++ b/Data Admin/settings.php @@ -18,6 +18,7 @@ */ use Gibbon\Forms\Form; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -37,6 +38,8 @@ $trueIcon = ""; $falseIcon = ""; + $settingGateway = $container->get(SettingGateway::class); + // Include the module version info, with required versions include $session->get('absolutePath').'/modules/'.$session->get('module').'/version.php'; ?> @@ -121,7 +124,7 @@ getSettingByScope('Data Admin', 'importCustomFolderLocation'); $importsFolderPath = $session->get('absolutePath').'/uploads/'.trim($importsFolder, '/ '); echo (is_writable($importsFolderPath))? $trueIcon : $falseIcon; ?> @@ -134,7 +137,7 @@ getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolderPath = $session->get('absolutePath').'/uploads/'.trim($snapshotFolder, '/ '); echo (is_writable($snapshotFolderPath))? $trueIcon : $falseIcon; ?> @@ -154,12 +157,12 @@ 'OpenDocument' => __('OpenDocument (.ods)', 'Data Admin'), 'CSV' => __('Comma Separated (.csv)', 'Data Admin'), ); - $setting = getSettingByScope($connection2, 'Data Admin', 'exportDefaultFileType', true); + $setting = $settingGateway->getSettingByScope('Data Admin', 'exportDefaultFileType', true); $row = $form->addRow(); $row->addLabel($setting['name'], __($setting['nameDisplay']))->description($setting['description']); $row->addSelect($setting['name'])->fromArray($fileTypes)->selected($setting['value']); - $setting = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation', true); + $setting = $settingGateway->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation', true); $row = $form->addRow(); $row->addLabel($setting['name'], __($setting['nameDisplay']))->description($setting['description']); $row->addTextField($setting['name'])->required()->setValue($setting['value']); diff --git a/Data Admin/snapshot_manage.php b/Data Admin/snapshot_manage.php index 8cb4648..fdd2389 100644 --- a/Data Admin/snapshot_manage.php +++ b/Data Admin/snapshot_manage.php @@ -17,6 +17,8 @@ along with this program. If not, see . */ +use Gibbon\Domain\System\SettingGateway; + // Module Bootstrap require __DIR__ . '/module.php'; @@ -47,7 +49,7 @@ } - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/'.trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath').'/uploads'.$snapshotFolder; diff --git a/Data Admin/snapshot_manage_addProcess.php b/Data Admin/snapshot_manage_addProcess.php index e068042..c8bd48b 100644 --- a/Data Admin/snapshot_manage_addProcess.php +++ b/Data Admin/snapshot_manage_addProcess.php @@ -17,6 +17,8 @@ along with this program. If not, see . */ +use Gibbon\Domain\System\SettingGateway; + // Gibbon Bootstrap include __DIR__ . '/../../gibbon.php'; @@ -34,7 +36,7 @@ //Proceed! //Check if file exists - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/'.trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath').'/uploads'.$snapshotFolder; diff --git a/Data Admin/snapshot_manage_delete.php b/Data Admin/snapshot_manage_delete.php index e77c9c7..256d13a 100644 --- a/Data Admin/snapshot_manage_delete.php +++ b/Data Admin/snapshot_manage_delete.php @@ -18,6 +18,7 @@ */ use Gibbon\Forms\Prefab\DeleteForm; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -41,7 +42,7 @@ echo __("You have not specified one or more required parameters.") ; echo "" ; } else { - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/'.trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath').'/uploads'.$snapshotFolder; diff --git a/Data Admin/snapshot_manage_deleteProcess.php b/Data Admin/snapshot_manage_deleteProcess.php index 40b7058..44aefb8 100644 --- a/Data Admin/snapshot_manage_deleteProcess.php +++ b/Data Admin/snapshot_manage_deleteProcess.php @@ -17,6 +17,8 @@ along with this program. If not, see . */ +use Gibbon\Domain\System\SettingGateway; + // Gibbon Bootstrap include __DIR__ . '/../../gibbon.php'; @@ -34,7 +36,7 @@ } else { //Proceed! //Check if file exists - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/'.trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath').'/uploads'.$snapshotFolder; diff --git a/Data Admin/snapshot_manage_load.php b/Data Admin/snapshot_manage_load.php index e3fef2d..e037fda 100644 --- a/Data Admin/snapshot_manage_load.php +++ b/Data Admin/snapshot_manage_load.php @@ -18,6 +18,7 @@ */ use Gibbon\Forms\Form; +use Gibbon\Domain\System\SettingGateway; // Module Bootstrap require __DIR__ . '/module.php'; @@ -51,7 +52,7 @@ echo __("You have not specified one or more required parameters."); echo ""; } else { - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/' . trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath') . '/uploads' . $snapshotFolder; diff --git a/Data Admin/snapshot_manage_loadProcess.php b/Data Admin/snapshot_manage_loadProcess.php index c7f21a7..0ebcff0 100644 --- a/Data Admin/snapshot_manage_loadProcess.php +++ b/Data Admin/snapshot_manage_loadProcess.php @@ -17,6 +17,8 @@ along with this program. If not, see . */ +use Gibbon\Domain\System\SettingGateway; + // Gibbon Bootstrap include __DIR__ . '/../../gibbon.php'; @@ -35,7 +37,7 @@ } else { //Proceed! //Check if file exists - $snapshotFolder = getSettingByScope($connection2, 'Data Admin', 'exportSnapshotsFolderLocation'); + $snapshotFolder = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportSnapshotsFolderLocation'); $snapshotFolder = '/'.trim($snapshotFolder, '/ '); $snapshotFolderPath = $session->get('absolutePath').'/uploads'.$snapshotFolder; diff --git a/Data Admin/src/DatabaseTools.php b/Data Admin/src/DatabaseTools.php index 71c2e3d..d9bef49 100644 --- a/Data Admin/src/DatabaseTools.php +++ b/Data Admin/src/DatabaseTools.php @@ -21,6 +21,7 @@ use Gibbon\Contracts\Database\Connection; use Gibbon\Data\ImportType; +use Gibbon\Contracts\Services\Session; /** * Database Tools class @@ -50,7 +51,7 @@ class DatabaseTools * @param Gibbon\session * @param Gibbon\Contracts\Database\Connection */ - public function __construct(\Gibbon\Session $session = NULL, Connection $pdo = NULL) + public function __construct(Session $session = NULL, Connection $pdo = NULL) { $this->session = $session; $this->pdo = $pdo; diff --git a/Data Admin/src/ImportType.php b/Data Admin/src/ImportType.php deleted file mode 100644 index ce8fd4e..0000000 --- a/Data Admin/src/ImportType.php +++ /dev/null @@ -1,1231 +0,0 @@ -. -*/ - -namespace Modules\DataAdmin; - -use Gibbon\Contracts\Database\Connection; -use Symfony\Component\Yaml\Yaml; - -/** - * Reads and holds the config info for a custom Import Type - * - * @version 25th April 2016 - * @since 25th April 2016 - * @author Sandra Kuipers - */ -class ImportType -{ - /** - * Information about the overall Import Type - */ - protected $details = array(); - - /** - * Permission information for user access - */ - protected $access = array(); - - /** - * Values that can be used for sync & updates - */ - protected $primaryKey; - protected $uniqueKeys = array(); - protected $keyFields = array(); - - /** - * Holds the table fields and information for each field - */ - protected $table = array(); - protected $tablesUsed = array(); - - /** - * Has the structure been checked against the database? - */ - protected $validated = false; - - /** - * Relational data: System-wide (for filters) - * @var array - */ - protected $useYearGroups = false; - protected $yearGroups = array(); - - protected $useLanguages = false; - protected $languages = array(); - - protected $useCountries = false; - protected $countries = array(); - - protected $usePhoneCodes = false; - protected $phoneCodes = array(); - - protected $useCustomFields = false; - protected $customFields = array(); - - /** - * Constructor - * - * @version 26th April 2016 - * @since 26th April 2016 - * @param array importType information - * @param Object PDO Connection - */ - public function __construct( $data, Connection $pdo = NULL, $validateStructure = true ) - { - if (isset($data['details'])) { - $this->details = $data['details']; - } - - if (isset($data['access'])) { - $this->access = $data['access']; - } - - if (isset($data['primaryKey'])) { - $this->primaryKey = $data['primaryKey']; - } - - if (isset($data['uniqueKeys'])) { - $this->uniqueKeys = $data['uniqueKeys']; - - //Grab the unique fields used in all keys - foreach ($this->uniqueKeys as $key) { - if (is_array($key) && count($key) > 1) { - foreach ($key as $keyName) { - if (!in_array($keyName, $this->keyFields)) $this->keyFields[] = $keyName; - } - } else { - if (!in_array($key, $this->keyFields)) $this->keyFields[] = $key; - } - } - } - - if (isset($data['table'])) { - $this->table = $data['table']; - $this->tablesUsed[] = $this->details['table']; - - // Add relational tables to the tablesUsed array so they're locked - foreach ($this->table as $fieldName => $field) { - if ($this->isFieldRelational($fieldName)) { - $relationship = $this->getField($fieldName, 'relationship'); - if (!in_array($relationship['table'], $this->tablesUsed)) { - $this->tablesUsed[] = $relationship['table']; - } - } - - // Check the filters so we know if extra data is nessesary - $filter = $this->getField($fieldName, 'filter'); - if ($filter == 'yearlist') $this->useYearGroups = true; - if ($filter == 'language') $this->useLanguages = true; - if ($filter == 'country') $this->useCountries = true; - if ($filter == 'phonecode') $this->usePhoneCodes = true; - if ($filter == 'customfield') $this->useCustomFields = true; - } - } - - if ($pdo != NULL) { - - if ($validateStructure == true) { - $this->validated = $this->validateWithDatabase( $pdo ); - $this->loadRelationalData( $pdo ); - } else { - $data = array('tableName' => $this->getDetail('table')); - $sql = "SHOW TABLES LIKE :tableName"; - $this->validated = !empty($pdo->selectOne($sql, $data)); - } - - $this->loadAccessData( $pdo ); - } - - if ( empty($this->primaryKey) || empty($this->uniqueKeys) || empty($this->details) || empty($this->table) ) { - return NULL; - } - } - - public static function getBaseDir(Connection $pdo) { - $absolutePath = getSettingByScope($pdo->getConnection(), 'System', 'absolutePath'); - return rtrim($absolutePath, '/ '); - } - - public static function getImportTypeDir(Connection $pdo) { - return self::getBaseDir($pdo) . "/modules/Data Admin/imports"; - } - - public static function getCustomImportTypeDir(Connection $pdo) { - $customFolder = getSettingByScope($pdo->getConnection(), 'Data Admin', 'importCustomFolderLocation'); - - return self::getBaseDir($pdo).'/uploads/'.trim($customFolder, '/ '); - } - - /** - * Load Import Type List - * Loads all YAML files from a folder and creates an importType object for each - * - * @access public - * @version 29th April 2016 - * @since 29th April 2016 - * @param Object PDO Connection - * - * @return array 2D array of importType objects - */ - public static function loadImportTypeList(Connection $pdo = NULL, $validateStructure = false ) { - - $yaml = new Yaml(); - $importTypes = array(); - - // Get the built-in import definitions - $defaultFiles = glob( self::getImportTypeDir($pdo) . "/*.yml" ); - - // Create importType objects for each file - foreach ( $defaultFiles as $file) { - $fileData = $yaml::parse( file_get_contents( $file ) ); - - if (isset($fileData['details']) && isset($fileData['details']['type']) ) { - $fileData['details']['grouping'] = (isset($fileData['access']['module']))? $fileData['access']['module'] : 'General'; - $importTypes[ $fileData['details']['type'] ] = new ImportType( $fileData, $pdo, $validateStructure ); - } - } - - // Get the user-defined custom definitions - $customFiles = glob( self::getCustomImportTypeDir($pdo) . "/*.yml" ); - - if (is_dir(self::getCustomImportTypeDir($pdo))==FALSE) { - mkdir(self::getCustomImportTypeDir($pdo), 0755, TRUE) ; - } - - foreach ( $customFiles as $file) { - $fileData = $yaml::parse( file_get_contents( $file ) ); - - if (isset($fileData['details']) && isset($fileData['details']['type']) ) { - $fileData['details']['grouping'] = '* Custom Imports'; - $fileData['details']['custom'] = true; - $importTypes[ $fileData['details']['type'] ] = new importType( $fileData, $pdo, $validateStructure ); - } - } - - uasort($importTypes, array('self', 'sortImportTypes')); - - return $importTypes; - } - - protected static function sortImportTypes($a, $b) { - if ($a->getDetail('grouping') < $b->getDetail('grouping')) - return -1; - else if ($a->getDetail('grouping') > $b->getDetail('grouping')) - return 1; - - if ($a->getDetail('category') < $b->getDetail('category')) - return -1; - else if ($a->getDetail('category') > $b->getDetail('category')) - return 1; - - if ($a->getDetail('name') < $b->getDetail('name')) - return -1; - else if ($a->getDetail('name') > $b->getDetail('name')) - return 1; - - return 0; - } - - /** - * Load Import Type - * Loads a YAML file and creates an importType object - * - * @access public - * @version 29th April 2016 - * @since 29th April 2016 - * @param string Filename of the Import Type - * @param Object PDO Conenction - * - * @return [importType] - */ - public static function loadImportType( $importTypeName, Connection $pdo = NULL ) { - - // Check custom first, this allows for local overrides - $path = self::getCustomImportTypeDir($pdo).'/'.$importTypeName.'.yml'; - if (!file_exists($path)) { - // Next check the built-in import types folder - $path = self::getImportTypeDir($pdo).'/'.$importTypeName.'.yml'; - - // Finally fail if nothing is found - if (!file_exists($path)) return NULL; - } - - $yaml = new Yaml(); - $fileData = $yaml::parse( file_get_contents($path) ); - - return new importType( $fileData, $pdo ); - } - - /** - * Is Import Accessible - * - * @access public - * @version 29th April 2016 - * @since 29th April 2016 - * @param string guid - * @param Object PDO Conenction - * - * @return bool - */ - public function isImportAccessible( $guid, $connection2 ) { - - if ($this->getAccessDetail('protected') == false) return true; - if ($connection2 == null) return false; - - return isActionAccessible($guid, $connection2, '/modules/' . $this->getAccessDetail('module').'/'.$this->getAccessDetail('entryURL') ); - } - - /** - * Validate With Database - * Compares the importType structure with the database table to ensure imports will succeed - * - * @access protected - * @version 29th April 2016 - * @since 29th April 2016 - * @param Object PDO Conenction - * - * @return bool true if all fields match existing table columns - */ - protected function validateWithDatabase( Connection $pdo ) { - - try { - $sql="SHOW COLUMNS FROM " . $this->getDetail('table'); - $result = $pdo->executeQuery(array(), $sql); - } - catch(\PDOException $e) { - return false; - } - - $columns = $result->fetchAll( \PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE ); - - $validatedFields = 0; - foreach ($this->table as $fieldName => $field) { - if ($this->isFieldReadOnly($fieldName)) { - $validatedFields++; - continue; - } - - if ( isset($columns[$fieldName]) ) { - foreach ($columns[$fieldName] as $columnName => $columnField) { - - if ($columnName == 'Type') { - $this->parseTableValueType($fieldName, $columnField); - } else { - $this->setField($fieldName, mb_strtolower($columnName), $columnField); - } - } - $validatedFields++; - } else { - echo '
Invalid field '. $fieldName .'
'; - } - } - - return ($validatedFields == count($this->table)); - } - - /** - * Load Access Data - for user permission checking, and category names - * @version 2016 - * @since 2016 - * @param Connection $pdo - */ - protected function loadAccessData( Connection $pdo ) { - - if ( empty($this->access['module']) || empty($this->access['action']) ) { - $this->access['protected'] = false; - $this->details['category'] = 'Gibbon'; - return; - } - - try { - $data = array('module' => $this->access['module'], 'action' => $this->access['action'] ); - $sql = "SELECT gibbonAction.category, gibbonAction.entryURL - FROM gibbonAction - JOIN gibbonModule ON (gibbonAction.gibbonModuleID=gibbonModule.gibbonModuleID) - WHERE gibbonModule.name=:module - AND gibbonAction.name=:action - ORDER BY gibbonAction.precedence ASC - LIMIT 1"; - $result = $pdo->executeQuery($data, $sql); - } catch(\PDOException $e) {} - - if ($result->rowCount() > 0) { - $action = $result->fetch(); - - $this->access['protected'] = true; - $this->access['entryURL'] = $action['entryURL']; - - if (empty($this->details['category'])) { - $this->details['category'] = $action['category']; - } - } - } - - /** - * Load Relational Data - * @version 2016 - * @since 2016 - * @param Connection $pdo - */ - protected function loadRelationalData( Connection $pdo ) { - - // Grab the year groups so we can translate Year Group Lists without a million queries - if ($this->useYearGroups) { - try { - $sql="SELECT gibbonYearGroupID, nameShort FROM gibbonYearGroup ORDER BY sequenceNumber"; - $resultYearGroups = $pdo->executeQuery(array(), $sql); - } catch(\PDOException $e) {} - - if ($resultYearGroups->rowCount() > 0) { - while ($yearGroup = $resultYearGroups->fetch() ) { - $this->yearGroups[ $yearGroup['nameShort'] ] = $yearGroup['gibbonYearGroupID']; - } - } - } - - // Grab the Languages for system-wide relational data (filters) - if ($this->useLanguages) { - try { - $sql="SELECT name FROM gibbonLanguage"; - $resultLanguages = $pdo->executeQuery(array(), $sql); - } catch(\PDOException $e) {} - - if ($resultLanguages->rowCount() > 0) { - while ($languages = $resultLanguages->fetch() ) { - $this->languages[ $languages['name'] ] = $languages['name']; - } - } - } - - // Grab the Countries for system-wide relational data (filters) - if ($this->useCountries || $this->usePhoneCodes) { - try { - $sql="SELECT printable_name, iddCountryCode FROM gibbonCountry"; - $resultCountries = $pdo->executeQuery(array(), $sql); - } catch(\PDOException $e) {} - - if ($resultCountries->rowCount() > 0) { - while ($countries = $resultCountries->fetch() ) { - if ($this->useCountries) $this->countries[ $countries['printable_name'] ] = $countries['printable_name']; - if ($this->usePhoneCodes) $this->phoneCodes[ $countries['iddCountryCode'] ] = $countries['iddCountryCode']; - } - } - } - - // Grab the user-defined Custom Fields - if ($this->useCustomFields) { - try { - $sql="SELECT gibbonPersonFieldID, name, type, options, required FROM gibbonPersonField where active = 'Y'"; - $resultCustomFields = $pdo->executeQuery(array(), $sql); - } catch(\PDOException $e) {} - - if ($resultCustomFields->rowCount() > 0) { - while ($fields = $resultCustomFields->fetch() ) { - $this->customFields[ $fields['name'] ] = $fields; - } - - foreach ($this->table as $fieldName => $field) { - $customFieldName = $this->getField($fieldName, 'name'); - if ( !isset($this->customFields[$customFieldName]) ) continue; - - $type = $this->customFields[ $customFieldName ]['type']; - if ($type == 'varchar') { - $this->setField( $fieldName, 'kind', 'char'); - $this->setField( $fieldName, 'type', 'varchar'); - $this->setField( $fieldName, 'length', $this->customFields[ $customFieldName ]['options'] ); - } else if ($type == 'select') { - $this->setField( $fieldName, 'kind', 'enum'); - $this->setField( $fieldName, 'type', 'enum'); - $elements = explode(',', $this->customFields[ $customFieldName ]['options']); - $this->setField( $fieldName, 'elements', $elements ); - $this->setField( $fieldName, 'length', count($elements) ); - } else if ($type == 'text' || $type == 'date') { - $this->setField( $fieldName, 'kind', $type); - $this->setField( $fieldName, 'type', $type); - } - - $this->setField( $fieldName, 'customField', $this->customFields[ $customFieldName ]['gibbonPersonFieldID'] ); - - $args = $this->getField( $fieldName, 'args'); - $args['required'] = ($this->customFields[ $customFieldName ]['required'] == 'Y'); - $this->setField( $fieldName, 'args', $args); - } - } - } - } - - /** - * Parse Table Value Type - * Split the SQL type eg: int(3) into a type name and length, etc. - * - * @access protected - * @version 25th May 2016 - * @since 29th April 2016 - * @param string $fieldName - * @param string $columnField - */ - protected function parseTableValueType( $fieldName, $columnField ) { - - // Split the info from inside the outer brackets, eg int(3) - $firstBracket = mb_strpos($columnField, '('); - $lastBracket = mb_strrpos($columnField, ')'); - - $type = ($firstBracket !== false)? mb_substr($columnField, 0, $firstBracket) : $columnField; - $details = ($firstBracket !== false)? mb_substr($columnField, $firstBracket+1, $lastBracket-$firstBracket-1 ) : ''; - - // Cancel out if the type is not valid - if (!isset($type)) return; - - $this->setField( $fieldName, 'type', $type ); - - if ($type == 'varchar' || $type == 'character') { - $this->setField( $fieldName, 'kind', 'char' ); - $this->setField( $fieldName, 'length', $details ); - } - else if ($type == 'text' || $type == 'mediumtext' || $type == 'longtext' || $type == 'blob') { - $this->setField( $fieldName, 'kind', 'text' ); - } - else if ($type == 'integer' || $type == 'int' || $type == 'tinyint' || $type == 'smallint' || $type == 'mediumint' || $type == 'bigint') { - $this->setField( $fieldName, 'kind', 'integer' ); - $this->setField( $fieldName, 'length', $details ); - } - else if ($type == 'decimal' || $type == 'numeric' || $type == 'float' || $type == 'real') { - $this->setField( $fieldName, 'kind', 'decimal' ); - $decimalParts = explode(',', $details); - $this->setField( $fieldName, 'length', $decimalParts[0] - $decimalParts[1] ); - $this->setField( $fieldName, 'precision', $decimalParts[0] ); - $this->setField( $fieldName, 'scale', $decimalParts[1] ); - } - else if ($type == 'enum') { - - // Grab the CSV enum elements as an array - $elements = explode(',', str_replace("'", "", $details) ); - $this->setField( $fieldName, 'elements', $elements ); - $this->setField( $fieldName, 'length', count($elements) ); - - if ($details == "'Y','N'" || $details == "'N','Y'") { - $this->setField( $fieldName, 'kind', 'yesno' ); - } else { - $this->setField( $fieldName, 'kind', 'enum' ); - } - - if ( empty($this->getField($fieldName, 'desc')) ) { - $this->setField( $fieldName, 'desc', implode(', ', $elements) ); - } - } - else { - $this->setField( $fieldName, 'kind', $type ); - } - - if ( $this->isFieldRelational($fieldName) ) { - $this->setField( $fieldName, 'kind', 'char' ); - $this->setField( $fieldName, 'length', 50 ); - } - } - - /** - * Get Detail - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string key - name of the detail to retrieve - * @param string default - an optional value to return if key doesn't exist - * - * @return var - */ - public function getDetail($key, $default = "") { - return ( isset($this->details[$key]) )? $this->details[$key] : $default; - } - - /** - * Get Access Detail - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string key - name of the access key to retrieve - * @param string default - an optional value to return if key doesn't exist - * - * @return var - */ - public function getAccessDetail($key, $default = "") { - return ( isset($this->access[$key]) )? $this->access[$key] : $default; - } - - /** - * Get Primary Key - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return array 2D array of available keys to sync with - */ - public function getPrimaryKey() { - return $this->primaryKey; - } - - /** - * Get Keys - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return array 2D array of available keys to sync with - */ - public function getUniqueKeys() { - return $this->uniqueKeys; - } - - /** - * Get Key Fields - * - * @access public - * @version 25th May 2016 - * @since 25th May 2016 - * - * @return array 2D array of available key fields - */ - public function getUniqueKeyFields() { - return ( isset($this->keyFields) )? $this->keyFields : array(); - } - - /** - * Get Tables - * Get the tables used in this import. All tables used must be locked. - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return array 2D array of table names used in this import - */ - public function getTables() { - return $this->tablesUsed; - } - - /** - * Get Table Fields - * - * @access public - * @version 28th April 2016 - * @since 28th April 2016 - * - * @return array 2D array of table field names used in this import - */ - public function getTableFields() { - return ( isset($this->table) )? array_keys($this->table) : array(); - } - - /** - * Get Field Information by Key - * - * @access public - * @version 28th April 2016 - * @since 28th April 2016 - * @param string Field Name - * @param string Key to retrieve - * @param string Default value to return if key doesn't exist - * - * @return var - */ - public function getField( $fieldName, $key, $default = "" ) { - - if (isset($this->table[$fieldName][$key])) { - return $this->table[$fieldName][$key]; - } else if (isset($this->table[$fieldName]['args'][$key])) { - return $this->table[$fieldName]['args'][$key]; - } else { - return $default; - } - } - - /** - * Set Field Information by Key - * - * @access protected - * @version 25th May 2016 - * @since 25th May 2016 - * @param string Field Name - * @param string Key to retrieve - * @param string Value to set - */ - protected function setField( $fieldName, $key, $value ) { - - if ( isset($this->table[$fieldName]) ) { - $this->table[$fieldName][$key] = $value; - } else { - $this->table[$fieldName] = array( $key => $value ); - } - } - - /** - * Filter Field Value - * Compares the value type, legth and properties with the expected values for the table column - * - * @access public - * @version 10th June 2016 - * @since 8th June 2016 - * @param string Field name - * @param var Value to validate - * - * @return bool true if the value checks out - */ - public function filterFieldValue( $fieldName, $value ) { - - $value = trim($value); - - $filter = $this->getField( $fieldName, 'filter' ); - $strvalue = mb_strtoupper($value); - - switch($filter) { - - case 'html': // Filter valid tags? requres db connection, which we dont store :( - break; - - case 'url': if (!empty($value)) $value = filter_var( $value, FILTER_SANITIZE_URL); - break; - - case 'email': if (mb_strpos($value, ',') !== false || mb_strpos($value, '/') !== false || mb_strpos($value, ' ') !== false ) { - $emails = preg_split('/[\s,\/]*/u', $value); - $value = (isset($emails[0]))? $emails[0] : ''; - } - - if (!empty($value)) $value = filter_var( $value, FILTER_SANITIZE_EMAIL); - break; - - case 'yesno': // Translate generic boolean values into Y or N, watch the === for TRUE/FALSE, otherwise it breaks! - if ($strvalue == 'TRUE' || $strvalue == 'YES' || $strvalue == 'Y') { - $value = 'Y'; - } else if ($value === FALSE || $strvalue == 'FALSE' || $strvalue == 'NO' || $strvalue == 'N' || $strvalue == '') { - $value = 'N'; - } - break; - - case 'date': // Handle various date formats - if ( !empty($value) ) { // && preg_match('/(^\d{4}[-]\d{2}[-]\d{2}$)/u', $value) === false - $date = strtotime($value); - $value = date('Y-m-d', $date); - } - if ( empty($value) || $value == '0000-00-00' || preg_match('/(^\d{4}[-]\d{2}[-]\d{2}$)/u', $value) === false) { - $value = NULL; - } - break; - - case 'time': // Handle various time formats - if ( !empty($value) ) { // && preg_match('/(^\d{2}[:]\d{2}$)/u', $value) === false - $time = strtotime($value); - $value = date('H:i:s', $time); - } - if (empty($value) || $value == '00:00:00' || preg_match('/(^\d{2}[:]\d{2}$)/u', $value) === false) { - $value = NULL; - } - break; - - case 'timestamp': - if ( !empty($value) ) { - $time = strtotime($value); - $value = date( 'Y-m-d H:i:s', $time ); - } - if (empty($value) || $value == '0000-00-00 00:00:00' || preg_match('/(^\d{4}[-]\d{2}[-]\d{2}[ ]+\d{2}[:]\d{2}[:]\d{2}$)/u', $value) === false) { - $value = NULL; - } - - break; - - case 'schoolyear': - // Change school years formated as 2015-16 to 2015-2016 - if ( preg_match('/(^\d{4}[-]\d{2}$)/u', $value) > 0 ) { - $value = mb_substr($value, 0, 5) . mb_substr($value, 0, 2) . mb_substr($value, 5, 2); - } - break; - - case 'gender': // Handle various gender formats - $strvalue = str_replace('.', '', $strvalue); - if ($strvalue == 'M' || $strvalue == 'MALE' || $strvalue == 'MR') { - $value = 'M'; - } else if ($strvalue == 'F' || $strvalue == 'FEMALE' || $strvalue == 'MS' || $strvalue == 'MRS' || $strvalue == 'MISS') { - $value = 'F'; - } else if (empty($value)) { - $value = 'Unspecified'; - } else { - $value = 'Other'; - } - break; - - case 'numeric': $value = preg_replace("/[^0-9]/u", '', $value); - break; - - case 'phone': // Handle phone numbers - strip all non-numeric chars - $value = preg_replace("/[^0-9,\/]/u", '', $value); - - if (mb_strpos($value, ',') !== false || mb_strpos($value, '/') !== false || mb_strpos($value, ' ') !== false ) { - //$value = preg_replace("/[^0-9,\/]/u", '', $value); - $numbers = preg_split("/[,\/]*/u", $value); - $value = (isset($numbers[0]))? $numbers[0] : ''; - } - break; - - case 'phonecode': $value = preg_replace("/[^0-9]/u", '', $value); - break; - - case 'phonetype': // Handle TIS phone types - if (mb_stripos($value, 'Mobile') !== false || mb_stripos($value, 'Cellular') !== false ) { - $value = 'Mobile'; - } - else if (mb_stripos($value, 'Home') !== false ) { - $value = 'Home'; - } - else if (mb_stripos($value, 'Office') !== false || mb_stripos($value, 'Business') !== false ) { - $value = 'Work'; - } else { - $value = 'Other'; - } - break; - - case 'country': if ($strvalue == "MACAU") $value = 'Macao'; - if ($strvalue == "HK") $value = 'Hong Kong'; - if ($strvalue == "USA") $value = 'United States'; - $value = ucfirst($value); - break; - - case 'language': // Translate a few languages to gibbon-specific use - if ($strvalue == "CANTONESE") $value = 'Chinese (Cantonese)'; - if ($strvalue == "MANDARIN") $value = 'Chinese (Mandarin)'; - if ($strvalue == "CHINESE") $value = 'Chinese (Mandarin)'; - $value = ucfirst($value); - break; - - case 'ethnicity': - $value = ucfirst($value); - break; - - case 'relation': if ($strvalue == "MOTHER") $value = 'Parent'; - else if ($strvalue == "FATHER") $value = 'Parent'; - else if ($strvalue == "SISTER") $value = 'Other Relation'; - else if ($strvalue == "BROTHER") $value = 'Other Relation'; - else $value = 'Other'; - break; - - case 'yearlist': // Handle incoming blackbaud Grade Level's Allowed, turn them into Year Group IDs - - if (!empty($value)) { - $yearGroupIDs = array(); - $yearGroupNames = explode(',', $value); - - foreach ( $yearGroupNames as $gradeLevel ) { - $gradeLevel = trim($gradeLevel); - if (isset($this->yearGroups[$gradeLevel])) { - $yearGroupIDs[] = $this->yearGroups[$gradeLevel]; - } - } - - $value = implode(',', $yearGroupIDs); - } - break; - - case 'status': // Transform positive values into Full and negative into Left - if ($strvalue == 'FULL' || $strvalue == 'YES' || $strvalue == 'Y' || $value === '1') { - $value = 'Full'; - } - else if ($strvalue == 'LEFT' || $strvalue == 'NO' || $strvalue == 'N' || $value == '' || $value === '0') { - $value = 'Left'; - } - else if ($strvalue == 'EXPECTED') { - $value = 'Expected'; - } - else if ($strvalue == 'PENDING APPROVAL') { - $value = 'Pending Approval'; - } - break; - - case 'csv': break; - - case 'customfield': break; - - case 'string': - default: $value = strip_tags($value); - - - } - - $kind = $this->getField( $fieldName, 'kind' ); - - switch($kind) { - case 'integer': $value = intval($value); break; - case 'decimal': $value = floatval($value); break; - case 'boolean': $value = boolval($value); break; - } - - if ($strvalue == 'NOT REQUIRED' || $value == 'N/A') { - $value = ''; - } - - return $value; - } - - /** - * Validate Field Value - * Compares the value type, legth and properties with the expected values for the table column - * - * @access public - * @version 10th June 2016 - * @since 29th April 2016 - * @param string Field name - * @param var Value to validate - * - * @return bool true if the value checks out - */ - public function validateFieldValue( $fieldName, $value ) { - - if (!$this->validated) return false; - - if ( $this->isFieldRelational($fieldName) ) { - return true; - } - - // Validate based on filter type (from args) - $filter = $this->getField( $fieldName, 'filter' ); - - switch($filter) { - - case 'url': if (!empty($value) && filter_var( $value, FILTER_VALIDATE_URL) === false) return false; break; - case 'email': //if (!empty($value) && filter_var( $value, FILTER_VALIDATE_EMAIL) === false) return false; - break; - - case 'country': if ( !empty($value) && !isset($this->countries[ $value ]) ) return false; break; - - case 'language': if ( !empty($value) && !isset($this->languages[ $value ]) ) return false; break; - - case 'phonecode': if ( !empty($value) && !isset($this->phoneCodes[ $value ]) ) return false; break; - - case 'schoolyear': if ( preg_match('/(^\d{4}[-]\d{4}$)/u', $value) > 1 ) return false; break; - - case 'nospaces': if ( preg_match('/\s/u', $value) > 0 ) return false; break; - - default: if (mb_substr($filter, 0, 1) == '/') { - if ( preg_match($filter, $value) == false ) { - return false; - } - }; - } - - // Validate based on value type (from db) - $kind = $this->getField( $fieldName, 'kind' ); - - switch($kind) { - case 'char': $length = $this->getField( $fieldName, 'length' ); - if ( mb_strlen($value) > $length ) return false; - break; - - case 'text': break; - - case 'integer': $value = intval($value); - $length = $this->getField( $fieldName, 'length' ); - if ( mb_strlen($value) > $length ) return false; - break; - - case 'decimal': $value = floatval($value); - $length = $this->getField( $fieldName, 'length' ); - - if (mb_strpos($value, '.') !== false) { - $number = mb_strstr($value, '.', true); - if ( mb_strlen($number) > $length ) return false; - } else { - if ( mb_strlen($value) > $length ) return false; - } - break; - - case 'yesno': if ( $value != 'Y' && $value != 'N' ) return false; - break; - - case 'boolean': if ( !is_bool($value) ) return false; - break; - - case 'enum': $elements = $this->getField( $fieldName, 'elements' ); - if ( !in_array($value, $elements) ) return false; - break; - } - - // TODO: More value validation - // TODO: Handle relational table data - // TODO: Sanitize - - return $value; - } - - /** - * Is Valid - * Has the ImportType been checked against the databate table for field validity? - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return bool true if the importType has been validated - */ - public function isValid() { - - return $this->validated; - } - - /** - * Is Using Custom Fields - * - * @access public - * @version 20th September 2016 - * @since 20th September 2016 - * - * @return bool - */ - public function isUsingCustomFields() { - - return $this->useCustomFields; - } - - /** - * Is Field Relational - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return bool true if marked as a required field - */ - public function isFieldRelational( $fieldName ) { - return ( isset($this->table[$fieldName]['relationship']) && !empty($this->table[$fieldName]['relationship']) ); - } - - /** - * Is Field Linked to another field (for relational reference) - * - * @access public - * @version 28th November 2016 - * @since 28th November 2016 - * @param string Field name - * - * @return bool true if marked as a linked field - */ - public function isFieldLinked( $fieldName ) { - return (isset( $this->table[$fieldName]['args']['linked']))? $this->table[$fieldName]['args']['linked'] : false; - } - - /** - * Is Field Read Only (for relational reference) - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return bool true if marked as a read only field - */ - public function isFieldReadOnly( $fieldName ) { - return (isset( $this->table[$fieldName]['args']['readonly']))? $this->table[$fieldName]['args']['readonly'] : false; - } - - /** - * Is Field Hidden - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return bool true if marked as a hidden field (or is linked) - */ - public function isFieldHidden( $fieldName ) { - if ($this->isFieldLinked($fieldName)) return true; - return (isset( $this->table[$fieldName]['args']['hidden']))? $this->table[$fieldName]['args']['hidden'] : false; - } - - /** - * Is Field Required - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return bool true if marked as a required field - */ - public function isFieldRequired( $fieldName ) { - return (isset( $this->table[$fieldName]['args']['required']))? $this->table[$fieldName]['args']['required'] : false; - } - - /** - * Is Field Required - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return bool true if marked as a required field - */ - public function isFieldUniqueKey( $fieldName ) { - return ( in_array($fieldName, $this->keyFields) ) ; - } - - /** - * Readable Field Type - * Create a human friendly representation of the field value type - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return string - */ - public function readableFieldType( $fieldName ) { - $output = ''; - $kind = $this->getField($fieldName, 'kind'); - - if ($this->isFieldRelational($fieldName)) { - extract( $this->getField($fieldName, 'relationship') ); - return $table.' '.( (is_array($field))? implode(', ', $field) : $field ); - } - - if (isset($kind)) { - $length = $this->getField($fieldName, 'length'); - - switch($kind) { - case 'char': $output = "Text (" . $length . " chars)"; break; - case 'text': $output = "Text"; break; - case 'integer': $output = "Number (" . $length . " digits)"; break; - case 'decimal': $scale = $this->getField($fieldName, 'scale'); - $output = "Decimal (" . str_repeat('0', $length) .".". str_repeat('0', $scale)." format)"; break; - case 'yesno': $output = "Y or N"; break; - case 'boolean': $output = "True or False"; break; - case 'enum': $options = $this->getField($fieldName, 'elements'); - $optionCount = $this->getField($fieldName, 'length'); - $optionString = ($optionCount > 4)? mb_substr(implode(', ', $options), 0, 60).' ...' : implode(', ', $options); - $output = "Options (".$optionString.")"; break; - default: $output = ucfirst($kind); - } - } - return $output; - } - - /** - * Do Import Function - * Returns the value of a dynmaic function name supplied by the importType field - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Field name - * - * @return var|NULL - */ - public function doImportFunction( $fieldName ) { - - $method = $this->getField($fieldName, 'function'); - - if ( !empty($method) && method_exists($this, 'userFunc_'.$method)) { - return call_user_func( array($this, 'userFunc_'.$method) ); - } else { - return NULL; - } - } - - public function getImportRestrictions() - { - $importRestrictions = array(); - - if (!empty($this->getUniqueKeys())) { - foreach ($this->getUniqueKeys() as $key) { - if (is_array($key)) { - $keyNames = array(); - foreach(array_reverse($key) as $keyName) { - $keyNames[] = $this->getField($keyName, 'name'); - } - $importRestrictions[] = __('Unique', 'Data Admin').' '. implode(__(' for each ', 'Data Admin'), $keyNames); - } else { - $importRestrictions[] = $this->getField($key, 'name') .' ' . __('must be unique', 'Data Admin'); - } - } - } - - foreach ($this->getTableFields() as $fieldName) { - if ($this->isFieldHidden($fieldName) ) continue; // Skip hidden fields - - if ($this->isFieldRelational($fieldName)) { - - extract( $this->getField($fieldName, 'relationship') ); - $field = (is_array($field))? implode(', ', $field) : $field; - - $importRestrictions[] = sprintf( __('Each %s should match the %s of a %s', 'Data Admin'), - $this->getField($fieldName, 'name'), $field, $table - ); - } - - if ($this->getField($fieldName, 'type') == 'enum') { - $importRestrictions[] = sprintf( __('%s must be one of: %s', 'Data Admin'), - $this->getField($fieldName, 'name'), - implode(', ', $this->getField($fieldName, 'elements')) - ); - - } - - if ($this->getField($fieldName, 'filter') == 'email') { - $importRestrictions[] = sprintf( __('%s must be a valid email address'), $this->getField($fieldName, 'name')); - } - - if ($this->getField($fieldName, 'filter') == 'url') { - $importRestrictions[] = sprintf( __('%s must be a valid url'), $this->getField($fieldName, 'name')); - } - } - - return $importRestrictions; - } - - /** - * Generate Password - * Custom function for run-time generation of passwords on import - * - * @access protected - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return string Random password, based on default Gibbon function - */ - protected function userFunc_generatePassword() { - return randomPassword(10); - } - - /** - * Timestamp - * Custom function for run-time generation of timestamps - * - * @access protected - * @version 1st December 2016 - * @since 1st December 2016 - * - * @return string current timestamp - */ - protected function userFunc_timestamp() { - return date( 'Y-m-d H:i:s', time() ); - } - -} diff --git a/Data Admin/src/Importer.php b/Data Admin/src/Importer.php deleted file mode 100644 index 1bc3be0..0000000 --- a/Data Admin/src/Importer.php +++ /dev/null @@ -1,967 +0,0 @@ -. -*/ - -namespace Modules\DataAdmin; - -use Gibbon\Contracts\Database\Connection; - -/** - * Extended Import class - * - * @version 25th April 2016 - * @since 25th April 2016 - * @author Sandra Kuipers - */ -class Importer -{ - const COLUMN_DATA_SKIP = -1; - const COLUMN_DATA_CUSTOM = -2; - const COLUMN_DATA_FUNCTION = -3; - const COLUMN_DATA_LINKED = -4; - const COLUMN_DATA_HIDDEN = -5; - - const ERROR_IMPORT_FILE = 200; - const ERROR_INVALID_INPUTS = 201; - const ERROR_REQUIRED_FIELD_MISSING = 205; - const ERROR_INVALID_FIELD_VALUE = 206; - const ERROR_LOCKING_DATABASE = 207; - const ERROR_DATABASE_GENERIC = 208; - const ERROR_DATABASE_FAILED_INSERT = 209; - const ERROR_DATABASE_FAILED_UPDATE = 210; - const ERROR_KEY_MISSING = 211; - const ERROR_NON_UNIQUE_KEY =212; - const ERROR_RELATIONAL_FIELD_MISMATCH = 213; - const ERROR_INVALID_HAS_SPACES = 214; - - const WARNING_DUPLICATE_KEY = 101; - const WARNING_RECORD_NOT_FOUND = 102; - - const MESSAGE_GENERATED_PASSWORD = 10; - - public $fieldDelimiter = ','; - public $stringEnclosure = '"'; - public $maxLineLength = 100000; - - public $mode; - public $syncField; - public $syncColumn; - - public $outputData = array(); - - /** - * File handler for line-by-line CSV read - */ - private $csvFileHandler; - - /** - * Array of header names from first CSV line - */ - private $importHeaders; - - /** - * Array of raw parsed CSV records - */ - private $importData; - - /** - * Array of validated, database-friendly records - */ - private $tableData = array(); - private $tableFields = array(); - - private $serializeData = array(); - - /** - * Errors - */ - private $importLog = array( 'error' => array(), 'warning' => array(), 'message' => array() ); - private $rowErrors = array(); - - /** - * ID of the last error message - */ - private $errorID = 0; - - /** - * Current counts for database operations - */ - private $databaseResults = array( - 'inserts' => 0, - 'inserts_skipped' => 0, - 'updates' => 0, - 'updates_skipped' => 0, - 'duplicates' => 0 - ); - - /** - * Valid import MIME types - */ - private $csvMimeTypes = array( - 'text/csv', 'text/xml', 'text/comma-separated-values', 'text/x-comma-separated-values', 'application/vnd.ms-excel', 'application/csv', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/vnd.oasis.opendocument.spreadsheet', 'application/octet-stream', - ); - - /** - * Gibbon\Contracts\Database\Connection - */ - private $pdo ; - - /** - * Gibbon\session - */ - private $gibbon ; - - private $headerRow; - private $firstRow; - - /** - * Constructor - * - * @version 25th April 2016 - * @since 25th April 2016 - * @param Gibbon\session - * @param Gibbon\config - * @param Gibbon\Contracts\Database\Connection - * @return void - */ - public function __construct(\Gibbon\Core $gibbon, Connection $pdo) - { - $this->gibbon = $gibbon; - $this->pdo = $pdo; - } - - public function __get($name) { - return isset($this->$name) ? $this->$name : null; - } - - public function __set($name, $value) - { - throw new \Exception('Trying to access a read-only property.'); - } - - /** - * Is Valid Mime Type - * Validates the supplied MIME Type with a list of valid types - * - * @access public - * @version 25th April 2016 - * @since 25th April 2016 - * @param string MIME Type - * - * @return bool - */ - public function isValidMimeType( $fileMimeType ) { - return in_array( $fileMimeType, $this->csvMimeTypes ); - } - - /** - * Open CSV File - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string Full File Path - * - * @return bool true on success - */ - public function openCSVFile( $csvFile ) { - - ini_set("auto_detect_line_endings", true); - $this->csvFileHandler=fopen($csvFile, "r"); - return ($this->csvFileHandler !== FALSE); - } - - /** - * Close CSV File - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - */ - public function closeCSVFile() { - fclose($this->csvFileHandler); - } - - /** - * Get CSV Line - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return array Next parsed CSV line, based on current handler - */ - public function getCSVLine() { - return fgetcsv($this->csvFileHandler, $this->maxLineLength, $this->fieldDelimiter, $this->stringEnclosure); - } - - /** - * Read CSV String - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * @param string CSV Data - * - * @return bool true on successful CSV parse - */ - public function readCSVString( $csvString ) { - - $csv = new ParseCSV(); - $csv->heading = true; - $csv->delimiter = $this->fieldDelimiter; - $csv->enclosure = $this->stringEnclosure; - - $csv->parse( $csvString ); - - $this->importHeaders = $csv->titles; - $this->importData = $csv->data; - - $this->importLog['error'] = $csv->error_info; - unset($csv); - - foreach ($this->importLog['error'] as $error) { - $this->rowErrors[ $error['row'] ] = 1; - } - - return (!empty($this->importHeaders) && count($this->importData) > 0 && count($this->rowErrors) == 0 ); - } - - - public function readFileIntoCSV() { - - $data = ''; - - $fileType = mb_substr($_FILES['file']['name'], mb_strpos($_FILES['file']['name'], '.')+1); - $fileType = mb_strtolower($fileType); - $mimeType = $_FILES['file']['type']; - - if ($fileType == 'csv') { - - $opts = array('http' => array('header' => "Accept-Charset: utf-8;q=0.7,*;q=0.7\r\n"."Content-Type: text/html; charset =utf-8\r\n")); - $context = stream_context_create($opts); - - $data = file_get_contents($_FILES['file']['tmp_name'], false, $context); - if ( mb_check_encoding($data, 'UTF-8') == false ) { - $data = mb_convert_encoding($data,'UTF-8'); - } - - // Grab the header & first row for Step 1 - if ($this->openCSVFile( $_FILES['file']['tmp_name'] )) { - $this->headerRow = $this->getCSVLine(); - $this->firstRow = $this->getCSVLine(); - $this->closeCSVFile(); - } - - } - else if ($fileType == 'xlsx' || $fileType == 'xls' || $fileType == 'xml' || $fileType == 'ods') { - - $filePath = $_FILES['file']['tmp_name']; - - // Try to use the best reader if available, otherwise catch any read errors - try { - if ($fileType == 'xml') { - $objReader = \PHPExcel_IOFactory::createReader('Excel2003XML'); - $objPHPExcel = $objReader->load( $filePath ); - } else { - $objPHPExcel = \PHPExcel_IOFactory::load( $filePath ); - } - } catch(\PHPExcel_Reader_Exception $e) { - $this->errorID = Importer::ERROR_IMPORT_FILE; - return false; - } - - $objWorksheet = $objPHPExcel->getActiveSheet(); - $lastColumn = $objWorksheet->getHighestColumn(); - - // Grab the header & first row for Step 1 - foreach( $objWorksheet->getRowIterator(0, 2) as $rowIndex => $row ){ - $array = $objWorksheet->rangeToArray('A'.$rowIndex.':'.$lastColumn.$rowIndex, null, true, true, false); - - if ($rowIndex == 1) $this->headerRow = $array[0]; - else if ($rowIndex == 2) $this->firstRow = $array[0]; - } - - $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'CSV'); - - // Export back to CSV - ob_start(); - $objWriter->save('php://output'); - $data = ob_get_clean(); - } - - return $data; - } - - /** - * Build Table Data - * Iterate over the imported records, validating and building table data for each one - * - * @access public - * @version 28th April 2016 - * @since 28th April 2016 - * @param Object Import Type - * @param array Column Order - * @param array Custom user-provided values - * - * @return bool true if build succeeded - */ - public function buildTableData( $importType, $columnOrder, $customValues = array() ) { - - if ( empty($this->importData) ) return false; - - $this->tableData = array(); - - foreach ( $this->importData as $rowNum => $row ) { - - $fields = array(); - $fieldCount = 0; - $partialFail = FALSE; - foreach ($importType->getTableFields() as $fieldName) { - $columnIndex = $columnOrder[ $fieldCount ]; - - $value = NULL; - // Skip marked columns - if ($columnIndex == Importer::COLUMN_DATA_SKIP) { - $fieldCount++; - continue; - } - // Get the custom text value provided by the user (from Step 2) - else if ($columnIndex == Importer::COLUMN_DATA_CUSTOM) { - - $value = (isset($customValues[ $fieldCount ]))? $customValues[ $fieldCount ] : ''; - } - // Run a user_func based on the function name defined for that field - else if ($columnIndex == Importer::COLUMN_DATA_FUNCTION) { - - $value = $importType->doImportFunction( $fieldName ); - } - // Grab another field value for linked fields. Fields with values must always preceed the linked field. - else if ($columnIndex == Importer::COLUMN_DATA_LINKED) { - - if ($importType->isFieldLinked($fieldName)) { - $linkedFieldName = $importType->getField($fieldName, 'linked'); - $value = (isset($fields[ $linkedFieldName ]))? $fields[ $linkedFieldName ] : null; - } - } - // Use the column index to grab to associated CSV value - else if ($columnIndex >= 0) { - // Get the associative key from the CSV headers using the current index - $columnKey = (isset($this->importHeaders[$columnIndex]))? $this->importHeaders[$columnIndex] : -1; - $value = (isset($row[ $columnKey ]))? $row[ $columnKey ] : NULL; - } - - // Filter - $value = $importType->filterFieldValue( $fieldName, $value ); - $filter = $importType->getField($fieldName, 'filter'); - - // Validate the value - if ( $importType->validateFieldValue( $fieldName, $value ) === false ) { - $type = $importType->getField($fieldName, 'type'); - - if ($filter == 'nospaces') { - $this->log( $rowNum, Importer::ERROR_INVALID_HAS_SPACES, $fieldName, $fieldCount, array($value) ); - } else { - $expectation = (!empty($type))? $importType->readableFieldType($fieldName) : $filter; - $this->log( $rowNum, Importer::ERROR_INVALID_FIELD_VALUE, $fieldName, $fieldCount, array($value, $expectation) ); - } - - $partialFail = TRUE; - } - - // Handle relational table data - // Moved from Insert/Update queries so we can confirm on the dry run (and multi-key relationships) - if ( $importType->isFieldRelational($fieldName) ) { - $join = ''; $on = ''; - extract( $importType->getField($fieldName, 'relationship') ); - - $table = $this->escapeIdentifier($table); - $join = $this->escapeIdentifier($join); - - // Handle table joins - $tableJoin = ''; - if (!empty($join) && !empty($on)) { - if (is_array($on) && count($on) == 2) { - $tableJoin = "JOIN {$join} ON ({$join}.{$on[0]}={$table}.{$on[1]})"; - } - } - - // Handle relational fields with CSV data - $values = $filter == 'csv' ? array_map('trim', explode(',', $value)) : [$value]; - $relationalValue = []; - - foreach ($values as $value) { - // Muli-key relationships - if (is_array($field) && count($field) > 0 ) { - $relationalField = $this->escapeIdentifier($field[0]); - $relationalData = array( $fieldName => $value ); - $relationalSQL = "SELECT {$table}.{$key} FROM {$table} {$tableJoin} WHERE {$relationalField}=:{$fieldName}"; - - for ($i=1; $iescapeIdentifier($relationalField)."=:{$relationalField}"; - } - } - // Single key/value relationship - } else { - $relationalField = $this->escapeIdentifier($field); - $relationalData = array( $fieldName => $value ); - $relationalSQL = "SELECT {$table}.{$key} FROM {$table} {$tableJoin} WHERE {$table}.{$relationalField}=:{$fieldName}"; - } - - $result = $this->pdo->executeQuery($relationalData, $relationalSQL); - if ($result->rowCount() > 0) { - $relationalValue[] = $result->fetchColumn(0); - } else { - // Missing relation for required field? Or missing a relation when value is provided? - if (!empty($value) || $importType->isFieldRequired($fieldName)) { - $field = (is_array($field))? implode(', ', $field) : $field; - $this->log( $rowNum, Importer::ERROR_RELATIONAL_FIELD_MISMATCH, $fieldName, $fieldCount, - array($importType->getField($fieldName, 'name'), $value, $field, $table) ); - $partialFail = TRUE; - } - } - } - - $value = implode(',', $relationalValue); - } - - // Required field is empty? - if ( (!isset($value) || $value === NULL) && $importType->isFieldRequired($fieldName) ) { - $this->log( $rowNum, Importer::ERROR_REQUIRED_FIELD_MISSING, $fieldName, $fieldCount); - $partialFail = TRUE; - } - - // Do we serialize this data? - $serialize = $importType->getField($fieldName, 'serialize'); - if ( !empty($serialize) ) { - - // Is this the field we're serializing? Grab the array - if ($serialize == $fieldName) { - $value = serialize( $this->serializeData[ $serialize ] ); - $fields[ $fieldName ] = $value; - } - // Otherwise collect values in an array - else { - $customField = $importType->getField($fieldName, 'customField'); - $this->serializeData[ $serialize ][ $customField ] = $value; - } - - - } - // Add the field to the field set for this row - else { - - $fields[ $fieldName ] = $value; - } - - $fieldCount++; - } - - // Add the primary key if we're syncing with a databse ID - if ($this->syncField == true) { - - if (isset($row[ $this->syncColumn ]) && !empty($row[ $this->syncColumn ])) { - $fields[ $importType->getPrimaryKey() ] = $row[ $this->syncColumn ]; - } else { - $this->log( $rowNum, Importer::ERROR_REQUIRED_FIELD_MISSING, $importType->getPrimaryKey(), $this->syncColumn); - $partialFail = TRUE; - } - } - - // Salt & hash passwords - if ( isset($fields['password'] ) ) { - if (!isset($this->outputData['passwords'])) $this->outputData['passwords'] = []; - $this->outputData['passwords'][] = ['username' => $fields['username'], 'password' => $fields['password']]; - - $salt=getSalt() ; - $value=$fields['password']; - $fields[ 'passwordStrong' ] = hash("sha256", $salt.$value); - $fields[ 'passwordStrongSalt' ] = $salt; - $fields[ 'password' ] = ''; - - } - - if (!empty($fields) && $partialFail == FALSE) { - $this->tableData[] = $fields; - } - } - - if ( count($this->tableData) > 0 && isset($this->tableData[0]) ) { - $this->tableFields = array_keys($this->tableData[0]); - } - - return ( !empty($this->tableData) && $this->getErrorCount() == 0 ); - } - - /** - * Import Into Database - * Iterate over the table data and INSERT or UPDATE the database, checking for existing records - * - * @access public - * @version 28th April 2016 - * @since 28th April 2016 - * @param Object Import Type - * @param bool Update the database? - * - * @return bool true if import succeeded - */ - public function importIntoDatabase( $importType, $liveRun = TRUE ) { - - if (empty($this->tableData) || count($this->tableData) < 1) { - return false; - } - - $tableName = $this->escapeIdentifier( $importType->getDetail('table') ); - $primaryKey = $importType->getPrimaryKey(); - - // Setup the query string for keys - $sqlKeyQueryString = $this->getKeyQueryString( $importType ); - - $partialFail = FALSE; - foreach ($this->tableData as $rowNum => $row) { - - // Ensure we have valid key(s) - if ( !empty($importType->getUniqueKeyFields()) && array_diff($importType->getUniqueKeyFields(), array_keys($row) ) != false ) { - $this->log( $rowNum, Importer::ERROR_KEY_MISSING ); - $partialFail = TRUE; - continue; - } - - // Find existing record(s) - try { - $data = array(); - // Add the unique keys - foreach ($importType->getUniqueKeyFields() as $keyField) { - $data[ $keyField ] = $row[ $keyField ]; - } - // Add the primary key if database IDs is enabled - if ($this->syncField == true) { - $data[ $primaryKey ] = $row[ $primaryKey ]; - } - - //print_r($data); - //echo '
'.$sqlKeyQueryString.'
'; - //print_r( array_keys($row) ); - - $result = $this->pdo->executeQuery($data, $sqlKeyQueryString); - $keyRow = $result->fetch(); - } - - catch(PDOException $e) { - $this->log( $rowNum, Importer::ERROR_DATABASE_GENERIC ); - $partialFail = TRUE; - continue; - } - - // Build the data and query field=:value associations - $sqlFields = array(); - $sqlData = array(); - - foreach ($row as $fieldName => $fieldData ) { - - if ( $importType->isFieldReadOnly($fieldName) || ($this->mode == 'update' && $fieldName == $primaryKey) ) { - continue; - } else { - $sqlFields[] = $this->escapeIdentifier($fieldName) . "=:" . $fieldName; - $sqlData[ $fieldName ] = $fieldData; - } - - // Handle merging existing custom field data with partial custom field imports - if ($importType->isUsingCustomFields() && $fieldName == 'fields') { - if (isset($keyRow['fields']) && !empty($keyRow['fields'])) { - $sqlData['fields'] = array_merge( unserialize($keyRow['fields']) , unserialize($fieldData) ); - $sqlData['fields'] = serialize($sqlData['fields']); - } - } - } - - $sqlFieldString = implode(", ", $sqlFields ); - - - // Handle Existing Records - if ($result->rowCount() == 1) { - - $primaryKeyValue = $keyRow[ $primaryKey ]; - - // Dont update records on INSERT ONLY mode - if ($this->mode == 'insert') { - $this->log( $rowNum, Importer::WARNING_DUPLICATE_KEY, $primaryKey, $primaryKeyValue ); - $this->databaseResults['updates_skipped'] += 1; - continue; - } - - // If these IDs don't match, then one of the unique keys matched (eg: non-unique value with different database ID) - if ($this->syncField == true && $primaryKeyValue != $row[ $primaryKey ] ) { - $this->log( $rowNum, Importer::ERROR_NON_UNIQUE_KEY, $primaryKey, $row[ $primaryKey ], array( $primaryKey, intval($primaryKeyValue) ) ); - $this->databaseResults['updates_skipped'] += 1; - continue; - } - - $this->databaseResults['updates'] += 1; - - // Skip now so we dont change the database - if (!$liveRun) continue; - - try { - $sqlData[ $primaryKey ] = $primaryKeyValue; - $sql="UPDATE {$tableName} SET " . $sqlFieldString . " WHERE ".$this->escapeIdentifier($primaryKey)."=:{$primaryKey}" ; - $this->pdo->executeQuery($sqlData, $sql); - } - catch(PDOException $e) { - $this->log( $rowNum, Importer::ERROR_DATABASE_FAILED_UPDATE, $e->getMessage() ); - $partialFail = TRUE; - continue; - } - - } - - // Handle New Records - else if ($result->rowCount() == 0) { - - // Dont add records on UPDATE ONLY mode - if ($this->mode == 'update') { - $this->log( $rowNum, Importer::WARNING_RECORD_NOT_FOUND ); - $this->databaseResults['inserts_skipped'] += 1; - continue; - } - - $this->databaseResults['inserts'] += 1; - - // Skip now so we dont change the database - if (!$liveRun) continue; - - try { - $sql="INSERT INTO {$tableName} SET ".$sqlFieldString; - $this->pdo->executeQuery($sqlData, $sql); - } - catch(PDOException $e) { - $this->log( $rowNum, Importer::ERROR_DATABASE_FAILED_INSERT, $e->getMessage() ); - $partialFail = TRUE; - continue; - } - - } - else { - - $primaryKeyValues = $result->fetchAll(\PDO::FETCH_COLUMN | \PDO::FETCH_UNIQUE, 0); - $this->log( $rowNum, Importer::ERROR_NON_UNIQUE_KEY, $primaryKey, -1, array( $primaryKey, implode(', ',$primaryKeyValues) ) ); - $partialFail = TRUE; - } - } - - return (!$partialFail); - } - - protected function getKeyQueryString( $importType ) { - - $tableName = $this->escapeIdentifier( $importType->getDetail('table') ); - $primaryKey = $importType->getPrimaryKey(); - $primaryKeyField = $this->escapeIdentifier($primaryKey); - - $sqlKeys = array(); - foreach ( $importType->getUniqueKeys() as $uniqueKey ) { - - // Handle multi-part unique keys (eg: school year AND course short name) - if ( is_array($uniqueKey) && count($uniqueKey) > 1 ) { - - $sqlKeysFields = array(); - foreach ($uniqueKey as $fieldName) { - if (!in_array($fieldName, $this->tableFields) ) continue; - - $fieldNameField = $this->escapeIdentifier($fieldName); - $sqlKeysFields[] = "({$fieldNameField}=:{$fieldName} AND {$fieldNameField} IS NOT NULL)"; - } - $sqlKeys[] = '('. implode(' AND ', $sqlKeysFields ) .')'; - } else { - // Skip key fields which dont exist in our imported data set - if (!in_array($uniqueKey, $this->tableFields) ) continue; - - $uniqueKeyField = $this->escapeIdentifier($uniqueKey); - $sqlKeys[] = "({$uniqueKeyField}=:{$uniqueKey} AND {$uniqueKeyField} IS NOT NULL)"; - } - - } - - // Add the primary key if database IDs is enabled - if ($this->syncField == true) { - $sqlKeys[] = $primaryKeyField.'=:'.$primaryKey; - } - - $sqlKeyString = implode(' OR ', $sqlKeys ); - - if (empty($sqlKeyString)) $sqlKeyString = "FALSE"; - - if ($importType->isUsingCustomFields()) { - $primaryKeyField = $primaryKeyField.", fields"; - } - - return "SELECT {$tableName}.{$primaryKeyField} FROM {$tableName} WHERE ". $sqlKeyString ; - } - - protected function escapeIdentifier($text) { - return "`".str_replace("`","``",$text)."`"; - } - - /** - * Get Header Row - * - * @access public - * @version 1st December 2016 - * @since 1st December 2016 - * - * @return array Row data - */ - public function getHeaderRow() { - return $this->headerRow; - } - - /** - * Get First Row - * - * @access public - * @version 1st December 2016 - * @since 1st December 2016 - * - * @return array Row data - */ - public function getFirstRow() { - return $this->firstRow; - } - - - /** - * Get Row Count - * - * @access public - * @version 27th April 2016 - * @since 27th April 2016 - * - * @return int Count of rows imported from file - */ - public function getRowCount() { - return count($this->importData); - } - - /** - * Get Database Results - * - * @access public - * @since 28th April 2016 - * @return int Current count of a database operation - */ - public function getDatabaseResult( $key ) { - return (isset($this->databaseResults[$key]))? $this->databaseResults[$key] : 'unknown'; - } - - /** - * Get Logs - * - * @access public - * @since 28th April 2016 - * @return array Errors logged with logError - */ - public function getLogs() { - return array_merge($this->importLog['message'], $this->importLog['warning'], $this->importLog['error']); - } - - /** - * Get Warning Count - * - * @access public - * @since 28th April 2016 - * @return int Warning count - */ - public function getWarningCount() { - return count($this->importLog['warning']); - } - - /** - * Get Error Count - * - * @access public - * @since 28th April 2016 - * @return int Error count - */ - public function getErrorCount() { - return count($this->importLog['error']); - } - - /** - * Get Error Row Count - * - * @access public - * @since 28th April 2016 - * @return int Count of rows with errors - */ - public function getErrorRowCount() { - return count($this->rowErrors); - } - - /** - * Get Last Error - * - * @access public - * @since 28th April 2016 - * @return string Translated error message - */ - public function getLastError() { - return $this->translateMessage( $this->errorID ); - } - - /** - * Log - * - * @access protected - * @version 27th May 2016 - * @since 28th April 2016 - * @param int Row Number - * @param int Error ID - * @param string Field Name - * @param string Field Index - * @param array Values to pass to String Format - */ - protected function log( $rowNum, $messageID, $fieldName = '', $fieldNum = -1, $args = array() ) { - - if ($messageID > 200 ) { - $type = 'error'; - } else if ($messageID > 100 ) { - $type = 'warning'; - } else { - $type = 'message'; - } - - $this->importLog[ $type ][] = array( - 'index' => $rowNum, - 'row' => $rowNum+2, - 'info' => vsprintf( $this->translateMessage($messageID), $args ), - 'field_name' => $fieldName, - 'field' => $fieldNum, - 'type' => $type - ); - - if ( $type == 'error' ) { - $this->rowErrors[ $rowNum ] = 1; - } - } - - /** - * Error Message - * - * @access protected - * @version 27th May 2016 - * @since 28th April 2016 - * @param int Error ID - * - * @return string Translated error message - */ - protected function translateMessage( $errorID ) { - - switch ($errorID) { - // ERRORS - case Importer::ERROR_IMPORT_FILE: - return __("There was an error reading the import file type %s", 'Data Admin'); break; - case Importer::ERROR_REQUIRED_FIELD_MISSING: - return __("Missing value for required field.", 'Data Admin'); break; - case Importer::ERROR_INVALID_FIELD_VALUE: - return __("Invalid value: \"%s\". Expected: %s", 'Data Admin'); break; - case Importer::ERROR_INVALID_HAS_SPACES: - return __("Invalid value: \"%s\". This field type cannot contain spaces.", 'Data Admin'); break; - case Importer::ERROR_INVALID_INPUTS: - return __("Your request failed because your inputs were invalid.", 'Data Admin'); break; - case Importer::ERROR_LOCKING_DATABASE: - return __("The database could not be locked/unlocked for use.", 'Data Admin'); break; - case Importer::ERROR_KEY_MISSING: - return __("Missing value for primary key or unique key set.", 'Data Admin'); break; - case Importer::ERROR_NON_UNIQUE_KEY: - return __("Encountered non-unique values used by %s: %s", 'Data Admin'); break; - case Importer::ERROR_DATABASE_GENERIC: - return __("There was an error accessing the database.", 'Data Admin'); break; - case Importer::ERROR_DATABASE_FAILED_INSERT: - return __("Failed to insert record into database.", 'Data Admin'); break; - case Importer::ERROR_DATABASE_FAILED_UPDATE: - return __("Failed to update database record.", 'Data Admin'); break; - case Importer::ERROR_RELATIONAL_FIELD_MISMATCH: - return __("%s: %s does not match an existing %s in %s", 'Data Admin'); break; - // WARNINGS - case Importer::WARNING_DUPLICATE_KEY: - return __("A duplicate entry already exists for this record. Record skipped.", 'Data Admin'); break; - case Importer::WARNING_RECORD_NOT_FOUND: - return __("A database entry for this record could not be found. Record skipped.", 'Data Admin'); break; - // MESSAGES - case Importer::MESSAGE_GENERATED_PASSWORD: - return __("Password generated for user %s: %s", 'Data Admin'); break; - default: - return __("An error occured, the import was aborted.", 'Data Admin'); break; - } - } - - /** - * Create Import Log - * Inserts a record of an import into the database - * - * @access public - * @version 25th April 2016 - * @since 25th April 2016 - * @param string gibbonPersonID - * @param string Import Type name - * @param array Results of the import - * @param array Column order used - * - * @return bool - */ - public function createImportLog( $gibbonPersonID, $type, $results = array(), $columnOrder = array() ) { - - $success = (( $results['importSuccess'] && $results['buildSuccess'] && $results['databaseSuccess'] ) || $results['ignoreErrors']); - - $data=array("gibbonPersonID"=>$gibbonPersonID, "type"=>$type, "success"=>$success, "importResults"=>serialize($results), "columnOrder"=>serialize($columnOrder) ); - - $sql="INSERT INTO dataAdminImportLog SET gibbonPersonID=:gibbonPersonID, type=:type, success=:success, importResults=:importResults, columnOrder=:columnOrder" ; - $result=$this->pdo->executeQuery($data, $sql); - - return $this->pdo->getQuerySuccess(); - } - - /** - * Lock Tables - * - * @access private - * @version 28th April 2016 - * @since 28th April 2016 - * @param array Tables to be locked - * - * @return bool true if database is now locked - */ - private function lockTables( $tables ) { - - if (empty($tables)) return false; - - try { - $sql="LOCK TABLES " . implode(' WRITE, ', $tables) ." WRITE"; - $result = $this->pdo->executeQuery(array(), $sql); - return true; - } - catch(PDOException $e) { - return false; - } - - } -} diff --git a/Data Admin/tools_findUsernamesProcess.php b/Data Admin/tools_findUsernamesProcess.php index 55434cf..3a7d928 100644 --- a/Data Admin/tools_findUsernamesProcess.php +++ b/Data Admin/tools_findUsernamesProcess.php @@ -18,6 +18,7 @@ */ use PhpOffice\PhpSpreadsheet\IOFactory; +use Gibbon\Domain\System\SettingGateway; // Gibbon Bootstrap include __DIR__ . '/../../gibbon.php'; @@ -157,7 +158,7 @@ $filename = mb_substr($_FILES['file']['name'], 0, mb_strpos($_FILES['file']['name'], '.')); $filename .= '-matches'; - $exportFileType = getSettingByScope($connection2, 'Data Admin', 'exportDefaultFileType'); + $exportFileType = $container->get(SettingGateway::class)->getSettingByScope('Data Admin', 'exportDefaultFileType'); if (empty($exportFileType)) $exportFileType = 'Excel2007'; switch($exportFileType) { diff --git a/Data Admin/version.php b/Data Admin/version.php index 6cdcb35..7276e48 100644 --- a/Data Admin/version.php +++ b/Data Admin/version.php @@ -20,9 +20,9 @@ /** * Sets version information */ -$moduleVersion="1.5.01" ; -$coreVersion = '22.0.00'; +$moduleVersion="1.6.00" ; +$coreVersion = '23.0.00'; -$gibbonVersionRequired="22.0.00"; -$phpVersionRequired="7.0.0"; -$mysqlVersionRequired="5"; +$gibbonVersionRequired="23.0.00"; +$phpVersionRequired="7.3.0"; +$mysqlVersionRequired="5.5";