X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=WEB-INF%2Flib%2FttOrgImportHelper.class.php;h=f8dbc523b20b1283cbcac058c16b39eed5f6c93d;hb=d79a4bb229f2ce666fdb8d663e51e55e39a91827;hp=f3a2d8eaa49b9dd6393d910e38718b797e74a524;hpb=fb57263a487993f368478238d3f65d3495349b22;p=timetracker.git diff --git a/WEB-INF/lib/ttOrgImportHelper.class.php b/WEB-INF/lib/ttOrgImportHelper.class.php index f3a2d8ea..f8dbc523 100644 --- a/WEB-INF/lib/ttOrgImportHelper.class.php +++ b/WEB-INF/lib/ttOrgImportHelper.class.php @@ -26,39 +26,31 @@ // | https://www.anuko.com/time_tracker/credits.htm // +----------------------------------------------------------------------+ -import('ttTeamHelper'); import('ttUserHelper'); -import('ttProjectHelper'); -import('ttTaskHelper'); -import('ttInvoiceHelper'); -import('ttTimeHelper'); -import('ttClientHelper'); -import('ttCustomFieldHelper'); -import('ttFavReportHelper'); -import('ttExpenseHelper'); import('ttRoleHelper'); +import('ttTaskHelper'); +import('ttProjectHelper'); // 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. class ttOrgImportHelper { - var $errors = null; // Errors go here. Set in constructor by reference. - - var $currentElement = array(); // Current element of the XML file we are parsing. - var $currentTag = ''; // XML tag of the current element. - - var $canImport = true; // False if we cannot import data due to a login collision. + 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 $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_parent_group_id = null; // Current parent group id as we parse the file. + 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. - // Entities for current group. - var $currentGroupRoles = array(); // Array of arrays of role properties. + // Entities for current group. -- Looks like they are not needed as we insert right away... + // var $currentGroupRoles = array(); // Array of arrays of role properties. // var $currentGroupUsers = array(); // Array of arrays of user properties. // Entity maps for current group. They map XML ids with database ids. - var $currentGroupRoleMap = array(); // Maps role ids from XML to their database ids. + var $currentGroupRoleMap = array(); + var $currentGroupTaskMap = array(); + var $currentGroupProjectMap = array(); //var $userMap = array(); // User ids. //var $projectMap = array(); // Project ids. //var $taskMap = array(); // Task ids. @@ -70,113 +62,118 @@ class ttOrgImportHelper { $this->errors = &$errors; } - // startElement - callback handler for opening tag of an XML element. - // In this function we assign passed in attributes to currentElement. + // startElement - callback handler for opening tag of an XML element in the file. function startElement($parser, $name, $attrs) { -/* - if ($name == 'GROUP' - || $name == 'USER') { - $this->currentElement = $attrs; - } - $this->currentTag = $name; -*/ + global $i18n; + // First pass. We only check user logins for potential collisions with existing. if ($this->firstPass) { if ($name == 'USER' && $this->canImport) { - if ('' != $attrs['STATUS'] && ttUserHelper::getUserByLogin($attrs['LOGIN'])) { - // We have a login collision, cannot import any data. - $this->canImport = false; + $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->currentTag = ''; } // Second pass processing. We import data here, one tag at a time. - if (!$this->firstPass && $this->canImport) { + if (!$this->firstPass && $this->canImport && $this->errors->no()) { $mdb2 = getConnection(); - // We are in second pass through the XML file and can import data. + // We are in second pass and can import data. if ($name == 'GROUP') { // Create a new group. - $group_id = $this->createGroup(array( + $this->current_group_id = $this->createGroup(array( 'parent_id' => $this->current_parent_group_id, 'org_id' => $this->org_id, - 'name' => $this->currentElement['NAME'], - 'currency' => $this->currentElement['CURRENCY'], - 'lang' => $this->currentElement['LANG'])); - // We only have 3 properties in export at the moment, while work is ongoing... + 'name' => $attrs['NAME'], + 'currency' => $attrs['CURRENCY'], + 'lang' => $attrs['LANG'])); + // We only have 3 properties at the moment, while work is ongoing... // Special handling for top group. - if (!$this->org_id) { - $this->org_id = $group_id; - $sql = "update tt_groups set org_id = $group_id where org_id is NULL and id = $group_id"; + if (!$this->org_id && $this->current_group_id) { + $this->org_id = $this->current_group_id; + $sql = "update tt_groups set org_id = $this->current_group_id where org_id is NULL and id = $this->current_group_id"; $affected = $mdb2->exec($sql); - // TODO: design a better error handling approach for the entire import process. } - // Set current parent group. - $this->current_parent_group_id = $group_id; + // Set parent group to create subgroups with this group as parent at next entry here. + $this->current_parent_group_id = $this->current_group_id; } if ($name == 'ROLES') { - // If we get here, we have to recycle both $currentGroupRoles and $currentGroupRoleMap. - unset($this->currentGroupRoles); + // If we get here, we have to recycle $currentGroupRoleMap. unset($this->currentGroupRoleMap); - $this->currentGroupRoles = array(); $this->currentGroupRoleMap = array(); - // Both arrays are now empty. - // They will get reconstructed after processing of elements in XML. See below. + // Role map is reconstructed after processing elements in XML. See below. } if ($name == 'ROLE') { - // We get here when processing a tag for the current group. - // Add new role to $this->currentGroupRoles and a mapping to $this->currentGroupRoleMap. - $this->currentGroupRoles[$this->currentElement['ID']] = $this->currentElement; - $this->currentElement = array(); + // 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'])); + if ($role_id) { + // Add a mapping. + $this->currentGroupRoleMap[$attrs['ID']] = $role_id; + } else $this->errors->add($i18n->get('error.db')); } - } - } - // endElement - callback handler for the closing tag of an XML element. - // When we are here, currentElement is an array of the element attributes (as set in startElement). - // Here we do the actual import of data into the database. - function endElement($parser, $name) { - // Do nothing here. Everything is done in startElement to keep things simple. - /* - // During first pass we only check user logins. - if ($this->firstPass) { - if ($name == 'USER' && $this->canImport) { - if ('' != $this->currentElement['STATUS'] && ttUserHelper::getUserByLogin($this->currentElement['LOGIN'])) { - // We have a login collision, cannot import any data. - $this->canImport = false; - } + if ($name == 'TASKS') { + // If we get here, we have to recycle $currentGroupTaskMap. + unset($this->currentGroupTaskMap); + $this->currentGroupTaskMap = array(); + // Task map is reconstructed after processing elements in XML. See below. } - $this->currentTag = ''; - } - // During second pass we import data. - if (!$this->firstPass && $this->canImport) { - // Nothing is done here, see startElement for second pass. - }*/ - } + if ($name == 'TASK') { + // We get here when processing tags for the current group. + $task_id = ttTaskHelper::insert(array( + 'group_id' => $this->current_group_id, + 'org_id' => $this->org_id, + 'name' => $attrs['NAME'], + 'description' => $attrs['DESCRIPTION'], + 'status' => $attrs['STATUS'])); + if ($task_id) { + // Add a mapping. + $this->currentGroupTaskMap[$attrs['ID']] = $task_id; + } else $this->errors->add($i18n->get('error.db')); + } + + if ($name == 'PROJECTS') { + // If we get here, we have to recycle $currentGroupProjectMap. + unset($this->currentGroupProjectMap); + $this->currentGroupProjectMap = array(); + // Project map is reconstructed after processing elements in XML. See below. + } + + if ($name == 'PROJECT') { + // We get here when processing tags for the current group. + + // Prepare a list of task ids. + $tasks = explode(',', $attrs['TASKS']); + foreach ($tasks as $id) + $mapped_tasks[] = $this->currentGroupTaskMap[$id]; - // dataElement - callback handler for text data fragments. It builds up currentElement array with text pieces from XML. - function dataElement($parser, $data) { - // New approach is to do nothing here. Everything is now done when processing start tag (startElement). - /* - if ($this->currentTag == 'NAME' - || $this->currentTag == 'DESCRIPTION' - || $this->currentTag == 'LABEL' - || $this->currentTag == 'VALUE' - || $this->currentTag == 'COMMENT' - || $this->currentTag == 'ADDRESS' - || $this->currentTag == 'ALLOW_IP' - || $this->currentTag == 'PASSWORD_COMPLEXITY') { - if (isset($this->currentElement[$this->currentTag])) - $this->currentElement[$this->currentTag] .= trim($data); - else - $this->currentElement[$this->currentTag] = trim($data); + $project_id = ttProjectHelper::insert(array( + 'group_id' => $this->current_group_id, + 'org_id' => $this->org_id, + 'name' => $attrs['NAME'], + 'description' => $attrs['DESCRIPTION'], + 'tasks' => $mapped_tasks, + 'status' => $attrs['STATUS'])); + if ($project_id) { + // Add a mapping. + $this->currentGroupProjectMap[$attrs['ID']] = $project_id; + } else $this->errors->add($i18n->get('error.db')); + } } - * */ } // importXml - uncompresses the file, reads and parses its content. During parsing, @@ -213,8 +210,7 @@ class ttOrgImportHelper { // Initialize XML parser. $parser = xml_parser_create(); xml_set_object($parser, $this); - xml_set_element_handler($parser, 'startElement', 'endElement'); - xml_set_character_data_handler($parser, 'dataElement'); + 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. @@ -227,15 +223,17 @@ class ttOrgImportHelper { $file = fopen($filename, 'r'); while ($data = fread($file, 4096)) { if (!xml_parse($parser, $data, feof($file))) { - $this->errors->add(sprintf("XML error: %s at line %d", - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser))); - } - if (!$this->canImport) { - $this->errors->add($i18n->get('error.user_exists')); - break; + $this->errors->add(sprintf($i18n->get('error.xml'), + xml_get_current_line_number($parser), + xml_error_string(xml_get_error_code($parser)))); } } + if ($this->conflicting_entities) { + $this->canImport = false; + $this->errors->add($i18n->get('error.user_exists')); + $this->errors->add(sprintf($i18n->get('error.cannot_import'), $this->conflicting_entities)); + } + $this->firstPass = false; // We are done with 1st pass. xml_parser_free($parser); if ($file) fclose($file); @@ -243,20 +241,20 @@ class ttOrgImportHelper { unlink($filename); return; } + if ($this->errors->yes()) return; // Exit if we have errors. // Now we can do a second pass, where real work is done. $parser = xml_parser_create(); xml_set_object($parser, $this); - xml_set_element_handler($parser, 'startElement', 'endElement'); - xml_set_character_data_handler($parser, 'dataElement'); + xml_set_element_handler($parser, 'startElement', false); // Read and parse the content of the file. During parsing, startElement, endElement, and dataElement functions are called. $file = fopen($filename, 'r'); while ($data = fread($file, 4096)) { if (!xml_parse($parser, $data, feof($file))) { - $this->errors->add(sprintf("XML error: %s at line %d", - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser))); + $this->errors->add(sprintf($i18n->get('error.xml'), + xml_get_current_line_number($parser), + xml_error_string(xml_get_error_code($parser)))); } } xml_parser_free($parser); @@ -293,7 +291,7 @@ class ttOrgImportHelper { // createGroup function creates a new group. private function createGroup($fields) { - global $user; + global $i18n; $mdb2 = getConnection(); $columns = '(parent_id, org_id, name, currency, lang)'; @@ -329,7 +327,10 @@ class ttOrgImportHelper { $sql = 'insert into tt_groups '.$columns.$values; $affected = $mdb2->exec($sql); - if (is_a($affected, 'PEAR_Error')) return false; + if (is_a($affected, 'PEAR_Error')) { + $this->errors->add($i18n->get('error.db')); + return false; + } $group_id = $mdb2->lastInsertID('tt_groups', 'id'); return $group_id;