Sindbad~EG File Manager

Current Path : /home/escuelai/www/it/marketplace/genericobject/inc/
Upload File :
Current File : /home/escuelai/www/it/marketplace/genericobject/inc/type.class.php

<?php

/**
 * -------------------------------------------------------------------------
 * GenericObject plugin for GLPI
 * -------------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GenericObject.
 *
 * GenericObject 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 3 of the License, or
 * (at your option) any later version.
 *
 * GenericObject 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 GenericObject. If not, see <http://www.gnu.org/licenses/>.
 * -------------------------------------------------------------------------
 * @copyright Copyright (C) 2009-2022 by GenericObject plugin team.
 * @license   GPLv3 https://www.gnu.org/licenses/gpl-3.0.html
 * @link      https://github.com/pluginsGLPI/genericobject
 * -------------------------------------------------------------------------
 */

class PluginGenericobjectType extends CommonDBTM {

   const INACTIVE = 0;
   const ACTIVE   = 1;

   const DRAFT     = 0;
   const PUBLISHED = 1;

   const CLASS_TEMPLATE              = "/objects/generic.class.tpl";
   const FORM_TEMPLATE               = "/objects/generic.form.tpl";
   const CLASS_DROPDOWN_TEMPLATE     = "/objects/generic.dropdown.class.tpl";
   const FRONTFORM_DROPDOWN_TEMPLATE = "/objects/front.form.tpl";
   const FRONT_DROPDOWN_TEMPLATE     = "/objects/front.tpl";
   const SEARCH_TEMPLATE             = "/objects/front.tpl";
   const AJAX_DROPDOWN_TEMPLATE      = "/objects/dropdown.tabs.tpl";
   const AJAX_TEMPLATE               = "/objects/ajax.tabs.tpl";
   const LOCALE_TEMPLATE             = "/objects/locale.tpl";
   const OBJECTINJECTION_TEMPLATE    = "/objects/objectinjection.class.tpl";
   const OBJECTITEM_TEMPLATE         = "/objects/object_item.class.tpl";

   const CAN_OPEN_TICKET             = 1024;

   var $dohistory                    = true;

   static $rightname                 = 'plugin_genericobject_types';


   function __construct($itemtype = false) {
      if ($itemtype) {
         $this->getFromDBByType($itemtype);
      }
   }

   function isEntityAssign() {
      return false;
   }

   static function getTypeName($nb = 0) {
      return __("Type of objects", "genericobject");
   }

   static function &getInstance($itemtype, $refresh = false) {
      static $singleton = [];
      if (!isset($singleton[$itemtype]) ||$refresh) {
         $singleton[$itemtype] = new self($itemtype);
      }
      return $singleton[$itemtype];
   }


   function getFromDBByType($itemtype) {
      global $DB;

      $query  = "SELECT * FROM `" . getTableForItemType(__CLASS__) . "` " .
                "WHERE `itemtype`='$itemtype'";
      $result = $DB->query($query);
      if ($DB->numrows($result) > 0) {
         $this->fields = $DB->fetchArray($result);
      } else {
         $this->getEmpty();
      }
   }


   //------------------------------------ Tabs management -----------------------------------
   function defineTabs($options = []) {
      $tabs = [];
      $this->addStandardTab(__CLASS__, $tabs, $options);
      return $tabs;
   }

