. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * Rack Class **/ class Rack extends CommonDBTM { use Glpi\Features\DCBreadcrumb; const FRONT = 0; const REAR = 1; const POS_NONE = 0; const POS_LEFT = 1; const POS_RIGHT = 2; // orientation in room const ROOM_O_NORTH = 1; const ROOM_O_EAST = 2; const ROOM_O_SOUTH = 3; const ROOM_O_WEST = 4; // From CommonDBTM public $dohistory = true; static $rightname = 'datacenter'; static function getTypeName($nb = 0) { //TRANS: Test of comment for translation (mark : //TRANS) return _n('Rack', 'Racks', $nb); } function defineTabs($options = []) { $ong = []; $this ->addStandardTab('Item_Rack', $ong, $options) ->addDefaultFormTab($ong) ->addImpactTab($ong, $options) ->addStandardTab('Infocom', $ong, $options) ->addStandardTab('Contract_Item', $ong, $options) ->addStandardTab('Document_Item', $ong, $options) ->addStandardTab('Ticket', $ong, $options) ->addStandardTab('Item_Problem', $ong, $options) ->addStandardTab('Change_Item', $ong, $options) ->addStandardTab('Log', $ong, $options); return $ong; } function showForm($ID, $options = []) { global $DB, $CFG_GLPI; $rand = mt_rand(); $tplmark = $this->getAutofillMark('name', $options); $this->initForm($ID, $options); $this->showFormHeader($options); echo ""; echo ""; echo ""; $objectName = autoName( $this->fields["name"], "name", (isset($options['withtemplate']) && ( $options['withtemplate']== 2)), $this->getType(), $this->fields["entities_id"] ); Html::autocompletionTextField( $this, 'name', [ 'value' => $objectName, 'rand' => $rand ] ); echo ""; echo ""; echo ""; State::dropdown([ 'value' => $this->fields["states_id"], 'entity' => $this->fields["entities_id"], 'condition' => ['is_visible_rack' => 1], 'rand' => $rand] ); echo "\n"; $this->showDcBreadcrumb(); echo ""; echo ""; echo ""; Location::dropdown([ 'value' => $this->fields["locations_id"], 'entity' => $this->fields["entities_id"], 'rand' => $rand ]); echo ""; echo ""; echo ""; RackType::dropdown([ 'value' => $this->fields["racktypes_id"], 'rand' => $rand ]); echo "\n"; echo ""; echo ""; echo ""; User::dropdown([ 'name' => 'users_id_tech', 'value' => $this->fields["users_id_tech"], 'right' => 'own_ticket', 'entity' => $this->fields["entities_id"], 'rand' => $rand ]); echo ""; echo ""; echo ""; Manufacturer::dropdown([ 'value' => $this->fields["manufacturers_id"], 'rand' => $rand ]); echo "\n"; echo ""; echo ""; echo ""; Group::dropdown([ 'name' => 'groups_id_tech', 'value' => $this->fields['groups_id_tech'], 'entity' => $this->fields['entities_id'], 'condition' => ['is_assign' => 1], 'rand' => $rand ]); echo ""; echo ""; echo ""; RackModel::dropdown([ 'value' => $this->fields["rackmodels_id"], 'rand' => $rand ]); echo "\n"; echo ""; echo ""; echo ""; Html::autocompletionTextField($this, 'serial', ['rand' => $rand]); echo ""; echo ""; echo ""; $objectName = autoName($this->fields["otherserial"], "otherserial", (isset($options['withtemplate']) && ($options['withtemplate'] == 2)), $this->getType(), $this->fields["entities_id"]); Html::autocompletionTextField( $this, 'otherserial', [ 'value' => $objectName, 'rand' => $rand ] ); echo "\n"; echo ""; echo ""; echo ""; $rooms = $DB->request([ 'SELECT' => ['id', 'name'], 'FROM' => DCRoom::getTable() ]); $rooms_list = []; while ($row = $rooms->next()) { $rooms_list[$row['id']] = $row['name']; } Dropdown::showFromArray( "dcrooms_id", $rooms_list, [ 'value' => $this->fields["dcrooms_id"], 'rand' => $rand, 'display_emptychoice' => true ] ); $current = $this->fields['position']; Ajax::updateItemOnSelectEvent( "dropdown_dcrooms_id$rand", "room_positions", $CFG_GLPI["root_doc"]."/ajax/dcroom_size.php", ['id' => '__VALUE__', 'current' => $current, 'rand' => $rand] ); Ajax::updateItemOnSelectEvent( "dropdown_dcrooms_id$rand", "dropdown_locations_id$rand", $CFG_GLPI["root_doc"]."/ajax/dropdownLocation.php", [ 'items_id' => '__VALUE__', 'itemtype' => 'DCRoom' ] ); echo ""; echo ""; echo ""; $dcroom = new DCRoom(); $positions = []; $used = []; if ((int)$this->fields['dcrooms_id'] > 0 && $dcroom->getFromDB($this->fields['dcrooms_id'])) { $used = $dcroom->getFilled($current); $positions = $dcroom->getAllPositions(); Dropdown::showFromArray( 'position', $positions, [ 'value' => $current, 'rand' => $rand, 'display_emptychoice' => true, 'used' => $used ] ); } else { echo __('No room found or selected'); } echo "\n"; echo ""; echo ""; echo ""; Dropdown::showFromArray( "room_orientation", [ self::ROOM_O_NORTH => __('North'), self::ROOM_O_EAST => __('East'), self::ROOM_O_SOUTH => __('South'), self::ROOM_O_WEST => __('West'), ], [ 'value' => $this->fields["room_orientation"], 'rand' => $rand, 'display_emptychoice' => true ] ); echo ""; echo ""; echo ""; echo ""; echo ""; Dropdown::showNumber( "number_units", [ 'value' => $this->fields["number_units"], 'min' => 1, 'max' => 100, 'step' => 1, 'rand' => $rand ] ); echo " ".__('U').""; echo ""; echo "".Html::input("width", ['id' => "width$rand", 'value' => $this->fields["width"]]); echo "\n"; echo ""; echo ""; echo "".Html::input("height", ['id' => "height$rand", 'value' => $this->fields["height"]]); echo ""; echo "".Html::input("depth", ['id' => "depth$rand", 'value' => $this->fields["depth"]]); echo "\n"; echo ""; echo ""; echo "".Html::input("max_power", ['id' => "max_power$rand", 'value' => $this->fields["max_power"]]); echo ""; echo "".Html::input("mesured_power", ['id' => "mesured_power$rand", 'value' => $this->fields["mesured_power"]]); echo "\n"; echo ""; echo ""; echo "".Html::input("max_weight", ['id' => "max_weight$rand", 'value' => $this->fields["max_weight"]]); echo ""; echo ""; Html::showColorField( 'bgcolor', [ 'value' => $this->fields['bgcolor'], 'rand' => $rand ] ); echo "\n"; echo ""; echo ""; echo ""; echo ""; $this->showFormButtons($options); return true; } function rawSearchOptions() { $tab = parent::rawSearchOptions(); $tab[] = [ 'id' => '2', 'table' => $this->getTable(), 'field' => 'id', 'name' => __('ID'), 'massiveaction' => false, // implicit field is id 'datatype' => 'number' ]; $tab = array_merge($tab, Location::rawSearchOptionsToAdd()); $tab[] = [ 'id' => '4', 'table' => 'glpi_racktypes', 'field' => 'name', 'name' => _n('Type', 'Types', 1), 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '40', 'table' => 'glpi_rackmodels', 'field' => 'name', 'name' => _n('Model', 'Models', 1), 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '31', 'table' => 'glpi_states', 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', 'condition' => ['is_visible_rack' => 1] ]; $tab[] = [ 'id' => '5', 'table' => $this->getTable(), 'field' => 'serial', 'name' => __('Serial number'), 'datatype' => 'string', 'autocomplete' => true, ]; $tab[] = [ 'id' => '6', 'table' => $this->getTable(), 'field' => 'otherserial', 'name' => __('Inventory number'), 'datatype' => 'string', 'autocomplete' => true, ]; $tab[] = [ 'id' => '7', 'table' => DCRoom::getTable(), 'field' => 'name', 'name' => DCRoom::getTypeName(1), 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '8', 'table' => $this->getTable(), 'field' => 'number_units', 'name' => __('Number of units'), 'datatype' => 'number' ]; $tab[] = [ 'id' => '16', 'table' => $this->getTable(), 'field' => 'comment', 'name' => __('Comments'), 'datatype' => 'text' ]; $tab[] = [ 'id' => '19', 'table' => $this->getTable(), 'field' => 'date_mod', 'name' => __('Last update'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '121', 'table' => $this->getTable(), 'field' => 'date_creation', 'name' => __('Creation date'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '23', 'table' => 'glpi_manufacturers', 'field' => 'name', 'name' => Manufacturer::getTypeName(1), 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '24', 'table' => 'glpi_users', 'field' => 'name', 'linkfield' => 'users_id_tech', 'name' => __('Technician in charge of the hardware'), 'datatype' => 'dropdown', 'right' => 'own_ticket' ]; $tab[] = [ 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', 'linkfield' => 'groups_id_tech', 'name' => __('Group in charge of the hardware'), 'condition' => ['is_assign' => 1], 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '50', 'table' => $this->getTable(), 'field' => 'template_name', 'name' => __('Template name'), 'datatype' => 'text', 'massiveaction' => false, 'nosearch' => true, 'nodisplay' => true, 'autocomplete' => true, ]; $tab[] = [ 'id' => '80', 'table' => 'glpi_entities', 'field' => 'completename', 'name' => Entity::getTypeName(1), 'datatype' => 'dropdown' ]; $tab = array_merge($tab, Notepad::rawSearchOptionsToAdd()); $tab = array_merge($tab, Datacenter::rawSearchOptionsToAdd(get_class($this))); return $tab; } function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { switch ($item->getType()) { case DCRoom::getType(): $nb = 0; if ($_SESSION['glpishow_count_on_tabs']) { $nb = countElementsInTable( self::getTable(), [ 'dcrooms_id' => $item->getID(), 'is_deleted' => 0 ] ); } return self::createTabEntry( self::getTypeName(Session::getPluralNumber()), $nb ); break; } } static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { switch ($item->getType()) { case DCRoom::getType(): self::showForRoom($item); break; } } /** * Print room's racks * * @param DCRoom $room DCRoom object * * @return void **/ static function showForRoom(DCRoom $room) { global $DB, $CFG_GLPI; $room_id = $room->getID(); $rand = mt_rand(); if (!$room->getFromDB($room_id) || !$room->can($room_id, READ)) { return false; } $canedit = $room->canEdit($room_id); $racks = $DB->request([ 'FROM' => self::getTable(), 'WHERE' => [ 'dcrooms_id' => $room->getID(), 'is_deleted' => 0 ] ]); Session::initNavigateListItems( self::getType(), //TRANS : %1$s is the itemtype name, // %2$s is the name of the item (used for headings of a list) sprintf( __('%1$s = %2$s'), $room->getTypeName(1), $room->getName() ) ); echo "
"; echo ""; echo ""; echo "
"; $racks = iterator_to_array($racks); echo "
"; $rack = new self(); if (!count($racks)) { echo ""; echo "
".__('No rack found')."
"; } else { if ($canedit) { Html::openMassiveActionsForm('mass'.__CLASS__.$rand); $massiveactionparams = [ 'num_displayed' => min($_SESSION['glpilist_limit'], count($racks)), 'container' => 'mass'.__CLASS__.$rand ]; Html::showMassiveActions($massiveactionparams); } echo ""; $header = ""; if ($canedit) { $header .= ""; } $header .= ""; $header .= ""; echo $header; foreach ($racks as $row) { $rack->getFromResultSet($row); echo ""; if ($canedit) { echo ""; } echo ""; echo ""; } echo $header; echo "
"; $header .= Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand); $header .= "".__('Name')."
"; Html::showMassiveActionCheckBox(__CLASS__, $row["id"]); echo "" . $rack->getLink() . "
"; if ($canedit && count($racks)) { $massiveactionparams['ontop'] = false; Html::showMassiveActions($massiveactionparams); } if ($canedit) { Html::closeForm(); } } echo "
"; echo "
"; $data = []; $rows = (int) $room->fields['vis_rows']; $cols = (int) $room->fields['vis_cols']; $w_prct = 100 / $cols; $grid_w = 40 * $cols; $grid_h = (39 * $rows) + 16; $ajax_url = $CFG_GLPI['root_doc']."/ajax/rack.php"; //fill rows $cells = []; $outbound = []; foreach ($racks as &$item) { $rack->getFromResultSet($item); $in = false; $x = $y = 0; $coord = explode(',', $item['position']); if (is_array($coord) && count($coord) == 2) { list($x, $y) = $coord; $item['_x'] = $x - 1; $item['_y'] = $y - 1; } else { $item['_x'] = null; $item['_y'] = null; } if ($x <= $cols && $y <= $rows && $x > 0 && $y > 0) { $in = true; $cells[] = $item; } if ($in === false) { $outbound[] = $item; } } if (count($outbound)) { echo ""; foreach ($outbound as $out) { $rack->getFromResultSet($out); echo ""; } echo "
"; echo __('Following elements are out of room bounds'); echo "
".self::getCell($rack, $out)."
"; } echo ""; $blueprint = ""; $blueprint_ctrl = ""; if (strlen($room->fields['blueprint'])) { $blueprint_url = Toolbox::getPictureUrl($room->fields['blueprint']); $blueprint = "
".__('Blueprint').""; } echo "
$blueprint_ctrl ".__('Grid')."
      "; $dcroom = new DCRoom(); if ($dcroom->canCreate()) { echo "
      "; } echo "
      "; foreach ($cells as $cell) { if ($rack->getFromDB($cell['id'])) { echo self::getCell($rack, $cell); } } // add a locked element to bottom to display a full grid echo "
      "; echo "
      "; //.grid-stack echo $blueprint; echo "
      "; //.grid-room echo "
      "; echo "
      "; echo "
      "; // #viewgraph $rack_add_tip = __s('Insert a rack here'); $js = <<' + getBijectiveIndex(x) + ''); } for (var y = 1; y <= $rows; y++) { $('.indexes-y').append('
    • ' + y + '
    • '); } // append cells for adding racks for (var y = 1; y <= $rows; y++) { for (var x = 1; x <= $cols; x++) { $('.racks_add') .append('
      {$rack_add_tip}
      '); } } var x_before_drag = 0; var y_before_drag = 0; var dirty = false; $('.grid-stack') .on('change', function(event, items) { if (dirty) { return; } var grid = $(event.target).data('gridstack'); $.each(items, function(index, item) { $.post('{$ajax_url}', { id: item.id, dcrooms_id: $room_id, action: 'move_rack', x: item.x + 1, y: item.y + 1, }, function(answer) { var answer = jQuery.parseJSON(answer); // revert to old position if (!answer.status) { dirty = true; grid.move(item.el, x_before_drag, y_before_drag); dirty = false; displayAjaxMessageAfterRedirect(); } }); }); }) .on('dragstart', function(event, ui) { var element = $(event.target); var node = element.data('_gridstack_node'); // store position before drag x_before_drag = Number(node.x); y_before_drag = Number(node.y); // disable qtip element.qtip('hide', true); }) .on('click', function(event, ui) { var grid = this; var element = $(event.target); var el_url = element.find('a').attr('href'); if (el_url) { window.location = el_url; } }); $('#viewgraph .cell_add').on('click', function(){ var _this = $(this); if (_this.find('div').length == 0) { var _x = _this.data('x'); var _y = _this.data('y'); $.ajax({ url : "{$rack->getFormURL()}", data: { room: $room_id, position: _x + ',' + _y, ajax: true }, success: function(data) { $('#grid-dialog') .html(data) .dialog({ modal: true, width: 'auto' }); } }); } }); $('#viewgraph .cell_add, #viewgraph .grid-stack-item').each(function() { var tipcontent = $(this).find('.tipcontent'); if (tipcontent.length) { $(this).qtip({ position: { my: 'left center', at: 'right center', }, content: { text: tipcontent }, style: { classes: 'qtip-shadow qtip-bootstrap rack_tipcontent' } }); } }); }); JAVASCRIPT; echo Html::scriptBlock($js); } function prepareInputForAdd($input) { if ($this->prepareInput($input)) { if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } unset($input['id']); unset($input['withtemplate']); return $input; } return false; } function prepareInputForUpdate($input) { return $this->prepareInput($input); } function post_getEmpty() { $this->fields['bgcolor'] = '#FEC95C'; } /** * Prepares input (for update and add) * * @param array $input Input data * * @return array */ private function prepareInput($input) { if (!array_key_exists('dcrooms_id', $input) || $input['dcrooms_id'] == 0) { // Position is not set if room not selected return $input; } if ($input['position'] == 0) { return $input; Session::addMessageAfterRedirect( __('Position must be set'), true, ERROR ); return false; } $where = [ 'dcrooms_id' => $input['dcrooms_id'], 'position' => $input['position'], 'is_deleted' => false ]; if (!$this->isNewItem()) { $where['NOT'] = ['id' => $input['id']]; } $existing = countElementsInTable(self::getTable(), $where); if ($existing > 0) { Session::addMessageAfterRedirect( sprintf( __('%1$s position is not available'), $input['position'] ), true, ERROR ); return false; } return $input; } /** * Get already filled places * * @param string $current Current position to exclude; defaults to null * * @return array [x => [left => [depth, depth, depth, depth]], [right => [depth, depth, depth, depth]]] */ public function getFilled($itemtype = null, $items_id = null) { global $DB; $iterator = $DB->request([ 'FROM' => Item_Rack::getTable(), 'WHERE' => [ 'racks_id' => $this->getID() ] ]); $filled = []; while ($row = $iterator->next()) { $item = new $row['itemtype']; if (!$item->getFromDB($row['items_id'])) { continue; } $units = 1; $width = 1; $depth = 1; if ($item->fields[strtolower($item->getType()) . 'models_id'] != 0) { $model_class = $item->getType() . 'Model'; $modelsfield = strtolower($item->getType()) . 'models_id'; $model = new $model_class; if ($model->getFromDB($item->fields[$modelsfield])) { $units = $model->fields['required_units']; $depth = $model->fields['depth']; $width = $model->fields['is_half_rack'] == 0 ? 1 : 0.5; } } $position = $row['position']; if (empty($itemtype) || empty($items_id) || $itemtype != $row['itemtype'] || $items_id != $row['items_id'] ) { while (--$units >= 0) { $content_filled = [ self::POS_LEFT => [0, 0, 0, 0], self::POS_RIGHT => [0, 0, 0, 0] ]; if (isset($filled[$position + $units])) { $content_filled = $filled[$position + $units]; } if ($row['hpos'] == self::POS_NONE || $row['hpos'] == self::POS_LEFT) { $d = 0; while ($d/4 < $depth) { $pos = ($row['orientation'] == self::REAR) ? 3 - $d : $d; $val = 1; if (isset($content_filled[self::POS_LEFT][$pos]) && $content_filled[self::POS_LEFT][$pos] != 0) { Toolbox::logError('Several elements exists in rack at same place :('); $val += $content_filled[self::POS_LEFT][$pos]; } $content_filled[self::POS_LEFT][$pos] = $val; ++$d; } } if ($row['hpos'] == self::POS_NONE || $row['hpos'] == self::POS_RIGHT) { $d = 0; while ($d/4 < $depth) { $pos = ($row['orientation'] == self::REAR) ? 3 - $d : $d; $val = 1; if (isset($content_filled[self::POS_RIGHT][$pos]) && $content_filled[self::POS_RIGHT][$pos] != 0) { Toolbox::logError('Several elements exists in rack at same place :('); $val += $content_filled[self::POS_RIGHT][$pos]; } $content_filled[self::POS_RIGHT][$pos] = $val; ++$d; } } $filled[$position + $units] = $content_filled; } } } return $filled; } public function getEmpty() { if (!parent::getEmpty()) { return false; } $this->fields['number_units'] = 42; return true; } function cleanDBonPurge() { $this->deleteChildrenAndRelationsFromDb( [ Item_Rack::class, PDU_Rack::class, ] ); } /** * Get cell content * * @param Rack $rack Rack instance * @param mixed $cell Rack cell (array or false) * * @return string */ private static function getCell(Rack $rack, $cell) { $bgcolor = $rack->getField('bgcolor'); $fgcolor = Html::getInvertedColor($bgcolor); return "
      ". $cell['name']." ". $cell['name']." ". $cell['serial']." ". $cell['otherserial']."
      "; // .grid-stack-item } static function getIcon() { return "fas fa-server"; } }