Save new folder
This commit is contained in:
746
inc/console/migration/appliancesplugintocorecommand.class.php
Normal file
746
inc/console/migration/appliancesplugintocorecommand.class.php
Normal file
@ -0,0 +1,746 @@
|
||||
<?php
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2015-2020 Teclib' and contributors.
|
||||
* Yves TESNIERE
|
||||
* http://glpi-project.org
|
||||
*
|
||||
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This file is part of GLPI.
|
||||
*
|
||||
* GLPI is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GLPI is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
namespace Glpi\Console\Migration;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
use Appliance;
|
||||
use ApplianceType;
|
||||
use ApplianceEnvironment;
|
||||
use Appliance_Item;
|
||||
use Appliance_Item_Relation;
|
||||
use Change_Item;
|
||||
use Contract_Item;
|
||||
use DB;
|
||||
use Document_Item;
|
||||
use Domain;
|
||||
use Infocom;
|
||||
use Location;
|
||||
use Network;
|
||||
use Toolbox;
|
||||
use Glpi\Console\AbstractCommand;
|
||||
use Item_Problem;
|
||||
use Item_Project;
|
||||
use Item_Ticket;
|
||||
use KnowbaseItem_Item;
|
||||
use Log;
|
||||
use Profile;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
|
||||
class AppliancesPluginToCoreCommand extends AbstractCommand {
|
||||
|
||||
/**
|
||||
* Error code returned if plugin version or plugin data is invalid.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ERROR_PLUGIN_VERSION_OR_DATA_INVALID = 1;
|
||||
|
||||
/**
|
||||
* Error code returned if import failed.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ERROR_PLUGIN_IMPORT_FAILED = 2;
|
||||
|
||||
/**
|
||||
* list of possible relations of the plugin indexed by their correspond integer in the plugin
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const PLUGIN_RELATION_TYPES = [
|
||||
1 => Location::class,
|
||||
2 => Network::class,
|
||||
3 => Domain::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* list of usefull plugin tables and fields
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const PLUGIN_APPLIANCE_TABLES = [
|
||||
"glpi_plugin_appliances_appliances" => [
|
||||
"id",
|
||||
"entities_id",
|
||||
"is_recursive",
|
||||
"name",
|
||||
"is_deleted",
|
||||
"plugin_appliances_appliancetypes_id",
|
||||
"comment",
|
||||
"locations_id",
|
||||
"plugin_appliances_environments_id",
|
||||
"users_id",
|
||||
"users_id_tech",
|
||||
"groups_id",
|
||||
"groups_id_tech",
|
||||
"relationtype",
|
||||
"date_mod",
|
||||
"states_id",
|
||||
"externalid",
|
||||
"serial",
|
||||
"otherserial"
|
||||
],
|
||||
"glpi_plugin_appliances_appliancetypes" => ["id","entities_id","is_recursive","name","comment"],
|
||||
"glpi_plugin_appliances_appliances_items" => ["id", "plugin_appliances_appliances_id","items_id","itemtype"],
|
||||
"glpi_plugin_appliances_environments" => ["id","name","comment" ],
|
||||
"glpi_plugin_appliances_relations" => ["id","plugin_appliances_appliances_items_id","relations_id"]
|
||||
];
|
||||
|
||||
/**
|
||||
* itemtype corresponding to appliance in plugin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PLUGIN_APPLIANCE_ITEMTYPE = "PluginAppliancesAppliance";
|
||||
|
||||
/**
|
||||
* itemtype corresponding to appliance in core
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CORE_APPLIANCE_ITEMTYPE = "Appliance";
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('glpi:migration:appliances_plugin_to_core');
|
||||
$this->setDescription(__('Migrate Appliances plugin data into GLPI core tables'));
|
||||
|
||||
$this->addOption(
|
||||
'skip-errors',
|
||||
's',
|
||||
InputOption::VALUE_NONE,
|
||||
__('Do not exit on import errors')
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$no_interaction = $input->getOption('no-interaction');
|
||||
if (!$no_interaction) {
|
||||
// Ask for confirmation (unless --no-interaction)
|
||||
$output->writeln([
|
||||
__('You are about to launch migration of Appliances plugin data into GLPI core tables.'),
|
||||
__('Any previous appliance created in core will be lost.'),
|
||||
__('It is better to make a backup of your existing data before continuing.')
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* @var QuestionHelper $question_helper
|
||||
*/
|
||||
$question_helper = $this->getHelper('question');
|
||||
$run = $question_helper->ask(
|
||||
$input,
|
||||
$output,
|
||||
new ConfirmationQuestion(
|
||||
'<comment>' . __('Do you want to launch migration ?') . ' [yes/No]</comment>',
|
||||
false
|
||||
)
|
||||
);
|
||||
if (!$run) {
|
||||
$output->writeln(
|
||||
'<comment>' . __('Migration aborted.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->checkPlugin()) {
|
||||
return self::ERROR_PLUGIN_VERSION_OR_DATA_INVALID;
|
||||
}
|
||||
|
||||
if (!$this->migratePlugin()) {
|
||||
return self::ERROR_PLUGIN_IMPORT_FAILED;
|
||||
}
|
||||
|
||||
$output->writeln('<info>' . __('Migration done.') . '</info>');
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that required tables exists and fields are OK for migration.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function checkPlugin(): bool {
|
||||
$missing_tables = false;
|
||||
foreach (self::PLUGIN_APPLIANCE_TABLES as $table => $fields) {
|
||||
if (!$this->db->tableExists($table)) {
|
||||
$this->output->writeln(
|
||||
'<error>' . sprintf(__('Appliances plugin table "%s" is missing.'), $table) . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
$missing_tables = true;
|
||||
} else {
|
||||
foreach ($fields as $field) {
|
||||
if (!$this->db->fieldExists($table, $field)) {
|
||||
$this->output->writeln(
|
||||
'<error>' . sprintf(__('Appliances plugin field "%s" is missing.'), $table.'.'.$field) . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
$missing_tables = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($missing_tables) {
|
||||
$this->output->writeln(
|
||||
'<error>' . __('Migration cannot be done.') . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean data from core tables.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function cleanCoreTables() {
|
||||
$core_tables = [
|
||||
Appliance::getTable(),
|
||||
ApplianceType::getTable(),
|
||||
ApplianceEnvironment::getTable(),
|
||||
Appliance_Item::getTable(),
|
||||
Appliance_Item_Relation::getTable(),
|
||||
];
|
||||
|
||||
foreach ($core_tables as $table) {
|
||||
$result = $this->db->query('TRUNCATE ' . DB::quoteName($table));
|
||||
|
||||
if (!$result) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Unable to truncate table "%s"', $table)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$table = Infocom::getTable();
|
||||
$result = $this->db->delete($table, [
|
||||
'itemtype' => self::CORE_APPLIANCE_ITEMTYPE
|
||||
]);
|
||||
if (!$result) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Unable to clean table "%s"', $table)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy plugin tables to backup tables from plugin to core keeping same ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function migratePlugin(): bool {
|
||||
$this->cleanCoreTables();
|
||||
|
||||
return $this->createApplianceTypes()
|
||||
&& $this->createApplianceEnvironments()
|
||||
&& $this->createApplianceRelations()
|
||||
&& $this->createApplianceItems()
|
||||
&& $this->createAppliances()
|
||||
&& $this->updateItemtypes()
|
||||
&& $this->updateProfilesApplianceRights();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update profile rights (Associable items to a ticket).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function updateProfilesApplianceRights(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Updating profiles...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$table = Profile::getTable();
|
||||
$result = $this->db->query(
|
||||
sprintf(
|
||||
"UPDATE %s SET helpdesk_item_type = REPLACE(helpdesk_item_type, '%s', '%s')",
|
||||
DB::quoteName($table),
|
||||
self::PLUGIN_APPLIANCE_ITEMTYPE,
|
||||
self::CORE_APPLIANCE_ITEMTYPE
|
||||
)
|
||||
);
|
||||
if (false === $result) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to update "%s" in profiles.'), __('Associable items to a ticket'))
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename itemtype in core tables.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function updateItemtypes(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Updating GLPI itemtypes...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
$itemtypes_tables = [
|
||||
Item_Ticket::getTable(),
|
||||
Item_Problem::getTable(),
|
||||
Change_Item::getTable(),
|
||||
Item_Project::getTable(),
|
||||
Log::getTable(),
|
||||
Infocom::getTable(),
|
||||
Document_Item::getTable(),
|
||||
Contract_Item::getTable(),
|
||||
KnowbaseItem_Item::getTable(),
|
||||
];
|
||||
|
||||
foreach ($itemtypes_tables as $itemtype_table) {
|
||||
$result = $this->db->update($itemtype_table, [
|
||||
'itemtype' => self::CORE_APPLIANCE_ITEMTYPE,
|
||||
], [
|
||||
'itemtype' => self::PLUGIN_APPLIANCE_ITEMTYPE,
|
||||
]);
|
||||
|
||||
if (false === $result) {
|
||||
$this->outputImportError(
|
||||
sprintf(
|
||||
__('Migration of table "%s" failed with message "(%s) %s".'),
|
||||
$itemtype_table,
|
||||
$this->db->errno(),
|
||||
$this->db->error()
|
||||
)
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appliance items.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createApplianceItems(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Creating Appliance Items...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_appliances_appliances_items'
|
||||
]);
|
||||
|
||||
if (!count($iterator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($this->output);
|
||||
|
||||
foreach ($progress_bar->iterate($iterator) as $item) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
sprintf(
|
||||
__('Importing Appliance item "%d"...'),
|
||||
(int) $item['id']
|
||||
),
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$app_fields = Toolbox::sanitize([
|
||||
'id' => $item['id'],
|
||||
'appliances_id' => $item['plugin_appliances_appliances_id'],
|
||||
'items_id' => $item['items_id'],
|
||||
'itemtype' => $item['itemtype']
|
||||
]);
|
||||
|
||||
$appi = new Appliance_Item();
|
||||
if (!($appi_id = $appi->getFromDBByCrit($app_fields))) {
|
||||
$appi_id = $appi->add($app_fields);
|
||||
}
|
||||
|
||||
if (false === $appi_id) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to create Appliance item %d.'), (int) $item['id']),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appliance environments.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createApplianceEnvironments(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Creating Appliance Environment...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_appliances_environments'
|
||||
]);
|
||||
|
||||
if (!count($iterator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($this->output);
|
||||
|
||||
foreach ($progress_bar->iterate($iterator) as $env) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
sprintf(
|
||||
__('Importing environment "%s"...'),
|
||||
$env['name']
|
||||
),
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$app_fields = Toolbox::sanitize([
|
||||
'id' => $env['id'],
|
||||
'name' => $env['name'],
|
||||
'comment' => $env['comment']
|
||||
]);
|
||||
|
||||
$appe = new ApplianceEnvironment();
|
||||
if (!($appe_id = $appe->getFromDBByCrit($app_fields))) {
|
||||
$appe_id = $appe->add($app_fields);
|
||||
}
|
||||
|
||||
if (false === $appe_id) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to create Appliance environment %s (%d).'), $env['name'], (int) $env['id']),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appliances.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createAppliances(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>'. __('Creating Appliances...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
$iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_appliances_appliances'
|
||||
]);
|
||||
|
||||
if (!count($iterator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($this->output);
|
||||
|
||||
foreach ($progress_bar->iterate($iterator) as $appliance) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
sprintf(
|
||||
__('Importing appliance "%s"...'),
|
||||
$appliance['name']
|
||||
),
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$app_fields = Toolbox::sanitize([
|
||||
'id' => $appliance['id'],
|
||||
'entities_id' => $appliance['entities_id'],
|
||||
'is_recursive' => $appliance['is_recursive'],
|
||||
'name' => $appliance['name'],
|
||||
'is_deleted' => $appliance['is_deleted'],
|
||||
'appliancetypes_id' => $appliance['plugin_appliances_appliancetypes_id'],
|
||||
'comment' => $appliance['comment'],
|
||||
'locations_id' => $appliance['locations_id'],
|
||||
'manufacturers_id' => '0',
|
||||
'applianceenvironments_id' => $appliance['plugin_appliances_environments_id'],
|
||||
'users_id' => $appliance['users_id'],
|
||||
'users_id_tech' => $appliance['users_id_tech'],
|
||||
'groups_id' => $appliance['groups_id'],
|
||||
'groups_id_tech' => $appliance['groups_id_tech'],
|
||||
'date_mod' => $appliance['date_mod'],
|
||||
'is_helpdesk_visible' => $appliance['is_helpdesk_visible'],
|
||||
'states_id' => $appliance['states_id'],
|
||||
'externalidentifier' => $appliance['externalid'],
|
||||
'serial' => $appliance['serial'],
|
||||
'otherserial' => $appliance['otherserial']
|
||||
]);
|
||||
|
||||
$app = new Appliance();
|
||||
if (!($app_id = $app->getFromDBByCrit($app_fields))) {
|
||||
$app_id = $app->add($app_fields);
|
||||
}
|
||||
|
||||
if (false === $app_id) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to create Appliance %s (%d).'), $appliance['name'], (int) $appliance['id']),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appliance types.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createApplianceTypes(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Creating Appliance types...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_appliances_appliancetypes'
|
||||
]);
|
||||
|
||||
if (!count($iterator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($this->output);
|
||||
|
||||
foreach ($progress_bar->iterate($iterator) as $type) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
sprintf(
|
||||
__('Importing type "%s"...'),
|
||||
$type['name']
|
||||
),
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$appt_fields = Toolbox::sanitize([
|
||||
'id' => $type['id'],
|
||||
'entities_id' => $type['entities_id'],
|
||||
'is_recursive' => $type['is_recursive'],
|
||||
'name' => $type['name'],
|
||||
'comment' => $type['comment'],
|
||||
'externalidentifier' => $type['externalid']
|
||||
]);
|
||||
|
||||
$appt = new ApplianceType();
|
||||
if (!($appt_id = $appt->getFromDBByCrit($appt_fields))) {
|
||||
$appt_id = $appt->add($appt_fields);
|
||||
}
|
||||
|
||||
if (false === $appt_id) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to create Appliance environment %s (%d).'), $type['name'], (int) $type['id']),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appliance relations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createApplianceRelations(): bool {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Creating Appliance relations...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$iterator = $this->db->request([
|
||||
'SELECT' => ['rel.*', 'app.relationtype'],
|
||||
'FROM' => 'glpi_plugin_appliances_relations AS rel',
|
||||
'INNER JOIN' => [
|
||||
'glpi_plugin_appliances_appliances_items AS items' => [
|
||||
'ON' => [
|
||||
'items' => 'id',
|
||||
'rel' => 'plugin_appliances_appliances_items_id'
|
||||
]
|
||||
],
|
||||
'glpi_plugin_appliances_appliances AS app' => [
|
||||
'ON' => [
|
||||
'app' => 'id',
|
||||
'items' => 'plugin_appliances_appliances_id'
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
if (!count($iterator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($this->output);
|
||||
|
||||
foreach ($progress_bar->iterate($iterator) as $row) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
sprintf(
|
||||
__('Importing relation "%s"...'),
|
||||
$row['id']
|
||||
),
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$itemtype = self::PLUGIN_RELATION_TYPES[$row['relationtype']] ?? "";
|
||||
if ($itemtype == "") {
|
||||
$this->outputImportError(
|
||||
sprintf(
|
||||
__('Unable to found relation type %s from Appliance Item Relation %d.'),
|
||||
$row['relationtype'],
|
||||
(int) $row['id']
|
||||
),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
} else {
|
||||
continue; // Skip this relation
|
||||
}
|
||||
}
|
||||
|
||||
$appr_fields = Toolbox::sanitize([
|
||||
'id' => $row['id'],
|
||||
'appliances_items_id' => $row['plugin_appliances_appliances_items_id'],
|
||||
'itemtype' => $itemtype,
|
||||
'items_id' => $row['relations_id']
|
||||
]);
|
||||
|
||||
$appr = new Appliance_Item_Relation();
|
||||
if (!($appr_id = $appr->getFromDBByCrit($appr_fields))) {
|
||||
$appr_id = $appr->add($appr_fields);
|
||||
}
|
||||
|
||||
if (false === $appr_id) {
|
||||
$this->outputImportError(
|
||||
sprintf(__('Unable to create Appliance Item Relation %d.'), (int) $row['id']),
|
||||
$progress_bar
|
||||
);
|
||||
if (!$this->input->getOption('skip-errors')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output import error message.
|
||||
*
|
||||
* @param string $message
|
||||
* @param ProgressBar|null $progress_bar
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function outputImportError($message, ProgressBar $progress_bar = null) {
|
||||
|
||||
$skip_errors = $this->input->getOption('skip-errors');
|
||||
|
||||
$verbosity = $skip_errors
|
||||
? OutputInterface::VERBOSITY_NORMAL
|
||||
: OutputInterface::VERBOSITY_QUIET;
|
||||
|
||||
$message = '<error>' . $message . '</error>';
|
||||
|
||||
if ($skip_errors && $progress_bar instanceof ProgressBar) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
$message,
|
||||
$progress_bar,
|
||||
$verbosity
|
||||
);
|
||||
} else {
|
||||
if (!$skip_errors && $progress_bar instanceof ProgressBar) {
|
||||
$this->output->write(PHP_EOL); // Keep progress bar last state and go to next line
|
||||
}
|
||||
$this->output->writeln(
|
||||
$message,
|
||||
$verbosity
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
129
inc/console/migration/buildmissingtimestampscommand.class.php
Normal file
129
inc/console/migration/buildmissingtimestampscommand.class.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2015-2020 Teclib' and contributors.
|
||||
*
|
||||
* http://glpi-project.org
|
||||
*
|
||||
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This file is part of GLPI.
|
||||
*
|
||||
* GLPI is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GLPI is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace Glpi\Console\Migration;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
use CommonDBTM;
|
||||
use Glpi\Console\AbstractCommand;
|
||||
use Log;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class BuildMissingTimestampsCommand extends AbstractCommand {
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('glpi:migration:build_missing_timestamps');
|
||||
$this->setDescription(__('Set missing `date_creation` and `date_mod` values using log entries.'));
|
||||
$this->setHidden(true); // Hide this command as it is when migrating from really old GLPI version
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
|
||||
$tables_iterator = $this->db->request(
|
||||
[
|
||||
'SELECT' => [
|
||||
'table_name AS TABLE_NAME',
|
||||
'column_name AS COLUMN_NAME',
|
||||
],
|
||||
'FROM' => 'information_schema.columns',
|
||||
'WHERE' => [
|
||||
'table_schema' => $this->db->dbdefault,
|
||||
'table_name' => ['LIKE', 'glpi_%'],
|
||||
'column_name' => ['date_creation', 'date_mod'],
|
||||
],
|
||||
'ORDER' => ['table_name', 'column_name'],
|
||||
]
|
||||
);
|
||||
|
||||
$log_table = Log::getTable();
|
||||
|
||||
foreach ($tables_iterator as $table_info) {
|
||||
$table = $table_info['TABLE_NAME'];
|
||||
$itemtype = getItemTypeForTable($table);
|
||||
$column = $table_info['COLUMN_NAME'];
|
||||
|
||||
if (!is_a($itemtype, CommonDBTM::class, true)) {
|
||||
continue; // getItemTypeForTable() may not return a class name ("UNKNOWN" for example)
|
||||
}
|
||||
/* @var $item CommonDBTM */
|
||||
$item = new $itemtype();
|
||||
|
||||
if (!$item->dohistory) {
|
||||
continue; // Skip items that does not have an history
|
||||
}
|
||||
|
||||
$output->writeln(
|
||||
'<comment>' . sprintf(__('Filling `%s`.`%s`...'), $table, $column) . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
|
||||
$target_date = $column === 'date_creation' ? 'MIN(`date_mod`)' : 'MAX(`date_mod`)';
|
||||
|
||||
$result = $this->db->query(
|
||||
"
|
||||
UPDATE `$table`
|
||||
LEFT JOIN (
|
||||
SELECT $target_date AS `date_mod`, `itemtype`, `items_id`
|
||||
FROM `$log_table`
|
||||
GROUP BY `itemtype`, `items_id`
|
||||
) as `logs`
|
||||
ON `logs`.`itemtype` = '$itemtype' AND `logs`.`items_id` = `$table`.`id`
|
||||
SET `$table`.`$column` = `logs`.`date_mod` WHERE `$table`.`$column` IS NULL
|
||||
"
|
||||
);
|
||||
if (false === $result) {
|
||||
$message = sprintf(
|
||||
__('Update of `%s`.`%s` failed with message "(%s) %s".'),
|
||||
$table,
|
||||
$column,
|
||||
$this->db->errno(),
|
||||
$this->db->error()
|
||||
);
|
||||
$output->writeln(
|
||||
'<error>' . $message . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('<info>' . __('Migration done.') . '</info>');
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
}
|
||||
758
inc/console/migration/domainsplugintocorecommand.class.php
Normal file
758
inc/console/migration/domainsplugintocorecommand.class.php
Normal file
@ -0,0 +1,758 @@
|
||||
<?php
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2015-2020 Teclib' and contributors.
|
||||
*
|
||||
* http://glpi-project.org
|
||||
*
|
||||
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This file is part of GLPI.
|
||||
*
|
||||
* GLPI is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GLPI is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace Glpi\Console\Migration;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
use CommonDBTM;
|
||||
use DB;
|
||||
use DomainType;
|
||||
use Domain;
|
||||
use Domain_Item;
|
||||
use Plugin;
|
||||
use Toolbox;
|
||||
use Glpi\Console\AbstractCommand;
|
||||
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Exception\LogicException;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
|
||||
class DomainsPluginToCoreCommand extends AbstractCommand {
|
||||
|
||||
/**
|
||||
* Error code returned if plugin version or plugin data is invalid.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ERROR_PLUGIN_VERSION_OR_DATA_INVALID = 1;
|
||||
|
||||
/**
|
||||
* Error code returned if import failed.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ERROR_PLUGIN_IMPORT_FAILED = 1;
|
||||
|
||||
/**
|
||||
* Version of Domains plugin required for this migration.
|
||||
* @var string
|
||||
*/
|
||||
const DOMAINS_REQUIRED_VERSION = '2.1.0';
|
||||
|
||||
/**
|
||||
* Imported elements mapping.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $elements_mapping;
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('glpi:migration:domains_plugin_to_core');
|
||||
$this->setDescription(__('Migrate Domains plugin data into GLPI core tables'));
|
||||
|
||||
$this->addOption(
|
||||
'update-plugin',
|
||||
'u',
|
||||
InputOption::VALUE_NONE,
|
||||
sprintf(
|
||||
__('Run Domains plugin update (you need version %s files to do this)'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
)
|
||||
);
|
||||
|
||||
$this->addOption(
|
||||
'without-plugin',
|
||||
'w',
|
||||
InputOption::VALUE_NONE,
|
||||
sprintf(
|
||||
__('Enable migration without plugin files (we cannot validate that plugin data are compatible with supported %s version)'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
|
||||
$this->elements_mapping = []; // Clear elements mapping
|
||||
|
||||
$no_interaction = $input->getOption('no-interaction');
|
||||
|
||||
if (!$no_interaction) {
|
||||
// Ask for confirmation (unless --no-interaction)
|
||||
$output->writeln(
|
||||
[
|
||||
__('You are about to launch migration of Domains plugin data into GLPI core tables.'),
|
||||
__('It is better to make a backup of your existing data before continuing.')
|
||||
]
|
||||
);
|
||||
|
||||
/** @var QuestionHelper $question_helper */
|
||||
$question_helper = $this->getHelper('question');
|
||||
$run = $question_helper->ask(
|
||||
$input,
|
||||
$output,
|
||||
new ConfirmationQuestion(
|
||||
'<comment>' . __('Do you want to launch migration ?') . ' [yes/No]</comment>',
|
||||
false
|
||||
)
|
||||
);
|
||||
if (!$run) {
|
||||
$output->writeln(
|
||||
'<comment>' . __('Migration aborted.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->checkPlugin()) {
|
||||
return self::ERROR_PLUGIN_VERSION_OR_DATA_INVALID;
|
||||
}
|
||||
|
||||
if (!$this->migratePlugin()) {
|
||||
return self::ERROR_PLUGIN_IMPORT_FAILED;
|
||||
}
|
||||
|
||||
$output->writeln('<info>' . __('Migration done.') . '</info>');
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that plugin state and existing data are OK for migration.
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function checkPlugin() {
|
||||
|
||||
$check_version = !$this->input->getOption('without-plugin');
|
||||
|
||||
if ($check_version) {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Checking plugin version...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
|
||||
$plugin = new Plugin();
|
||||
$plugin->checkPluginState('domains');
|
||||
|
||||
if (!$plugin->getFromDBbyDir('domains')) {
|
||||
$message = __('Domains plugin is not part of GLPI plugin list. It has never been installed or has been cleaned.')
|
||||
. ' '
|
||||
. sprintf(
|
||||
__('You have to install Domains plugin files in version %s to be able to continue.'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
);
|
||||
$this->output->writeln(
|
||||
[
|
||||
'<error>' . $message . '</error>',
|
||||
],
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$is_version_ok = self::DOMAINS_REQUIRED_VERSION === $plugin->fields['version'];
|
||||
if (!$is_version_ok) {
|
||||
$message = sprintf(
|
||||
__('You have to install Domains plugin files in version %s to be able to continue.'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
);
|
||||
$this->output->writeln(
|
||||
'<error>' . $message . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$is_installable = in_array(
|
||||
$plugin->fields['state'],
|
||||
[
|
||||
Plugin::TOBECLEANED, // Can be in this state if check was done without the plugin dir
|
||||
Plugin::NOTINSTALLED, // Can be not installed if plugin has been cleaned in plugin list
|
||||
Plugin::NOTUPDATED, // Plugin 1.8.0 version has never been installed
|
||||
]
|
||||
);
|
||||
if ($is_installable) {
|
||||
if ($this->input->getOption('update-plugin')) {
|
||||
$message = sprintf(
|
||||
__('Migrating plugin to %s version...'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
);
|
||||
$this->output->writeln(
|
||||
'<info>' . $message . '</info>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
ob_start();
|
||||
$plugin->install($plugin->fields['id']);
|
||||
ob_end_clean();
|
||||
|
||||
// Reload and check migration result
|
||||
$plugin->getFromDB($plugin->fields['id']);
|
||||
if (!in_array($plugin->fields['state'], [Plugin::TOBECONFIGURED, Plugin::NOTACTIVATED])) {
|
||||
$message = sprintf(
|
||||
__('Plugin migration to %s version failed.'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
);
|
||||
$this->output->writeln(
|
||||
'<error>' . $message . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$message = sprintf(
|
||||
__('Domains plugin data has to be updated to %s version. It can be done using the --update-plugin option.'),
|
||||
self::DOMAINS_REQUIRED_VERSION
|
||||
);
|
||||
$this->output->writeln(
|
||||
'<comment>' . $message . '</comment>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$is_state_ok = in_array(
|
||||
$plugin->fields['state'],
|
||||
[
|
||||
Plugin::ACTIVATED, // Should not be possible as 1.8.0 is not compatible with 9.3
|
||||
Plugin::TOBECONFIGURED, // Should not be possible as check_config of plugin returns always true
|
||||
Plugin::NOTACTIVATED,
|
||||
]
|
||||
);
|
||||
if (!$is_state_ok) {
|
||||
// Should not happens as installation should put plugin in awaited state
|
||||
throw new LogicException('Unexpected plugin state.');
|
||||
}
|
||||
}
|
||||
|
||||
$domains_tables = [
|
||||
'glpi_plugin_domains_configs',
|
||||
'glpi_plugin_domains_domains',
|
||||
'glpi_plugin_domains_domains_items',
|
||||
'glpi_plugin_domains_domaintypes',
|
||||
];
|
||||
$missing_tables = false;
|
||||
foreach ($domains_tables as $table) {
|
||||
if (!$this->db->tableExists($table)) {
|
||||
$this->output->writeln(
|
||||
'<error>' . sprintf(__('Domains plugin table "%s" is missing.'), $table) . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
$missing_tables = true;
|
||||
}
|
||||
}
|
||||
if ($missing_tables) {
|
||||
$this->output->writeln(
|
||||
'<error>' . __('Migration cannot be done.') . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function migratePlugin() {
|
||||
|
||||
$failure = !$this->importDomainTypes()
|
||||
|| !$this->importDomains()
|
||||
|| !$this->importDomainItems();
|
||||
|
||||
return !$failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate domain types
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function importDomainTypes() {
|
||||
$has_errors = false;
|
||||
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Importing domains types...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$types_iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_domains_domaintypes',
|
||||
'ORDER' => 'id ASC'
|
||||
]);
|
||||
|
||||
$core_types = [];
|
||||
$coret_iterator = $this->db->request([
|
||||
'SELECT' => ['id', 'name'],
|
||||
'FROM' => DomainType::getTable()
|
||||
]);
|
||||
while ($row = $coret_iterator->next()) {
|
||||
$core_types[$row['name']] = $row['id'];
|
||||
}
|
||||
|
||||
if ($types_iterator->count()) {
|
||||
$progress_bar = new ProgressBar($this->output, $types_iterator->count());
|
||||
$progress_bar->start();
|
||||
|
||||
foreach ($types_iterator as $typ) {
|
||||
$progress_bar->advance(1);
|
||||
$core_type = null;
|
||||
|
||||
if (isset($core_types[$typ['name']])) {
|
||||
$core_type = $core_types[$typ['name']];
|
||||
$message = sprintf(
|
||||
__('Updating existing domain type %s...'),
|
||||
$typ['name']
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
__('Importing domain type %s...'),
|
||||
$typ['name']
|
||||
);
|
||||
}
|
||||
$this->writelnOutputWithProgressBar(
|
||||
$message,
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$type_input = [
|
||||
'name' => $typ['name'],
|
||||
'entities_id' => $typ['entities_id'],
|
||||
'comment' => $typ['comment'],
|
||||
];
|
||||
$type_input = Toolbox::addslashes_deep($type_input);
|
||||
|
||||
$domaintype = new DomainType();
|
||||
if ($core_type !== null) {
|
||||
$res = (bool)$domaintype->update($type_input + ['id' => $core_type]);
|
||||
} else {
|
||||
$new_tid = (int)$domaintype->add($type_input);
|
||||
$res = $new_tid > 0;
|
||||
if ($res) {
|
||||
$core_types[$typ['name']] = $new_tid;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
$has_errors = true;
|
||||
|
||||
$message = sprintf(
|
||||
$core_type === null ?
|
||||
__('Unable to add domain type %s.') :
|
||||
__('Unable to update domain type %s.'),
|
||||
$typ['name']
|
||||
);
|
||||
$this->outputImportError($message, $progress_bar);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addElementToMapping(
|
||||
'PluginDomainsDomaintype',
|
||||
$typ['id'],
|
||||
'DomainType',
|
||||
$new_tid ?? $core_type
|
||||
);
|
||||
}
|
||||
|
||||
$progress_bar->finish();
|
||||
$this->output->write(PHP_EOL);
|
||||
} else {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('No domains types found.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
}
|
||||
|
||||
return !$has_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate domains
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function importDomains() {
|
||||
$has_errors = false;
|
||||
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Importing domains...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$domains_iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_domains_domains',
|
||||
'ORDER' => 'id ASC'
|
||||
]);
|
||||
|
||||
$core_domains = [];
|
||||
$cored_iterator = $this->db->request([
|
||||
'SELECT' => ['id', 'name'],
|
||||
'FROM' => Domain::getTable()
|
||||
]);
|
||||
while ($row = $cored_iterator->next()) {
|
||||
$core_domains[$row['name']] = $row['id'];
|
||||
}
|
||||
|
||||
if ($domains_iterator->count()) {
|
||||
$progress_bar = new ProgressBar($this->output, $domains_iterator->count());
|
||||
$progress_bar->start();
|
||||
|
||||
foreach ($domains_iterator as $dom) {
|
||||
$progress_bar->advance(1);
|
||||
$core_dom = null;
|
||||
|
||||
if (isset($core_domains[$dom['name']])) {
|
||||
$core_dom = $core_domains[$dom['name']];
|
||||
$message = sprintf(
|
||||
__('Updating existing domain %s...'),
|
||||
$dom['name']
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
__('Importing domain %s...'),
|
||||
$dom['name']
|
||||
);
|
||||
}
|
||||
$this->writelnOutputWithProgressBar(
|
||||
$message,
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
$mapped_type = $this->getCorrespondingItem('PluginDomainsDomaintype', $dom['plugin_domains_domaintypes_id']);
|
||||
if ($dom['plugin_domains_domaintypes_id'] != 0 && $mapped_type === null) {
|
||||
$message = sprintf(
|
||||
__('Unable to find mapping for type %s.'),
|
||||
$dom['plugin_domains_domaintypes_id']
|
||||
);
|
||||
$this->outputImportError($message, $progress_bar);
|
||||
return false;
|
||||
}
|
||||
$types_id = $mapped_type !== null ? $mapped_type->fields['id'] : 0;
|
||||
$domain_input = [
|
||||
'name' => $dom['name'],
|
||||
'entities_id' => $dom['entities_id'],
|
||||
'is_recursive' => $dom['is_recursive'],
|
||||
'domaintypes_id' => $types_id,
|
||||
'date_creation' => $dom['date_creation'],
|
||||
'date_expiration' => $dom['date_expiration'],
|
||||
'users_id_tech' => $dom['users_id_tech'],
|
||||
'groups_id_tech' => $dom['groups_id_tech'],
|
||||
//suppliers_id not present in core
|
||||
'comment' => $dom['comment'],
|
||||
'others' => $dom['others'],
|
||||
'is_helpdesk_visible' => $dom['is_helpdesk_visible'],
|
||||
'date_mod' => $dom['date_mod'],
|
||||
'is_deleted' => $dom['is_deleted']
|
||||
];
|
||||
$domain_input = Toolbox::addslashes_deep($domain_input);
|
||||
|
||||
$domain = new Domain();
|
||||
if ($core_dom !== null) {
|
||||
$res = (bool)$domain->update($domain_input + ['id' => $core_dom]);
|
||||
} else {
|
||||
$new_did = (int)$domain->add($domain_input);
|
||||
$res = $new_did > 0;
|
||||
if ($res) {
|
||||
$core_domains[$dom['name']] = $new_did;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
$has_errors = true;
|
||||
|
||||
$message = sprintf(
|
||||
$core_dom === null ?
|
||||
__('Unable to add domain %s.') :
|
||||
__('Unable to update domain %s.'),
|
||||
$dom['name']
|
||||
);
|
||||
$this->outputImportError($message, $progress_bar);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addElementToMapping(
|
||||
'PluginDomainsDomains',
|
||||
$dom['id'],
|
||||
'Domain',
|
||||
$new_did ?? $core_dom
|
||||
);
|
||||
|
||||
//handle infocoms
|
||||
$infocom = new \Infocom();
|
||||
$infocom_input = [
|
||||
'itemtype' => 'Domain',
|
||||
'items_id' => $new_did ?? $core_dom,
|
||||
'suppliers_id' => $dom['suppliers_id'],
|
||||
'entities_id' => $dom['entities_id'],
|
||||
'is_recursive' => $dom['is_recursive']
|
||||
];
|
||||
if ($core_dom === null) {
|
||||
$infocom->add($infocom_input);
|
||||
} else {
|
||||
$found = $infocom->getFromDBByCrit([
|
||||
'itemtype' => 'Domain',
|
||||
'items_id' => $core_dom
|
||||
]);
|
||||
if ($found) {
|
||||
$infocom_input['id'] = $infocom->fields['id'];
|
||||
$infocom->update($infocom_input);
|
||||
} else {
|
||||
$infocom->add($infocom_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$progress_bar->finish();
|
||||
$this->output->write(PHP_EOL);
|
||||
} else {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('No domains found.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
}
|
||||
|
||||
return !$has_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate domain items
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function importDomainItems() {
|
||||
$has_errors = false;
|
||||
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('Importing domains items...') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
|
||||
$items_iterator = $this->db->request([
|
||||
'FROM' => 'glpi_plugin_domains_domains_items',
|
||||
'ORDER' => 'id ASC'
|
||||
]);
|
||||
|
||||
$core_items = [];
|
||||
$coreitems_iterator = $this->db->request([
|
||||
'FROM' => Domain_Item::getTable()
|
||||
]);
|
||||
while ($row = $coreitems_iterator->next()) {
|
||||
$core_items[$row['domains_id'].$row['itemtype'].$row['items_id']] = $row['id'];
|
||||
}
|
||||
|
||||
if ($items_iterator->count()) {
|
||||
$progress_bar = new ProgressBar($this->output, $items_iterator->count());
|
||||
$progress_bar->start();
|
||||
|
||||
foreach ($items_iterator as $itm) {
|
||||
$progress_bar->advance(1);
|
||||
$core_item = null;
|
||||
$mapped_domain = $this->getCorrespondingItem('PluginDomainsDomains', $itm['plugin_domains_domains_id']);
|
||||
if ($mapped_domain === null) {
|
||||
$message = sprintf(
|
||||
__('Unable to find corresponding domain for item %s (%s).'),
|
||||
$itm['itemtype'],
|
||||
$itm['items_id']
|
||||
);
|
||||
$this->outputImportError($message, $progress_bar);
|
||||
// Do not block migration as this error is probably resulting in presence of obsolete data in DB
|
||||
continue;
|
||||
}
|
||||
$domains_id = $mapped_domain->fields['id'];
|
||||
|
||||
if (isset($core_items[$domains_id.$itm['itemtype'].$itm['items_id']])) {
|
||||
$core_item = $core_items[$domains_id.$itm['itemtype'].$itm['items_id']];
|
||||
$message = sprintf(
|
||||
__('Skip existing domain item %s...'),
|
||||
$domains_id . ' ' . $itm['itemtype'] . ' ' . $itm['items_id']
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
__('Importing domain item %s...'),
|
||||
$domains_id . ' ' . $itm['itemtype'] . ' ' . $itm['items_id']
|
||||
);
|
||||
}
|
||||
$this->writelnOutputWithProgressBar(
|
||||
$message,
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
||||
);
|
||||
|
||||
if ($core_item !== null) {
|
||||
//if it already exist in DB, there is nothing to change
|
||||
continue;
|
||||
}
|
||||
|
||||
$item_input = [
|
||||
'domains_id' => $domains_id,
|
||||
'itemtype' => $itm['itemtype'],
|
||||
'items_id' => $itm['items_id'],
|
||||
'domainrelations_id' => 0
|
||||
];
|
||||
$item_input = Toolbox::addslashes_deep($item_input);
|
||||
|
||||
$item = new Domain_Item();
|
||||
$new_iid = (int)$item->add($item_input);
|
||||
$res = $new_iid > 0;
|
||||
if ($res) {
|
||||
$core_items[$domains_id.$itm['itemtype'].$itm['items_id']] = $new_iid;
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
$has_errors = true;
|
||||
|
||||
$message = sprintf(
|
||||
$core_item === null ?
|
||||
__('Unable to add domain item %s.') :
|
||||
__('Unable to update domain item %s.'),
|
||||
$domains_id . ' ' . $itm['itemtype'] . ' ' . $itm['items_id']
|
||||
);
|
||||
$this->outputImportError($message, $progress_bar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$progress_bar->finish();
|
||||
$this->output->write(PHP_EOL);
|
||||
} else {
|
||||
$this->output->writeln(
|
||||
'<comment>' . __('No domains items found.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_NORMAL
|
||||
);
|
||||
}
|
||||
|
||||
return !$has_errors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an element to mapping.
|
||||
*
|
||||
* @param string $old_itemtype
|
||||
* @param integer $old_id
|
||||
* @param string $new_itemtype
|
||||
* @param integer $new_id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addElementToMapping($old_itemtype, $old_id, $new_itemtype, $new_id) {
|
||||
|
||||
if (!array_key_exists($old_itemtype, $this->elements_mapping)) {
|
||||
$this->elements_mapping[$old_itemtype] = [];
|
||||
}
|
||||
$this->elements_mapping[$old_itemtype][$old_id] = [
|
||||
'itemtype' => $new_itemtype,
|
||||
'id' => $new_id,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns item corresponding to itemtype and id.
|
||||
* If item has been migrated to another itemtype, il will return the new item.
|
||||
*
|
||||
* @param string $itemtype
|
||||
* @param integer $id
|
||||
*
|
||||
* @return null|CommonDBTM
|
||||
*/
|
||||
private function getCorrespondingItem($itemtype, $id) {
|
||||
|
||||
if (array_key_exists($itemtype, $this->elements_mapping)
|
||||
&& array_key_exists($id, $this->elements_mapping[$itemtype])) {
|
||||
// Element exists in mapping, get new element
|
||||
$mapping = $this->elements_mapping[$itemtype][$id];
|
||||
$id = $mapping['id'];
|
||||
$itemtype = $mapping['itemtype'];
|
||||
}
|
||||
|
||||
if (!class_exists($itemtype)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = new $itemtype();
|
||||
if ($id !== 0 && !$item->getFromDB($id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output import error message.
|
||||
*
|
||||
* @param string $message
|
||||
* @param ProgressBar|null $progress_bar
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function outputImportError($message, ProgressBar $progress_bar = null) {
|
||||
|
||||
$verbosity = OutputInterface::VERBOSITY_QUIET;
|
||||
|
||||
$message = '<error>' . $message . '</error>';
|
||||
|
||||
if ($progress_bar instanceof ProgressBar) {
|
||||
$this->writelnOutputWithProgressBar(
|
||||
$message,
|
||||
$progress_bar,
|
||||
$verbosity
|
||||
);
|
||||
} else {
|
||||
if ($progress_bar instanceof ProgressBar) {
|
||||
$this->output->write(PHP_EOL); // Keep progress bar last state and go to next line
|
||||
}
|
||||
$this->output->writeln(
|
||||
$message,
|
||||
$verbosity
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
126
inc/console/migration/myisamtoinnodbcommand.class.php
Normal file
126
inc/console/migration/myisamtoinnodbcommand.class.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2015-2020 Teclib' and contributors.
|
||||
*
|
||||
* http://glpi-project.org
|
||||
*
|
||||
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This file is part of GLPI.
|
||||
*
|
||||
* GLPI is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GLPI is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace Glpi\Console\Migration;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
use DB;
|
||||
use Glpi\Console\AbstractCommand;
|
||||
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
class MyIsamToInnoDbCommand extends AbstractCommand {
|
||||
|
||||
/**
|
||||
* Error code returned when failed to migrate one table.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ERROR_TABLE_MIGRATION_FAILED = 1;
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('glpi:migration:myisam_to_innodb');
|
||||
$this->setDescription(__('Migrate MyISAM tables to InnoDB'));
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
|
||||
$no_interaction = $input->getOption('no-interaction'); // Base symfony/console option
|
||||
|
||||
$myisam_tables = $this->db->getMyIsamTables();
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
'<info>' . __('Found %s table(s) using MyISAM engine.') . '</info>',
|
||||
$myisam_tables->count()
|
||||
)
|
||||
);
|
||||
|
||||
if (0 === $myisam_tables->count()) {
|
||||
$output->writeln('<info>' . __('No migration needed.') . '</info>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$no_interaction) {
|
||||
// Ask for confirmation (unless --no-interaction)
|
||||
/** @var QuestionHelper $question_helper */
|
||||
$question_helper = $this->getHelper('question');
|
||||
$run = $question_helper->ask(
|
||||
$input,
|
||||
$output,
|
||||
new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
|
||||
);
|
||||
if (!$run) {
|
||||
$output->writeln(
|
||||
'<comment>' . __('Migration aborted.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while ($table = $myisam_tables->next()) {
|
||||
$table_name = DB::quoteName($table['TABLE_NAME']);
|
||||
$output->writeln(
|
||||
'<comment>' . sprintf(__('Migrating table "%s"...'), $table_name) . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
$result = $this->db->query(sprintf('ALTER TABLE %s ENGINE = InnoDB', $table_name));
|
||||
|
||||
if (false === $result) {
|
||||
$message = sprintf(
|
||||
__('Migration of table "%s" failed with message "(%s) %s".'),
|
||||
$table_name,
|
||||
$this->db->errno(),
|
||||
$this->db->error()
|
||||
);
|
||||
$output->writeln(
|
||||
'<error>' . $message . '</error>',
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
return self::ERROR_TABLE_MIGRATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('<info>' . __('Migration done.') . '</info>');
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
}
|
||||
1500
inc/console/migration/racksplugintocorecommand.class.php
Normal file
1500
inc/console/migration/racksplugintocorecommand.class.php
Normal file
File diff suppressed because it is too large
Load Diff
216
inc/console/migration/timestampscommand.class.php
Normal file
216
inc/console/migration/timestampscommand.class.php
Normal file
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2015-2020 Teclib' and contributors.
|
||||
*
|
||||
* http://glpi-project.org
|
||||
*
|
||||
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
||||
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This file is part of GLPI.
|
||||
*
|
||||
* GLPI is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GLPI is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace Glpi\Console\Migration;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
use Glpi\Console\AbstractCommand;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
class TimestampsCommand extends AbstractCommand {
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('glpi:migration:timestamps');
|
||||
$this->setDescription(__('Convert "datetime" fields to "timestamp" to use timezones.'));
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
//convert db
|
||||
|
||||
// we are going to update datetime, date and time (?) types to timestamp type
|
||||
$tbl_iterator = $this->db->request([
|
||||
'SELECT' => ['information_schema.columns.table_name as TABLE_NAME'],
|
||||
'DISTINCT' => true,
|
||||
'FROM' => 'information_schema.columns',
|
||||
'INNER JOIN' => [
|
||||
'information_schema.tables' => [
|
||||
'ON' => [
|
||||
'information_schema.tables.table_name',
|
||||
'information_schema.columns.table_name', [
|
||||
'AND' => ['information_schema.tables.table_type' => 'BASE TABLE']
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [
|
||||
'information_schema.columns.table_schema' => $this->db->dbdefault,
|
||||
'information_schema.columns.data_type' => 'datetime'
|
||||
],
|
||||
'ORDER' => [
|
||||
'information_schema.columns.table_name'
|
||||
]
|
||||
]);
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
'<info>' . __('Found %s table(s) requiring migration.') . '</info>',
|
||||
$tbl_iterator->count()
|
||||
)
|
||||
);
|
||||
|
||||
if ($tbl_iterator->count() === 0) {
|
||||
$output->writeln('<info>' . __('No migration needed.') . '</info>');
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
if (!$input->getOption('no-interaction')) {
|
||||
// Ask for confirmation (unless --no-interaction)
|
||||
/** @var \Symfony\Component\Console\Helper\QuestionHelper $question_helper */
|
||||
$question_helper = $this->getHelper('question');
|
||||
$run = $question_helper->ask(
|
||||
$input,
|
||||
$output,
|
||||
new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
|
||||
);
|
||||
if (!$run) {
|
||||
$output->writeln(
|
||||
'<comment>' . __('Migration aborted.') . '</comment>',
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
$progress_bar = new ProgressBar($output, $tbl_iterator->count());
|
||||
$progress_bar->start();
|
||||
|
||||
while ($table = $tbl_iterator->next()) {
|
||||
$progress_bar->advance(1);
|
||||
|
||||
$tablealter = ''; // init by default
|
||||
|
||||
// get accurate info from information_schema to perform correct alter
|
||||
$col_iterator = $this->db->request([
|
||||
'SELECT' => [
|
||||
'table_name AS TABLE_NAME',
|
||||
'column_name AS COLUMN_NAME',
|
||||
'column_default AS COLUMN_DEFAULT',
|
||||
'column_comment AS COLUMN_COMMENT',
|
||||
'is_nullable AS IS_NULLABLE',
|
||||
],
|
||||
'FROM' => 'information_schema.columns',
|
||||
'WHERE' => [
|
||||
'table_schema' => $this->db->dbdefault,
|
||||
'table_name' => $table['TABLE_NAME'],
|
||||
'data_type' => 'datetime'
|
||||
]
|
||||
]);
|
||||
|
||||
while ($column = $col_iterator->next()) {
|
||||
$nullable = false;
|
||||
$default = null;
|
||||
//check if nullable
|
||||
if ('YES' === $column['IS_NULLABLE']) {
|
||||
$nullable = true;
|
||||
}
|
||||
|
||||
//guess default value
|
||||
if (is_null($column['COLUMN_DEFAULT']) && !$nullable) { // no default
|
||||
$default = null;
|
||||
} else if ((is_null($column['COLUMN_DEFAULT']) || strtoupper($column['COLUMN_DEFAULT']) == 'NULL') && $nullable) {
|
||||
$default = "NULL";
|
||||
} else if (!is_null($column['COLUMN_DEFAULT']) && strtoupper($column['COLUMN_DEFAULT']) != 'NULL') {
|
||||
if ($column['COLUMN_DEFAULT'] < '1970-01-01 00:00:01') {
|
||||
// Prevent default value to be out of range (lower to min possible value)
|
||||
$defaultDate = new \DateTime('1970-01-01 00:00:01', new \DateTimeZone('UTC'));
|
||||
$defaultDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
$default = $defaultDate->format("Y-m-d H:i:s");
|
||||
} else if ($column['COLUMN_DEFAULT'] > '2038-01-19 03:14:07') {
|
||||
// Prevent default value to be out of range (greater to max possible value)
|
||||
$defaultDate = new \DateTime('2038-01-19 03:14:07', new \DateTimeZone('UTC'));
|
||||
$defaultDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
$default = $defaultDate->format("Y-m-d H:i:s");
|
||||
} else {
|
||||
$default = $column['COLUMN_DEFAULT'];
|
||||
}
|
||||
}
|
||||
|
||||
//build alter
|
||||
$tablealter .= "\n\t MODIFY COLUMN ".$this->db->quoteName($column['COLUMN_NAME'])." TIMESTAMP";
|
||||
if ($nullable) {
|
||||
$tablealter .= " NULL";
|
||||
} else {
|
||||
$tablealter .= " NOT NULL";
|
||||
}
|
||||
if ($default !== null) {
|
||||
if ($default !== 'NULL') {
|
||||
$default = "'" . $this->db->escape($default) . "'";
|
||||
}
|
||||
$tablealter .= " DEFAULT $default";
|
||||
}
|
||||
if ($column['COLUMN_COMMENT'] != '') {
|
||||
$tablealter .= " COMMENT '".$this->db->escape($column['COLUMN_COMMENT'])."'";
|
||||
}
|
||||
$tablealter .= ",";
|
||||
}
|
||||
$tablealter = rtrim($tablealter, ",");
|
||||
|
||||
// apply alter to table
|
||||
$query = "ALTER TABLE " . $this->db->quoteName($table['TABLE_NAME']) . " " . $tablealter.";\n";
|
||||
$this->writelnOutputWithProgressBar(
|
||||
'<comment>' . sprintf(__('Running %s'), $query) . '</comment>',
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_VERBOSE
|
||||
);
|
||||
|
||||
$result = $this->db->query($query);
|
||||
if (false === $result) {
|
||||
$message = sprintf(
|
||||
__('Update of `%s` failed with message "(%s) %s".'),
|
||||
$table['TABLE_NAME'],
|
||||
$this->db->errno(),
|
||||
$this->db->error()
|
||||
);
|
||||
$this->writelnOutputWithProgressBar(
|
||||
'<error>' . $message . '</error>',
|
||||
$progress_bar,
|
||||
OutputInterface::VERBOSITY_QUIET
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$progress_bar->finish();
|
||||
$this->output->write(PHP_EOL);
|
||||
|
||||
$output->writeln('<info>' . __('Migration done.') . '</info>');
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user