moduleinstaller.class.inc.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. // Copyright (C) 2010-2017 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. require_once(APPROOT.'setup/setuppage.class.inc.php');
  19. /**
  20. * Class ModuleInstaller
  21. * Defines the API to implement module specific actions during the setup
  22. *
  23. * @copyright Copyright (C) 2010-2012 Combodo SARL
  24. * @license http://opensource.org/licenses/AGPL-3.0
  25. */
  26. abstract class ModuleInstallerAPI
  27. {
  28. public static function BeforeWritingConfig(Config $oConfiguration)
  29. {
  30. return $oConfiguration;
  31. }
  32. /**
  33. * Handler called before creating or upgrading the database schema
  34. * @param $oConfiguration Config The new configuration of the application
  35. * @param $sPreviousVersion string Previous version number of the module (empty string in case of first install)
  36. * @param $sCurrentVersion string Current version number of the module
  37. */
  38. public static function BeforeDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
  39. {
  40. }
  41. /**
  42. * Handler called after the creation/update of the database schema
  43. * @param $oConfiguration Config The new configuration of the application
  44. * @param $sPreviousVersion string Previous version number of the module (empty string in case of first install)
  45. * @param $sCurrentVersion string Current version number of the module
  46. */
  47. public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
  48. {
  49. }
  50. /**
  51. * Handler called at the end of the setup of the database (profiles and admin accounts created), but before the data load
  52. * @param $oConfiguration Config The new configuration of the application
  53. * @param $sPreviousVersion string Previous version number of the module (empty string in case of first install)
  54. * @param $sCurrentVersion string Current version number of the module
  55. */
  56. public static function AfterDatabaseSetup(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
  57. {
  58. }
  59. /**
  60. * Handler called at the end of the data load
  61. * @param $oConfiguration Config The new configuration of the application
  62. * @param $sPreviousVersion string Previous version number of the module (empty string in case of first install)
  63. * @param $sCurrentVersion string Current version number of the module
  64. */
  65. public static function AfterDataLoad(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
  66. {
  67. }
  68. /**
  69. * Helper to complete the renaming of a class
  70. * The renaming is made in the datamodel definition, but the name has to be changed in the DB as well
  71. * Must be called after DB update, i.e within an implementation of AfterDatabaseCreation()
  72. *
  73. * @param string $sFrom Original name (already INVALID in the current datamodel)
  74. * @param string $sTo New name (valid in the current datamodel)
  75. * @return void
  76. */
  77. public static function RenameClassInDB($sFrom, $sTo)
  78. {
  79. try
  80. {
  81. if (!MetaModel::IsStandaloneClass($sTo))
  82. {
  83. $sRootClass = MetaModel::GetRootClass($sTo);
  84. $sTableName = MetaModel::DBGetTable($sRootClass);
  85. $sFinalClassCol = MetaModel::DBGetClassField($sRootClass);
  86. $sRepair = "UPDATE `$sTableName` SET `$sFinalClassCol` = '$sTo' WHERE `$sFinalClassCol` = BINARY '$sFrom'";
  87. CMDBSource::Query($sRepair);
  88. $iAffectedRows = CMDBSource::AffectedRows();
  89. SetupPage::log_info("Renaming class in DB - final class from '$sFrom' to '$sTo': $iAffectedRows rows affected");
  90. }
  91. }
  92. catch(Exception $e)
  93. {
  94. SetupPage::log_warning("Failed to rename class in DB - final class from '$sFrom' to '$sTo'. Reason: ".$e->getMessage());
  95. }
  96. }
  97. /**
  98. * Helper to modify an enum value
  99. * The change is made in the datamodel definition, but the value has to be changed in the DB as well
  100. * Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
  101. * This helper does change ONE value at a time
  102. *
  103. * @param string $sClass A valid class name
  104. * @param string $sAttCode The enum attribute code
  105. * @param string $sFrom Original value (already INVALID in the current datamodel)
  106. * @param string $sTo New value (valid in the current datamodel)
  107. * @return void
  108. */
  109. public static function RenameEnumValueInDB($sClass, $sAttCode, $sFrom, $sTo)
  110. {
  111. try
  112. {
  113. if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
  114. {
  115. SetupPage::log_warning("Changing enum in DB - $sClass::$sAttCode - from '$sFrom' to '$sTo' failed. Reason '$sAttCode' is not a valid attribute of the class '$sClass'.");
  116. return;
  117. }
  118. $sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
  119. $sTableName = MetaModel::DBGetTable($sOriginClass);
  120. $oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sAttCode);
  121. if ($oAttDef instanceof AttributeEnum)
  122. {
  123. $oValDef = $oAttDef->GetValuesDef();
  124. if ($oValDef)
  125. {
  126. $aNewValues = array_keys($oValDef->GetValues(array(), ""));
  127. if (in_array($sTo, $aNewValues))
  128. {
  129. $sEnumCol = $oAttDef->Get("sql");
  130. $aFields = CMDBSource::QueryToArray("SHOW COLUMNS FROM `$sTableName` WHERE Field = '$sEnumCol'");
  131. if (isset($aFields[0]['Type']))
  132. {
  133. $sColType = $aFields[0]['Type'];
  134. // Note: the parsing should rely on str_getcsv (requires PHP 5.3) to cope with escaped string
  135. if (preg_match("/^enum\(\'(.*)\'\)$/", $sColType, $aMatches))
  136. {
  137. $aCurrentValues = explode("','", $aMatches[1]);
  138. }
  139. }
  140. if (!in_array($sFrom, $aNewValues))
  141. {
  142. if (!in_array($sTo, $aCurrentValues)) // if not already transformed!
  143. {
  144. $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
  145. if (strtolower($sTo) == strtolower($sFrom))
  146. {
  147. SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo' (just a change in the case)");
  148. $aTargetValues = array();
  149. foreach ($aCurrentValues as $sValue)
  150. {
  151. if ($sValue == $sFrom)
  152. {
  153. $sValue = $sTo;
  154. }
  155. $aTargetValues[] = $sValue;
  156. }
  157. $sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aTargetValues)).") $sNullSpec";
  158. $sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
  159. CMDBSource::Query($sRepair);
  160. }
  161. else
  162. {
  163. // 1st - Allow both values in the column definition
  164. //
  165. SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo'");
  166. $aAllValues = $aCurrentValues;
  167. $aAllValues[] = $sTo;
  168. $sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aAllValues)).") $sNullSpec";
  169. $sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
  170. CMDBSource::Query($sRepair);
  171. // 2nd - Change the old value into the new value
  172. //
  173. $sRepair = "UPDATE `$sTableName` SET `$sEnumCol` = '$sTo' WHERE `$sEnumCol` = BINARY '$sFrom'";
  174. CMDBSource::Query($sRepair);
  175. $iAffectedRows = CMDBSource::AffectedRows();
  176. SetupPage::log_info("Changing enum in DB - $iAffectedRows rows updated");
  177. // 3rd - Remove the useless value from the column definition
  178. //
  179. $aTargetValues = array();
  180. foreach ($aCurrentValues as $sValue)
  181. {
  182. if ($sValue == $sFrom)
  183. {
  184. $sValue = $sTo;
  185. }
  186. $aTargetValues[] = $sValue;
  187. }
  188. $sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aTargetValues)).") $sNullSpec";
  189. $sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
  190. CMDBSource::Query($sRepair);
  191. SetupPage::log_info("Changing enum in DB - removed useless value '$sFrom'");
  192. }
  193. }
  194. }
  195. else
  196. {
  197. SetupPage::log_warning("Changing enum in DB - $sClass::$sAttCode - '$sFrom' is still a valid value (".implode(', ', $aNewValues).")");
  198. }
  199. }
  200. else
  201. {
  202. SetupPage::log_warning("Changing enum in DB - $sClass::$sAttCode - '$sTo' is not a known value (".implode(', ', $aNewValues).")");
  203. }
  204. }
  205. }
  206. }
  207. catch(Exception $e)
  208. {
  209. SetupPage::log_warning("Changing enum in DB - $sClass::$sAttCode - '$sTo' failed. Reason ".$e->getMessage());
  210. }
  211. }
  212. }