/install-dev/classes/xmlLoader.php
PHP | 1277 lines | 947 code | 170 blank | 160 comment | 179 complexity | c692d6854961711855f2ee12589f56c7 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0
- <?php
- /*
- * 2007-2012 PrestaShop
- *
- * NOTICE OF LICENSE
- *
- * This source file is subject to the Open Software License (OSL 3.0)
- * that is bundled with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://opensource.org/licenses/osl-3.0.php
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to license@prestashop.com so we can send you a copy immediately.
- *
- * DISCLAIMER
- *
- * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
- * versions in the future. If you wish to customize PrestaShop for your
- * needs please refer to http://www.prestashop.com for more information.
- *
- * @author PrestaShop SA <contact@prestashop.com>
- * @copyright 2007-2012 PrestaShop SA
- * @version Release: $Revision$
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
- * International Registered Trademark & Property of PrestaShop SA
- */
- class InstallXmlLoader
- {
- /**
- * @var InstallLanguages
- */
- protected $language;
- /**
- * @var array List of languages stored as array(id_lang => iso)
- */
- protected $languages = array();
- /**
- * @var array Store in cache all loaded XML files
- */
- protected $cache_xml_entity = array();
- /**
- * @var array List of errors
- */
- protected $errors = array();
- protected $data_path;
- protected $lang_path;
- protected $img_path;
- public $path_type;
- protected $ids = array();
- protected $primaries = array();
- protected $delayed_inserts = array();
- public function __construct()
- {
- $this->language = InstallLanguages::getInstance();
- $this->setDefaultPath();
- require_once _PS_ROOT_DIR_.'/images.inc.php';
- }
- /**
- * Set list of installed languages
- *
- * @param array $languages array(id_lang => iso)
- */
- public function setLanguages(array $languages)
- {
- $this->languages = $languages;
- }
- public function setDefaultPath()
- {
- $this->path_type = 'common';
- $this->data_path = _PS_INSTALL_DATA_PATH_.'xml/';
- $this->lang_path = _PS_INSTALL_LANGS_PATH_;
- $this->img_path = _PS_INSTALL_DATA_PATH_.'img/';
- }
- public function setFixturesPath()
- {
- $this->path_type = 'fixture';
- $this->data_path = _PS_INSTALL_FIXTURES_PATH_.'apple/data/';
- $this->lang_path = _PS_INSTALL_FIXTURES_PATH_.'apple/langs/';
- $this->img_path = _PS_INSTALL_FIXTURES_PATH_.'apple/img/';
- }
- /**
- * Get list of errors
- *
- * @return array
- */
- public function getErrors()
- {
- return $this->errors;
- }
- /**
- * Add an error
- *
- * @param string $error
- */
- public function setError($error)
- {
- $this->errors[] = $error;
- }
- /**
- * Store an ID related to an entity and its identifier (E.g. we want to save that product with ID "ipod_nano" has the ID 1)
- *
- * @param string $entity
- * @param string $identifier
- * @param int $id
- */
- public function storeId($entity, $identifier, $id)
- {
- $this->ids[$entity.':'.$identifier] = $id;
- }
- /**
- * Retrieve an ID related to an entity and its identifier
- *
- * @param string $entity
- * @param string $identifier
- */
- public function retrieveId($entity, $identifier)
- {
- return isset($this->ids[$entity.':'.$identifier]) ? $this->ids[$entity.':'.$identifier] : 0;
- }
- public function getIds()
- {
- return $this->ids;
- }
- public function setIds($ids)
- {
- $this->ids = $ids;
- }
- public function getSortedEntities()
- {
- // Browse all XML files from data/xml directory
- $entities = array();
- $dependencies = array();
- $fd = opendir($this->data_path);
- while ($file = readdir($fd))
- if (preg_match('#^(.+)\.xml$#', $file, $m))
- {
- $entity = $m[1];
- $xml = $this->loadEntity($entity);
- // Store entities dependencies (with field type="relation")
- if ($xml->fields)
- {
- foreach ($xml->fields->field as $field)
- {
- if ($field['relation'] && $field['relation'] != $entity)
- {
- if (!isset($dependencies[(string)$field['relation']]))
- $dependencies[(string)$field['relation']] = array();
- $dependencies[(string)$field['relation']][] = $entity;
- }
- }
- }
- $entities[] = $entity;
- }
- closedir($fd);
- // Sort entities to populate database in good order (E.g. zones before countries)
- do
- {
- $current = (isset($sort_entities)) ? $sort_entities : array();
- $sort_entities = array();
- foreach ($entities as $key => $entity)
- {
- if (isset($dependencies[$entity]))
- {
- $min = count($entities) - 1;
- foreach ($dependencies[$entity] as $item)
- if (($key = array_search($item, $sort_entities)) !== false)
- $min = min($min, $key);
- if ($min == 0)
- array_unshift($sort_entities, $entity);
- else
- array_splice($sort_entities, $min, 0, array($entity));
- }
- else
- $sort_entities[] = $entity;
- }
- $entities = $sort_entities;
- }
- while ($current != $sort_entities);
- return $sort_entities;
- }
- /**
- * Read all XML files from data folder and populate tables
- */
- public function populateFromXmlFiles()
- {
- $entities = $this->getSortedEntities();
- // Populate entities
- foreach ($entities as $entity)
- $this->populateEntity($entity);
- }
- /**
- * Populate an entity
- *
- * @param string $entity
- */
- public function populateEntity($entity)
- {
- if (method_exists($this, 'populateEntity'.Tools::toCamelCase($entity)))
- {
- $this->{'populateEntity'.Tools::toCamelCase($entity)}();
- return;
- }
- $xml = $this->loadEntity($entity);
- // Read list of fields
- if (!$xml->fields)
- throw new PrestashopInstallerException('List of fields not found for entity '.$entity);
- if ($this->isMultilang($entity))
- {
- $multilang_columns = $this->getColumns($entity, true);
- $xml_langs = array();
- $default_lang = null;
- foreach ($this->languages as $id_lang => $iso)
- {
- if ($iso == 'en')
- $default_lang = $id_lang;
- try
- {
- $xml_langs[$id_lang] = $this->loadEntity($entity, $iso);
- }
- catch (PrestashopInstallerException $e)
- {
- $xml_langs[$id_lang] = null;
- }
- }
- }
- // Load all row for current entity and prepare data to be populated
- foreach ($xml->entities->$entity as $node)
- {
- $data = array();
- $identifier = (string)$node['id'];
- // Read attributes
- foreach ($node->attributes() as $k => $v)
- if ($k != 'id')
- $data[$k] = (string)$v;
- // Read cdatas
- foreach ($node->children() as $child)
- $data[$child->getName()] = (string)$child;
- // Load multilang data
- $data_lang = array();
- if ($this->isMultilang($entity))
- {
- $xpath_query = $entity.'[@id="'.$identifier.'"]';
- foreach ($xml_langs as $id_lang => $xml_lang)
- {
- if (!$xml_lang)
- continue;
- if (($node_lang = $xml_lang->xpath($xpath_query)) || ($node_lang = $xml_langs[$default_lang]->xpath($xpath_query)))
- {
- $node_lang = $node_lang[0];
- foreach ($multilang_columns as $column => $is_text)
- {
- $value = '';
- if ($node_lang[$column])
- $value = (string)$node_lang[$column];
- if ($node_lang->$column)
- $value = (string)$node_lang->$column;
- $data_lang[$column][$id_lang] = $value;
- }
- }
- }
- }
- $data = $this->rewriteRelationedData($entity, $data);
- if (method_exists($this, 'createEntity'.Tools::toCamelCase($entity)))
- {
- // Create entity with custom method in current class
- $method = 'createEntity'.Tools::toCamelCase($entity);
- $this->$method($identifier, $data, $data_lang);
- }
- else
- $this->createEntity($entity, $identifier, (string)$xml->fields['class'], $data, $data_lang);
- if ($xml->fields['image'])
- {
- if (method_exists($this, 'copyImages'.Tools::toCamelCase($entity)))
- $this->{'copyImages'.Tools::toCamelCase($entity)}($identifier, $data);
- else
- $this->copyImages($entity, $identifier, (string)$xml->fields['image'], $data);
- }
- }
- $this->flushDelayedInserts();
- unset($this->cache_xml_entity[$this->path_type][$entity]);
- }
- /**
- * Special case for "tag" entity
- */
- public function populateEntityTag()
- {
- foreach ($this->languages as $id_lang => $iso)
- {
- if (!file_exists($this->lang_path.$iso.'/data/tag.xml'))
- continue;
- $xml = $this->loadEntity('tag', $iso);
- $tags = array();
- foreach ($xml->tag as $tag_node)
- {
- $products = trim((string)$tag_node['products']);
- if (!$products)
- continue;
- foreach (explode(',', $products) as $product)
- {
- $product = trim($product);
- $product_id = $this->retrieveId('product', $product);
- if (!isset($tags[$product_id]))
- $tags[$product_id] = array();
- $tags[$product_id][] = trim((string)$tag_node['name']);
- }
- }
- foreach ($tags as $id_product => $tag_list)
- Tag::addTags($id_lang, $id_product, $tag_list);
- }
- }
- /**
- * Load an entity XML file
- *
- * @param string $entity
- * @return SimpleXMLElement
- */
- protected function loadEntity($entity, $iso = null)
- {
- if (!isset($this->cache_xml_entity[$this->path_type][$entity][$iso]))
- {
- $path = $this->data_path.$entity.'.xml';
- if ($iso)
- $path = $this->lang_path.$iso.'/data/'.$entity.'.xml';
- if (!file_exists($path))
- throw new PrestashopInstallerException('XML data file '.$entity.'.xml not found');
- $this->cache_xml_entity[$this->path_type][$entity][$iso] = @simplexml_load_file($path, 'InstallSimplexmlElement');
- if (!$this->cache_xml_entity[$this->path_type][$entity][$iso])
- throw new PrestashopInstallerException('XML data file '.$entity.'.xml invalid');
- }
- return $this->cache_xml_entity[$this->path_type][$entity][$iso];
- }
- /**
- * Check fields related to an other entity, and replace their values by the ID created by the other entity
- *
- * @param string $entity
- * @param array $data
- */
- protected function rewriteRelationedData($entity, array $data)
- {
- $xml = $this->loadEntity($entity);
- foreach ($xml->fields->field as $field)
- if ($field['relation'])
- {
- $id = $this->retrieveId((string)$field['relation'], $data[(string)$field['name']]);
- if (!$id && $data[(string)$field['name']] && is_numeric($data[(string)$field['name']]))
- $id = $data[(string)$field['name']];
- $data[(string)$field['name']] = $id;
- }
- return $data;
- }
- public function flushDelayedInserts()
- {
- foreach ($this->delayed_inserts as $entity => $queries)
- {
- $type = Db::INSERT_IGNORE;
- if ($entity == 'access')
- $type = Db::REPLACE;
- if (!Db::getInstance()->insert($entity, $queries, false, true, $type))
- $this->setError($this->language->l('An SQL error occured for entity <i>%1$s</i>: <i>%2$s</i>', $entity, Db::getInstance()->getMsgError()));
- unset($this->delayed_inserts[$entity]);
- }
- }
- /**
- * Create a simple entity with all its data and lang data
- * If a methode createEntity$entity exists, use it. Else if $classname is given, use it. Else do a simple insert in database.
- *
- * @param string $entity
- * @param string $identifier
- * @param string $classname
- * @param array $data
- * @param array $data_lang
- */
- public function createEntity($entity, $identifier, $classname, array $data, array $data_lang = array())
- {
- $xml = $this->loadEntity($entity);
- if ($classname)
- {
- // Create entity with ObjectModel class
- $object = new $classname();
- $object->hydrate($data);
- if ($data_lang)
- $object->hydrate($data_lang);
- $object->add(true, (isset($xml->fields['null'])) ? true : false);
- $entity_id = $object->id;
- unset($object);
- }
- else
- {
- // Generate primary key manually
- $primary = '';
- $entity_id = 0;
- if (!$xml->fields['primary'])
- $primary = 'id_'.$entity;
- else if (strpos((string)$xml->fields['primary'], ',') === false)
- $primary = (string)$xml->fields['primary'];
- unset($xml);
- if ($primary)
- {
- $entity_id = $this->generatePrimary($entity, $primary);
- $data[$primary] = $entity_id;
- }
- // Store INSERT queries in order to optimize install with grouped inserts
- $this->delayed_inserts[$entity][] = array_map('pSQL', $data);
- if ($data_lang)
- {
- $real_data_lang = array();
- foreach ($data_lang as $field => $list)
- foreach ($list as $id_lang => $value)
- $real_data_lang[$id_lang][$field] = $value;
- foreach ($real_data_lang as $id_lang => $insert_data_lang)
- {
- $insert_data_lang['id_'.$entity] = $entity_id;
- $insert_data_lang['id_lang'] = $id_lang;
- $this->delayed_inserts[$entity.'_lang'][] = array_map('pSQL', $insert_data_lang);
- }
- // Store INSERT queries for _shop associations
- $entity_asso = Shop::getAssoTable($entity);
- if ($entity_asso !== false && $entity_asso['type'] == 'shop')
- $this->delayed_inserts[$entity.'_shop'][] = array(
- 'id_shop' => 1,
- 'id_'.$entity => $entity_id,
- );
- }
- }
- $this->storeId($entity, $identifier, $entity_id);
- }
- public function createEntityConfiguration($identifier, array $data, array $data_lang)
- {
- if (Db::getInstance()->getValue('SELECT id_configuration FROM '._DB_PREFIX_.'configuration WHERE name = \''.pSQL($data['name']).'\''))
- return;
- $entity = 'configuration';
- $entity_id = $this->generatePrimary($entity, 'id_configuration');
- $data['id_configuration'] = $entity_id;
- // Store INSERT queries in order to optimize install with grouped inserts
- $this->delayed_inserts[$entity][] = array_map('pSQL', $data);
- if ($data_lang)
- {
- $real_data_lang = array();
- foreach ($data_lang as $field => $list)
- foreach ($list as $id_lang => $value)
- $real_data_lang[$id_lang][$field] = $value;
- foreach ($real_data_lang as $id_lang => $insert_data_lang)
- {
- $insert_data_lang['id_'.$entity] = $entity_id;
- $insert_data_lang['id_lang'] = $id_lang;
- $this->delayed_inserts[$entity.'_lang'][] = array_map('pSQL', $insert_data_lang);
- }
- }
- $this->storeId($entity, $identifier, $entity_id);
- }
-
- public function createEntityStockAvailable($identifier, array $data, array $data_lang)
- {
- $stock_available = new StockAvailable();
- $stock_available->updateQuantity($data['id_product'], $data['id_product_attribute'], $data['quantity'], $data['id_shop']);
- }
- public function createEntityTab($identifier, array $data, array $data_lang)
- {
- static $position = array();
- $entity = 'tab';
- $xml = $this->loadEntity($entity);
- if (!isset($position[$data['id_parent']]))
- $position[$data['id_parent']] = 0;
- $data['position'] = $position[$data['id_parent']]++;
- // Generate primary key manually
- $primary = '';
- $entity_id = 0;
- if (!$xml->fields['primary'])
- $primary = 'id_'.$entity;
- else if (strpos((string)$xml->fields['primary'], ',') === false)
- $primary = (string)$xml->fields['primary'];
- if ($primary)
- {
- $entity_id = $this->generatePrimary($entity, $primary);
- $data[$primary] = $entity_id;
- }
- // Store INSERT queries in order to optimize install with grouped inserts
- $this->delayed_inserts[$entity][] = array_map('pSQL', $data);
- if ($data_lang)
- {
- $real_data_lang = array();
- foreach ($data_lang as $field => $list)
- foreach ($list as $id_lang => $value)
- $real_data_lang[$id_lang][$field] = $value;
- foreach ($real_data_lang as $id_lang => $insert_data_lang)
- {
- $insert_data_lang['id_'.$entity] = $entity_id;
- $insert_data_lang['id_lang'] = $id_lang;
- $this->delayed_inserts[$entity.'_lang'][] = array_map('pSQL', $insert_data_lang);
- }
- }
- $this->storeId($entity, $identifier, $entity_id);
- }
- public function generatePrimary($entity, $primary)
- {
- if (!isset($this->primaries[$entity]))
- $this->primaries[$entity] = (int)Db::getInstance()->getValue('SELECT '.$primary.' FROM '._DB_PREFIX_.$entity.' ORDER BY '.$primary.' DESC');
- return ++$this->primaries[$entity];
- }
- public function copyImages($entity, $identifier, $path, array $data, $extension = 'jpg')
- {
- // Get list of image types
- $reference = array(
- 'product' => 'products',
- 'category' => 'categories',
- 'manufacturer' => 'manufacturers',
- 'supplier' => 'suppliers',
- 'scene' => 'scenes',
- 'store' => 'stores',
- );
- $types = array();
- if (isset($reference[$entity]))
- $types = ImageType::getImagesTypes($reference[$entity]);
- // For each path copy images
- $path = array_map('trim', explode(',', $path));
- foreach ($path as $p)
- {
- $from_path = $this->img_path.$p.'/';
- $dst_path = _PS_IMG_DIR_.$p.'/';
- $entity_id = $this->retrieveId($entity, $identifier);
- if (!copy($from_path.$identifier.'.'.$extension, $dst_path.$entity_id.'.'.$extension))
- {
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier, $entity));
- return;
- }
- foreach ($types as $type)
- {
- $origin_file = $from_path.$identifier.'-'.$type['name'].'.'.$extension;
- $target_file = $dst_path.$entity_id.'-'.$type['name'].'.'.$extension;
- // Test if dest folder is writable
- if (!is_writable(dirname($target_file)))
- $this->setError($this->language->l('Cannot create image "%1$s" (bad permissions on folder "%2$s")', $identifier.'-'.$type['name'], dirname($target_file)));
- // If a file named folder/entity-type.extension exists just copy it, this is an optimisation in order to prevent to much resize
- elseif (file_exists($origin_file))
- {
- if (!@copy($origin_file, $target_file))
- $this->setError($this->language->l('Cannot create image "%s"', $identifier.'-'.$type['name']));
- @chmod($target_file, 0644);
- }
- // Resize the image if no cache was prepared in fixtures
- elseif (!ImageManager::resize($from_path.$identifier.'.'.$extension, $target_file, $type['width'], $type['height']))
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier.'-'.$type['name'], $entity));
- }
- }
- Image::moveToNewFileSystem();
- }
- public function copyImagesScene($identifier, array $data)
- {
- $this->copyImages('scene', $identifier, 'scenes', $data);
- $from_path = $this->img_path.'scenes/thumbs/';
- $dst_path = _PS_IMG_DIR_.'scenes/thumbs/';
- $entity_id = $this->retrieveId('scene', $identifier);
- if (!@copy($from_path.$identifier.'-m_scene_default.jpg', $dst_path.$entity_id.'-m_scene_default.jpg'))
- {
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier, 'scene'));
- return;
- }
- }
- public function copyImagesOrderState($identifier, array $data)
- {
- $this->copyImages('order_state', $identifier, 'os', $data, 'gif');
- }
- public function copyImagesTab($identifier, array $data)
- {
- $from_path = $this->img_path.'t/';
- $dst_path = _PS_IMG_DIR_.'t/';
- if (file_exists($from_path.$data['class_name'].'.gif') && !file_exists($dst_path.$data['class_name'].'.gif'))//test if file exist in install dir and if do not exist in dest folder.
- if (!@copy($from_path.$data['class_name'].'.gif', $dst_path.$data['class_name'].'.gif'))
- {
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier, 'tab'));
- return;
- }
- }
- public function copyImagesImage($identifier)
- {
- $path = $this->img_path.'p/';
- $image = new Image($this->retrieveId('image', $identifier));
- $dst_path = $image->getPathForCreation();
- if (!@copy($path.$identifier.'.jpg', $dst_path.'.'.$image->image_format))
- {
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier, 'product'));
- return;
- }
- @chmod($dst_path.'.'.$image->image_format, 0644);
- $types = ImageType::getImagesTypes('products');
- foreach ($types as $type)
- {
- $origin_file = $path.$identifier.'-'.$type['name'].'.jpg';
- $target_file = $dst_path.'-'.$type['name'].'.'.$image->image_format;
- // Test if dest folder is writable
- if (!is_writable(dirname($target_file)))
- $this->setError($this->language->l('Cannot create image "%1$s" (bad permissions on folder "%2$s")', $identifier.'-'.$type['name'], dirname($target_file)));
- // If a file named folder/entity-type.jpg exists just copy it, this is an optimisation in order to prevent to much resize
- else if (file_exists($origin_file))
- {
- if (!@copy($origin_file, $target_file))
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier.'-'.$type['name'], 'product'));
- @chmod($target_file, 0644);
- }
- // Resize the image if no cache was prepared in fixtures
- else if (!ImageManager::resize($path.$identifier.'.jpg', $target_file, $type['width'], $type['height']))
- $this->setError($this->language->l('Cannot create image "%1$s" for entity "%2$s"', $identifier.'-'.$type['name'], 'product'));
- }
- }
- public function getTables()
- {
- static $tables = null;
- if (is_null($tables))
- {
- $sql = 'SHOW TABLES';
- $tables = array();
- foreach (Db::getInstance()->executeS($sql) as $row)
- {
- $table = current($row);
- if (preg_match('#^'._DB_PREFIX_.'(.+?)(_lang)?$#i', $table, $m))
- if (preg_match('#^'._DB_PREFIX_.'(.+?)_shop$#i', $table, $m2) && !isset($tables[$m2[1]]))
- $tables[$m[1]] = (isset($m[2]) && $m[2]) ? true : false;
- else
- $tables[$m[1]] = (isset($m[2]) && $m[2]) ? true : false;
- }
- }
- return $tables;
- }
- public function hasElements($table)
- {
- return (bool)Db::getInstance()->getValue('SELECT COUNT(*) FROM '._DB_PREFIX_.$table);
- }
- public function getColumns($table, $multilang = false, array $exclude = array())
- {
- static $columns = array();
- if ($multilang)
- return ($this->isMultilang($table)) ? $this->getColumns($table.'_lang', false, array('id_'.$table)) : array();
- if (!isset($columns[$table]))
- {
- $columns[$table] = array();
- $sql = 'SHOW COLUMNS FROM `'._DB_PREFIX_.bqSQL($table).'`';
- foreach (Db::getInstance()->executeS($sql) as $row)
- $columns[$table][$row['Field']] = $this->checkIfTypeIsText($row['Type']);
- }
- $exclude = array_merge(array('id_'.$table, 'date_add', 'date_upd', 'deleted', 'id_lang'), $exclude);
- $list = array();
- foreach ($columns[$table] as $k => $v)
- if (!in_array($k, $exclude))
- $list[$k] = $v;
- return $list;
- }
- public function getClasses($path = null)
- {
- static $cache = null;
- if (!is_null($cache))
- return $cache;
- $dir = $path;
- if (is_null($dir))
- $dir = _PS_CLASS_DIR_;
- $classes = array();
- foreach (scandir($dir) as $file)
- if ($file[0] != '.' && $file != 'index.php')
- {
- if (is_dir($dir.$file))
- $classes = array_merge($classes, $this->getClasses($dir.$file.'/'));
- else if (preg_match('#^(.+)\.php$#', $file, $m))
- $classes[] = $m[1];
- }
- sort($classes);
- if (is_null($path))
- $cache = $classes;
- return $classes;
- }
- public function checkIfTypeIsText($type)
- {
- if (preg_match('#^(longtext|text|tinytext)#i', $type))
- return true;
- if (preg_match('#^varchar\(([0-9]+)\)$#i', $type, $m))
- return intval($m[1]) >= 64 ? true : false;
- return false;
- }
- public function isMultilang($entity)
- {
- $tables = $this->getTables();
- return isset($tables[$entity]) && $tables[$entity];
- }
- public function entityExists($entity)
- {
- return file_exists($this->data_path.$entity.'.xml');
- }
- public function getEntitiesList()
- {
- $entities = array();
- foreach (scandir($this->data_path) as $file)
- if ($file[0] != '.' && preg_match('#^(.+)\.xml$#', $file, $m))
- $entities[] = $m[1];
- return $entities;
- }
- public function getEntityInfo($entity)
- {
- $info = array(
- 'config' => array(
- 'id' => '',
- 'primary' => '',
- 'class' => '',
- 'sql' => '',
- 'ordersql' => '',
- 'image' => '',
- 'null' => '',
- ),
- 'fields' => array(),
- );
- if (!$this->entityExists($entity))
- return $info;
- $xml = @simplexml_load_file($this->data_path.$entity.'.xml', 'InstallSimplexmlElement');
- if (!$xml)
- return $info;
- if ($xml->fields['id'])
- $info['config']['id'] = (string)$xml->fields['id'];
- if ($xml->fields['primary'])
- $info['config']['primary'] = (string)$xml->fields['primary'];
- if ($xml->fields['class'])
- $info['config']['class'] = (string)$xml->fields['class'];
- if ($xml->fields['sql'])
- $info['config']['sql'] = (string)$xml->fields['sql'];
- if ($xml->fields['ordersql'])
- $info['config']['ordersql'] = (string)$xml->fields['ordersql'];
- if ($xml->fields['null'])
- $info['config']['null'] = (string)$xml->fields['null'];
- if ($xml->fields['image'])
- $info['config']['image'] = (string)$xml->fields['image'];
- foreach ($xml->fields->field as $field)
- {
- $column = (string)$field['name'];
- $info['fields'][$column] = array();
- if (isset($field['relation']))
- $info['fields'][$column]['relation'] = (string)$field['relation'];
- }
- return $info;
- }
- public function getDependencies()
- {
- $entities = array();
- foreach ($this->getEntitiesList() as $entity)
- $entities[$entity] = $this->getEntityInfo($entity);
- $dependencies = array();
- foreach ($entities as $entity => $info)
- {
- foreach ($info['fields'] as $field => $info_field)
- {
- if (isset($info_field['relation']) && $info_field['relation'] != $entity)
- {
- if (!isset($dependencies[$info_field['relation']]))
- $dependencies[$info_field['relation']] = array();
- $dependencies[$info_field['relation']][] = $entity;
- }
- }
- }
- return $dependencies;
- }
- public function generateEntitySchema($entity, array $fields, array $config)
- {
- if ($this->entityExists($entity))
- $xml = $this->loadEntity($entity);
- else
- $xml = new InstallSimplexmlElement('<entity_'.$entity.' />');
- unset($xml->fields);
- // Fill <fields> attributes (config)
- $xml_fields = $xml->addChild('fields');
- foreach ($config as $k => $v)
- if ($v)
- $xml_fields[$k] = $v;
- // Create list of fields
- foreach ($fields as $column => $info)
- {
- $field = $xml_fields->addChild('field');
- $field['name'] = $column;
- if (isset($info['relation']))
- $field['relation'] = $info['relation'];
- }
- // Recreate entities nodes, in order to have the <entities> node after the <fields> node
- $store_entities = clone $xml->entities;
- unset($xml->entities);
- $xml->addChild('entities', $store_entities);
- $xml->asXML($this->data_path.$entity.'.xml');
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function generateAllEntityFiles()
- {
- $entities = array();
- foreach ($this->getEntitiesList() as $entity)
- $entities[$entity] = $this->getEntityInfo($entity);
- $this->generateEntityFiles($entities);
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function generateEntityFiles($entities)
- {
- $dependencies = $this->getDependencies();
- // Sort entities to populate database in good order (E.g. zones before countries)
- do
- {
- $current = (isset($sort_entities)) ? $sort_entities : array();
- $sort_entities = array();
- foreach ($entities as $entity)
- {
- if (isset($dependencies[$entity]))
- {
- $min = count($entities) - 1;
- foreach ($dependencies[$entity] as $item)
- if (($key = array_search($item, $sort_entities)) !== false)
- $min = min($min, $key);
- if ($min == 0)
- array_unshift($sort_entities, $entity);
- else
- array_splice($sort_entities, $min, 0, array($entity));
- }
- else
- $sort_entities[] = $entity;
- }
- $entities = $sort_entities;
- }
- while ($current != $sort_entities);
- foreach ($sort_entities as $entity)
- $this->generateEntityContent($entity);
- }
- public function generateEntityContent($entity)
- {
- $xml = $this->loadEntity($entity);
- if (method_exists($this, 'getEntityContents'.Tools::toCamelCase($entity)))
- $content = $this->{'getEntityContents'.Tools::toCamelCase($entity)}($entity);
- else
- $content = $this->getEntityContents($entity);
- unset($xml->entities);
- $entities = $xml->addChild('entities');
- $this->createXmlEntityNodes($entity, $content['nodes'], $entities);
- $xml->asXML($this->data_path.$entity.'.xml');
- // Generate multilang XML files
- if ($content['nodes_lang'])
- foreach ($content['nodes_lang'] as $id_lang => $nodes)
- {
- if (!isset($this->languages[$id_lang]))
- continue;
- $iso = $this->languages[$id_lang];
- if (!is_dir($this->lang_path.$iso.'/data'))
- mkdir($this->lang_path.$iso.'/data');
- $xml_node = new InstallSimplexmlElement('<entity_'.$entity.' />');
- $this->createXmlEntityNodes($entity, $nodes, $xml_node);
- $xml_node->asXML($this->lang_path.$iso.'/data/'.$entity.'.xml');
- }
- if ($xml->fields['image'])
- {
- if (method_exists($this, 'backupImage'.Tools::toCamelCase($entity)))
- $this->{'backupImage'.Tools::toCamelCase($entity)}((string)$xml->fields['image']);
- else
- $this->backupImage($entity, (string)$xml->fields['image']);
- }
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function getEntityContents($entity)
- {
- $xml = $this->loadEntity($entity);
- $primary = (isset($xml->fields['primary']) && $xml->fields['primary']) ? (string)$xml->fields['primary'] : 'id_'.$entity;
- $is_multilang = $this->isMultilang($entity);
- // Check if current table is an association table (if multiple primary keys)
- $is_association = false;
- if (strpos($primary, ',') !== false)
- {
- $is_association = true;
- $primary = array_map('trim', explode(',', $primary));
- }
- // Build query
- $sql = new DbQuery();
- $sql->select('a.*');
- $sql->from($entity, 'a');
- if ($is_multilang)
- {
- $sql->select('b.*');
- $sql->leftJoin($entity.'_lang', 'b', 'a.'.$primary.' = b.'.$primary);
- }
- if (isset($xml->fields['sql']) && $xml->fields['sql'])
- $sql->where((string)$xml->fields['sql']);
- if (!$is_association)
- {
- $sql->select('a.'.$primary);
- if (!isset($xml->fields['ordersql']) || !$xml->fields['ordersql'])
- $sql->orderBy('a.'.$primary);
- }
- if ($is_multilang && (!isset($xml->fields['ordersql']) || !$xml->fields['ordersql']))
- $sql->orderBy('b.id_lang');
- if (isset($xml->fields['ordersql']) && $xml->fields['ordersql'])
- $sql->orderBy((string)$xml->fields['ordersql']);
- // Get multilang columns
- $alias_multilang = array();
- if ($is_multilang)
- {
- $columns = $this->getColumns($entity);
- $multilang_columns = $this->getColumns($entity, true);
- // If some columns from _lang table have same name than original table, rename them (E.g. value in configuration)
- foreach ($multilang_columns as $c => $is_text)
- if (isset($columns[$c]))
- {
- $alias = $c.'_alias';
- $alias_multilang[$c] = $alias;
- $sql->select('a.'.$c.' as '.$c.', b.'.$c.' as '.$alias);
- }
- }
- // Get all results
- $nodes = $nodes_lang = array();
- $results = Db::getInstance()->executeS($sql);
- if (Db::getInstance()->getNumberError())
- $this->setError($this->language->l('SQL error on query <i>%s</i>', $sql));
- else
- {
- foreach ($results as $row)
- {
- // Store common columns
- if ($is_association)
- {
- $id = $entity;
- foreach ($primary as $key)
- $id .= '_'.$row[$key];
- }
- else
- $id = $this->generateId($entity, $row[$primary], $row, (isset($xml->fields['id']) && $xml->fields['id']) ? (string)$xml->fields['id'] : null);
- if (!isset($nodes[$id]))
- {
- $node = array();
- foreach ($xml->fields->field as $field)
- {
- $column = (string)$field['name'];
- if (isset($field['relation']))
- {
- $sql = 'SELECT `id_'.bqSQL($field['relation']).'`
- FROM `'.bqSQL(_DB_PREFIX_.$field['relation']).'`
- WHERE `id_'.bqSQL($field['relation']).'` = '.(int)$row[$column];
- $node[$column] = $this->generateId((string)$field['relation'], Db::getInstance()->getValue($sql));
- // A little trick to allow storage of some hard values, like '-1' for tab.id_parent
- if (!$node[$column] && $row[$column])
- $node[$column] = $row[$column];
- }
- else
- $node[$column] = $row[$column];
- }
- $nodes[$id] = $node;
- }
- // Store multilang columns
- if ($is_multilang && $row['id_lang'])
- {
- $node = array();
- foreach ($multilang_columns as $column => $is_text)
- $node[$column] = $row[isset($alias_multilang[$column]) ? $alias_multilang[$column] : $column];
- $nodes_lang[$row['id_lang']][$id] = $node;
- }
- }
- }
- return array(
- 'nodes' => $nodes,
- 'nodes_lang' => $nodes_lang,
- );
- }
- public function getEntityContentsTag()
- {
- $nodes_lang = array();
- $sql = 'SELECT t.id_tag, t.id_lang, t.name, pt.id_product
- FROM '._DB_PREFIX_.'tag t
- LEFT JOIN '._DB_PREFIX_.'product_tag pt ON t.id_tag = pt.id_tag
- ORDER BY id_lang';
- foreach (Db::getInstance()->executeS($sql) as $row)
- {
- $identifier = $this->generateId('tag', $row['id_tag']);
- if (!isset($nodes_lang[$row['id_lang']]))
- $nodes_lang[$row['id_lang']] = array();
- if (!isset($nodes_lang[$row['id_lang']][$identifier]))
- $nodes_lang[$row['id_lang']][$identifier] = array(
- 'name' => $row['name'],
- 'products' => '',
- );
- $nodes_lang[$row['id_lang']][$identifier]['products'] .= (($nodes_lang[$row['id_lang']][$identifier]['products']) ? ',' : '').$this->generateId('product', $row['id_product']);
- }
- return array(
- 'nodes' => array(),
- 'nodes_lang' => $nodes_lang,
- );
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function generateId($entity, $primary, array $row = array(), $id_format = null)
- {
- static $ids = array();
- if (isset($ids[$entity][$primary]))
- return $ids[$entity][$primary];
- if (!isset($ids[$entity]))
- $ids[$entity] = array();
- if (!$primary)
- return '';
- if (!$id_format || !$row || !$row[$id_format])
- $ids[$entity][$primary] = $entity.'_'.$primary;
- else
- {
- $value = $row[$id_format];
- $value = preg_replace('#[^a-z0-9_-]#i', '_', $value);
- $value = preg_replace('#_+#', '_', $value);
- $value = preg_replace('#^_+#', '', $value);
- $value = preg_replace('#_+$#', '', $value);
- $store_identifier = $value;
- $i = 1;
- while (in_array($store_identifier, $ids[$entity]))
- $store_identifier = $value.'_'.$i++;
- $ids[$entity][$primary] = $store_identifier;
- }
- return $ids[$entity][$primary];
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function createXmlEntityNodes($entity, array $nodes, SimpleXMLElement $entities)
- {
- $types = array_merge($this->getColumns($entity), $this->getColumns($entity, true));
- foreach ($nodes as $id => $node)
- {
- $entity_node = $entities->addChild($entity);
- $entity_node['id'] = $id;
- foreach ($node as $k => $v)
- {
- if (isset($types[$k]) && $types[$k])
- $entity_node->addChild($k, $v);
- else
- $entity_node[$k] = $v;
- }
- }
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function backupImage($entity, $path)
- {
- $reference = array(
- 'product' => 'products',
- 'category' => 'categories',
- 'manufacturer' => 'manufacturers',
- 'supplier' => 'suppliers',
- 'scene' => 'scenes',
- 'store' => 'stores',
- );
- $types = array();
- if (isset($reference[$entity]))
- {
- $types = array();
- foreach (ImageType::getImagesTypes($reference[$entity]) as $type)
- $types[] = $type['name'];
- }
- $path_list = array_map('trim', explode(',', $path));
- foreach ($path_list as $p)
- {
- $backup_path = $this->img_path.$p.'/';
- $from_path = _PS_IMG_DIR_.$p.'/';
- if (!is_dir($backup_path) && !mkdir($backup_path))
- $this->setError(sprintf('Cannot create directory <i>%s</i>', $backup_path));
- foreach (scandir($from_path) as $file)
- if ($file[0] != '.' && preg_match('#^(([0-9]+)(-('.implode('|', $types).'))?)\.(gif|jpg|jpeg|png)$#i', $file, $m))
- {
- $file_id = $m[2];
- $file_type = $m[3];
- $file_extension = $m[5];
- copy($from_path.$file, $backup_path.$this->generateId($entity, $file_id).$file_type.'.'.$file_extension);
- }
- }
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function backupImageImage()
- {
- $types = array();
- foreach (ImageType::getImagesTypes('products') as $type)
- $types[] = $type['name'];
- $backup_path = $this->img_path.'p/';
- $from_path = _PS_PROD_IMG_DIR_;
- if (!is_dir($backup_path) && !mkdir($backup_path))
- $this->setError(sprintf('Cannot create directory <i>%s</i>', $backup_path));
- foreach (Image::getAllImages() as $image)
- {
- $image = new Image($image['id_image']);
- $image_path = $image->getExistingImgPath();
- if (file_exists($from_path.$image_path.'.'.$image->image_format))
- copy($from_path.$image_path.'.'.$image->image_format, $backup_path.$this->generateId('image', $image->id).'.'.$image->image_format);
- foreach ($types as $type)
- if (file_exists($from_path.$image_path.'-'.$type.'.'.$image->image_format))
- copy($from_path.$image_path.'-'.$type.'.'.$image->image_format, $backup_path.$this->generateId('image', $image->id).'-'.$type.'.'.$image->image_format);
- }
- }
- /**
- * ONLY FOR DEVELOPMENT PURPOSE
- */
- public function backupImageTab()
- {
- $backup_path = $this->img_path.'t/';
- $from_path = _PS_IMG_DIR_.'t/';
- if (!is_dir($backup_path) && !mkdir($backup_path))
- $this->setError(sprintf('Cannot create directory <i>%s</i>', $backup_path));
- $xml = $this->loadEntity('tab');
- foreach ($xml->entities->tab as $tab)
- if (file_exists($from_path.$tab->class_name.'.gif'))
- copy($from_path.$tab->class_name.'.gif', $backup_path.$tab->class_name.'.gif');
- }
- }