X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=WEB-INF%2Flib%2FttOrgImportHelper.class.php;h=77458cfaafa2e45174f314fcbd78413177d46b99;hb=509d1d70d0580e26a4467e123c06d2c8939f32cc;hp=80dc8a1990087cda4b20edb236f8effb59d81c12;hpb=67c8fa59139659b2da15c560dd10d18601ab311f;p=timetracker.git diff --git a/WEB-INF/lib/ttOrgImportHelper.class.php b/WEB-INF/lib/ttOrgImportHelper.class.php index 80dc8a19..77458cfa 100644 --- a/WEB-INF/lib/ttOrgImportHelper.class.php +++ b/WEB-INF/lib/ttOrgImportHelper.class.php @@ -36,18 +36,17 @@ import('ttCustomFieldHelper'); import('ttExpenseHelper'); import('ttFavReportHelper'); -// ttOrgImportHelper - this class is a future replacement for ttImportHelper. -// Currently, it is work in progress. -// When done, it should handle import of complex groups consisting of other groups. +// ttOrgImportHelper class is used to import organization data from an XML file +// prepared by ttOrgExportHelper and consisting of nested groups with their info. class ttOrgImportHelper { var $errors = null; // Errors go here. Set in constructor by reference. - var $conflicting_entities = null; // A comma-separated list of entity names we cannot import. + var $schema_version = null; // Database schema version from XML file we import from. + var $conflicting_logins = null; // A comma-separated list of logins we cannot import. var $canImport = true; // False if we cannot import data due to a conflict such as login collision. var $firstPass = true; // True during first pass through the file. var $org_id = null; // Organization id (same as top group_id). var $current_group_id = null; // Current group id during parsing. var $current_parent_group_id = null; // Current parent group id during parsing. - // Set when we create a new group. var $top_role_id = 0; // Top role id. // Entity maps for current group. They map XML ids with database ids. @@ -67,17 +66,34 @@ class ttOrgImportHelper { $this->top_role_id = ttRoleHelper::getRoleByRank(512, 0); } - // startElement - callback handler for opening tag of an XML element in the file. + // startElement - callback handler for opening tags in XML. function startElement($parser, $name, $attrs) { global $i18n; - // First pass. We only check user logins for potential collisions with existing. + // First pass through the file determines if we can import data. + // We require 2 things: + // 1) Database schema version must be set. This ensures we have a compatible file. + // 2) No login coillisions are allowed. if ($this->firstPass) { + if ($name == 'ORG' && $this->canImport) { + if ($attrs['SCHEMA'] == null) { + // We need (database) schema attribute to be available for import to work. + // Old Time Tracker export files don't have this. + // Current import code does not work with old format because we had to + // restructure data in export files for subgroup support. + $this->canImport = false; + $this->errors->add($i18n->get('error.format')); + return; + } + } + + // In first pass we check user logins for potential collisions with existing. if ($name == 'USER' && $this->canImport) { $login = $attrs['LOGIN']; if ('' != $attrs['STATUS'] && ttUserHelper::getUserByLogin($login)) { // We have a login collision. Append colliding login to a list of things we cannot import. - $this->conflicting_entities .= ($this->conflicting_entities ? ", $login" : $login); + $this->conflicting_logins .= ($this->conflicting_logins ? ", $login" : $login); + // The above is printed in error message with all found colliding logins. } } } @@ -120,6 +136,7 @@ class ttOrgImportHelper { } // Set parent group to create subgroups with this group as parent at next entry here. $this->current_parent_group_id = $this->current_group_id; + return; } if ($name == 'ROLES') { @@ -127,22 +144,24 @@ class ttOrgImportHelper { unset($this->currentGroupRoleMap); $this->currentGroupRoleMap = array(); // Role map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'ROLE') { // We get here when processing tags for the current group. $role_id = ttRoleHelper::insert(array( - 'group_id' => $this->current_group_id, - 'org_id' => $this->org_id, - 'name' => $attrs['NAME'], - 'description' => $attrs['DESCRIPTION'], - 'rank' => $attrs['RANK'], - 'rights' => $attrs['RIGHTS'], - 'status' => $attrs['STATUS'])); + 'group_id' => $this->current_group_id, + 'org_id' => $this->org_id, + 'name' => $attrs['NAME'], + 'description' => $attrs['DESCRIPTION'], + 'rank' => $attrs['RANK'], + 'rights' => $attrs['RIGHTS'], + 'status' => $attrs['STATUS'])); if ($role_id) { // Add a mapping. $this->currentGroupRoleMap[$attrs['ID']] = $role_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'TASKS') { @@ -150,6 +169,7 @@ class ttOrgImportHelper { unset($this->currentGroupTaskMap); $this->currentGroupTaskMap = array(); // Task map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'TASK') { @@ -164,6 +184,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupTaskMap[$attrs['ID']] = $task_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'PROJECTS') { @@ -171,6 +192,7 @@ class ttOrgImportHelper { unset($this->currentGroupProjectMap); $this->currentGroupProjectMap = array(); // Project map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'PROJECT') { @@ -192,6 +214,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupProjectMap[$attrs['ID']] = $project_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'CLIENTS') { @@ -199,15 +222,18 @@ class ttOrgImportHelper { unset($this->currentGroupClientMap); $this->currentGroupClientMap = array(); // Client map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'CLIENT') { // We get here when processing tags for the current group. // Prepare a list of project ids. - $projects = explode(',', $attrs['PROJECTS']); - foreach ($projects as $id) - $mapped_projects[] = $this->currentGroupProjectMap[$id]; + if ($attrs['PROJECTS']) { + $projects = explode(',', $attrs['PROJECTS']); + foreach ($projects as $id) + $mapped_projects[] = $this->currentGroupProjectMap[$id]; + } $client_id = ttClientHelper::insert(array( 'group_id' => $this->current_group_id, @@ -221,6 +247,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupClientMap[$attrs['ID']] = $client_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'USERS') { @@ -228,6 +255,7 @@ class ttOrgImportHelper { unset($this->currentGroupUserMap); $this->currentGroupUserMap = array(); // User map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'USER') { @@ -250,6 +278,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupUserMap[$attrs['ID']] = $user_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'USER_PROJECT_BIND') { @@ -262,6 +291,7 @@ class ttOrgImportHelper { 'status' => $attrs['STATUS']))) { $this->errors->add($i18n->get('error.db')); } + return; } if ($name == 'INVOICES') { @@ -269,6 +299,7 @@ class ttOrgImportHelper { unset($this->currentGroupInvoiceMap); $this->currentGroupInvoiceMap = array(); // Invoice map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'INVOICE') { @@ -284,6 +315,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupInvoiceMap[$attrs['ID']] = $invoice_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'LOG') { @@ -291,6 +323,7 @@ class ttOrgImportHelper { unset($this->currentGroupLogMap); $this->currentGroupLogMap = array(); // Log map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'LOG_ITEM') { @@ -315,6 +348,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupLogMap[$attrs['ID']] = $log_item_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'CUSTOM_FIELDS') { @@ -322,6 +356,7 @@ class ttOrgImportHelper { unset($this->currentGroupCustomFieldMap); $this->currentGroupCustomFieldMap = array(); // Custom field map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'CUSTOM_FIELD') { @@ -337,6 +372,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupCustomFieldMap[$attrs['ID']] = $custom_field_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'CUSTOM_FIELD_OPTIONS') { @@ -344,6 +380,7 @@ class ttOrgImportHelper { unset($this->currentGroupCustomFieldOptionMap); $this->currentGroupCustomFieldOptionMap = array(); // Custom field option map is reconstructed after processing elements in XML. See below. + return; } if ($name == 'CUSTOM_FIELD_OPTION') { @@ -357,6 +394,7 @@ class ttOrgImportHelper { // Add a mapping. $this->currentGroupCustomFieldOptionMap[$attrs['ID']] = $custom_field_option_id; } else $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'CUSTOM_FIELD_LOG_ENTRY') { @@ -371,6 +409,7 @@ class ttOrgImportHelper { 'status' => $attrs['STATUS']))) { $this->errors->add($i18n->get('error.db')); } + return; } if ($name == 'EXPENSE_ITEM') { @@ -388,6 +427,7 @@ class ttOrgImportHelper { 'paid' => $attrs['PAID'], 'status' => $attrs['STATUS'])); if (!$expense_item_id) $this->errors->add($i18n->get('error.db')); + return; } if ($name == 'MONTHLY_QUOTA') { @@ -398,6 +438,7 @@ class ttOrgImportHelper { $attrs['MINUTES'])) { $this->errors->add($i18n->get('error.db')); } + return; } if ($name == 'FAV_REPORT') { @@ -437,6 +478,7 @@ class ttOrgImportHelper { 'group_by3' => $attrs['GROUP_BY3'], 'chtotalsonly' => (int) $attrs['SHOW_TOTALS_ONLY'])); if (!$fav_report_id) $this->errors->add($i18n->get('error.db')); + return; } } } @@ -478,13 +520,10 @@ class ttOrgImportHelper { xml_set_element_handler($parser, 'startElement', false); // We need to parse the file 2 times: - // 1) First pass: determine if import is possible - there must be no login collisions. - // 2) Second pass: if we can import, then do import in a second pass. - // This is different from earlier approach for single group import, where we could - // do both things in one pass because user info was in the beginning of XML file. - // Now, with subgroups, users can be located anywhere in the file. + // 1) First pass: determine if import is possible. + // 2) Second pass: import data, one tag at a time. - // Read and parse the content of the file. During parsing, startElement, endElement, and dataElement functions are called. + // Read and parse the content of the file. During parsing, startElement is called back for each tag. $file = fopen($filename, 'r'); while ($data = fread($file, 4096)) { if (!xml_parse($parser, $data, feof($file))) { @@ -493,10 +532,10 @@ class ttOrgImportHelper { xml_error_string(xml_get_error_code($parser)))); } } - if ($this->conflicting_entities) { + if ($this->conflicting_logins) { $this->canImport = false; $this->errors->add($i18n->get('error.user_exists')); - $this->errors->add(sprintf($i18n->get('error.cannot_import'), $this->conflicting_entities)); + $this->errors->add(sprintf($i18n->get('error.cannot_import'), $this->conflicting_logins)); } $this->firstPass = false; // We are done with 1st pass. @@ -513,7 +552,7 @@ class ttOrgImportHelper { xml_set_object($parser, $this); xml_set_element_handler($parser, 'startElement', false); - // Read and parse the content of the file. During parsing, startElement, endElement, and dataElement functions are called. + // Read and parse the content of the file. During parsing, startElement is called back for each tag. $file = fopen($filename, 'r'); while ($data = fread($file, 4096)) { if (!xml_parse($parser, $data, feof($file))) {