   function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
      if (!$withtemplate) {
         switch ($item->getType()) {
            case __CLASS__ :
               // Number of fields in database
               $itemtype = $item->fields['itemtype'];
               $nb_fields = 0;
               if (class_exists($itemtype)) {
                  $obj = new $itemtype();
                  $obj->getEmpty();
                  $nb_fields = count($obj->fields);
               }

               $tabs =  [
                  1  => __("Main"),
                  3 => _n("Field", "Fields", 2),
                  3 => self::createTabEntry(_n("Field", "Fields", Session::getPluralNumber()), $nb_fields),
                  5 => __("Preview")
               ];
               if ($item->canUseDirectConnections()) {
                  $tabs[7] = __("Associated element");
               }
               return $tabs;
         }
      }
      return '';

   }

   static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
      if ($item->getType() == __CLASS__) {
         switch ($tabnum) {
            case 1 :
               $item->showBehaviorForm($item->getID());
               break;

            case 3:
               PluginGenericobjectField::showObjectFieldsForm($item->getID());
               break;

            case 5:
               PluginGenericobjectObject::showPrevisualisationForm($item);
               break;

            case 6:
               PluginGenericobjectProfile::showForItemtype($item);
               break;
         }
      }
      return true;
   }
   //------------------------------------- End tabs management ------------------------------

   //------------------------------------- Framework hooks ----------------------------------
   function prepareInputForAdd($input) {
      //Name must not be empty
      if (isset($input['name']) && $input['name'] == '') {
         Session::addMessageAfterRedirect(__("Type name is missing", "genericobject"), ERROR, true);
         return [];
      }

      // Name must be more than 1 char
      if (isset($input['name']) && strlen($input['name']) < 2) {
         Session::addMessageAfterRedirect(__("Type name must be longer", "genericobject"), ERROR, true);
         return [];
      }

      //Name must not match specific names
      if (in_array($input['name'], ['field', 'object', 'type'])) {
         Session::addMessageAfterRedirect(__("Types 'field', 'object' and 'type' are reserved. Please choose another one",
                                             "genericobject"), ERROR, true);
         return [];
      }

      //Name must start with a letter
      if (!preg_match("/^[a-zA-Z]+/i", $input['name'])) {
         Session::addMessageAfterRedirect(__("Type must start with a letter", "genericobject"), ERROR, true);
         return [];
      }
      $input['name']     = self::filterInput($input['name']);

      //Name must not be present in DB
      if (countElementsInTable(getTableForItemType(__CLASS__), ['name' => $input['name']])) {
         Session::addMessageAfterRedirect(__("A type already exists with the same name", "genericobject"), ERROR, true);
         return [];
      } else {
         $input['itemtype'] = self::getClassByName($input['name']);
         return $input;
      }
   }

   function post_addItem() {
      self::addNewObject(
         $this->input["name"],
         $this->input["itemtype"],
         ['add_table' => 1, 'create_default_profile' => 1, 'overwrite_locales' => true]
      );
      return true;
   }

   function prepareInputForUpdate($input) {
      // Handle impact_icon
      $input = $this->handleImpactIconUpdate($input);

      // Handle use_impact
      $input = $this->handleUseImpactUpdate($input);

      //If itemtype is active : register it !
      if (isset ($input["is_active"]) && $input["is_active"]) {
         self::registerOneType($this->fields['itemtype']);
      }
      return $input;
   }

   function handleImpactIconUpdate($input) {
      // Read submitted icon
      $icon = $input['_impact_icon'][0] ?? null;

      // Icon wasn't submitted, nothing more to do
      if (empty($icon)) {
         return $input;
      }

      // Convert to realpath
      $icon_path = realpath(GLPI_TMP_DIR . "/$icon");

      // Realpath didn't find the file, shouldn't really happenn but just in case
      if (!$icon_path) {
         return $input;
      }

      // Wrong file type, ignore
      if (!Document::isImage($icon_path)) {
         return $input;
      }

      // File is outside of GLPI_TMP_DIR
      if (!str_starts_with($icon_path, realpath(GLPI_TMP_DIR))) {
         trigger_error("Trying to read forbidden file: $icon_path", E_USER_WARNING);
         return $input;
      }

      // Reread base file name
      $icon_filename = pathinfo($icon_path, PATHINFO_BASENAME);

      // Remove previous icon if exist
      $existing_icon_path = self::getImpactIconFileStoragePath(
         $this->fields['impact_icon'],
         $this->fields['itemtype']
      );
      if ($existing_icon_path
         && file_exists($existing_icon_path)
         && str_starts_with(
            realpath($existing_icon_path),
            realpath(GLPI_PLUGIN_DOC_DIR . "/genericobject/impact_icons/")
         )
      ) {
         unlink($existing_icon_path);
      }

      // Move file and update input on success
      $icons_dir = GLPI_PLUGIN_DOC_DIR . '/genericobject/impact_icons/';
      if (!is_dir($icons_dir) && !mkdir($icons_dir)) {
         trigger_error(sprintf('Unable to create "%s" directory.', $icons_dir), E_USER_WARNING);
         return $input;
      }

      $new_path = self::getImpactIconFileStoragePath(
         $icon_filename,
         $this->fields['itemtype']
      );
      if (rename($icon_path, $new_path)) {
         $input['impact_icon'] = $icon_filename;
      }

      return $input;
   }

   function handleUseImpactUpdate($input) {
      $use_impact = $input['use_impact'] ?? null;
      unset($input['use_impact']);

      // Value wasn't modified, nothing to be done
      if ($use_impact === null) {
         return $input;
      }

      // Impact analysis will now be enabled, update conf if needed
      if ($use_impact && !Impact::isEnabled($this->fields['itemtype'])) {
         $enabled = Config::getConfigurationValue('core', Impact::CONF_ENABLED);
         $enabled = importArrayFromDB($enabled);
         $enabled[] = $this->fields['itemtype'];
         Config::setConfigurationValues('core', [
            Impact::CONF_ENABLED => exportArrayToDB($enabled)
         ]);
         return $input;
      }

      // Impact analysis will now be disabled, update config if needed
      if (!$use_impact && Impact::isEnabled($this->fields['itemtype'])) {
         $enabled = Config::getConfigurationValue('core', Impact::CONF_ENABLED);
         $enabled = importArrayFromDB($enabled);
         $enabled = array_filter(
            $enabled,
            fn($i) => $i != $this->fields['itemtype']
         );
         Config::setConfigurationValues('core', [
            Impact::CONF_ENABLED => exportArrayToDB($enabled)
         ]);

         return $input;
      }

      return $input;
   }

   function post_updateItem($history = 1) {
      //Check if some fields need to be added, because of GLPI framework
      $this->checkNecessaryFieldsUpdate();
   }

   function pre_deleteItem() {
      if ($this->getFromDB($this->fields["id"])) {
         $name     = $this->fields['name'];
         $itemtype = $this->fields['itemtype'];

         //Delete all network ports
         self::deleteNetworking($itemtype);

         //Drop all dropdowns associated with itemtype
         self::deleteDropdownsForItemtype($itemtype);

         //Delete loans associated with this type
         self::deleteLoans($itemtype);

         //Delete loans associated with this type
         self::deleteUnicity($itemtype);

         //Delete reservations with this tyoe
         self::deleteReservations($itemtype);
         self::deleteReservationItems($itemtype);

         //Remove datainjection specific file
         self::deleteInjectionFile($name);

         //Delete profile informations associated with this type
         PluginGenericobjectProfile::deleteTypeFromProfile($itemtype);

         self::deleteTicketAssignation($itemtype);

         //Remove associations to simcards with this type
         self::deleteSimcardAssignation($itemtype);

         //Remove existing datainjection models
         self::removeDataInjectionModels($itemtype);

         //Delete specific locale directory
         self::deleteLocales($name, $itemtype);

         self::deleteItemtypeReferencesInGLPI($itemtype);

         self::deleteItemTypeFilesAndClasses($name, $this->getTable(), $itemtype);

         //self::deleteNotepad($itemtype);

         if (preg_match("/PluginGenericobject(.*)/", $itemtype, $results)) {
                  $newrightname = 'plugin_genericobject_'.strtolower($results[1]).'s';
            ProfileRight::deleteProfileRights([$newrightname]);
         }

         $prof     = new Profile();
         $profiles = getAllDataFromTable('glpi_profiles');
         foreach ($profiles as $profile) {
            $helpdesk_item_types = json_decode($profile['helpdesk_item_type'], true);
            if ($helpdesk_item_types !== null) {
               $index               = array_search($itemtype, $helpdesk_item_types);
               if ($index) {
                  unset($helpdesk_item_types[$index]);
                  $tmp['id']                 = $profile['id'];
                  $tmp['helpdesk_item_type'] = json_encode($helpdesk_item_types);
                  $prof->update($tmp);
               }
            }
         }

         return true;
      } else {
         return false;
      }

   }

   public function post_deleteItem() {

   }

   function rawSearchOptions() {
      $sopt = [];

      $sopt[] = [
         'id'   => 'common',
         'name' => __("Objects management", "genericobject"),
      ];

      $sopt[] = [
         'id'            => 1,
         'table'         => $this->getTable(),
         'field'         => 'name',
         'name'          => __('Model'),
         'datatype'      => 'itemlink',
         'autocomplete'  => true,
      ];

      $sopt[] = [
         'id'            => 5,
         'table'         => $this->getTable(),
         'field'         => 'is_active',
         'name'          => __('Active'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 6,
         'table'         => $this->getTable(),
         'field'         => 'use_tickets',
         'name'          => __('Associable to a ticket'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 9,
         'table'         => $this->getTable(),
         'field'         => 'use_history',
         'name'          => _sx('button', 'Use') . ' ' . __('Historical'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 13,
         'table'         => $this->getTable(),
         'field'         => 'use_infocoms',
         'name'          => _sx('button', 'Use') . ' ' . __('Financial and administratives information'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 14,
         'table'         => $this->getTable(),
         'field'         => 'use_documents',
         'name'          => _sx('button', 'Use') . ' ' . _n('Document', 'Documents', 2),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 15,
         'table'         => $this->getTable(),
         'field'         => 'use_loans',
         'name'          => _sx('button', 'Use') . ' ' . _n('Reservation', 'Reservations', 2),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 16,
         'table'         => $this->getTable(),
         'field'         => 'use_contracts',
         'name'          => _sx('button', 'Use') . ' ' . _n('Contract', 'Contracts', 2),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 17,
         'table'         => $this->getTable(),
         'field'         => 'use_unicity',
         'name'          => _sx('button', 'Use') . ' ' . __('Fields unicity'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 18,
         'table'         => $this->getTable(),
         'field'         => 'use_global_search',
         'name'          => __('Global search'),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 19,
         'table'         => 'glpi_plugin_genericobject_typefamilies',
         'field'         => 'name',
         'name'          => __('Family of type of objects', 'genericobject'),
         'datatype'      => 'dropdown',
      ];

      $sopt[] = [
         'id'            => 20,
         'table'         => $this->getTable(),
         'field'         => 'use_projects',
         'name'          => _n('Project', 'Projects', 2),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 21,
         'table'         => $this->getTable(),
         'field'         => 'date_mod',
         'name'          => __('Last update'),
         'datatype'      => 'datetime',
         'massiveaction' => false,
      ];

      $sopt[] = [
         'id'            => 22,
         'table'         => $this->getTable(),
         'field'         => 'use_itemdevices',
         'name'          => _sx('button', 'Use') . ' ' . _n('Component', 'Components', 2),
         'datatype'      => 'bool',
      ];

      $sopt[] = [
         'id'            => 121,
         'table'         => $this->getTable(),
         'field'         => 'date_creation',
         'name'          => __('Creation date'),
         'datatype'      => 'datetime',
         'massiveaction' => false,
      ];

      return $sopt;
   }

   /**
    * Define name of type to display in menu
    *
    * @return type name
    */
   static function getMenuName() {
      return __('Objects management', 'genericobject');
   }

   //------------------------------------- End Framework hooks -----------------------------

   //------------------------------------- Forms -------------------------------------------
   function showForm($ID, $options = []) {

      if ($ID > 0) {
         $this->check($ID, READ);
      } else {
         // Create item
         $this->check(-1, CREATE);
         $this->getEmpty();
      }

      $this->initForm($ID);

      $item = new self();
      $item->showBehaviorForm($ID);

      return true;
   }

   function showBehaviorForm($ID, $options = []) {
      global $CFG_GLPI;

      if ($ID > 0) {
         $this->check($ID, READ);
      } else {
         // Create item
         $this->check($ID, CREATE);
         $use_cache = false;
         $this->getEmpty();
      }

      $this->fields['id'] = $ID;

      $right_name = PluginGenericobjectProfile::getProfileNameForItemtype(
         __CLASS__
      );

      $canedit = Session::haveRight($right_name, UPDATE);

      self::includeLocales($this->fields["name"]);
      self::includeConstants($this->fields["name"]);

      $this->showFormHeader($options);

      echo "<tr class='tab_bg_1'>";
      echo "<td>" . __("Internal identifier", "genericobject") . "</td>";
      echo "<td>";
      if (!$ID) {
         echo Html::input(
            'name',
            [
               'value' => $this->fields['name'],
            ]
         );
      } else {
         echo "<input type='hidden' name='name' value='" . $this->fields["name"] . "'>";
         echo $this->fields["name"];
      }

      echo "</td>";
      echo "<td></td>";
      echo "<td></td>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td>" . __("Label") . "</td>";
      echo "<td>";
      if ($ID) {
         $itemtype = $this->fields["itemtype"];
         echo $itemtype::getTypeName();
      }
      echo "</td>";
      echo "<td rowspan='3' class='middle right'>".__("Comments")."&nbsp;: </td>";
      echo "<td class='center middle' rowspan='3'><textarea cols='45' rows='4'
             name='comment' >".$this->fields["comment"]."</textarea></td>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td>".__("Active")."</td>";
      echo "<td>";
      if (!$ID) {
         echo __("No");
      } else {
         Dropdown::showYesNo("is_active", $this->fields["is_active"]);
      }
      echo "</td></td>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td>".__("Family of type of objects", 'genericobject')."</td>";
      echo "<td>";
      PluginGenericobjectTypeFamily::dropdown([
         'value' => $this->fields["plugin_genericobject_typefamilies_id"]
      ]);
      echo "</td></td>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td colspan='2'></td>";
      echo "</tr>";

      if (!$this->isNewID($ID)) {
         $canedit = $this->can($ID, CREATE);
         echo "<tr class='tab_bg_1'><th colspan='4'>";
         echo __("Behaviour", "genericobject");
         echo "</th></tr>";

         $use = [
            "use_recursivity"   => __("Child entities"),
            "use_tickets"       => __("Assistance"),
            "use_deleted"       => __("Item in the dustbin"),
            "use_notepad"       => _n('Note', 'Notes', 2),
            "use_history"       => __("Historical"),
            "use_template"      => __("Templates"),
            "use_infocoms"      => __("Financial and administratives information"),
            "use_contracts"     => _n("Contract", "Contracts", 2),
            "use_documents"     => _n("Document", "Documents", 2),
            "use_loans"         => _n("Reservation", "Reservations", 2),
            // Disable unicity feature; see #16
            // Related code : search for #16
            "use_unicity"       => __("Fields unicity"),
            "use_global_search" => __("Global search"),
            "use_projects"      => _n("Project", "Projects", 2),
            "use_network_ports" => __("Network connections", "genericobject"),
            "use_itemdevices"   => _n('Component', 'Components', 2),
            "use_impact"        => Impact::getTypeName(),
         ];

         $plugins = [
            "use_plugin_datainjection"      => __("injection file plugin", "genericobject"),
            //"use_plugin_pdf"                => __("PDF plugin", "genericobject"),
            "use_plugin_geninventorynumber" => __("geninventorynumber plugin", "genericobject"),
            "use_plugin_order"              => __("order plugin", "genericobject"),
            "use_plugin_uninstall"          => __("item's uninstallation plugin", "genericobject"),
            "use_plugin_simcard"            => __("simcard plugin", "genericobject"),
            "use_plugin_treeview"           => __("treeview plugin", "genericobject"),
         ];

         $plugin = new Plugin();
         $odd = 0;
         foreach ($use as $right => $label) {
            if (!$odd) {
               echo "<tr class='tab_bg_2'>";
            }
            echo "<td>" . _sx('button', 'Use') . " " . $label . "</td>";
            echo "<td>";

            switch ($right) {
               case 'use_deleted':
                  Html::showCheckbox(['name'    => $right,
                                      'checked' => $this->canBeDeleted()]);
                  break;

               case 'use_recursivity':
                  Html::showCheckbox(['name'    => $right,
                                      'value'   => $this->canBeRecursive(),
                                      'checked' => $this->canBeRecursive()]);
                  break;

               case 'use_notes':
                  Html::showCheckbox(['name'    => $right,
                                      'checked' => $this->canUseNotepad()]);
                  break;

               case 'use_template':
                  Html::showCheckbox(['name'    => $right,
                                      'checked' => $this->canUseTemplate()]);
                  break;

               case 'use_impact':
                  Html::showCheckbox([
                     'name'    => $right,
                     'checked' => Impact::isEnabled($this->fields['itemtype'])
                  ]);
                  break;

               default :
                  Html::showCheckbox(['name'    => $right,
                                      'checked' => $this->fields[$right]]);
                  break;
            }
            echo "</td>";
            if ($odd == 1) {
               $odd = 0;
               echo "</tr>";
            } else {
               $odd++;
            }
         }
         if ($odd != 0) {
            echo "<td></td></tr>";
         }

         echo "<tr class='tab_bg_1'><th colspan='4'>";
         echo __("Icon (impact analysis)", "genericobject");
         echo "</th></tr>";

         echo '<tr>';
         echo "<td colspan='4'>";
         $src = $this->getImpactIconUrl() ?? $CFG_GLPI["root_doc"] . "/pics/impact/default.png";
         echo "<img src='$src' height='128px'></img>";
         echo "</td>";
         echo '</tr>';

         echo '<tr>';
         echo "<td colspan='2'>";
         echo Html::file([
            'name'       => "impact_icon",
            'onlyimages' => true,
         ]);
         echo "</td>";
         echo "<td></td>";
         echo '</tr>';

         echo "<tr class='tab_bg_1'><th colspan='4'>";
         echo _n("Plugin", "Plugins", 2);
         echo "</th></tr>";
         $odd=0;
         foreach ($plugins as $right => $label) {
            if (!$odd) {
               echo "<tr class='tab_bg_2'>";
            }
            echo "<td>" . _sx('button', 'Use') . " " . $label . "</td>";
            echo "<td>";
            switch ($right) {
               case 'use_plugin_datainjection' :
                  if ($plugin->isActivated('datainjection')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_datainjection' value='0'>\n";
                  }
                  break;

               case 'use_plugin_pdf' :
                  if ($plugin->isActivated('pdf')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_pdf' value='0'>\n";
                  }
                  break;

               case 'use_plugin_order' :
                  if ($plugin->isActivated('order')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_order' value='0'>\n";
                  }
                  break;

               case 'use_plugin_uninstall' :
                  if ($plugin->isActivated('uninstall')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_uninstall' value='0'>\n";
                  }
                  break;

               case 'use_plugin_simcard' :
                  if ($plugin->isActivated('simcard')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_simcard' value='0'>\n";
                  }
                  break;
               case 'use_plugin_treeview' :
                  if ($plugin->isActivated('treeview')) {
                     Html::showCheckbox(['name' => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_treeview' value='0'>\n";
                  }

                  break;
               case 'use_plugin_geninventorynumber' :
                  if ($plugin->isActivated('geninventorynumber')) {
                     Html::showCheckbox(['name'    => $right,
                                         'checked' => $this->fields[$right]]);
                  } else {
                     echo Dropdown::EMPTY_VALUE;
                     echo "<input type='hidden' name='use_plugin_geninventorynumber' value='0'>\n";
                  }
                  break;
            }
            echo "</td>";
            if ($odd == 1) {
               $odd = 0;
               echo "</tr>";
            } else {
               $odd++;
            }
         }
         if ($odd != 0) {
            echo "<td></td></tr>";
         }

      }

      $this->showFormButtons($options);
   }

   /**
    *
    * Show a form with a button to regenerate all files
    * @since 2.2.0
    * @param $ID type ID
    * @return nothing
    */
   function showFilesForm() {
      echo "<form name='generate' method='post'>";
      echo "<div class='center'>";
      echo "<table class='tab_cadre_fixe'>";
      echo "<tr class='tab_bg_1'>";
      echo "<td class='center'>";
      echo "<input type='hidden' name='id' value='".$this->getID()."'>";
      echo "<input type='submit' class='submit' name='regenerate'
                    value='".__("Regenerate files", "genericobject")."'>";
      echo "</td></tr></table></div>";
      Html::closeForm();
   }

   function showLinkedTypesForm() {
      global $GO_LINKED_TYPES;

      $this->showFormHeader();
      echo "<form name='link' method='post'>";
      echo "<div class='center'>";
      echo "<table class='tab_cadre_fixe'>";
      echo "<tr class='tab_bg_1'>";
      echo "<th colspan='2'>".__("Link to other objects", "genericobject")."</th></tr>";
      echo "<tr class='tab_bg_1'>";
      echo "<td>"._n("Type", "Types", 2)."</td>";
      echo "<td class='center'>";
      echo "<select name='itemtypes[]' multiple size='10'>";
      $selected = [];
      if (!empty($this->fields['linked_itemtypes'])) {
         $selected = json_decode($this->fields['linked_itemtypes'], false);
      }
      foreach ($GO_LINKED_TYPES as $itemtype) {
         if ($itemtype == $this->fields['itemtype']) {
            continue;
         }
         echo "<option value='$itemtype'";
         if (in_array($itemtype, $selected)) {
            echo " selected ";
         }
         echo ">".$itemtype::getTypeName()."</options>";
      }
      echo "</select>";
      echo "</td></tr>";
      echo "<input type='hidden' name='id' value='".$this->getID()."'>";
      $this->showFormButtons(['candel' => false, 'canadd' => false]);
      Html::closeForm();

   }
   //------------------------------------- End Forms --------------------------------------

   /**
    * Create an object, it's table, files and rights
    *
    * @since 2.1.5
    * @param name object short name
    * @param itemtype object class name
    * @param options create options :
    *    - add_table : add the object table (default is no)
    *    - create_default_profile : add default right (default is no) for current user profile
    *    - add_injection_file : add file to integrate itemtype into the datainjection plugin
    *    - add_language_file : create a default language for the itemtype
    * @return none
    */
   static function addNewObject($name, $itemtype, $options = []) {
      $params['add_table']              = false;
      $params['create_default_profile'] = false;
      $params['add_injection_file']     = false;
      $params['add_language_file']      = true;
      $params['overwrite_locales']      = false;

      foreach ($options as $key => $value) {
         $params[$key] = $value;
      }

      if ($params['add_table']) {
         self::addTable($itemtype);
      }

      //Write object class on the filesystem
      self::addClassFile($name, $itemtype);

      //Write the form on the filesystem
      self::addFormFile($name, $itemtype);
      self::addSearchFile($name, $itemtype);

      if ($params['overwrite_locales']) {
         //Add language file
         self::addLocales($name, $itemtype);
      }

      //Add file needed by datainjectin plugin
      if ($params['add_injection_file']) {
         self::addDatainjectionFile($name);
      }
      PluginGenericobjectProfile::installRights();
      if ($params['create_default_profile']) {
         //Create rights for this new object
         PluginGenericobjectProfile::createAccess($_SESSION["glpiactiveprofile"]["id"], $itemtype, true);
         //Reload profiles
         PluginGenericobjectProfile::changeProfile();
      }
   }

   /**
    *
    * Add a new dropdown :class & files
    * @param string $name
    * @param string $itemtype
    * @param array $options
    */
   static function addNewDropdown($name, $itemtype, $options = []) {
      $params['entities_id']     = false;
      $params['is_recursive']    = false;
      $params['is_tree']         = false;
      $params['linked_itemtype'] = false;
      foreach ($options as $key => $value) {
         $params[$key] = $value;
      }
      //Add files on the disk
      self::addDropdownClassFile($name, $itemtype, $params);
      self::addDropdownTable(getTableForItemType($itemtype), $params);
      self::addDropdownFrontFile($name);
      self::addDropdownFrontformFile($name);

      // Invalidate submenu data in current session
      unset($_SESSION['glpimenu']);
   }

   /**
    *
    * Add or delete, if needed some fields to make sure that the itemtype is compatible with
    * GLPI framework
    */
   function checkNecessaryFieldsUpdate() {
      global $DB;

      $itemtype = $this->fields["itemtype"];
      $item     = new $itemtype();
      $item->getEmpty();
      $table    = getTableForItemType($itemtype);

      //Global search (inventory > status)
      if (isset($this->input['use_global_search']) && $this->input['use_global_search']) {
         PluginGenericobjectField::addNewField($table, 'serial', 'name');
         PluginGenericobjectField::addNewField($table, 'otherserial', 'serial');
         PluginGenericobjectField::addNewField($table, 'locations_id', 'otherserial');
         PluginGenericobjectField::addNewField($table, 'states_id', 'locations_id');
         PluginGenericobjectField::addNewField($table, 'users_id', 'states_id');
         PluginGenericobjectField::addNewField($table, 'groups_id', 'users_id');
         PluginGenericobjectField::addNewField($table, 'manufacturers_id', 'groups_id');
         PluginGenericobjectField::addNewField($table, 'users_id_tech', 'manufacturers_id');
         PluginGenericobjectField::addNewField($table, 'is_deleted', 'id');
      }

      if (isset($this->input['use_recursivity']) && $this->input['use_recursivity']) {
         PluginGenericobjectField::addNewField($table, 'is_recursive', 'entities_id');
      } else {
         PluginGenericobjectField::deleteField($table, 'is_recursive');
      }

      //Template
      if (isset($this->input['use_template']) && $this->input['use_template']) {
         PluginGenericobjectField::addNewField($table, 'is_template', 'id');
         PluginGenericobjectField::addNewField($table, 'template_name', 'is_template');
      } else {
         PluginGenericobjectField::deleteField($table, 'is_template');
         PluginGenericobjectField::deleteField($table, 'template_name');
      }

      //Trash
      if (isset($this->input['use_deleted']) && $this->input['use_deleted']) {
         PluginGenericobjectField::addNewField($table, 'is_deleted', 'id');
      } else {
         if (!$this->canBeReserved()) {
            PluginGenericobjectField::deleteField($table, 'is_deleted');
         } else {
            _log($DB->fieldExists($table, 'is_deleted'));
            if ($DB->fieldExists($table, 'is_deleted')) {
               Session::addMessageAfterRedirect(
                  __("Dustbin can't be removed since Reservations are used on this type."),
                  false,
                  WARNING
               );
            }
         }
      }

      //Reservation needs is_deleted field !
      if ($this->canBeReserved()) {
         PluginGenericobjectField::addNewField($table, 'is_deleted', 'id');
         PluginGenericobjectField::addNewField($table, 'locations_id');
         PluginGenericobjectField::addNewField($table, 'users_id');
      }

      //Helpdesk post-only
      if ($this->canUseTickets()) {
         //TODO rename is_helpdesk_visible into is_helpdeskvisible
         PluginGenericobjectField::addNewField($table, 'is_helpdesk_visible', 'comment');
      } else {
         PluginGenericobjectField::deleteField($table, 'is_helpdesk_visible');
      }

      //Notes
      if (isset($this->input['use_notepad']) && $this->input['use_notepad']) {
         PluginGenericobjectField::addNewField($table, 'notepad', 'id');
      } else {
         PluginGenericobjectField::deleteField($table, 'notepad');
      }

      //Networkport
      if ($this->canUseNetworkPorts()) {
         PluginGenericobjectField::addNewField($table, 'locations_id');
      }

      if ($this->canUseDirectConnections()) {
         self::addItemsTable($itemtype);
         //self::addItemClassFile($this->fields['name'], $itemtype);
      } else {
         self::deleteItemsTable($itemtype);
         self::deleteClassFile($this->fields['name']."_item");
      }

      if ($this->canUsePluginDataInjection() &&
         !file_exists(self::getCompleteInjectionFilename($this->fields['name']))) {
         self::addDatainjectionFile($this->fields['name']);
      }

      if (!$this->canUsePluginDataInjection() &&
         file_exists(self::getCompleteInjectionFilename($this->fields['name']))) {
         self::deleteInjectionFile($this->fields['name']);
      }

      //Device item needs locations_id field !
      if ($this->canUseItemDevice()) {
         PluginGenericobjectField::addNewField($table, 'locations_id');
      }
   }


   /**
    * Add object type table + entries in glpi_display
    * @name object type's name
    * @return nothing
    */
   public static function addTable($itemtype) {
      global $DB;

      $default_charset = DBConnection::getDefaultCharset();
      $default_collation = DBConnection::getDefaultCollation();
      $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption();

      $query = "CREATE TABLE IF NOT EXISTS `".getTableForItemType($itemtype)."` (
                  `id` INT {$default_key_sign} NOT NULL AUTO_INCREMENT,
                  `entities_id` INT {$default_key_sign} NOT NULL DEFAULT '0',
                  `name` VARCHAR( 255 ) NOT NULL DEFAULT '',
                  `comment` text,
                  `notepad` text,
                  `date_mod` TIMESTAMP NULL DEFAULT NULL,
                  `date_creation` TIMESTAMP NULL DEFAULT NULL,
                  PRIMARY KEY ( `id` ),
                  KEY `date_mod` (`date_mod`),
                  KEY `date_creation` (`date_creation`)
                  ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";
      $DB->query($query);

      $query = "INSERT INTO `glpi_displaypreferences` (`id`, `itemtype`, `num`, `rank`, `users_id`) " .
               "VALUES (NULL, '$itemtype', '2', '1', '0');";
      $DB->query($query);

   }

   /**
    * Add object_items table to connect an object to others
    * @name object type's name
    * @return nothing
    */
   public static function addItemsTable($itemtype) {
      global $DB;

      $default_charset = DBConnection::getDefaultCharset();
      $default_collation = DBConnection::getDefaultCollation();
      $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption();

      $table = getTableForItemType($itemtype);
      $fk    = getForeignKeyFieldForTable($table);
      $query = "CREATE TABLE IF NOT EXISTS `".getTableForItemType($itemtype)."_items` (
        `id` int {$default_key_sign} NOT NULL AUTO_INCREMENT,
        `items_id` int {$default_key_sign} NOT NULL DEFAULT '0' COMMENT 'RELATION to various table, according to itemtype (ID)',
        `date_mod` TIMESTAMP NULL DEFAULT NULL,
        `date_creation` TIMESTAMP NULL DEFAULT NULL,
        `$fk` int {$default_key_sign} NOT NULL DEFAULT '0',
        `itemtype` varchar(100) NOT NULL,
        PRIMARY KEY (`id`),
        KEY `$fk` (`$fk`),
        KEY `date_mod` (`date_mod`),
        KEY `date_creation` (`date_creation`),
        KEY `item` (`itemtype`,`items_id`)
      ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";
      $DB->query($query);
   }


   //-------------------------------- FILE CREATION / DELETION ----------------------------//
   public static function deleteFile($filename) {
      if (file_exists($filename)) {
         unlink($filename);
      }
   }


   static function getCompleteClassFilename($name) {
      return GENERICOBJECT_CLASS_PATH . "/".self::getSystemName($name).".class.php";
   }


   static function getCompleteItemClassFilename($name) {
      return GENERICOBJECT_CLASS_PATH . "/".self::getSystemName($name)."_item.class.php";
   }


   static function getCompleteFormFilename($name) {
      return GENERICOBJECT_FRONT_PATH . "/".self::getSystemName($name).".form.php";
   }


   static function getCompleteSearchFilename($name) {
      return GENERICOBJECT_FRONT_PATH . "/".self::getSystemName($name).".php";
   }


   static function getCompleteAjaxTabFilename($name) {
      return GENERICOBJECT_AJAX_PATH . "/".self::getSystemName($name).".tabs.php";
   }


   static function getCompleteInjectionFilename($name) {
      return GENERICOBJECT_CLASS_PATH . "/".self::getSystemName($name).".injection.class.php";
   }


   static function getCompleteConstantFilename($name) {
      return GENERICOBJECT_FIELDS_PATH . "/".self::getSystemName($name).".constant.php";
   }


   /**
    * Delete an used form file
    * @param name the name of the object type
    * @return nothing
    */
   public static function deleteFormFile($name) {
      self::deleteFile(self::getCompleteFormFilename($name));
   }


   public static function deleteSearchFile($name) {
      self::deleteFile(self::getCompleteSearchFilename($name));
   }


   public static function deleteAjaxFile($name) {
      self::deleteFile(self::getCompleteAjaxTabFilename($name));
   }


   /**
    * Delete an used class file
    * @param name the name of the object type
    * @return nothing
    */
   public static function deleteClassFile($name) {
      self::deleteFile(self::getCompleteClassFilename($name));
   }


   public static function deleteInjectionFile($name) {
      self::deleteFile(self::getCompleteInjectionFilename($name));
   }


   public static function addLocales($name, $itemtype) {
      global $CFG_GLPI;

      $fsname = self::getSystemName($name);

      $locale_dir = GENERICOBJECT_LOCALES_PATH."/".$fsname;
      if (!is_dir($locale_dir)) {
         @ mkdir($locale_dir, 0755, true);
      }

      $locale_files = [
         $fsname . '.' . $_SESSION['glpilanguage'],
      ];
      if ($CFG_GLPI['language'] != $_SESSION['glpilanguage']) {
         $locale_files[] = $fsname . '.' . $CFG_GLPI['language'];
      }

      foreach ($locale_files as $locale_file) {
         self::addFileFromTemplate(
            [
               'NAME'      => $name,
               'CLASSNAME' => self::getClassByName($name),
            ],
            self::LOCALE_TEMPLATE,
            $locale_dir,
            $locale_file
         );
      }
   }


   public static function deleteLocales($name, $itemtype) {
      $locale_dir = GENERICOBJECT_LOCALES_PATH."/".self::getSystemName($name);
      if (file_exists($locale_dir)) {
         foreach (glob($locale_dir.'/*.php') as $file) {
            @unlink($file);
         }
         @rmdir($locale_dir);
      }
   }


   public static function addFileFromTemplate($mappings, $template, $directory,
                                                 $filename) {
      if (!empty($mappings)) {
         $file_read = @fopen(GENERICOBJECT_DIR.$template, "rt");
         if ($file_read) {
            $template_file = fread($file_read, filesize(GENERICOBJECT_DIR.$template));
            foreach ($mappings as $name => $value) {
               $template_file = str_replace("%%$name%%", $value, $template_file);
            }
            fclose($file_read);
            $file_write = @fopen($directory . "/".$filename.".php", "w");
            if ($file_write) {
               fwrite($file_write, $template_file);
               fclose($file_write);
            }
         }
      }
   }


   public static function addDatainjectionFile($name) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name),
                                 'INJECTIONCLASS' => self::getClassByName($name)."Injection"],
                                self::OBJECTINJECTION_TEMPLATE, GENERICOBJECT_CLASS_PATH,
                                self::getSystemName($name)."injection.class");
   }


   public static function addDropdownFrontFile($name) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::FRONT_DROPDOWN_TEMPLATE, GENERICOBJECT_FRONT_PATH,
                                self::getSystemName($name));
   }


   public static function addAjaxFile($name, $field) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::AJAX_TEMPLATE, GENERICOBJECT_AJAX_PATH,
                                self::getSystemName($name).".tabs");
   }


   public static function addDropdownFrontformFile($name) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::FRONTFORM_DROPDOWN_TEMPLATE, GENERICOBJECT_FRONT_PATH,
                                self::getSystemName($name).".form");
   }


   public static function addDropdownClassFile($name, $field, $options) {
      $params['is_tree']            = false;
      $params['realname']        = false;
      $params['linked_itemtype'] = false;
      foreach ($options as $key => $value) {
         $params[$key] = $value;
      }
      self::addFileFromTemplate([
         'CLASSNAME'       => self::getClassByName($name),
         'EXTENDS'         =>
            'PluginGenericobject' . ($params['is_tree']?'CommonTree':'Common') . 'Dropdown',
         'FIELDNAME'       => $params['realname'],
         'LINKED_ITEMTYPE' => $params['linked_itemtype']
      ], self::CLASS_DROPDOWN_TEMPLATE, GENERICOBJECT_CLASS_PATH, self::getSystemName($name).".class");
   }


   /**
    * Write on the the class file for the new object type
    * @param name the name of the object type
    * @param classname the name of the new object
    * @param itemtype the object device type
    * @return nothing
    */
   public static function addClassFile($name, $classname) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::CLASS_TEMPLATE, GENERICOBJECT_CLASS_PATH,
                                self::getSystemName($name).".class");
   }

   /**
    * Write on the the _Item class file for the new object type
    * @param name the name of the object type
    * @param classname the name of the new object
    * @param itemtype the object device type
    * @return nothing
    */
   public static function addItemClassFile($name, $classname) {
      $class = self::getClassByName($name)."_Item";
      self::addFileFromTemplate(['CLASSNAME'    => $class,
                                 'FOREIGNKEY'   => getForeignKeyFieldForItemType($classname),
                                 'SOURCEOBJECT' => $classname],
            self::OBJECTITEM_TEMPLATE, GENERICOBJECT_CLASS_PATH,
                                self::getSystemName($name)."_item.class");
   }

   /**
    * Write on the the form file for the new object type
    * @param name the name of the object type
    * @param classname the name of the new object
    * @param itemtype the object device type
    * @return nothing
    */
   public static function addFormFile($name, $classname) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::FORM_TEMPLATE, GENERICOBJECT_FRONT_PATH,
                                self::getSystemName($name).".form");
   }


   /**
    * Write on the the form file for the new object type
    * @param name the name of the object type
    * @param classname the name of the new object
    * @param itemtype the object device type
    * @return nothing
    */
   public static function addSearchFile($name, $classname) {
      self::addFileFromTemplate(['CLASSNAME' => self::getClassByName($name)],
                                self::SEARCH_TEMPLATE, GENERICOBJECT_FRONT_PATH,
                                self::getSystemName($name));
   }


   /**
    * Create, if needed files for an itemtype and it's dropdown
    *
    * @since 2.2.0
    *
    * @return void
    */
   static function checkClassAndFilesForItemType() {
      global $DB;

      foreach (self::getTypes(true) as $type) {
         //ensure old files has been removed,
         $fsname = self::getSystemName($type['name']);
         if (file_exists(GENERICOBJECT_DIR . "/inc/{$fsname}.class.php")) {
            unlink(GENERICOBJECT_DIR . "/inc/{$fsname}.class.php");
         }
         if (file_exists(GENERICOBJECT_DIR . "/front/{$fsname}.form.php")) {
            unlink(GENERICOBJECT_DIR . "/front/{$fsname}.form.php");
         }
         if (file_exists(GENERICOBJECT_DIR . "/front/{$fsname}.php")) {
            unlink(GENERICOBJECT_DIR . "/front/{$fsname}.form.php");
         }
         if (file_exists(GENERICOBJECT_DIR . "/ajax/{$fsname}.tabs.php")) {
            unlink(GENERICOBJECT_DIR . "/ajax/{$fsname}.tabs.php");
         }
         if (file_exists(GENERICOBJECT_DIR . "/inc/{$fsname}.injection.class.php")) {
            unlink(GENERICOBJECT_DIR . "/inc/{$fsname}.injection.class.php");
         }

         self::checkClassAndFilesForOneItemType($type['itemtype'], $type['name'], true, false);
      }
   }

   /**
    *
    * Create or overwrite files for an itemtype
    * @since 2.2.0
    * @param $itemtype the itemtype to check
    * @param $name type's short name
    * @param $overwrite force to overwrite existing files
    * @return nothing
    */
   static function checkClassAndFilesForOneItemType($itemtype, $name, $overwrite = false, $overwrite_locales = true) {
      global $DB;
      $table = getTableForItemType($itemtype);

      //If class doesn't exist but table exists, create class
      if ($DB->tableExists($table) && ($overwrite || !class_exists($itemtype))) {
         self::addNewObject($name, $itemtype, ['add_table'              => false,
                                               'create_default_profile' => false,
                                               'add_injection_file'     => $overwrite,
                                               'add_language_file'      => false,
                                               'overwrite_locales'      => $overwrite_locales ]);
      }

      foreach ($DB->listFields($table) as $field => $options) {
         if (preg_match("/s_id$/", $field)) {
            $dropdowntable = getTableNameForForeignKeyField($field);
            $dropdownclass = getItemTypeForTable($dropdowntable);

            if ($DB->tableExists($dropdowntable) && ! class_exists($dropdownclass)) {
               $name                       = str_replace("glpi_plugin_genericobject_", "", $dropdowntable);
               $name                       = getSingular($name);
               $params= PluginGenericobjectField::getFieldOptions($field, $dropdownclass);
               if (isset($params['dropdown_type'])
                  and $params['dropdown_type'] === 'isolated'
               ) {
                  $params['linked_itemtype'] = $itemtype;
               }
               self::addNewDropdown($name, self::getClassByName($name), $params);
            }
         }
      }
   }

   /**
    *
    * Delete all files and classes for an itemtype (including dropdowns)
    * @since 2.2.0
    * @param unknown_type $name file name
    */
   static function deleteItemTypeFilesAndClasses($name, $table, $itemtype) {
      global $DB;

      _log("Delete Type", [
         "table"=>$table,
         "name"=>$name,
         "itemtype" => $itemtype,

      ]);
      //Delete files related to dropdowns
      foreach ($DB->listFields($table) as $field => $options) {
         if (preg_match("/plugin_genericobject_(.*)_id/", $field, $results)) {
            $table = getTableNameForForeignKeyField($field);

            if ($table != getTableForItemType("PluginGenericobjectTypeFamily")) {
               self::deleteFilesAndClassesForOneItemtype(getSingular($results[1]));
               $DB->query("DROP TABLE IF EXISTS `$table`");
            }
         }
      }

      //Delete reference in various GLPI core tables
      self::deleteItemtypeReferencesInGLPI($itemtype);

      //Delete itemtype files
      self::deleteFilesAndClassesForOneItemtype($name);

      //Drop itemtype table
      self::deleteItemsTable($itemtype);
      self::deleteTable($itemtype);

   }

   /**
    * Delete all files for an itemtype
    *
    * @since 2.2.0
    * @param name class file name
    */
   static function deleteFilesAndClassesForOneItemtype($name) {
      global $DB;

      //This is for compatibility with older versions of GLPI
      //(where ajax files were used for tabs display, which is not the case anymore with GLPI 0.83+)
      self::deleteAjaxFile($name);
      //Delete itemtype class
      self::deleteClassFile($name);
      //Delete forms
      self::deleteSearchFile($name);
      self::deleteFormFile($name);
      //Delete datainjection compatiblity file
      self::deleteInjectionFile($name);
   }

   static function deleteItemtypeReferencesInGLPI($itemtype) {
      //Delete references to PluginGenericobjectType in the following tables
      $itemtypes =  ["Contract_Item", "DisplayPreference", "Document_Item", "SavedSearch", "Log"];
      foreach ($itemtypes as $type) {
         $item     = new $type();
         $item->deleteByCriteria(['itemtype' => $itemtype]);
      }
   }

   //-------------------- ADD / DELETE TABLES ----------------------------------//

   /**
    * Add a new dropdown table
    * @param table the table name
    * @param entity_assign can the dropdown be assigned to an entity
    * @param recursive can the dropdown be recursive
    * @param tree can the dropdown be a tree dropdown
    * @return nothing
    */
   public static function addDropdownTable($table, $options = []) {
      global $DB;

      $default_charset = DBConnection::getDefaultCharset();
      $default_collation = DBConnection::getDefaultCollation();
      $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption();

      $params['entities_id']  = false;
      $params['is_recursive'] = false;
      $params['is_tree']      = false;
      foreach ($options as $key => $value) {
         $params[$key] = $value;
      }

      if (!$DB->tableExists($table)) {
         $query = "CREATE TABLE IF NOT EXISTS `$table` (
                       `id` int {$default_key_sign} NOT NULL auto_increment,
                       `name` varchar(255) default NULL,
                       `comment` text,
                       `date_mod` TIMESTAMP NULL DEFAULT NULL,
                       `date_creation` TIMESTAMP NOT NULL,
                       PRIMARY KEY  (`id`),
                       KEY `date_mod` (`date_mod`),
                       KEY `date_creation` (`date_creation`),
                       KEY `name` (`name`)
                     ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";
         $DB->query($query);
      }
      if ($params['entities_id']) {
         $query = "ALTER TABLE `$table` ADD `entities_id` INT {$default_key_sign} NOT NULL DEFAULT '0'";
         $DB->query($query);
         if ($params['is_recursive']) {
            $query = "ALTER TABLE `$table` " .
                     "ADD `is_recursive` TINYINT NOT NULL DEFAULT '0' AFTER `entities_id`";
            $DB->query($query);
         }
      }
      if ($params['is_tree']) {
         $fk    = getForeignKeyFieldForTable($table);
         $query = "ALTER TABLE `$table` ADD `completename` text,
                                        ADD `$fk` int {$default_key_sign} NOT NULL DEFAULT '0',
                                        ADD `level` int NOT NULL DEFAULT '0',
                                        ADD `ancestors_cache` longtext,
                                        ADD `sons_cache` longtext";
         $DB->query($query);
      }
   }


   /**
    * Delete object type table + entries in glpi_display
    * @name object type's name
    * @return nothing
    */
   public static function deleteTable($itemtype) {
      global $DB;
      _log($itemtype);
      $preferences = new DisplayPreference();
      $preferences->deleteByCriteria(["itemtype" => $itemtype]);
      $DB->query("DROP TABLE IF EXISTS `".getTableForItemType($itemtype)."`");
   }


   /**
    * Delete object _items table
    * @name object type's name
    * @return nothing
    */
   public static function deleteItemsTable($itemtype) {
      global $DB;
      $DB->query("DROP TABLE IF EXISTS `".getTableForItemType($itemtype)."_items`");
   }

   /**
    * Get object name by ID
    * @param ID the internal ID
    * @return the name associated with the ID
    */
   static function getNameByID($itemtype) {
      global $DB;
      $query = "SELECT `name` FROM `".getTableForItemType(__CLASS__)."` " .
               "WHERE `itemtype`='$itemtype'";
      $result = $DB->query($query);
      if ($DB->numrows($result)) {
         return $DB->result($result, 0, "name");
      } else {
         return "";
      }
   }


   /**
    * Delete all tickets for an itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function deleteTicketAssignation($itemtype) {
      global $DB;

      $types = ['Item_Ticket', 'Item_Problem', 'Change_Item'];
      foreach ($types as $type) {
         $item = new $type();
         $item->deleteByCriteria(['itemtype' => $itemtype]);
      }
   }

   /**
    * Delete all simcards for an itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function deleteSimcardAssignation($itemtype) {
      global $DB;

      $plugin = new Plugin();
      if ($plugin->isActivated('simcard') && $plugin->isActivated('simcard')) {
         $types = ['PluginSimcardSimcard_Item'];
         foreach ($types as $type) {
            $item = new $type();
            $item->deleteByCriteria([
                  'itemtype' => $itemtype
            ]);
         }
      }
   }

   /**
    * Remove datainjection models for an itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function removeDataInjectionModels($itemtype) {
      //Delete if exists datainjection models
      if (Plugin::isPluginActive("datainjection")) {
         $model = new PluginDatainjectionModel();
         foreach ($model->find(['itemtype' => $itemtype]) as $data) {
            $model->delete($data);
         }
      }
   }


   /**
    * Delete all loans associated with a itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function deleteLoans($itemtype) {
      $reservation_item = new ReservationItem();
      foreach ($reservation_item->find(['itemtype' => $itemtype]) as $data) {
         $reservation_item->delete($data);
      }
   }


   /**
    * Delete all loans associated with a itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function deleteUnicity($itemtype) {
      $unicity = new FieldUnicity();
      $unicity->deleteByCriteria(['itemtype' => $itemtype]);
   }


   /**
    * Delete all notes associated with a itemtype
    * @param the itemtype
    * @return nothing
    */
   public static function deleteNotepad($itemtype) {
      $notepad = new Notepad();
      $notepad->deleteByCriteria(['itemtype' => $itemtype]);
   }


   /**
    * Delete network ports for an itemtype
    * @param the itemtype
    * @return nothing
    */
   static function deleteNetworking($itemtype) {
      $networkport = new NetworkPort();
      foreach ($networkport->find(['itemtype' => $itemtype]) as $port) {
         $networkport->delete($port);
      }
   }

   /**
    * Delete reservations for an itemtype
    * @param $itemtype
    * @return nothing
    */
   static function deleteReservations($itemtype) {
      global $DB;

      $reservation = new Reservation();
      $query = "DELETE FROM
            `glpi_reservations`
         WHERE `reservationitems_id` in (
            SELECT `id` from `glpi_reservationitems` WHERE `itemtype`='$itemtype'
         )";
      $DB->query($query);
   }

   /**
    * Delete reservations for an itemtype
    * @param $itemtype
    * @return nothing
    */
   static function deleteReservationItems($itemtype) {
      $reservationItem = new ReservationItem();
      $reservationItem->deleteByCriteria(['itemtype' => $itemtype], true);
   }

   /**
    * Filter values inserted by users : remove accented chars
    * @param value the value to be filtered
    * @return the filtered value
    */
   static function filterInput($value) {
      $value = strtolower($value);
      //Itemtype must always be singular, otherwise it breaks when using GLPI's framework
      $value = getSingular($value);

      $search  = explode(",", "ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,e,i,ø,u");
      $replace = explode(",", "c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,e,i,o,u");
      $value = str_replace($search, $replace, $value);
      $value = preg_replace("/[^a-zA-Z0-9]/", '', $value);

      return  str_replace($search, $replace, $value);
   }


   /**
    * Get the object system name (for files and itemtype), by giving the name
    *
    * @param string $name
    *
    * @return string
    */
   static function getSystemName($name) {
      // Force filtering of name (will have no effect if already done).
      $name = self::filterInput($name);

      // Replace numbers by letters
      return str_replace(
         ['0',    '1',   '2',   '3',     '4',    '5',    '6',   '7',     '8',     '9'],
         ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'],
         $name
      );
   }

   /**
    * Get the object class, by giving the name
    * @param name the object's internal identifier
    * @return the class associated with the object
    */
   static function getClassByName($name) {
      return 'PluginGenericobject' . ucfirst(self::getSystemName($name));
   }


   static function getFamilyNameByItemtype($itemtype) {
      $types = getAllDataFromTable("glpi_plugin_genericobject_types",
                                    ['itemtype' => $itemtype, 'is_active' => 1]);
      if (empty($types)) {
         return false;
      } else {
         $type = array_pop($types);
         if ($type['plugin_genericobject_typefamilies_id'] > 0) {
            $family = new PluginGenericobjectTypeFamily();
            $family->getFromDB($type['plugin_genericobject_typefamilies_id']);
            return $family->getName();
         } else {
            return false;
         }
      }
   }

   /**
    * Get all types of active&published objects
    */
   static function getTypes($all = false) {
      global $DB;
      $table = getTableForItemType(__CLASS__);
      if ($DB->tableExists($table)) {
         $mytypes = [];
         $all_types = getAllDataFromTable(
            $table,
            [
               'WHERE' => !$all ? ['is_active' => self::ACTIVE] : [],
               'ORDER' => 'name',
            ]
         );
         foreach ($all_types as $data) {
            //If class is not present on the filesystem, do not list itemtype
            $mytypes[$data['itemtype']] = $data;
         }
         return $mytypes;
      } else {
         return  [];
      }
   }

   /**
    * Get all types of active&published objects
    * order by family
    */
   static function getTypesByFamily($all = false) {
      global $DB;
      $table = getTableForItemType(__CLASS__);
      if ($DB->tableExists($table)) {
         $mytypes = [];
         foreach (getAllDataFromTable($table, (!$all ? ['is_active' => self::ACTIVE] : [])) as $data) {
            //If class is not present on the filesystem, do not list itemtype
            if (file_exists(self::getCompleteClassFilename($data['name']))) {
               $mytypes[$data['plugin_genericobject_typefamilies_id']][$data['itemtype']] = $data;
            }
         }
         return $mytypes;
      } else {
         return  [];
      }
   }

   static function getTypesForFormcreator($param) {
      $families = PluginGenericobjectTypeFamily::getFamilies();
      $familyFk = PluginGenericobjectTypeFamily::getForeignKeyField();
      foreach(self::getTypes() as $type => $typeData) {
         $familyName = isset($families[$typeData[$familyFk]])
                       ? $families[$typeData[$familyFk]]
                       : _n('Other', 'Others', Session::getPluralNumber(), 'genericobject');
         $param[$familyName][$type] = $typeData['name'];
      }
      return $param;
   }

   /**
    * Register all variables for a type
    * @param type the type's attributes
    * @return nothing
    */
   static function registerOneType($itemtype) {
      //If table doesn't exists, do not try to register !
      if (class_exists($itemtype)) {
         $itemtype::registerType();
      }
   }


   /**
    * Include locales for a specific type
    * @name object type's name
    * @return nothing
    */
   static function includeLocales($name) {
      global $CFG_GLPI,$LANG;

      $fsname = self::getSystemName($name);

      $prefix = GENERICOBJECT_LOCALES_PATH . "/$fsname/$fsname";
        //Dirty hack because the plugin doesn't support gettext...
      $language= str_replace('.mo', '', $CFG_GLPI["languages"][$_SESSION["glpilanguage"]][1]);
      if (isset ($_SESSION["glpilanguage"])
             && file_exists("$prefix.$language.php")) {
         include_once ("$prefix.$language.php");

      } else {
         if (file_exists($prefix . ".en_GB.php")) {
            include_once ($prefix . ".en_GB.php");
         } else {
            if (file_exists($prefix . ".fr_FR.php")) {
               include_once ($prefix . ".fr_FR.php");
            } else {
               return false;
            }
         }
      }
      return true;
   }


   static function includeConstants($name, $force = false) {

      $file = self::getCompleteConstantFilename($name);
      if (file_exists($file)) {
         if (!$force) {
            include_once($file);
         } else {
            include($file);
         }
      }
   }


   /**
    * Get all dropdown fields associated with an itemtype
    * @param itemtype the itemtype
    * @return an array or fields that represents the dropdown tables
    */
   static function getDropdownForItemtype($itemtype) {
      global $DB;
      $associated_tables = [];
      if (class_exists($itemtype)) {
         $source_table = getTableForItemType($itemtype);
         foreach (PluginGenericobjectSingletonObjectField::getInstance($itemtype) as $field => $value) {
            $table = getTableNameForForeignKeyField($field);
            $options = PluginGenericobjectField::getFieldOptions($field, $itemtype);
            if (isset($options['input_type'])
               and $options['input_type'] === 'dropdown'
               and preg_match('/^glpi_plugin_genericobject/', $table)
            ) {
               $associated_tables[] = $table;
            }
         }
      }
      return $associated_tables;
   }


   static function deleteDropdownsForItemtype($itemtype) {
      global $DB;
      //Foreach dropdown : drop table & remove files !
      foreach (self::getDropdownForItemtype($itemtype) as $table) {
         $results = [];
         if (preg_match("/glpi_plugin_genericobject_(.*)/i", getSingular($table), $results)
            && isset($results[1])) {
            $name = $results[1];
            $DB->query("DROP TABLE IF EXISTS `$table`");
            self::deleteFormFile($name);
            self::deleteSearchFile($name);
            self::deleteClassFile($name);
         }
      }

      // Invalidate submenu data in current session for minor cleanup
      unset($_SESSION['glpimenu']);
   }
   //------------------------------- GETTERS -------------------------//

   function canUseTickets() {
      return $this->fields['use_tickets'];
   }

   function canBeLinked() {
      return $this->fields['use_links'];
   }

   function canUseTemplate() {
      global $DB;
      return $DB->fieldExists(getTableForItemType($this->fields['itemtype']), 'is_template');
   }


   function canUseUnicity() {
      return $this->fields['use_unicity'];
   }


   function canBeDeleted() {
      global $DB;
      return $DB->fieldExists(getTableForItemType($this->fields['itemtype']), 'is_deleted');
   }


   function canBeEntityAssigned() {
      global $DB;
      return $DB->fieldExists(getTableForItemType($this->fields['itemtype']), 'entities_id');
   }


   function canBeRecursive() {
      global $DB;
      return $DB->fieldExists(getTableForItemType($this->fields['itemtype']), 'is_recursive');
   }


   function canBeReserved() {
      return $this->fields['use_loans'];
   }


   function canUseNotepad() {
      return $this->fields['use_notepad'] != 0;
   }


   function canUseHistory() {
      return $this->fields['use_history'];
   }


   function canUseDocuments() {
      return $this->fields['use_documents'];
   }


   function canUseInfocoms() {
      return $this->fields['use_infocoms'];
   }

   function canUseItemDevice() {
      return $this->fields['use_itemdevices'];
   }

   function canUseImpact() {
      return Impact::isEnabled($this->fields['itemtype']);
   }

   function canUseContracts() {
      return $this->fields['use_contracts'];
   }


   function canUseGlobalSearch() {
      return $this->fields['use_global_search'];
   }


   function canUseNetworkPorts() {
      return $this->fields['use_network_ports'];
   }


   function canUseDirectConnections() {
      return $this->fields['use_direct_connections'];
   }

   function canUseProjects() {
      return $this->fields['use_projects'];
   }

   function canUsePluginDataInjection() {
      if (!Plugin::isPluginActive("datainjection")) {
         return false;
      }
      return $this->fields['use_plugin_datainjection'];
   }


   function canUsePluginOrder() {
      if (!Plugin::isPluginActive("order")) {
         return false;
      }
      return $this->fields['use_plugin_order'];
   }


   function canUsePluginPDF() {
      if (!Plugin::isPluginActive("pdf")) {
         return false;
      }
      return $this->fields['use_plugin_pdf'];
   }


   function canUsePluginUninstall() {
      if (!Plugin::isPluginActive("uninstall")) {
         return false;
      }
      return $this->fields['use_plugin_uninstall'];
   }

   function canUsePluginSimcard() {
      if (!Plugin::isPluginActive("simcard")) {
         return false;
      }
      return $this->fields['use_plugin_simcard'];
   }

   function canUsePluginTreeview() {
      if (!Plugin::isPluginActive("treeview")) {
         return false;
      }
      return $this->fields['use_plugin_treeview'];
   }

   function canUsePluginGeninventoryNumber() {
      if (!Plugin::isPluginActive("geninventorynumber")) {
         return false;
      }
      return $this->fields['use_plugin_geninventorynumber'];
   }


   function isTransferable() {
      return Session::isMultiEntitiesMode();

   }

   function getLinkedItemTypesAsArray() {
      if (!empty($this->fields['linked_itemtypes'])) {
         return json_decode($this->fields['linked_itemtypes'], true);
      } else {
         return [];
      }
   }

   static function canViewAtLeastOneType() {
      $types = self::getTypes();
      $view  = false;
      foreach ($types as $ID => $value) {
         if (Session::haveRight($value['itemtype'], READ)) {
            $view = true;
            break;
         }
      }
      return $view;
   }

   /**
    * Display debug information for current object
    **/
   function showDebug() {
       $this->showFilesForm();
      //NotificationEvent::debugEvent($this);
   }
   //------------------------------- INSTALL / UNINSTALL METHODS -------------------------//


   static function install(Migration $migration) {
      global $DB;

      $default_charset = DBConnection::getDefaultCharset();
      $default_collation = DBConnection::getDefaultCollation();
      $default_key_sign = DBConnection::getDefaultPrimaryKeySignOption();

      $table = getTableForItemType(__CLASS__);
      if (!$DB->tableExists($table)) {
         $query = "CREATE TABLE `$table` (
                           `id` INT {$default_key_sign} NOT NULL AUTO_INCREMENT,
                           `entities_id` INT {$default_key_sign} NOT NULL DEFAULT 0,
                           `itemtype` varchar(255) default NULL,
                           `is_active` tinyint NOT NULL default '0',
                           `name` varchar(255) default NULL,
                           `comment` text NULL,
                           `date_mod` TIMESTAMP NULL DEFAULT NULL,
                           `date_creation` TIMESTAMP NULL DEFAULT NULL,
                           `use_global_search` tinyint NOT NULL default '0',
                           `use_unicity` tinyint NOT NULL default '0',
                           `use_history` tinyint NOT NULL default '0',
                           `use_infocoms` tinyint NOT NULL default '0',
                           `use_contracts` tinyint NOT NULL default '0',
                           `use_documents` tinyint NOT NULL default '0',
                           `use_tickets` tinyint NOT NULL default '0',
                           `use_links` tinyint NOT NULL default '0',
                           `use_loans` tinyint NOT NULL default '0',
                           `use_network_ports` tinyint NOT NULL default '0',
                           `use_direct_connections` tinyint NOT NULL default '0',
                           `use_plugin_datainjection` tinyint NOT NULL default '0',
                           `use_plugin_pdf` tinyint NOT NULL default '0',
                           `use_plugin_order` tinyint NOT NULL default '0',
                           `use_plugin_uninstall` tinyint NOT NULL default '0',
                           `use_plugin_geninventorynumber` tinyint NOT NULL default '0',
                           `use_menu_entry` tinyint NOT NULL default '0',
                           `use_projects` tinyint NOT NULL default '0',
                           `linked_itemtypes` text NULL,
                           `plugin_genericobject_typefamilies_id` INT {$default_key_sign} NOT NULL DEFAULT 0,
                           `use_itemdevices` tinyint NOT NULL default '0',
                           `impact_icon` varchar(255) default NULL,
                           PRIMARY KEY ( `id` )
                           ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;";
         $DB->query($query) or die($DB->error());
      }

      $migration->addField($table, "use_network_ports", "bool");
      $migration->addField($table, "use_direct_connections", "bool");
      $migration->addField($table, "use_plugin_geninventorynumber", "bool");
      $migration->addField($table, "use_contracts", "bool");
      $migration->addField($table, "use_menu_entry", "bool");
      $migration->addField($table, "use_global_search", "bool");
      $migration->addField($table, "use_projects", "bool");
      $migration->addField($table, "use_notepad", "bool");
      $migration->addField($table, "comment", "text");
      if (!$migration->addField($table, "date_mod", "timestamp")) {
         $migration->changeField($table, "date_mod", "date_mod", "timestamp");
      }
      $migration->addField($table, "date_creation", "timestamp");
      $migration->addField($table, "linked_itemtypes", "text");
      $migration->addField($table, "plugin_genericobject_typefamilies_id", "INT {$default_key_sign} NOT NULL DEFAULT 0");
      $migration->addField($table, "use_plugin_simcard", "bool");
      $migration->addField($table, "use_plugin_treeview", "bool");
      $migration->addField($table, "use_itemdevices", "bool");
      $migration->addField($table, "impact_icon", "string");
      $migration->migrationOneTable($table);

      //Normalize names and itemtypes (prior to using them).
      self::normalizeNamesAndItemtypes($migration);

      //If files are missing, recreate them!
      self::checkClassAndFilesForItemType();

      // Migrate notepad data
      $allGenericObjectTypes = PluginGenericobjectType::getTypes(true);

      $notepad = new Notepad();
      foreach ($allGenericObjectTypes as $genericObjectType => $genericObjectData) {
         $itemtype = $genericObjectData['itemtype'];
         if (! class_exists($itemtype, true)) {
            // TRANS:  %1$s is itemtype name
            $warning  = sprintf(__('Unable to load the class %1$s.', 'genericobject'), $itemtype);
            // TRANS:  %1$s is itemtype name
            $warning .= sprintf(__('You probably have garbage data in your database for this plugin and missing files in %1$s', 'genericobject'), GENERICOBJECT_DOC_DIR);
            $migration->displayWarning($warning, true);
            die();
         }
         $genericObjectTypeInstance = new $genericObjectType();
         if ($DB->fieldExists($genericObjectTypeInstance->getTable(), "notepad")) {
            $query = "INSERT INTO `" . $notepad->getTable() . "`
                  (`items_id`,
                  `itemtype`,
                  `date_creation`,
                  `date_mod`,
                  `content`
               )
               SELECT
                  `id` as `items_id`,
                  '" . $genericObjectType . "' as `itemtype`,
                  now() as `date_creation`,
                  now() as `date_mod`,
                  `notepad` as `content`
               FROM `" . $genericObjectTypeInstance->getTable() . "`
               WHERE notepad IS NOT NULL
               AND notepad <> ''";
            $DB->query($query) or die($DB->error());
         }
         $migration->dropField($genericObjectTypeInstance->getTable(), "notepad");
         $migration->migrationOneTable($genericObjectTypeInstance->getTable());
      }

      //Displayprefs
      $prefs = [10 => 6, 9 => 5, 8 => 4, 7 => 3, 6 => 2, 2 => 1, 4 => 1, 11 => 7,  12 => 8,
                14 => 10, 15 => 11];
      foreach ($prefs as $num => $rank) {
         if (!countElementsInTable("glpi_displaypreferences",
                                   ['itemtype' => __CLASS__, 'num' => $num, 'users_id' => 0])) {
            $preference      = new DisplayPreference();
            $tmp['itemtype'] = __CLASS__;
            $tmp['num']      = $num;
            $tmp['rank']     = $rank;
            $tmp['users_id'] = 0;
            $preference->add($tmp);
         }
      }
   }


   static function uninstall() {
      global $DB;

      //Delete references to PluginGenericobjectType in the following tables
      self::deleteItemtypeReferencesInGLPI(__CLASS__);

      foreach ($DB->request("glpi_plugin_genericobject_types") as $type) {
         //Delete references to PluginGenericobjectType in the following tables
         self::deleteItemtypeReferencesInGLPI($type['itemtype']);
         //Dropd files and classes
         self::deleteItemTypeFilesAndClasses($type['name'], getTableForItemType($type['itemtype']), $type['itemtype']);
      }

      //Delete table
      $query = "DROP TABLE IF EXISTS `glpi_plugin_genericobject_types`";
      $DB->query($query) or die($DB->error());
   }


   static function getIcon() {
      return "fas fa-car";
   }

   /**
    * Normalize itemtype and name for all types.
    * This method will ensure that new normalization rules will be taken into account
    * during migration from an old version without loosing existing data.
    *
    * @param Migration $migration
    * @return void
    */
   private static function normalizeNamesAndItemtypes(Migration $migration) {
      global $DB;
      $DB->disableTableCaching();

      $types_iterator = $DB->request(
         [
            'FROM'  => self::getTable(),
            'ORDER' => 'name',
         ]
      );

      foreach ($types_iterator as $type) {
         $old_name     = $type['name'];
         $new_name     = self::filterInput($old_name);
         $old_itemtype = $type['itemtype'];
         $new_itemtype = self::getClassByName($new_name);

         if ($old_name == $new_name && $old_itemtype == $new_itemtype) {
            continue;
         }

         self::updateNameAndItemtype(
            $migration,
            $old_name,
            $new_name,
            $old_itemtype,
            $new_itemtype
         );

         $DB->update(
            self::getTable(),
            [
               'name'     => $new_name,
               'itemtype' => $new_itemtype,
            ],
            ['id' => $type['id']]
         );

         $DB->update(
            self::getTable(),
            [
               'linked_itemtypes' => new \QueryExpression(
                  'REPLACE('
                  . $DB->quoteName('linked_itemtypes')
                  . ','
                  . $DB->quoteValue('"' . $old_itemtype .'"') // itemtype is surrounded by quotes
                  . ','
                  . $DB->quoteValue('"' . $new_itemtype .'"') // itemtype is surrounded by quotes
                  . ')'
               ),
            ],
            ['linked_itemtypes' => ['LIKE', '%"' . $old_itemtype . '"%']]
         );

         // Handle dropdowns related to itemtype
         $table  = getTableForItemType($new_itemtype);
         $fields = $DB->listFields($table);
         foreach ($fields as $field => $options) {
            if (preg_match("/s_id$/", $field)) {
               $dropdown_old_table    = getTableNameForForeignKeyField($field);

               if (!preg_match('/^glpi_plugin_genericobject_/', $dropdown_old_table)) {
                  continue;
               }

               $dropdown_old_name     = getSingular(
                  str_replace(
                     "glpi_plugin_genericobject_",
                     "",
                     $dropdown_old_table
                  )
               );
               $dropdown_old_itemtype = 'PluginGenericobject' . ucfirst($dropdown_old_name);
               $dropdown_new_name     = self::filterInput($dropdown_old_name);
               $dropdown_new_itemtype = self::getClassByName($dropdown_new_name);

               if ($dropdown_old_name == $dropdown_new_name
                   && $dropdown_old_itemtype == $dropdown_new_itemtype) {
                  continue;
               }

               self::updateNameAndItemtype(
                  $migration,
                  $dropdown_old_name,
                  $dropdown_new_name,
                  $dropdown_old_itemtype,
                  $dropdown_new_itemtype
               );
            }
         }
      }

      ProfileRight::cleanAllPossibleRights(); // Clean all possible rights are their name may have change
   }

   /**
    * Update itemtype and/or name for a given itemtype.
    *
    * @param Migration $migration
    * @param string    $old_name      Current type name in database
    * @param string    $new_name      New type name to use
    * @param string    $old_itemtype  Current itemtype
    * @param string    $new_itemtype  New itemtype to use
    *
    * @return void
    */
   private static function updateNameAndItemtype(Migration $migration, $old_name, $new_name,
                                                 $old_itemtype, $new_itemtype) {

      global $DB;

      if ($old_itemtype != $new_itemtype) {
         $migration->renameItemtype($old_itemtype, $new_itemtype);
         $migration->executeMigration(); // Execute migration to flush updates on tables that may be renamed
      }

      $destination_files = [];

      $old_systemname = $old_name; // Old system name was same as name in DB
      $new_systemname = self::getSystemName($new_name);
      $old_fkey       = getForeignKeyFieldForItemType($old_itemtype);
      $new_fkey       = getForeignKeyFieldForItemType($new_itemtype);
      $old_locale_dir = GENERICOBJECT_LOCALES_PATH . '/' . $old_systemname;
      $new_locale_dir = GENERICOBJECT_LOCALES_PATH . '/' . $new_systemname;

      $destination_files = [
         self::getCompleteClassFilename($new_name),
         self::getCompleteItemClassFilename($new_name),
         self::getCompleteFormFilename($new_name),
         self::getCompleteSearchFilename($new_name),
         self::getCompleteAjaxTabFilename($new_name),
         self::getCompleteInjectionFilename($new_name),
         self::getCompleteConstantFilename($new_name),
      ];

      // Rename locale folder and map files
      if (is_dir($old_locale_dir) && $old_locale_dir != $new_locale_dir) {
         if (rename($old_locale_dir, $new_locale_dir)) {
            // Add all locale files to destination files
            foreach (glob($new_locale_dir . '/*.php') as $old_filename) {
               $destination_files[] = preg_replace(
                  '/(.*\/)' . preg_quote($old_systemname, '/') . '([^\/]*)$/',
                  '$1' . $new_systemname . '$2',
                  $old_filename
               );
            }
         } else {
            $migration->displayWarning(
               sprintf('Unable to rename "%s" locale directory to "%s"', $old_locale_dir, $new_locale_dir),
               true
            );
         }
      }

      // Handle *_item class table/itemtype
      if ($DB->tableExists(getTableForItemType($old_itemtype . '_Item'))) {
         $migration->renameItemtype($old_itemtype . '_Item', $new_itemtype . '_Item');
         $migration->executeMigration(); // Execute migration to flush updates on tables that may be renamed
      }

      // Add all constant files as they may contains foreign keys to update
      foreach (glob(GENERICOBJECT_FIELDS_PATH . '/*.php') as $constant_filename) {
         $destination_files[] = $constant_filename;
      }

      // Rename files (replace "/{$old name}*" by "/{$new_system_name}*")
      $migration->displayMessage(
         sprintf('Rename files related to "%s" itemtype and update their content', $old_itemtype),
         true
      );
      foreach ($destination_files as $new_filename) {
         $old_filename = preg_replace(
            '/(.*\/)' . preg_quote($new_systemname, '/') . '([^\/]*)$/',
            '$1' . $old_systemname . '$2',
            $new_filename
         );

         if (!file_exists($old_filename)) {
            // Do nothing if old file does not exists
            continue;
         }

         if ($old_filename != $new_filename) {
            if (!rename($old_filename, $new_filename)) {
               $migration->displayWarning(
                  sprintf('Unable to rename "%s" file to "%s"', $old_filename, $new_filename),
                  true
               );
               continue;
            }
         } else {
            $migration->displayMessage(
               sprintf('Update "%s" file content', $old_filename),
               true
            );
         }

         $file_contents = file_get_contents($new_filename);
         if (!$file_contents) {
            $migration->displayWarning(
               sprintf('Unable to read "%s" file contents', $new_filename),
               true
            );
            continue;
         }

         $replace_count = 0;
         $old_fkey_truncated = preg_replace('/^plugin_genericobject_/', '', $old_fkey);
         $new_fkey_truncated = preg_replace('/^plugin_genericobject_/', '', $new_fkey);
         $file_contents = str_replace(
            [$old_itemtype, $old_fkey, $old_fkey_truncated],
            [$new_itemtype, $new_fkey, $new_fkey_truncated],
            $file_contents,
            $replace_count
         );
         if ($replace_count > 0 && !file_put_contents($new_filename, $file_contents)) {
            $migration->displayWarning(
               sprintf('Unable to update "%s" file contents', $new_filename),
               true
            );
         }
      }

      // Update profile rights
      if ($old_itemtype != $new_itemtype) {
         $migration->addPostQuery(
            $DB->buildUpdate(
               ProfileRight::getTable(),
               ['name' => PluginGenericobjectProfile::getProfileNameForItemtype($new_itemtype)],
               ['name' => PluginGenericobjectProfile::getProfileNameForItemtype($old_itemtype)]
            )
         );
      }
   }

   /**
    * Given an impact icon filename, return the expected full or relative path
    * where it should be stored
    *
    * @param string $filename
    * @param string $itemtype Impact itemtype, needed to avoid filename colision
    * @param bool   $relative (default: false)
    *
    * @return null|string
    */
   public static function getImpactIconFileStoragePath(
      ?string $filename,
      string $itemtype,
      bool $relative = false
   ): ?string {
      if (empty($filename)) {
         return null;
      }

      // Make sure $filename does not contains any directory changes like ".."
      if ($filename != pathinfo($filename)['basename']) {
         trigger_error(
            "Trying to access forbidden file: $filename",
            E_USER_WARNING
         );
         return null;
      }

      $filename = "{$itemtype}_{$filename}";
      $path = GLPI_PLUGIN_DOC_DIR . "/genericobject/impact_icons/$filename";

      if ($relative) {
         $path = str_replace(GLPI_ROOT, "", $path);
      }

      return $path;
   }

   /**
    * Get file path to impact icon file
    *
    * @return string|null
    */
   public function getImpactIconFilePath(): ?string
   {
      if (empty($this->fields['impact_icon'])) {
         return null;
      }

      $path = self::getImpactIconFileStoragePath(
         $this->fields['impact_icon'],
         $this->fields['itemtype']
      );
      if (empty($path) || !file_exists($path)) {
         return null;
      }

      return $path;
   }

   /**
    * Get public URL to impact icon file
    *
    * @return null|string
    */
   public function getImpactIconUrl(): ?string
   {
      // Check that the file exist
      if (!$this->getImpactIconFilePath()) {
         return null;
      }

      return Plugin::getWebDir('genericobject') . "/front/getimpacticon.php?itemtype=" . $this->fields['itemtype'];
   }
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists