grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026. -------------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** @file * @brief */ /** * Common DataBase Table Manager Class - Persistent Object **/ class CommonDBTM extends CommonGLPI { /// Data of the Item public $fields = array(); /// Make an history of the changes public $dohistory = false; /// Black list fields for history log or date mod update public $history_blacklist = array(); /// Set false to desactivate automatic message on action public $auto_message_on_action = true; /// Set true to desactivate link generation because form page do not permit show/edit item public $no_form_page = false; /// Set true to desactivate auto compute table name static protected $notable = false; ///Additional fiedls for dictionnary processing public $additional_fields_for_dictionnary = array(); /// Forward entity datas to linked items static protected $forward_entity_to = array(); /// Foreign key field cache : set dynamically calling getForeignKeyField protected $fkfield = ""; /// Search options of the item : to avoid multiple load protected $searchopt = false; /// Tab orientation : horizontal or vertical public $taborientation = 'vertical'; /// Need to get item to show tab public $get_item_to_display_tab = true; /// Forward entity to plugins itemtypes static protected $plugins_forward_entity = array(); /// Profile name static $rightname = ''; /// Is this item use notepad ? protected $usenotepad = false; /// FLush mail queue for public $mailqueueonaction = false; const SUCCESS = 0; //Process is OK const TYPE_MISMATCH = 1; //Type is not good, value cannot be inserted const ERROR_FIELDSIZE_EXCEEDED = 2; //Value is bigger than the field's size const HAS_DUPLICATE = 3; //Can insert or update because it's duplicating another item const NOTHING_TO_DO = 4; //Nothing to insert or update /** * Constructor **/ function __construct () { } /** * Return the table used to stor this object * * @return string **/ static function getTable() { if (static::$notable) { return ''; } if (empty($_SESSION['glpi_table_of'][get_called_class()])) { $_SESSION['glpi_table_of'][get_called_class()] = getTableForItemType(get_called_class()); } return $_SESSION['glpi_table_of'][get_called_class()]; } /** * force table value (used for config management for old versions) * * @param $table name of the table to be forced * * @return nothing **/ static function forceTable($table) { $_SESSION['glpi_table_of'][get_called_class()] = $table; } static function getForeignKeyField() { if (empty($_SESSION['glpi_foreign_key_field_of'][get_called_class()])) { $_SESSION['glpi_foreign_key_field_of'][get_called_class()] = getForeignKeyFieldForTable(static::getTable()); } return $_SESSION['glpi_foreign_key_field_of'][get_called_class()]; } /** * Retrieve an item from the database * * @param $ID ID of the item to get * * @return true if succeed else false **/ function getFromDB($ID) { global $DB; // Make new database object and fill variables // != 0 because 0 is consider as empty if (strlen($ID) == 0) { return false; } return $this->getFromDBByQuery("WHERE `" . $this->getTable() . "`.`" . $this->getIndexName() . "` = '" . Toolbox::cleanInteger($ID) . "' LIMIT 1"); } /** * Retrieve an item from the database by query. The query must include the WHERE keyword. Thus, * we can replace "WHERE" to make complex SQL JOINED queries (for instance, see * User::getFromDBbyEmail()). * * @since version 0.84 * * @param $query the "WHERE" or "JOIN" part of the SQL query * * @return true if succeed else false **/ function getFromDBByQuery($query) { global $DB; // Make new database object and fill variables if (empty($query)) { return false; } $query = "SELECT `".$this->getTable()."`.* FROM `".$this->getTable()."` $query"; if ($result = $DB->query($query)) { if ($DB->numrows($result) == 1) { $this->fields = $DB->fetch_assoc($result); $this->post_getFromDB(); return true; } } return false; } /** * Get the identifier of the current item * * @return ID **/ function getID() { if (isset($this->fields[static::getIndexName()])) { return $this->fields[static::getIndexName()]; } return -1; } /** * Actions done at the end of the getFromDB function * * @return nothing **/ function post_getFromDB() { } /** * Actions done to not show some fields when geting a single item from API calls * * @param array $fields Fields to unset undiscloseds */ static public function unsetUndisclosedFields(&$fields) { } /** * Retrieve all items from the database * * @param $condition condition used to search if needed (empty get all) (default '') * @param $order order field if needed (default '') * @param $limit limit retrieved datas if needed (default '') * * @return all retrieved data in a associative array by id **/ function find($condition="", $order="", $limit="") { global $DB; // Make new database object and fill variables $query = "SELECT * FROM `".$this->getTable()."`"; if (!empty($condition)) { $query .= " WHERE $condition"; } if (!empty($order)) { $query .= " ORDER BY $order"; } if (!empty($limit)) { $query .= " LIMIT ".intval($limit); } $data = array(); if ($result = $DB->query($query)) { if ($DB->numrows($result)) { while ($line = $DB->fetch_assoc($result)) { $data[$line['id']] = $line; } } } return $data; } /** * Get the name of the index field * * @return name of the index field **/ static function getIndexName() { return "id"; } /** * Get an empty item * *@return true if succeed else false **/ function getEmpty() { global $DB; //make an empty database object $table = $this->getTable(); if (!empty($table) && ($fields = $DB->list_fields($table))) { foreach ($fields as $key => $val) { $this->fields[$key] = ""; } } else { return false; } if (array_key_exists('entities_id',$this->fields) && isset($_SESSION["glpiactive_entity"])) { $this->fields['entities_id'] = $_SESSION["glpiactive_entity"]; } $this->post_getEmpty(); // Call the plugin hook - $this->fields can be altered Plugin::doHook("item_empty", $this); return true; } /** * Actions done at the end of the getEmpty function * * @return nothing **/ function post_getEmpty() { } /** * Get type to register log on * * @since version 0.83 * * @return array of type + ID **/ function getLogTypeID() { return array($this->getType(), $this->fields['id']); } /** * Update the item in the database * * @param $updates fields to update * @param $oldvalues array of old values of the updated fields * * @return nothing **/ function updateInDB($updates, $oldvalues=array()) { global $DB, $CFG_GLPI; foreach ($updates as $field) { if (isset($this->fields[$field])) { $query = "UPDATE `".$this->getTable()."` SET `".$field."`"; if ($this->fields[$field]=="NULL") { $query .= " = ".$this->fields[$field]; } else { $query .= " = '".$this->fields[$field]."'"; } $query .= " WHERE `id` ='".$this->fields["id"]."'"; if (!$DB->query($query)) { if (isset($oldvalues[$field])) { unset($oldvalues[$field]); } } } else { // Clean oldvalues if (isset($oldvalues[$field])) { unset($oldvalues[$field]); } } } if (count($oldvalues)) { Log::constructHistory($this, $oldvalues, $this->fields); } return true; } /** * Add an item to the database * * @return new ID of the item is insert successfull else false **/ function addToDB() { global $DB; //unset($this->fields["id"]); $nb_fields = count($this->fields); if ($nb_fields > 0) { // Build query $query = "INSERT INTO `".$this->getTable()."` ("; $i = 0; foreach ($this->fields as $key => $val) { $fields[$i] = $key; $values[$i] = $val; $i++; } for ($i=0 ; $i<$nb_fields; $i++) { $query .= "`".$fields[$i]."`"; if ($i != ($nb_fields-1)) { $query .= ","; } } $query .= ") VALUES ("; for ($i=0 ; $i<$nb_fields ; $i++) { if ($values[$i] == 'NULL') { $query .= $values[$i]; } else { if (($this->getType() == 'ProfileRight') && ($values[$i] == '')) { $values[$i] = 0; } $query .= "'".$values[$i]."'"; } if ($i != ($nb_fields-1)) { $query .= ","; } } $query .= ")"; if ($result=$DB->query($query)) { // Already define for entity / insert_id does not work if (!isset($this->fields['id']) || is_null($this->fields['id']) || ($this->fields['id'] == 0)) { $this->fields['id'] = $DB->insert_id(); } return $this->fields['id']; } } return false; } /** * Restore item = set deleted flag to 0 * * @return true if succeed else false **/ function restoreInDB() { global $DB,$CFG_GLPI; if ($this->maybeDeleted()) { // Auto set date_mod if exsist $toadd = ''; if (isset($this->fields['date_mod'])) { $toadd = ", `date_mod` ='".$_SESSION["glpi_currenttime"]."' "; } $query = "UPDATE `".$this->getTable()."` SET `is_deleted`='0' $toadd WHERE `id` = '".$this->fields['id']."'"; if ($result = $DB->query($query)) { return true; } } return false; } /** * Mark deleted or purge an item in the database * * @param $force force the purge of the item (not used if the table do not have a deleted field) * (default 0) * * @return true if succeed else false **/ function deleteFromDB($force=0) { global $DB, $CFG_GLPI; if (($force == 1) || !$this->maybeDeleted() || ($this->useDeletedToLockIfDynamic() && !$this->isDynamic())) { $this->cleanDBonPurge(); if ($this instanceof CommonDropdown) { $this->cleanTranslations(); } $this->cleanHistory(); $this->cleanRelationData(); $this->cleanRelationTable(); $query = "DELETE FROM `".$this->getTable()."` WHERE `id` = '".$this->fields['id']."'"; if ($result = $DB->query($query)) { $this->post_deleteFromDB(); return true; } } else { // Auto set date_mod if exsist $toadd = ''; if (isset($this->fields['date_mod'])) { $toadd = ", `date_mod` ='".$_SESSION["glpi_currenttime"]."' "; } $query = "UPDATE `".$this->getTable()."` SET `is_deleted`='1' $toadd WHERE `id` = '".$this->fields['id']."'"; $this->cleanDBonMarkDeleted(); if ($result = $DB->query($query)) { return true; } } return false; } /** * Clean data in the tables which have linked the deleted item * * @return nothing **/ function cleanHistory() { global $DB; if ($this->dohistory) { $query = "DELETE FROM `glpi_logs` WHERE (`itemtype` = '".$this->getType()."' AND `items_id` = '".$this->fields['id']."')"; $DB->query($query); } } /** * Clean data in the tables which have linked the deleted item * Clear 1/N Relation * * @return nothing **/ function cleanRelationData() { global $DB, $CFG_GLPI; $RELATION = getDbRelations(); if (isset($RELATION[$this->getTable()])) { $newval = (isset($this->input['_replace_by']) ? $this->input['_replace_by'] : 0); foreach ($RELATION[$this->getTable()] as $tablename => $field) { if ($tablename[0] != '_') { $itemtype = getItemTypeForTable($tablename); // Code factorization : we transform the singleton to an array if (!is_array($field)) { $field = array($field); } foreach ($field as $f) { foreach ($DB->request($tablename, array($f => $this->getID())) as $data) { // Be carefull : we must use getIndexName because self::update rely on that ! if ($object = getItemForItemtype($itemtype)) { $idName = $object->getIndexName(); // And we must ensure that the index name is not the same as the field // we try to modify. Otherwise we will loose this element because all // will be set to $newval ... if ($idName != $f) { $object->update(array($idName => $data[$idName], $f => $newval, '_disablenotif' => true)); // Disable notifs } } } } } } } // Clean ticket open against the item if (in_array($this->getType(),$CFG_GLPI["ticket_types"])) { $job = new Ticket(); $itemsticket = new Item_Ticket(); $query = "SELECT * FROM `glpi_items_tickets` WHERE `items_id` = '".$this->fields['id']."' AND `itemtype`='".$this->getType()."'"; $result = $DB->query($query); if ($DB->numrows($result)) { while ($data=$DB->fetch_assoc($result)) { $cnt = countElementsInTable('glpi_items_tickets', "`tickets_id`='".$data['tickets_id']."'"); $job->getFromDB($data['tickets_id']); if ($cnt == 1) { if ($CFG_GLPI["keep_tickets_on_delete"] == 1) { $itemsticket->delete(array("id" => $data["id"])); } else { $job->delete(array("id" => $data["tickets_id"])); } } else { $itemsticket->delete(array("id" => $data["id"])); } } } } } /** * Actions done after the DELETE of the item in the database * * @return nothing **/ function post_deleteFromDB() { } /** * Actions done when item is deleted from the database * * @return nothing **/ function cleanDBonPurge() { } /** * Clean translations associated to a dropdown * * @since version 0.85 **/ function cleanTranslations() { //Do not try to clean is dropdown translation is globally off if (DropdownTranslation::isDropdownTranslationActive()) { $translation = new DropdownTranslation(); $translation->deleteByCriteria(array('itemtype' => get_class($this), 'items_id' => $this->getID())); } } /** * Clean the date in the relation tables for the deleted item * Clear N/N Relation **/ function cleanRelationTable() { global $CFG_GLPI, $DB; // If this type have INFOCOM, clean one associated to purged item if (Infocom::canApplyOn($this)) { $infocom = new Infocom(); if ($infocom->getFromDBforDevice($this->getType(), $this->fields['id'])) { $infocom->delete(array('id' => $infocom->fields['id'])); } } // If this type have NETPORT, clean one associated to purged item if (in_array($this->getType(),$CFG_GLPI['networkport_types'])) { // If we don't use delete, then cleanDBonPurge() is not call and the NetworkPorts are not // clean properly $networkPortObject = new NetworkPort(); $networkPortObject->cleanDBonItemDelete($this->getType(), $this->getID()); // Manage networkportmigration if exists if (TableExists('glpi_networkportmigrations')) { $networkPortMigObject = new NetworkPortMigration(); $networkPortMigObject->cleanDBonItemDelete($this->getType(), $this->getID()); } } // If this type is RESERVABLE clean one associated to purged item if (in_array($this->getType(),$CFG_GLPI['reservation_types'])) { $rr = new ReservationItem(); if ($rr->getFromDBbyItem($this->getType(), $this->fields['id'])) { $rr->delete(array('id' => $infocom->fields['id'])); } } // If this type have CONTRACT, clean one associated to purged item if (in_array($this->getType(),$CFG_GLPI['contract_types'])) { $ci = new Contract_Item(); $ci->cleanDBonItemDelete($this->getType(), $this->fields['id']); } // If this type have DOCUMENT, clean one associated to purged item if (Document::canApplyOn($this)) { $di = new Document_Item(); $di->cleanDBonItemDelete($this->getType(), $this->fields['id']); } // If this type have NOTEPAD, clean one associated to purged item if ($this->usenotepad) { $note = new Notepad(); $note->cleanDBonItemDelete($this->getType(), $this->fields['id']); } } /** * Actions done when item flag deleted is set to an item * * @return nothing **/ function cleanDBonMarkDeleted() { } /** * Save the input data in the Session * * @since version 0.84 **/ protected function saveInput() { $_SESSION['saveInput'][$this->getType()] = $this->input; } /** * Clear the saved data stored in the session * * @since version 0.84 **/ protected function clearSavedInput() { unset($_SESSION['saveInput'][$this->getType()]); } /** * Get the data saved in the session * * @since version 0.84 * * @param $default Array of value used if session is empty * * @return Array of value **/ protected function restoreInput(Array $default=array()) { if (isset($_SESSION['saveInput'][$this->getType()])) { $saved = Html::cleanPostForTextArea($_SESSION['saveInput'][$this->getType()]); // clear saved data when restored (only need once) unset($_SESSION['saveInput'][$this->getType()]); return $saved; } return $default; } // Common functions /** * Add an item in the database with all it's items. * * @param $input array the _POST vars returned by the item form when press add * @param options array with the insert options * - unicity_message : do not display message if item it a duplicate (default is yes) * @param $history boolean do history log ? (true by default) * * @return integer the new ID of the added item (or false if fail) **/ function add(array $input, $options=array(), $history=true) { global $DB, $CFG_GLPI; if ($DB->isSlave()) { return false; } // Store input in the object to be available in all sub-method / hook $this->input = $input; // Manage the _no_history if (!isset($this->input['_no_history'])) { $this->input['_no_history'] = !$history; } if (isset($this->input['add'])) { // Input from the interface // Save this data to be available if add fail $this->saveInput(); } // Call the plugin hook - $this->input can be altered // This hook get the data from the form, not yet altered Plugin::doHook("pre_item_add", $this); if ($this->input && is_array($this->input)) { if (isset($this->input['add'])) { $this->input['_add'] = $this->input['add']; unset($this->input['add']); } $this->input = $this->prepareInputForAdd($this->input); } if ($this->input && is_array($this->input)) { // Call the plugin hook - $this->input can be altered // This hook get the data altered by the object method Plugin::doHook("post_prepareadd", $this); } if ($this->input && is_array($this->input)) { //Check values to inject $this->filterValues(!isCommandLine()); } if ($this->input && is_array($this->input)) { $this->fields = array(); $table_fields = $DB->list_fields($this->getTable()); // fill array for add foreach ($this->input as $key => $val) { if (($key[0] != '_') && isset($table_fields[$key])) { $this->fields[$key] = $this->input[$key]; } } // Auto set date_creation if exsist if (isset($table_fields['date_creation'])) { $this->fields['date_creation'] = $_SESSION["glpi_currenttime"]; } // Auto set date_mod if exsist if (isset($table_fields['date_mod'])) { $this->fields['date_mod'] = $_SESSION["glpi_currenttime"]; } if ($this->checkUnicity(true,$options)) { if ($this->addToDB() !== false) { $this->post_addItem(); $this->addMessageOnAddAction(); if ($this->dohistory && $history) { $changes[0] = 0; $changes[1] = $changes[2] = ""; Log::history($this->fields["id"], $this->getType(), $changes, 0, Log::HISTORY_CREATE_ITEM); } // Auto create infocoms if (isset($CFG_GLPI["auto_create_infocoms"]) && $CFG_GLPI["auto_create_infocoms"] && Infocom::canApplyOn($this)) { $ic = new Infocom(); if (!$ic->getFromDBforDevice($this->getType(), $this->fields['id'])) { $ic->add(array('itemtype' => $this->getType(), 'items_id' => $this->fields['id'])); } } // If itemtype is in infocomtype and if states_id field is filled // and item is not a template if (InfoCom::canApplyOn($this) && isset($this->input['states_id']) && (!isset($this->input['is_template']) || !$this->input['is_template'])) { //Check if we have to automatical fill dates Infocom::manageDateOnStatusChange($this); } Plugin::doHook("item_add", $this); // As add have suceed, clean the old input value if (isset($this->input['_add'])) { $this->clearSavedInput(); } if ($this->mailqueueonaction) { QueuedMail::forceSendFor($this->getType(), $this->fields['id']); } // For unit test (workaround for MyIsam without transaction) if (isset($DB->objcreated) && is_array($DB->objcreated)) { $DB->objcreated[$this->getTable()][] = $this->fields['id']; } return $this->fields['id']; } } } $this->last_status = self::NOTHING_TO_DO; return false; } /** * Get the link to an item * * @param $options array of options * - comments : boolean / display comments * - complete : boolean / display completename instead of name * - additional : boolean / display additionals information * - linkoption : string / additional options to add to * * @return string : HTML link **/ function getLink($options=array()) { $p['linkoption'] = ''; if (isset($options['linkoption'])) { $p['linkoption'] = $options['linkoption']; } if (!isset($this->fields['id'])) { return ''; } if ($this->no_form_page || !$this->can($this->fields['id'], READ)) { return $this->getNameID($options); } $link = $this->getLinkURL(); return "".$this->getNameID($options).""; } /** * Get the link url to an item * * @return string : HTML link **/ function getLinkURL() { global $CFG_GLPI; if (!isset($this->fields['id'])) { return ''; } $link = $this->getFormURLWithID($this->getID()); $link .= ($this->isTemplate() ? "&withtemplate=1" : ""); return $link; } /** * Add a message on add action **/ function addMessageOnAddAction() { global $CFG_GLPI; $addMessAfterRedirect = false; if (isset($this->input['_add'])) { $addMessAfterRedirect = true; } if (isset($this->input['_no_message']) || !$this->auto_message_on_action) { $addMessAfterRedirect = false; } if ($addMessAfterRedirect) { $link = $this->getFormURL(); if (!isset($link)) { return; } if (($name = $this->getName()) == NOT_AVAILABLE) { //TRANS: %1$s is the itemtype, %2$d is the id of the item $this->fields['name'] = sprintf(__('%1$s - ID %2$d'), $this->getTypeName(1), $this->fields['id']); } $display = (isset($this->input['_no_message_link'])?$this->getNameID() :$this->getLink()); // Do not display quotes //TRANS : %s is the description of the added item Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully added'), stripslashes($display))); } } /** * Add needed information to $input (example entities_id) * * @param $input datas used to add the item * * @since version 0.84 * * @return the modified $input array **/ function addNeededInfoToInput($input) { return $input; } /** * Prepare input datas for adding the item * * @param $input datas used to add the item * * @return the modified $input array **/ function prepareInputForAdd($input) { return $input; } /** * Actions done after the ADD of the item in the database * * @return nothing **/ function post_addItem() { } /** * Update some elements of an item in the database. * * @param $input array the _POST vars returned by the item form when press update * @param $history boolean do history log ? (default 1) * @param options array with the insert options * * @return boolean : true on success **/ function update(array $input, $history=1, $options=array()) { global $DB, $CFG_GLPI; if ($DB->isSlave()) { return false; } if (!$this->getFromDB($input[static::getIndexName()])) { return false; } // Store input in the object to be available in all sub-method / hook $this->input = $input; // Manage the _no_history if (!isset($this->input['_no_history'])) { $this->input['_no_history'] = !$history; } // Plugin hook - $this->input can be altered Plugin::doHook("pre_item_update", $this); if ($this->input && is_array($this->input)) { $this->input = $this->prepareInputForUpdate($this->input); if (isset($this->input['update'])) { $this->input['_update'] = $this->input['update']; unset($this->input['update']); } $this->filterValues(!isCommandLine()); } // Valid input for update if ($this->checkUnicity(false,$options)) { if ($this->input && is_array($this->input)) { // Fill the update-array with changes $x = 0; $this->updates = array(); $this->oldvalues = array(); foreach ($this->input as $key => $val) { if (array_key_exists($key,$this->fields)) { // Prevent history for date statement (for date for example) if (is_null($this->fields[$key]) && ($this->input[$key] == 'NULL')) { $this->fields[$key] = 'NULL'; } // Compare item $ischanged = true; $searchopt = $this->getSearchOptionByField('field', $key, $this->getTable()); if (isset($searchopt['datatype'])) { switch ($searchopt['datatype']) { case 'string' : case 'text' : $ischanged = (strcmp($DB->escape($this->fields[$key]), $this->input[$key]) != 0); break; case 'itemlink' : if ($key == 'name') { $ischanged = (strcmp($DB->escape($this->fields[$key]), $this->input[$key]) != 0); break; } // else default default : $ischanged = ($DB->escape($this->fields[$key]) != $this->input[$key]); break; } } else { // No searchoption case $ischanged = ($DB->escape($this->fields[$key]) != $this->input[$key]); } if ($ischanged) { if ($key != "id") { // Store old values if (!in_array($key,$this->history_blacklist)) { $this->oldvalues[$key] = $this->fields[$key]; } $this->fields[$key] = $this->input[$key]; $this->updates[$x] = $key; $x++; } } } } if (count($this->updates)) { if (array_key_exists('date_mod',$this->fields)) { // is a non blacklist field exists if (count(array_diff($this->updates, $this->history_blacklist)) > 0) { $this->fields['date_mod'] = $_SESSION["glpi_currenttime"]; $this->updates[$x++] = 'date_mod'; } } $this->pre_updateInDB(); if (count($this->updates)) { if ($this->updateInDB($this->updates, ($this->dohistory && $history ? $this->oldvalues : array()))) { $this->addMessageOnUpdateAction(); Plugin::doHook("item_update", $this); //Fill forward_entity_to array with itemtypes coming from plugins if (isset(self::$plugins_forward_entity[$this->getType()])) { foreach (self::$plugins_forward_entity[$this->getType()] as $itemtype) { static::$forward_entity_to[] = $itemtype; } } // forward entity information if needed if (count(static::$forward_entity_to) && (in_array("entities_id",$this->updates) || in_array("is_recursive",$this->updates)) ) { $this->forwardEntityInformations(); } // If itemtype is in infocomtype and if states_id field is filled // and item not a template if (InfoCom::canApplyOn($this) && in_array('states_id',$this->updates) && ($this->getField('is_template') != NOT_AVAILABLE)) { //Check if we have to automatical fill dates Infocom::manageDateOnStatusChange($this, false); } } } } $this->post_updateItem($history); if ($this->mailqueueonaction) { QueuedMail::forceSendFor($this->getType(), $this->fields['id']); } return true; } } return false; } /** * Forward entity information to linked items **/ protected function forwardEntityInformations() { global $DB; if (!isset($this->fields['id']) || !($this->fields['id'] >= 0)) { return false; } if (count(static::$forward_entity_to)) { foreach (static::$forward_entity_to as $type) { $item = new $type(); $query = "SELECT `id` FROM `".$item->getTable()."` WHERE "; if ($item->isField('itemtype')) { $query .= " `itemtype` = '".$this->getType()."' AND `items_id` = '".$this->fields['id']."'"; } else { $query .= " `".$this->getForeignKeyField()."` = '".$this->fields['id']."'"; } $input = array('entities_id' => $this->getEntityID()); if ($this->maybeRecursive()) { $input['is_recursive'] = $this->isRecursive(); } foreach ($DB->request($query) as $data) { $input['id'] = $data['id']; // No history for such update $item->update($input, 0); } } } } /** * Add a message on update action **/ function addMessageOnUpdateAction() { $addMessAfterRedirect = false; if (isset($this->input['_update'])) { $addMessAfterRedirect = true; } if (isset($this->input['_no_message']) || !$this->auto_message_on_action) { $addMessAfterRedirect = false; } if ($addMessAfterRedirect) { $link = $this->getFormURL(); if (!isset($link)) { return; } // Do not display quotes if (isset($this->fields['name'])) { $this->fields['name'] = stripslashes($this->fields['name']); } else { //TRANS: %1$s is the itemtype, %2$d is the id of the item $this->fields['name'] = sprintf(__('%1$s - ID %2$d'), $this->getTypeName(1), $this->fields['id']); } if (isset($this->input['_no_message_link'])) { $display = $this->getNameID(); } else { $display = $this->getLink(); } //TRANS : %s is the description of the updated item Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully updated'), $display)); } } /** * Prepare input datas for updating the item * * @param $input datas used to update the item * * @return the modified $input array **/ function prepareInputForUpdate($input) { return $input; } /** * Actions done after the UPDATE of the item in the database * * @param $history store changes history ? (default 1) * * @return nothing **/ function post_updateItem($history=1) { } /** * Actions done before the UPDATE of the item in the database * * @return nothing **/ function pre_updateInDB() { } /** * Delete an item in the database. * * @param $input array the _POST vars returned by the item form when press delete * @param $force boolean force deletion (default 0) * @param $history boolean do history log ? (default 1) * * @return boolean : true on success **/ function delete(array $input, $force=0, $history=1) { global $DB; if ($DB->isSlave()) { return false; } if (!$this->getFromDB($input[static::getIndexName()])) { return false; } // Force purge for templates / may not to be deleted / not dynamic lockable items if ($this->isTemplate() || !$this->maybeDeleted() // Do not take into account deleted field if maybe dynamic but not dynamic || ($this->useDeletedToLockIfDynamic() && !$this->isDynamic())) { $force = 1; } // Store input in the object to be available in all sub-method / hook $this->input = $input; if (isset($this->input['purge'])) { $this->input['_purge'] = $this->input['purge']; unset($this->input['purge']); } if (isset($this->input['delete'])) { $this->input['_delete'] = $this->input['delete']; unset($this->input['delete']); } if (!isset($this->input['_no_history'])) { $this->input['_no_history'] = !$history; } // Purge if ($force) { Plugin::doHook("pre_item_purge", $this); } else { Plugin::doHook("pre_item_delete", $this); } if (!is_array($this->input)) { // $input clear by a hook to cancel delete return false; } if ($this->pre_deleteItem()) { if ($this->deleteFromDB($force)) { if ($force) { $this->addMessageOnPurgeAction(); $this->post_purgeItem(); Plugin::doHook("item_purge", $this); } else { $this->addMessageOnDeleteAction(); if ($this->dohistory && $history) { $changes[0] = 0; $changes[1] = $changes[2] = ""; $logaction = Log::HISTORY_DELETE_ITEM; if ($this->useDeletedToLockIfDynamic() && $this->isDynamic()) { $logaction = Log::HISTORY_LOCK_ITEM; } Log::history($this->fields["id"], $this->getType(), $changes, 0, $logaction); } $this->post_deleteItem(); Plugin::doHook("item_delete",$this); } if ($this->mailqueueonaction) { QueuedMail::forceSendFor($this->getType(), $this->fields['id']); } return true; } } return false; } /** * Actions done after the DELETE (mark as deleted) of the item in the database * * @return nothing **/ function post_deleteItem() { } /** * Actions done after the PURGE of the item in the database * * @return nothing **/ function post_purgeItem() { } /** * Add a message on delete action **/ function addMessageOnDeleteAction() { if (!$this->maybeDeleted()) { return; } $addMessAfterRedirect = false; if (isset($this->input['_delete'])) { $addMessAfterRedirect = true; } if (isset($this->input['_no_message']) || !$this->auto_message_on_action) { $addMessAfterRedirect = false; } if ($addMessAfterRedirect) { $link = $this->getFormURL(); if (!isset($link)) { return; } if (isset($this->input['_no_message_link'])) { $display = $this->getNameID(); } else { $display = $this->getLink(); } //TRANS : %s is the description of the updated item Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully deleted'), $display)); } } /** * Add a message on purge action **/ function addMessageOnPurgeAction() { $addMessAfterRedirect = false; if (isset($this->input['_purge']) || isset($this->input['_delete'])) { $addMessAfterRedirect = true; } if (isset($this->input['_purge'])) { $this->input['_no_message_link'] = true; } if (isset($this->input['_no_message']) || !$this->auto_message_on_action) { $addMessAfterRedirect = false; } if ($addMessAfterRedirect) { $link = $this->getFormURL(); if (!isset($link)) { return; } if (isset($this->input['_no_message_link'])) { $display = $this->getNameID(); } else { $display = $this->getLink(); } //TRANS : %s is the description of the updated item Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully purged'), $display)); } } /** * Actions done before the DELETE of the item in the database / * Maybe used to add another check for deletion * * @return bool : true if item need to be deleted else false **/ function pre_deleteItem() { return true; } /** * Restore an item put in the dustbin in the database. * * @param $input array the _POST vars returned by the item form when press restore * @param $history boolean do history log ? (default 1) * * @return boolean : true on success **/ function restore(array $input, $history=1) { if (!$this->getFromDB($input[static::getIndexName()])) { return false; } if (isset($input['restore'])) { $input['_restore'] = $input['restore']; unset($input['restore']); } // Store input in the object to be available in all sub-method / hook $this->input = $input; Plugin::doHook("pre_item_restore", $this); if ($this->restoreInDB()) { $this->addMessageOnRestoreAction(); if ($this->dohistory && $history) { $changes[0] = 0; $changes[1] = $changes[2] = ""; $logaction = Log::HISTORY_RESTORE_ITEM; if ($this->useDeletedToLockIfDynamic() && $this->isDynamic()) { $logaction = Log::HISTORY_UNLOCK_ITEM; } Log::history($this->input["id"], $this->getType(), $changes, 0, $logaction); } $this->post_restoreItem(); Plugin::doHook("item_restore", $this); if ($this->mailqueueonaction) { QueuedMail::forceSendFor($this->getType(), $this->fields['id']); } return true; } return false; } /** * Actions done after the restore of the item * * @return nothing **/ function post_restoreItem() { } /** * Add a message on restore action **/ function addMessageOnRestoreAction() { $addMessAfterRedirect = false; if (isset($this->input['_restore'])) { $addMessAfterRedirect = true; } if (isset($this->input['_no_message']) || !$this->auto_message_on_action) { $addMessAfterRedirect = false; } if ($addMessAfterRedirect) { $link = $this->getFormURL(); if (!isset($link)) { return; } if (isset($this->input['_no_message_link'])) { $display = $this->getNameID(); } else { $display = $this->getLink(); } //TRANS : %s is the description of the updated item Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully restored'), $display)); } } /** * Reset fields of the item **/ function reset() { $this->fields = array(); } /** * Have I the global right to add an item for the Object * May be overloaded if needed (ex Ticket) * * @since version 0.83 * * @param $type itemtype of object to add * * @return rights **/ function canAddItem($type) { return $this->can($this->getID(), UPDATE); } /** * Have I the global right to "create" the Object * May be overloaded if needed (ex KnowbaseItem) * * @return booleen **/ static function canCreate() { if (static::$rightname) { return Session::haveRight(static::$rightname, CREATE); } return false; } /** * Have I the global right to "delete" the Object * * May be overloaded if needed * * @return booleen **/ static function canDelete() { if (static::$rightname) { return Session::haveRight(static::$rightname, DELETE); } return false; } /** * Have I the global right to "purge" the Object * * May be overloaded if needed * * @return booleen **/ static function canPurge() { if (static::$rightname) { return Session::haveRight(static::$rightname, PURGE); } return false; } /** * Have I the global right to "update" the Object * * Default is calling canCreate * May be overloaded if needed * * @return booleen **/ static function canUpdate() { if (static::$rightname) { return Session::haveRight(static::$rightname, UPDATE); } } /** * Have I the right to "create" the Object * * Default is true and check entity if the objet is entity assign * * May be overloaded if needed * * @return booleen **/ function canCreateItem() { if (!$this->checkEntity()) { return false; } return true; } /** * Have I the right to "update" the Object * * Default is true and check entity if the objet is entity assign * * May be overloaded if needed * * @return booleen **/ function canUpdateItem() { if (!$this->checkEntity()) { return false; } return true; } /** * Have I the right to "delete" the Object * * Default is true and check entity if the objet is entity assign * * May be overloaded if needed * * @return booleen **/ function canDeleteItem() { if (!$this->checkEntity()) { return false; } return true; } /** * Have I the right to "purge" the Object * * Default is true and check entity if the objet is entity assign * * @since version 0.85 * * @return booleen **/ function canPurgeItem() { if (!$this->checkEntity()) { return false; } // Can purge an object with Infocom only if can purge Infocom if (InfoCom::canApplyOn($this)) { $infocom = new Infocom(); if ($infocom->getFromDBforDevice($this->getType(), $this->fields['id'])) { return $infocom->canPurge(); } } return true; } /** * Have I the global right to "view" the Object * * Default is true and check entity if the objet is entity assign * * May be overloaded if needed * * @return booleen **/ static function canView() { if (static::$rightname) { return Session::haveRight(static::$rightname, READ); } return false; } /** * Have I the right to "view" the Object * May be overloaded if needed * * @return booleen **/ function canViewItem() { if (!$this->checkEntity(true)) { return false; } // else : Global item return true; } /** * Have i right to see action button * * @param $ID integer ID to check * * @since version 0.85 * * @return booleen **/ function canEdit($ID) { if ($this->maybeDeleted()) { return ($this->can($ID, CREATE) || $this->can($ID, UPDATE) || $this->can($ID, DELETE) || $this->can($ID, PURGE)); } return ($this->can($ID, CREATE) || $this->can($ID, UPDATE) || $this->can($ID, PURGE)); } /** * Can I change recursive flag to false * check if there is "linked" object in another entity * * May be overloaded if needed * * @return booleen **/ function canUnrecurs() { global $DB, $CFG_GLPI; $ID = $this->fields['id']; if (($ID < 0) || !$this->fields['is_recursive']) { return true; } $entities = "('".$this->fields['entities_id']."'"; foreach (getAncestorsOf("glpi_entities",$this->fields['entities_id']) as $papa) { $entities .= ",'$papa'"; } $entities .= ")"; $RELATION = getDbRelations(); if ($this instanceof CommonTreeDropdown) { $f = getForeignKeyFieldForTable($this->getTable()); if (countElementsInTable($this->getTable(), "`$f`='$ID' AND entities_id NOT IN $entities") > 0) { return false; } } if (isset($RELATION[$this->getTable()])) { foreach ($RELATION[$this->getTable()] as $tablename => $field) { if ($tablename[0] != '_') { $itemtype = getItemTypeForTable($tablename); $item = new $itemtype(); if ($item->isEntityAssign()) { // 1->N Relation if (is_array($field)) { foreach ($field as $f) { if (countElementsInTable($tablename, "`$f`='$ID' AND entities_id NOT IN $entities") > 0) { return false; } } } else { if (countElementsInTable($tablename, "`$field`='$ID' AND entities_id NOT IN $entities") > 0) { return false; } } } else { foreach ($RELATION as $othertable => $rel) { // Search for a N->N Relation with devices if (($othertable == "_virtual_device") && isset($rel[$tablename])) { $devfield = $rel[$tablename][0]; // items_id... $typefield = $rel[$tablename][1]; // itemtype... $sql = "SELECT DISTINCT `$typefield` AS itemtype FROM `$tablename` WHERE `$field`='$ID'"; $res = $DB->query($sql); // Search linked device of each type if ($res) { while ($data = $DB->fetch_assoc($res)) { $itemtype = $data["itemtype"]; $itemtable = getTableForItemType($itemtype); $item = new $itemtype(); if ($item->isEntityAssign()) { if (countElementsInTable(array($tablename, $itemtable), "`$tablename`.`$field`='$ID' AND `$tablename`.`$typefield` ='$itemtype' AND `$tablename`.`$devfield` =`$itemtable`.id AND `$itemtable`.`entities_id` NOT IN $entities") > '0') { return false; } } } } // Search for another N->N Relation } else if (($othertable != $this->getTable()) && isset($rel[$tablename])) { $itemtype = getItemTypeForTable($othertable); $item = new $itemtype(); if ($item->isEntityAssign()) { if (is_array($rel[$tablename])) { foreach ($rel[$tablename] as $otherfield) { if (countElementsInTable(array($tablename, $othertable), "`$tablename`.`$field`='$ID' AND `$tablename`.`$otherfield` =`$othertable`.id AND `$othertable`.`entities_id` NOT IN $entities") > '0') { return false; } } } else { $otherfield = $rel[$tablename]; if (countElementsInTable(array($tablename, $othertable), "`$tablename`.`$field`=$ID AND `$tablename`.`$otherfield` =`$othertable`.id AND `$othertable`.`entities_id` NOT IN $entities") > '0') { return false; } } } } } } } } } // Doc links to this item if (($this->getType() > 0) && countElementsInTable(array('glpi_documents_items', 'glpi_documents'), "`glpi_documents_items`.`items_id`='$ID' AND `glpi_documents_items`.`itemtype`=".$this->getType()." AND `glpi_documents_items`.`documents_id`=`glpi_documents`.`id` AND `glpi_documents`.`entities_id` NOT IN $entities") > '0') { return false; } // TODO : do we need to check all relations in $RELATION["_virtual_device"] for this item return true; } /** * check if this action can be done on this field of this item by massive actions * * @since 0.83 * * @param $action string name of the action * @param $field integer id of the field * @param $value string value of the field * **/ function canMassiveAction($action, $field, $value) { return true; } /** * @since version 9.1 * * @param $options array * * @return boolean **/ function showDates($options = array()) { $isNewID = ((isset($options['withtemplate']) && ($options['withtemplate'] == 2)) || $this->isNewID($this->getID())); if ($isNewID) { return true; } $date_creation_exists = ($this->getField('date_creation') != NOT_AVAILABLE); $date_mod_exists = ($this->getField('date_mod') != NOT_AVAILABLE); $colspan = $options['colspan']; if ((!isset($options['withtemplate']) || ($options['withtemplate'] == 0)) && !empty($this->fields['template_name'])) { $colspan = 1; } echo "