%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/plugins/glpiinventory/inc/ |
Current File : //var/www/projetos/suporte.iigd.com.br/plugins/glpiinventory/inc/deploypackage.class.php |
<?php /** * --------------------------------------------------------------------- * GLPI Inventory Plugin * Copyright (C) 2021 Teclib' and contributors. * * http://glpi-project.org * * based on FusionInventory for GLPI * Copyright (C) 2010-2021 by the FusionInventory Development Team. * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI Inventory Plugin. * * GLPI Inventory Plugin is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPI Inventory Plugin 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with GLPI Inventory Plugin. If not, see <https://www.gnu.org/licenses/>. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access directly to this file"); } use Glpi\Toolbox\Sanitizer; /** * Manage the deploy packages. */ class PluginGlpiinventoryDeployPackage extends CommonDBTM { /** * Initialize the tasks running with this package (updated with overrided getFromDB method) * * @var array */ public $running_tasks = []; /** * The right name for this class * * @var string */ public static $rightname = 'plugin_glpiinventory_package'; /** * Initialize the users visibility of package for self-service deploy * * @var array */ protected $users = []; /** * Initialize the groups visibility of package for self-service deploy * * @var array */ protected $groups = []; /** * Initialize the profiles visibility of package for self-service deploy * * @var array */ protected $profiles = []; /** * Initialize the entities visibility of package for self-service deploy * * @var array */ protected $entities = []; /** * Get name of this type by language of the user connected * * @param integer $nb number of elements * @return string name of this type */ public static function getTypeName($nb = 0) { return __('Package', 'glpiinventory'); } public function getFromDB($ID) { $found = parent::getFromDB($ID); if ($found) { // Get all tasks runnning $this->running_tasks = PluginGlpiinventoryTask::getItemsFromDB( [ 'is_active' => true, 'is_running' => true, 'targets' => [__CLASS__ => $this->fields['id']], 'by_entities' => false, ] ); } return $found; } /** * Have I the right to "update" the object content (package actions) * * Also call canUpdateItem() * * @return boolean **/ public function canUpdateContent() { // check if a task is currently running with this package if (count($this->running_tasks)) { return false; } return parent::canUpdateItem(); } /** * Get the massive actions for this object * * @param object|null $checkitem * @return array list of actions */ public function getSpecificMassiveActions($checkitem = null) { $actions = []; if (strstr($_SERVER["HTTP_REFERER"], 'deploypackage.import.php')) { $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'import'] = __('Import', 'glpiinventory'); } else { $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'transfert'] = __('Transfer'); $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'export'] = __('Export', 'glpiinventory'); $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'duplicate'] = _sx('button', 'Duplicate'); } return $actions; } /** * Define standard massiveaction actions to deny * * @return array list of actions to deny */ public function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); if (strstr($_SERVER["HTTP_REFERER"], 'deploypackage.import.php')) { $forbidden[] = 'update'; $forbidden[] = 'add'; $forbidden[] = 'delete'; $forbidden[] = 'purge'; } return $forbidden; } /** * Display form related to the massive action selected * * @param MassiveAction $ma MassiveAction instance * @return boolean */ public static function showMassiveActionsSubForm(MassiveAction $ma) { switch ($ma->getAction()) { case 'transfert': Dropdown::show('Entity'); echo "<br><br>" . Html::submit( __('Post'), ['name' => 'massiveaction'] ); return true; case 'duplicate': echo Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']); return true; } return parent::showMassiveActionsSubForm($ma); } /** * Execution code for massive action * * @param MassiveAction $ma MassiveAction instance * @param CommonDBTM $item item on which execute the code * @param array $ids list of ID on which execute the code */ public static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) { /** @var PluginGlpiinventoryDeployPackage $item */ switch ($ma->getAction()) { case 'export': foreach ($ids as $key) { if ($item->can($key, UPDATE)) { $item->exportPackage($key); $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK); } } break; case 'transfert': $pfDeployPackage = new PluginGlpiinventoryDeployPackage(); foreach ($ids as $key) { if ($pfDeployPackage->getFromDB($key)) { $input = []; $input['id'] = $key; $input['entities_id'] = $ma->POST['entities_id']; $pfDeployPackage->update($input); } } break; case 'import': foreach ($ids as $key) { $item->importPackage($key); $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK); } break; case 'duplicate': $pfPackage = new self(); foreach ($ids as $key) { if ($pfPackage->getFromDB($key)) { if ($pfPackage->duplicate($pfPackage->getID())) { //set action massive ok for this item $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK); } else { // KO $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO); } } } break; } } /** * Define error message if package used in task. This will prevent edit the * package * * @return string */ public function getEditErrorMessage() { $error_message = ""; if (count($this->running_tasks) > 0) { // Display error message $error_message .= "<div class='warning'>"; $error_message .= "<i class='fa fa-exclamation-triangle fa-3x'></i>"; $error_message .= "<h3>" . __("Modification Denied", 'glpiinventory') . "</h3>\n"; $error_message .= "<h4>" . _n( "The following task is running with this package", "The following tasks are running with this package", count($this->running_tasks), 'glpiinventory' ) . "</h4>\n"; foreach ($this->running_tasks as $task) { $taskurl = PluginGlpiinventoryTask::getFormURLWithID($task['task']['id'], true); $error_message .= "<a href='$taskurl'>" . $task['task']['name'] . "</a>, "; } $error_message .= "</div>"; } return $error_message; } /** * Prepare data before add to database * * @param array $input * @return array */ public function prepareInputForAdd($input) { if (!isset($input['json'])) { $input['json'] = json_encode([ 'jobs' => [ 'checks' => [], 'associatedFiles' => [], 'actions' => [], 'userinteractions' => [] ], 'associatedFiles' => []]); } return parent::prepareInputForAdd($input); } /** * Get search function for the class * * @return array */ public function rawSearchOptions() { $tab = []; $tab[] = [ 'id' => 'common', 'name' => __('Characteristics') ]; $tab[] = [ 'id' => '1', 'table' => $this->getTable(), 'field' => 'name', 'name' => __('Name'), 'datatype' => 'itemlink', 'itemlink_link' => $this->getType() ]; $tab[] = [ 'id' => '2', 'table' => $this->getTable(), 'field' => 'id', 'name' => __('ID'), ]; $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', ]; $tab[] = [ 'id' => '80', 'table' => 'glpi_entities', 'field' => 'completename', 'name' => Entity::getTypeName(1), 'datatype' => 'dropdown', ]; $tab[] = [ 'id' => '86', 'table' => $this->getTable(), 'field' => 'is_recursive', 'name' => __('Child entities'), 'datatype' => 'bool', ]; $tab[] = [ 'id' => '20', 'table' => 'glpi_plugin_glpiinventory_deploygroups', 'field' => 'name', 'name' => __('Enable deploy on demand for the following group', 'glpiinventory'), 'datatype' => 'dropdown', ]; return $tab; } /** * Get all packages in json format * * @return string json */ public function getAllDatas() { global $DB; $iterator = $DB->request([ 'SELECT' => ['id', 'name'], 'FROM' => $this->getTable(), 'ORDER' => 'name' ]); $json = []; $i = 0; foreach ($iterator as $row) { $json['packages'][$i]['package_id'] = $row['id']; $json['packages'][$i]['package_name'] = $row['name']; $i++; } $json['results'] = count($iterator); return json_encode($json); } /** * Clean orders after delete the package * */ public function post_deleteFromDB() { $pfDeployFile = new PluginGlpiinventoryDeployFile(); // remove file in repo $json = json_decode($this->fields['json'], true); if (is_null($json)) { return; } foreach ($json['associatedFiles'] as $sha512 => $file) { $pfDeployFile->removeFileInRepo($sha512); } } /** * Display the menu / list of packages * * @param array $options */ public function showMenu($options = []) { $this->displaylist = false; $this->fields['id'] = -1; $this->showList(); } /** * Display list of packages */ public function showList() { Search::show('PluginGlpiinventoryDeployPackage'); } /** * Define tabs to display on form page * * @param array $options * @return array containing the tabs name */ public function defineTabs($options = []) { $ong = []; $this->addDefaultFormTab($ong); $this->addStandardTab(__CLASS__, $ong, $options); $ong['no_all_tab'] = true; return $ong; } /** * Display form * * @param integer $ID * @param array $options * @return true */ public function showForm($ID, array $options = []) { $this->initForm($ID, $options); $this->showFormHeader($options); //Add redips_clone element before displaying tabs //If we don't do this, dragged element won't be visible on the other tab not displayed at //first (for reminder, GLPI tabs are displayed dynamically on-demand) echo "<div id='redips_clone'></div>"; echo "<tr class='tab_bg_1'>"; echo "<td>" . __('Name') . " :</td>"; echo "<td>"; echo Html::input('name', ['size' => 40, 'value' => $this->fields['name']]); echo "</td>"; echo "<td>" . __('Comments') . " :</td>"; echo "<td>"; echo "<textarea cols='40' class='form-control' name='comment' >" . $this->fields["comment"] . "</textarea>"; echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'>"; echo "<td>" . __('Enable deploy on demand for the following group', 'glpiinventory') . " :</td>"; echo "<td>"; PluginGlpiinventoryDeployGroup::dropdown(['value' => $this->fields["plugin_glpiinventory_deploygroups_id"]]); echo "</td>"; echo "<td colspan='2'></td>"; echo "</tr>"; $this->showFormButtons($options); return true; } /** * Display order type form * * @global array $CFG_GLPI */ public function displayOrderTypeForm() { global $CFG_GLPI; $subtypes = [ 'check' => __("Audits", 'glpiinventory'), 'file' => __("Files", 'glpiinventory'), 'action' => __("Actions", 'glpiinventory'), 'userinteraction' => __("User interactions", 'glpiinventory') ]; $json_subtypes = [ 'check' => 'checks', 'file' => 'associatedFiles', 'action' => 'actions', 'userinteraction' => 'userinteractions' ]; $rand = mt_rand(); $datas = json_decode($this->fields['json'], true); echo "<table class='tab_cadre_fixe' id='package_order_" . $this->getID() . "'>"; // Display an error if the package modification is not possible $canedit = $this->canUpdateContent(); $error_msg = $this->getEditErrorMessage(); if (!empty($error_msg)) { echo "<tr><td>$error_msg</td></tr>"; } // Display the lists of each subtypes of a package foreach ($subtypes as $subtype => $label) { echo "<tr>"; echo "<th id='th_title_{$subtype}_$rand'>"; echo "<img src='" . Plugin::getWebDir('glpiinventory') . "/pics/$subtype.png' />"; echo " " . __($label, 'glpiinventory'); if ($canedit) { $this->plusButtonSubtype($this->getID(), $subtype, $rand); } echo "</th>"; echo "</tr>"; /** * File's form must be encoded as multipart/form-data **/ $multipart = ""; if ($subtype == "file") { $multipart = "enctype='multipart/form-data'"; } echo "<tr>"; echo "<td style='vertical-align:top'>"; /** * Display subtype form **/ echo "<form name='addition$subtype' method='post' " . $multipart . " action='deploypackage.form.php'>"; echo "<input type='hidden' name='id' value='" . $this->getID() . "' />"; echo "<input type='hidden' name='itemtype' value='PluginGlpiinventoryDeploy" . ucfirst($subtype) . "' />"; $classname = "PluginGlpiinventoryDeploy" . ucfirst($subtype); $class = new $classname(); $class->displayForm($this, $datas, $rand, "init"); Html::closeForm(); $json_subtype = $json_subtypes[$subtype]; /** * Display stored actions datas **/ if ( isset($datas['jobs'][$json_subtype]) && !empty($datas['jobs'][$json_subtype]) ) { echo "<div id='drag_deploypackage_" . $subtype . "s'>"; echo "<form name='remove" . $subtype . "s' " . "method='post' action='deploypackage.form.php' " . "id='" . $subtype . "sList" . $rand . "'>"; echo Html::hidden('remove_item'); echo Html::hidden('itemtype', ['value' => $classname]); echo Html::hidden('packages_id', ['value' => $this->getID()]); $class->displayList($this, $datas, $rand); Html::closeForm(); echo "</div>"; } /** * Initialize drag and drop on subtype lists **/ echo "<script type='text/javascript'>"; echo "redipsInit('deploypackage', '$subtype', '" . $this->getID() . "');"; echo "</script>"; echo "</td>"; echo "</tr>"; } echo "</table>"; } /** * Manage + button (audits, files, actions) * * @global array $CFG_GLPI * @param integer $id id of the package * @param string $subtype name of subtype (audits, files, actions) * @param string $rand random string for js to prevent collisions */ public function plusButtonSubtype($id, $subtype, $rand) { global $CFG_GLPI; if ($this->can($id, UPDATE)) { echo " "; echo "<img id='plus_{$subtype}s_block{$rand}'"; echo " onclick=\"new_subtype('{$subtype}', {$id}, {$rand})\" "; echo " title='" . __('Add') . "' alt='" . __('Add') . "' "; echo " class='pointer' src='" . $CFG_GLPI["root_doc"] . "/pics/add_dropdown.png' /> "; } } /** * Plus button used to add an element * * @global array $CFG_GLPI * @param string $dom_id * @param boolean $clone */ public static function plusButton($dom_id, $clone = false) { global $CFG_GLPI; echo " "; echo "<img id='plus_$dom_id' "; if ($clone !== false) { echo " onClick=\"plusbutton('$dom_id', '$clone')\" "; } else { echo " onClick=\"plusbutton('$dom_id')\" "; } echo " title='" . __('Add') . "' alt='" . __('Add') . "' "; echo " class='pointer' src='" . $CFG_GLPI["root_doc"] . "/pics/add_dropdown.png'> "; } /** * When user is in DEBUG mode, we display the json * * @global array $CFG_GLPI */ public function showDebug() { global $CFG_GLPI; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th>" . __('JSON package representation', 'glpiinventory') . "</th></tr>"; echo "<tr><td>"; echo "<textarea cols='132' rows='50' style='border:1' name='json'>"; echo PluginGlpiinventoryToolbox::formatJson($this->fields['json']); echo "</textarea></td></tr>"; echo "</table>"; } /** * Update the json structure * * @param string $action_type type of action * @param array $params data used to update the json */ public static function alterJSON($action_type, $params) { //route to sub class $item_type = $params['itemtype']; if ( in_array($item_type, [ 'PluginGlpiinventoryDeployCheck', 'PluginGlpiinventoryDeployFile', 'PluginGlpiinventoryDeployAction', 'PluginGlpiinventoryDeployUserinteraction' ]) ) { $class = new $item_type(); switch ($action_type) { case "add_item": return $class->add_item($params); break; case "save_item": return $class->save_item($params); break; case "remove_item": return $class->remove_item($params); break; case "move_item": return $class->move_item($params); break; } } else { Toolbox::logDebug("package subtype not found : " . $params['itemtype']); Html::displayErrorAndDie("package subtype not found"); } } /** * Export the package (information, actions, files...) * * @param integer $packages_id id of the package to export */ public function exportPackage($packages_id) { $this->getFromDB($packages_id); if (empty($this->fields['uuid'])) { $input = [ 'id' => $this->fields['id'], 'uuid' => Rule::getUuid() ]; $this->update($input); } $pfDeployFile = new PluginGlpiinventoryDeployFile(); // Generate JSON $input = $this->fields; unset($input['id']); $a_xml = [ 'package' => $input, 'files' => [], 'manifests' => [], 'repository' => [], 'orders' => [['json' => $this->fields['json']]], ]; $json = json_decode($this->fields['json'], true); $a_files = $json['associatedFiles']; // Add files foreach ($a_files as $files_id => $data) { $a_pkgfiles = current($pfDeployFile->find(['sha512' => $files_id], [], 1)); if (count($a_pkgfiles) > 0) { unset($a_pkgfiles['id']); $a_xml['files'][] = $a_pkgfiles; } } // Create zip with JSON and files $name = preg_replace("/[^a-zA-Z0-9]/", '', $this->fields['name']); $filename = GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/export/" . $this->fields['uuid'] . "." . $name . ".zip"; if (file_exists($filename)) { unlink($filename); } $zip = new ZipArchive(); if ($zip->open($filename) == true) { if ($zip->open($filename, ZipArchive::CREATE) == true) { $zip->addEmptyDir('files'); $zip->addEmptyDir('files/manifests'); $zip->addEmptyDir('files/repository'); $pfDeployFile = new PluginGlpiinventoryDeployFile(); foreach ($a_files as $hash => $data) { $sha512 = trim(file_get_contents(PLUGIN_GLPI_INVENTORY_MANIFESTS_DIR . $hash)); $zip->addFile(PLUGIN_GLPI_INVENTORY_MANIFESTS_DIR . $hash, "files/manifests/" . $hash); $a_xml['manifests'][] = $hash; $file = $pfDeployFile->getDirBySha512($sha512) . "/" . $sha512; $zip->addFile(GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/repository/" . $file, "files/repository/" . $file); $a_xml['repository'][] = $file; } $json_string = json_encode($a_xml); $zip->addFromString('information.json', $json_string); } $zip->close(); Session::addMessageAfterRedirect(__("Package exported in", "glpiinventory") . " " . GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/export/" . $this->fields['uuid'] . "." . $name . ".zip"); } } /** * Import the package * * @param string $zipfile the zip file with all data inside */ public function importPackage($zipfile) { $zip = new ZipArchive(); $pfDeployFile = new PluginGlpiinventoryDeployFile(); $filename = GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/import/" . $zipfile; $extract_folder = GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/import/" . $zipfile . ".extract"; if ($zip->open($filename, ZipArchive::CREATE) == true) { $zip->extractTo($extract_folder); $zip->close(); } $json_string = file_get_contents($extract_folder . "/information.json"); $a_info = json_decode($json_string, true); // Find package with this uuid $a_packages = $this->find(['uuid' => $a_info['package']['uuid']]); if (count($a_packages) == 0) { // Create it $_SESSION['tmp_clone_package'] = true; $this->add($a_info['package']); foreach ($a_info['files'] as $input) { $pfDeployFile->add($input); } } // Copy files foreach ($a_info['manifests'] as $manifest) { rename($extract_folder . "/files/manifests/" . $manifest, PLUGIN_GLPI_INVENTORY_MANIFESTS_DIR . $manifest); } foreach ($a_info['repository'] as $repository) { $split = explode('/', $repository); array_pop($split); $folder = ''; foreach ($split as $dir) { $folder .= '/' . $dir; if (!file_exists(GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/repository" . $folder)) { mkdir(GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/repository" . $folder); } } rename($extract_folder . "/files/repository/" . $repository, GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/repository/" . $repository); } } /** * Display list of packages to import */ public function listPackagesToImport() { $rand = mt_rand(); echo "<div class='spaced'>"; Html::openMassiveActionsForm('mass' . __CLASS__ . $rand); $massiveactionparams = ['container' => 'mass' . __CLASS__ . $rand]; Html::showMassiveActions($massiveactionparams); echo "<table class='tab_cadre_fixe'>"; echo "<tr class='tab_bg_1'>"; echo "<th colspan='5'>"; echo __('Packages to import', 'glpiinventory'); echo "</th>"; echo "</tr>"; echo "<tr class='tab_bg_1'>"; echo "<th width='10'>" . Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand) . "</th>"; echo "<th>"; echo __('Name'); echo "</th>"; echo "<th>"; echo __('uuid'); echo "</th>"; echo "<th>"; echo __('Package to update'); echo "</th>"; echo "</tr>"; foreach (glob(GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/import/*.zip") as $file) { echo "<tr class='tab_bg_1'>"; $file = str_replace(GLPI_PLUGIN_DOC_DIR . "/glpiinventory/files/import/", "", $file); $split = explode('.', $file); echo "<td>"; Html::showMassiveActionCheckBox(__CLASS__, $file); echo "</td>"; echo "<td>"; echo $split[2]; echo "</td>"; echo "<td>"; echo $split[0] . "." . $split[1]; echo "</td>"; echo "<td>"; $a_packages = current($this->find(['uuid' => $split[0] . "." . $split[1]], [], 1)); if (count($a_packages) > 1) { $this->getFromDB($a_packages['id']); echo $this->getLink(); } echo "</td>"; echo "</tr>"; } echo "</table>"; $massiveactionparams['ontop'] = false; Html::showMassiveActions($massiveactionparams); echo "</div>"; } /** * Get a sub element at index * * @param string $subtype * @param integer $index * @return string */ public function getSubElement($subtype, $index) { $data_o = json_decode($this->fields['json'], true); return $data_o['jobs'][$subtype][$index]; } /** * Get Order's associated file by hash * * @param string $hash * @return null|string */ public function getAssociatedFile($hash) { $data_o = json_decode($this->fields['json'], true); if (array_key_exists($hash, $data_o['associatedFiles'])) { return $data_o['associatedFiles'][$hash]; } return null; } /** * Get the json * * @param integer $packages_id id of the order * @return boolean|string the string is in json format */ public static function getJson($packages_id) { $pfDeployPackage = new self(); $pfDeployPackage->getFromDB($packages_id); if (!empty($pfDeployPackage->fields['json'])) { return $pfDeployPackage->fields['json']; } else { return false; } } /** * Update the order json * * @param integer $packages_id * @param array $datas * @return integer error number */ public static function updateOrderJson($packages_id, $datas) { $pfDeployPackage = new self(); $options = JSON_UNESCAPED_SLASHES; $json = json_encode($datas, $options); $json_error_consts = [ JSON_ERROR_NONE => "JSON_ERROR_NONE", JSON_ERROR_DEPTH => "JSON_ERROR_DEPTH", JSON_ERROR_STATE_MISMATCH => "JSON_ERROR_STATE_MISMATCH", JSON_ERROR_CTRL_CHAR => "JSON_ERROR_CTRL_CHAR", JSON_ERROR_SYNTAX => "JSON_ERROR_SYNTAX", JSON_ERROR_UTF8 => "JSON_ERROR_UTF8" ]; $error_json = json_last_error(); $error_json_message = json_last_error_msg(); $error = 0; if ($error_json != JSON_ERROR_NONE) { $error_msg = $json_error_consts[$error_json]; Session::addMessageAfterRedirect( __("The modified JSON contained a syntax error :", "glpiinventory") . "<br/>" . $error_msg . "<br/>" . $error_json_message, false, ERROR, false ); $error = 1; } else { $error = $pfDeployPackage->update( [ 'id' => $packages_id, 'json' => Toolbox::addslashes_deep($json) ] ); } return $error; } /** * Get the tab name used for item * * @param CommonGLPI $item the item object * @param integer $withtemplate 1 if is a template form * @return string name of the tab */ public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if (!$withtemplate) { switch (get_class($item)) { case self::class: if ($item->canUpdateItem()) { $tabs = []; if ($item->fields['id'] > 0) { $tabs[1] = __('Package actions', 'glpiinventory'); } if ($item->fields['plugin_glpiinventory_deploygroups_id'] > 0) { if ($_SESSION['glpishow_count_on_tabs']) { $nb = $item->countVisibilities(); $tabs[2] = self::createTabEntry( _n( 'Target for deploy on demand', 'Targets for deploy on demand', $nb, 'glpiinventory' ), $nb ); } else { $tabs[2] = _n( 'Target for deploy on demand', 'Targets for deploy on demand', 2, 'glpiinventory' ); } } return $tabs; } break; case Computer::class: if ( Session::haveRight("plugin_glpiinventory_selfpackage", READ) && PluginGlpiinventoryToolbox::isAnInventoryDevice($item) && self::isDeployEnabled($item->fields['id']) ) { return __('Package deploy', 'glpiinventory'); } break; } } return ''; } /** * Display the content of the tab * * @param CommonGLPI $item * @param integer $tabnum number of the tab to display * @param integer $withtemplate 1 if is a template form * @return boolean */ public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { if ($item instanceof self) { if ($tabnum == 2) { $item->showVisibility(); return true; } elseif ($tabnum == 1) { $item->displayOrderTypeForm(); return true; } } elseif ($item instanceof Computer) { $package = new self(); $package->showPackageForMe($_SESSION['glpiID'], $item); return true; } return false; } /** * Count number elements for the visibility * * @return integer */ public function countVisibilities() { return (count($this->entities) + count($this->users) + count($this->groups) + count($this->profiles)); } /** * Display the visibility, so who can read. write... * * @global array $CFG_GLPI * @return true */ public function showVisibility() { global $CFG_GLPI; $ID = $this->fields['id']; $canedit = $this->can($ID, UPDATE); echo "<div class='center'>"; $rand = mt_rand(); $nb = count($this->users) + count($this->groups) + count($this->profiles) + count($this->entities); if ($canedit) { echo "<div class='firstbloc'>"; echo "<form name='deploypackagevisibility_form$rand' id='deploypackagevisibility_form$rand' "; echo " method='post' action='" . Toolbox::getItemTypeFormURL('PluginGlpiinventoryDeployPackage') . "'>"; echo "<input type='hidden' name='plugin_glpiinventory_deploypackages_id' value='$ID'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . __('Add a target for self-service', 'glpiinventory') . "</th></tr>"; echo "<tr class='tab_bg_2'><td width='100px'>"; $types = ['Entity', 'Group', 'Profile', 'User']; $addrand = Dropdown::showItemTypes('_type', $types); $params = ['type' => '__VALUE__', 'right' => 'plugin_glpiinventory_selfpackage']; Ajax::updateItemOnSelectEvent( "dropdown__type" . $addrand, "visibility$rand", $CFG_GLPI["root_doc"] . "/ajax/visibility.php", $params ); echo "</td>"; echo "<td><span id='visibility$rand'></span>"; echo "</td></tr>"; echo "</table>"; Html::closeForm(); echo "</div>"; } echo "<div class='spaced'>"; if ($canedit && $nb) { Html::openMassiveActionsForm('mass' . __CLASS__ . $rand); $massiveactionparams = ['num_displayed' => $nb, 'container' => 'mass' . __CLASS__ . $rand, 'specific_actions' => ['delete' => _x('button', 'Delete permanently')] ]; Html::showMassiveActions($massiveactionparams); } echo "<table class='tab_cadre_fixehov'>"; $header_begin = "<tr>"; $header_top = ''; $header_bottom = ''; $header_end = ''; if ($canedit && $nb) { $header_begin .= "<th width='10'>"; $header_top .= Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand); $header_bottom .= Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand); $header_end .= "</th>"; } $header_end .= "<th>" . __('Type') . "</th>"; $header_end .= "<th>" . _n('Recipient', 'Recipients', Session::getPluralNumber()) . "</th>"; $header_end .= "</tr>"; echo $header_begin . $header_top . $header_end; // Users if (count($this->users)) { foreach ($this->users as $key => $val) { foreach ($val as $data) { echo "<tr class='tab_bg_1'>"; if ($canedit) { echo "<td>"; Html::showMassiveActionCheckBox('PluginGlpiinventoryDeployPackage_User', $data["id"]); echo "</td>"; } echo "<td>" . __('User') . "</td>"; echo "<td>" . getUserName($data['users_id']) . "</td>"; echo "</tr>"; } } } // Groups if (count($this->groups)) { foreach ($this->groups as $key => $val) { foreach ($val as $data) { echo "<tr class='tab_bg_1'>"; if ($canedit) { echo "<td>"; Html::showMassiveActionCheckBox('PluginGlpiinventoryDeployPackage_Group', $data["id"]); echo "</td>"; } echo "<td>" . __('Group') . "</td>"; echo "<td>"; $names = Dropdown::getDropdownName('glpi_groups', $data['groups_id'], 1); $groupname = sprintf( __('%1$s %2$s'), $names["name"], Html::showToolTip($names["comment"], ['display' => false]) ); if ($data['entities_id'] >= 0) { $groupname = sprintf( __('%1$s / %2$s'), $groupname, Dropdown::getDropdownName( 'glpi_entities', $data['entities_id'] ) ); if ($data['is_recursive']) { $groupname = sprintf( __('%1$s %2$s'), $groupname, "<span class='b'>(" . __('R') . ")</span>" ); } } echo $groupname; echo "</td>"; echo "</tr>"; } } } // Entity if (count($this->entities)) { foreach ($this->entities as $key => $val) { foreach ($val as $data) { echo "<tr class='tab_bg_1'>"; if ($canedit) { echo "<td>"; Html::showMassiveActionCheckBox('PluginGlpiinventoryDeployPackage_Entity', $data["id"]); echo "</td>"; } echo "<td>" . Entity::getTypeName(1) . "</td>"; echo "<td>"; $names = Dropdown::getDropdownName('glpi_entities', $data['entities_id'], 1); $entityname = sprintf( __('%1$s %2$s'), $names["name"], Html::showToolTip($names["comment"], ['display' => false]) ); if ($data['is_recursive']) { $entityname = sprintf( __('%1$s %2$s'), $entityname, "<span class='b'>(" . __('R') . ")</span>" ); } echo $entityname; echo "</td>"; echo "</tr>"; } } } // Profiles if (count($this->profiles)) { foreach ($this->profiles as $key => $val) { foreach ($val as $data) { echo "<tr class='tab_bg_1'>"; if ($canedit) { echo "<td>"; Html::showMassiveActionCheckBox('PluginGlpiinventoryDeployPackage_Profile', $data["id"]); echo "</td>"; } echo "<td>" . _n('Profile', 'Profiles', 1) . "</td>"; echo "<td>"; $names = Dropdown::getDropdownName('glpi_profiles', $data['profiles_id'], 1); $profilename = sprintf( __('%1$s %2$s'), $names["name"], Html::showToolTip($names["comment"], ['display' => false]) ); if ($data['entities_id'] >= 0) { $profilename = sprintf( __('%1$s / %2$s'), $profilename, Dropdown::getDropdownName( 'glpi_entities', $data['entities_id'] ) ); if ($data['is_recursive']) { $profilename = sprintf( __('%1$s %2$s'), $profilename, "<span class='b'>(" . __('R') . ")</span>" ); } } echo $profilename; echo "</td>"; echo "</tr>"; } } } if ($nb) { echo $header_begin . $header_bottom . $header_end; } echo "</table>"; if ($canedit && $nb) { $massiveactionparams['ontop'] = false; Html::showMassiveActions($massiveactionparams); Html::closeForm(); } echo "</div>"; return true; } /** * Fill internal variable with visibility elements when load package * information from database */ public function post_getFromDB() { // Users $this->users = PluginGlpiinventoryDeployPackage_User::getUsers($this->fields['id']); // Entities $this->entities = PluginGlpiinventoryDeployPackage_Entity::getEntities($this->fields['id']); // Group / entities $this->groups = PluginGlpiinventoryDeployPackage_Group::getGroups($this->fields['id']); // Profile / entities $this->profiles = PluginGlpiinventoryDeployPackage_Profile::getProfiles($this->fields['id']); } /** * Get all available states for a package * @return array of states and their labels */ public static function getPackageDeploymentStates() { return [ 'agents_notdone' => __('Not done yet', 'glpiinventory'), 'agents_error' => __('In error', 'glpiinventory'), 'agents_success' => __('Successful', 'glpiinventory'), 'agents_running' => __('Running', 'glpiinventory'), 'agents_prepared' => __('Prepared', 'glpiinventory'), 'agents_cancelled' => __('Cancelled', 'glpiinventory') ]; } /** * Get a label for a state * @param string $state the state * @return string the label associated to a state */ public static function getDeploymentLabelForAState($state) { $states = self::getPackageDeploymentStates(); if (isset($states[$state])) { return $states[$state]; } else { return ''; } } /** * Display a form with a list of packages and their state, that a user * has request to install on it's computer * * @param integer $users_id id of the user * @param $item source item (maybe a User or a computer) */ public function showPackageForMe($users_id, $item = false) { global $CFG_GLPI; $computer = new Computer(); $self_service = !($_SESSION['glpiactiveprofile']['interface'] == 'central'); if (!$self_service) { $computers_id = false; if ($item && $item instanceof Computer) { $computers_id = $item->getID(); } $my_packages = $this->getPackageForMe(false, $computers_id); } else { $my_packages = $this->getPackageForMe($users_id); } // check current interface $is_tech = isset($_SESSION['glpiactiveprofile']['interface']) && $_SESSION['glpiactiveprofile']['interface'] == "central"; // retrieve state name $joblogs_labels = PluginGlpiinventoryTaskjoblog::dropdownStateValues(); // Display for each computer, list of packages you can deploy $url = Plugin::getWebDir('glpiinventory'); echo "<form name='onetimedeploy_form' id='onetimedeploy_form' method='POST' action='$url/front/deploypackage.public.php' enctype=\"multipart/form-data\">"; echo "<table class='tab_cadre_fixe'>"; foreach ($my_packages as $computers_id => $data) { $package_to_install = []; $computer->getFromDB($computers_id); echo "<tr>"; echo "<th><img src='$url/pics/computer_icon.png'/> " . _n('Computer', 'Computers', 1) . " <i>" . $computer->fields['name'] . "</i></th>"; echo "</tr>"; if (count($data)) { echo "<tr class='tab_bg_1'>"; echo "<td>"; echo '<div class="target_block">'; echo '<div class="target_details">'; echo '<div class="target_stats">'; foreach ($data as $packages_id => $package_info) { if (isset($package_info['taskjobs_id'])) { $taskjob_id = $package_info['taskjobs_id']; echo "<div class='counter_block " . $package_info['last_taskjobstate']['state'] . "' id='block_$taskjob_id'>"; // display deploy informations echo "<table>"; echo "<tr>"; echo "<td style='min-width: 600px'>"; // add a toggle control if ($is_tech) { echo "<a class='toggle_run' href='#' id='toggle_run_$taskjob_id'>"; echo $package_info['name']; echo "</a>"; } else { echo $package_info['name']; } echo "</td>"; echo "<td style='width: 200px'>"; echo Html::convDateTime($package_info['last_taskjobstate']['date']); echo "</td>"; echo "<td style='width: 200px'>"; echo self::getDeploymentLabelForAState($package_info['last_taskjobstate']['state']); echo "</td>"; echo "</tr>"; echo "</table>"; if ($is_tech) { // display also last log (folded) echo "<div class='agent_block' id='run_$taskjob_id' style='display:none;'>"; echo "<div class='buttons'>"; // if job is in error, suggest restart if ( in_array( $package_info['last_taskjobstate']['state'], ["agents_error", "agents_success"] ) ) { echo "<a class='restart btn' href='#' title='" . __("Restart job", 'glpiinventory') . "' id='restart_run_$taskjob_id'> <i class='fa fa-bolt'></i></a>"; } // if job has not started, user can cancel it if ($package_info['last_taskjobstate']['state'] == "agents_prepared") { echo "<a class='cancel btn' href='#' title='" . __("Cancel job", 'glpiinventory') . "' id='cancel_run_$taskjob_id'> <i class='fa fa-stop'></i></a>"; } // permits to "soft" refresh echo "<a href='#' title='" . __("refresh job", 'glpiinventory') . "' class='btn' id='refresh_run_$taskjob_id'> <i class='fa fa-sync fa-fx'></i></a>"; echo "</div>"; // .buttons // log list echo "<table class='runs' id='runs_$taskjob_id'>"; foreach ($package_info['last_taskjobstate']['logs'] as $log) { echo "<tr class='run log'>"; echo "<td>" . $log['log.f_date'] . "</td>"; echo "<td>" . $joblogs_labels[$log['log.state']] . "</td>"; echo "<td>" . $log['log.comment'] . "</td>"; echo "</tr>"; } echo "</table>"; // .runs echo '</div>'; // .agent_block } echo '</div>'; // .counter_block // js controls (toggle, restart) echo Html::scriptBlock("$(function() { var logstatuses_names = " . json_encode($joblogs_labels) . "; $('#toggle_run_$taskjob_id').click(function(event){ event.preventDefault(); $('#run_$taskjob_id').toggle(); $(this).toggleClass('expand') .parent('td') .nextAll('td').toggle(); }); $('#cancel_run_$taskjob_id').click(function(event){ event.preventDefault(); $.ajax({ url: '" . $url . "/ajax/cancel_job.php', data: { 'jobstate_id': " . $package_info['last_taskjobstate']['id'] . ", 'agent_id': " . $package_info['agent_id'] . " }, complete: function() { document.location.reload(); } }); }); $('#restart_run_$taskjob_id').click(function(event){ event.preventDefault(); $.ajax({ url: '" . $url . "/ajax/restart_job.php', data: { 'jobstate_id': " . $package_info['last_taskjobstate']['id'] . ", 'agent_id': " . $package_info['agent_id'] . " }, complete: function() { document.location.reload(); } }); }); $('#refresh_run_$taskjob_id i').click(function() { var fa = $(this); fa.addClass('fa-spin fa-spinner') .removeClass('fa-sync'); $.ajax({ url: '" . $url . "/ajax/jobstates_logs.php', data: { 'id': " . $package_info['last_taskjobstate']['id'] . ", 'last_date': '2999-01-01 00:00:00' // force a future date }, success: function(data){ // no data -> reload tab if (typeof data.logs == 'undefined') { reloadTab(); return; } if (data.logs.length) { // remove old data $('#runs_$taskjob_id').empty(); $.each(data.logs, function( index, log ) { $('#runs_$taskjob_id').append( '<tr>'+ '<td>'+log['log.f_date']+'</td>'+ '<td>'+logstatuses_names[log['log.state']]+'</td>'+ '<td>'+log['log.comment']+'</td>'+ '<tr>' ) }); var class_to_apply = ''; switch (data.logs[0]['log.state'] + '') { case '" . PluginGlpiinventoryTaskjoblog::TASK_RUNNING . "': class_to_apply = 'agents_running'; break; case '" . PluginGlpiinventoryTaskjoblog::TASK_ERROR . "': class_to_apply = 'agents_error'; break; case '" . PluginGlpiinventoryTaskjoblog::TASK_OK . "': class_to_apply = 'agents_success'; break; } if (class_to_apply.length) { $('#block_$taskjob_id') .attr('class', 'counter_block '+class_to_apply); } } }, complete: function() { setTimeout(function() { fa.removeClass('fa-spin fa-spinner') .addClass('fa-sync'); }, 300); } }); }) });"); } else { $package_to_install[$packages_id] = $package_info['name']; } } echo '</div>'; // .target_stats echo '</div>'; // .target_details echo '</div>'; // .target_block echo "</td>"; echo "</tr>"; } if (count($package_to_install)) { $p['name'] = 'deploypackages_' . $computers_id; $p['display'] = true; $p['multiple'] = true; $p['size'] = 3; $p['width'] = 950; echo "<tr class='tab_bg_1'>"; echo "<td>"; echo __('Select packages you want install', 'glpiinventory'); echo "<br/>"; Dropdown::showFromArray($p['name'], $package_to_install, $p); echo "</td>"; echo "</tr>"; echo "<tr>"; echo "<th colspan='2'>"; echo Html::submit( __('Prepare for install', 'glpiinventory'), ['name' => 'prepareinstall'] ); echo " "; if (!$self_service) { $options = ['local' => __("I'm on this computer: local wakeup", 'glpiinventory'), 'remote' => __("I'm not on this computer: wakeup from the server", 'glpiinventory'), 'none' => __("Don't wakeup", 'glpiinventory') ]; Dropdown::showFromArray( 'wakeup_type', $options, ['value' => 'remote'] ); } else { echo Html::hidden('wakeup_type', ['value' => 'local']); } echo Html::hidden('self_service', ['value' => $self_service]); echo "</th>"; echo "</tr>"; } else { echo "<tr>"; echo "<th colspan='2'>"; echo __('No packages available to install', 'glpiinventory'); echo "</th>"; echo "</tr>"; } } echo "</table>"; // .tab_cadre_fixe Html::closeForm(); } /** * Check if an agent have deploy feature enabled * @since 9.2 * * @param integer $computers_id the ID of the computer to check * @return boolean true if deploy is enabled for the agent */ public static function isDeployEnabled($computers_id) { $agent = new Agent(); //If the agent associated with the computer has not the //deploy feature enabled, do not propose to deploy packages on if (!$agent->getFromDBByCrit(['itemtype' => 'Computer', 'items_id' => $computers_id])) { return false; } $pfAgentModule = new PluginGlpiinventoryAgentmodule(); if ($pfAgentModule->isAgentCanDo('deploy', $agent->getID())) { return true; } else { return false; } } /** * Get deploy packages available to install on user computer(s) and for * packages requested the state of deploy * * @param integer $users_id id of the user */ public function getPackageForMe($users_id, $computers_id = false) { $computer = new Computer(); $pfDeployGroup = new PluginGlpiinventoryDeployGroup(); $my_packages = []; //Store all installable packages $query = []; if ($users_id) { $query += ['users_id' => $users_id]; } if ($computers_id) { $query += ['id' => $computers_id]; } $query += ['entities_id' => $_SESSION['glpiactiveentities']]; //Get all computers of the user $mycomputers = $computer->find($query); $agent = new Agent(); foreach ($mycomputers as $mycomputers_id => $data) { $my_packages[$mycomputers_id] = []; } //Get packages used for the user or a specific computer $packages_used = $this->getMyDepoyPackages($my_packages, $users_id); //Get packages that a the user can deploy $packages = $this->canUserDeploySelf(); if ($packages) { //Browse all packages that the user can install foreach ($packages as $package) { //Get computers that can be targeted for this package installation $computers = $pfDeployGroup->getTargetsForGroup($package['plugin_glpiinventory_deploygroups_id']); //Browse all computers that are target by a package installation foreach ($mycomputers as $comp_id => $data) { //If we only want packages for one computer //check if it's the computer we look for if ($computers_id && $comp_id != $computers_id) { continue; } //If the agent associated with the computer has not the //deploy feature enabled, do not propose to deploy packages on it if (!self::isDeployEnabled($comp_id)) { continue; } //Get computers that can be targeted for this package installation //Check if the package belong to one of the entity that //are currently visible //The package is recursive, and visible in computer's entity if (Session::isMultiEntitiesMode()) { if ( !$package['is_recursive'] && $package['entities_id'] != $data['entities_id'] ) { continue; } elseif ( $package['is_recursive'] && $package['entities_id'] != $data['entities_id'] && !in_array( $package['entities_id'], getAncestorsOf('glpi_entities', $data['entities_id']) ) ) { //The package is not recursive, and invisible in the computer's entity continue; } } //Does the computer belongs to the group //associated with the package ? if (isset($computers[$comp_id])) { $my_packages[$comp_id][$package['id']] = ['name' => $package['name'], 'agent_id' => $agent->getId()]; //The package has already been deployed or requested to deploy if (isset($packages_used[$comp_id][$package['id']])) { $taskjobs_id = $packages_used[$comp_id][$package['id']]; $my_packages[$comp_id][$package['id']]['taskjobs_id'] = $taskjobs_id; $last_job_state = $this->getMyDepoyPackagesState($comp_id, $taskjobs_id); if ($last_job_state) { $my_packages[$comp_id][$package['id']]['last_taskjobstate'] = $last_job_state; } } } } } } return $my_packages; } /** * Add the package in task or use existant task and add the computer in * taskjob * * @global object $DB * @param integer $computers_id id of the computer where depoy package * @param integer $packages_id id of the package to install in computer * @param integer $users_id id of the user have requested the installation */ public function deployToComputer($computers_id, $packages_id, $users_id) { global $DB; $pfTask = new PluginGlpiinventoryTask(); $pfTaskJob = new PluginGlpiinventoryTaskjob(); $computer = new Computer(); $computer->getFromDB($computers_id); //Get jobs for a package on a computer $iterator = $DB->request([ 'SELECT' => [ 'job.*', ], 'FROM' => 'glpi_plugin_glpiinventory_taskjobs AS job', 'LEFT JOIN' => [ 'glpi_plugin_glpiinventory_tasks AS task' => [ 'ON' => [ 'job' => 'plugin_glpiinventory_tasks_id', 'task' => 'id' ] ] ], 'WHERE' => [ 'job.targets' => '[{"PluginGlpiinventoryDeployPackage":"' . $packages_id . '"}]', 'task.is_active' => 1, 'task.is_deploy_on_demand' => 1, 'task.entities_id' => $computer->fields['entities_id'], 'task.reprepare_if_successful' => 0, 'method' => 'deployinstall' ], 'LIMIT' => 1 ]); $tasks_id = 0; // case 1: if exist, we add computer in actors of the taskjob if ($iterator->numrows() == 1) { foreach ($iterator as $data) { //Get current list of actors $actors = importArrayFromDB($data['actors']); //Add a new actor : the computer that is being processed $actors[] = ['Computer' => $computers_id]; //Get end user computers $enduser = importArrayFromDB($data['enduser']); if (isset($enduser[$users_id])) { if (!in_array($computers_id, $enduser[$users_id])) { $enduser[$users_id][] = $computers_id; } } else { $enduser[$users_id] = [$computers_id]; } $input = [ 'id' => $data['id'], 'actors' => exportArrayToDB($actors), 'enduser' => exportArrayToDB($enduser) ]; //Update the job with the new actor $pfTaskJob->update($input); $tasks_id = $data['plugin_glpiinventory_tasks_id']; } } else { // case 2: if not exist, create a new task + taskjob $this->getFromDB($packages_id); //Add the new task $input = [ 'name' => '[deploy on demand] ' . Sanitizer::dbEscape($this->fields['name']), 'entities_id' => $computer->fields['entities_id'], 'reprepare_if_successful' => 0, 'is_deploy_on_demand' => 1, 'is_active' => 1, ]; $tasks_id = $pfTask->add($input); //Add a new job for the newly created task //and enable it $input = [ 'plugin_glpiinventory_tasks_id' => $tasks_id, 'entities_id' => $computer->fields['entities_id'], 'name' => 'deploy', 'method' => 'deployinstall', 'targets' => '[{"PluginGlpiinventoryDeployPackage":"' . $packages_id . '"}]', 'actors' => exportArrayToDB([['Computer' => $computers_id]]), 'enduser' => exportArrayToDB([$users_id => [$computers_id]]), ]; $pfTaskJob->add($input); } //Prepare the task (and only this one) $pfTask->prepareTaskjobs(['deployinstall'], $tasks_id); } /** * Get all packages that a user has requested to install * on one of it's computer * * @param array $computers_packages * @param false|integer $users_id * @return array */ public function getMyDepoyPackages($computers_packages, $users_id = false) { /** @var DBmysql $DB */ global $DB; // Get packages yet deployed by enduser $packages_used = []; $computers_id = 0; foreach ($computers_packages as $computers_id => $data) { $packages_used[$computers_id] = []; } $where = []; if ($users_id) { $where ['NOT'] = ['enduser' => null]; } $iterator = $DB->request([ 'SELECT' => 'job.*', 'FROM' => 'glpi_plugin_glpiinventory_taskjobs AS job', 'LEFT JOIN' => [ 'glpi_plugin_glpiinventory_tasks AS task' => [ 'ON' => [ 'job' => 'plugin_glpiinventory_tasks_id', 'task' => 'id' ] ] ], 'WHERE' => [ 'task.is_deploy_on_demand' => 1, 'task.is_active' => 1, 'task.entities_id' => $_SESSION['glpiactiveentities'] ] + $where ]); foreach ($iterator as $data) { //Only look for deploy tasks if ($data['method'] != 'deployinstall') { continue; } //Look for all deploy on demand packages for a user if ($users_id) { $enduser = importArrayFromDB($data['enduser']); if (isset($enduser[$users_id])) { $targets = importArrayFromDB($data['targets']); foreach ($enduser[$users_id] as $computers_id) { $packages_used[$computers_id][$targets[0]['PluginGlpiinventoryDeployPackage']] = $data['id']; } } //Look for all deploy on demand package for a computer } else { $targets = importArrayFromDB($data['targets']); $actors = importArrayFromDB($data['actors']); foreach ($actors as $actor) { foreach ($actor as $itemtype => $items_id) { if ($itemtype == 'Computer' && $items_id == $computers_id) { $packages_used[$computers_id][$targets[0]['PluginGlpiinventoryDeployPackage']] = $data['id']; } } } } } return $packages_used; } /** * Get the state of the package I have requested to install * * @param integer $computers_id id of the computer * @param integer $taskjobs_id id of the taskjob (where order defined) */ public function getMyDepoyPackagesState($computers_id, $taskjobs_id) { $pfTaskJobState = new PluginGlpiinventoryTaskjobstate(); $agent = new Agent(); // Get a taskjobstate by giving a taskjobID and a computer ID $agent->getFromDBByCrit(['itemtype' => Computer::getType(), 'items_id' => $computers_id]); $agents_id = $agent->fields['id']; $last_job_state = []; $taskjobstates = current($pfTaskJobState->find( ['plugin_glpiinventory_taskjobs_id' => $taskjobs_id, 'agents_id' => $agents_id], ['id DESC'], 1 )); if ($taskjobstates) { $state = ''; switch ($taskjobstates['state']) { case PluginGlpiinventoryTaskjobstate::CANCELLED: $state = 'agents_cancelled'; break; case PluginGlpiinventoryTaskjobstate::PREPARED: $state = 'agents_prepared'; break; case PluginGlpiinventoryTaskjobstate::SERVER_HAS_SENT_DATA: case PluginGlpiinventoryTaskjobstate::AGENT_HAS_SENT_DATA: $state = 'agents_running'; break; case PluginGlpiinventoryTaskjobstate::IN_ERROR: $state = 'agents_error'; break; case PluginGlpiinventoryTaskjobstate::FINISHED: $state = 'agents_success'; break; } $logs = $pfTaskJobState->getLogs( $taskjobstates['id'], $_SESSION['glpi_currenttime'] ); $last_job_state['id'] = $taskjobstates['id']; $last_job_state['state'] = $state; $last_job_state['date'] = $logs['logs'][0]['log.date']; $last_job_state['logs'] = $logs['logs']; } return $last_job_state; } /** * Check I have rights to deploy packages * * @global object $DB * @return false|array */ public function canUserDeploySelf() { global $DB; if (Session::getCurrentInterface() !== 'helpdesk') { if (!Session::haveRight('plugin_glpiinventory_selfpackage', READ)) { return false; } } $table = "glpi_plugin_glpiinventory_deploypackages"; $where = [ $table . ".plugin_glpiinventory_deploygroups_id" => ['>', 0] ]; //Include groups if (!empty($_SESSION['glpigroups'])) { $where['OR'][] = ['glpi_plugin_glpiinventory_deploypackages_groups.groups_id' => $_SESSION['glpigroups']]; } //Include entity $where['OR'][] = getEntitiesRestrictCriteria( 'glpi_plugin_glpiinventory_deploypackages_entities', 'entities_id', $_SESSION['glpiactive_entity'], true ); //Include user $where['OR'][] = ['glpi_plugin_glpiinventory_deploypackages_users.users_id' => $_SESSION['glpiID']]; //Include profile $where['OR'][] = ['glpi_plugin_glpiinventory_deploypackages_profiles.profiles_id' => $_SESSION['glpiactiveprofile']['id']]; $iterator = $DB->request([ 'SELECT' => $table . '.*', 'DISTINCT' => true, 'FROM' => $table, 'LEFT JOIN' => [ 'glpi_plugin_glpiinventory_deploypackages_groups' => [ 'ON' => [ $table => 'id', 'glpi_plugin_glpiinventory_deploypackages_groups' => 'plugin_glpiinventory_deploypackages_id' ] ], 'glpi_plugin_glpiinventory_deploypackages_entities' => [ 'ON' => [ $table => 'id', 'glpi_plugin_glpiinventory_deploypackages_entities' => 'plugin_glpiinventory_deploypackages_id' ] ], 'glpi_plugin_glpiinventory_deploypackages_users' => [ 'ON' => [ $table => 'id', 'glpi_plugin_glpiinventory_deploypackages_users' => 'plugin_glpiinventory_deploypackages_id' ] ], 'glpi_plugin_glpiinventory_deploypackages_profiles' => [ 'ON' => [ $table => 'id', 'glpi_plugin_glpiinventory_deploypackages_profiles' => 'plugin_glpiinventory_deploypackages_id' ] ], ], 'WHERE' => $where ]); $a_packages = []; if (count($iterator) > 0) { foreach ($iterator as $data) { $a_packages[$data['id']] = $data; } return $a_packages; } return false; } /** * Duplicate a deploy package * @param integer $deploypackages_id the ID of the package to duplicate * @return boolean duplication process status */ public function duplicate($deploypackages_id) { if (!$this->getFromDB($deploypackages_id)) { return false; } $result = true; $input = $this->fields; $input['name'] = sprintf( __('Copy of %s'), $this->fields['name'] ); unset($input['id']); $input = Toolbox::addslashes_deep($input); if (!$this->add($input)) { $result = false; } return $result; } /** * Append needed informations to the json job for an agent * @since 9.2 * @param $agent_task_version the version of the agent's deploy task * @param $job the job as an array * @return array the job plus new needed fields */ public function buildJson($agent_task_version, $job) { //If task doesn't support checks skip, info, warning, //send an ignore instead //tasks version needs to be at least 2.2 $is_old_agent = version_compare($agent_task_version, '2.2', 'lt'); if ($is_old_agent && isset($job['job']['checks'])) { foreach ($job['job']['checks'] as $key => $value) { if (in_array($value['return'], ['skip', 'info', 'warning'])) { $job['job']['checks'][$key]['return'] = 'ignore'; } } } //No need to perform further test if the agent doesn't support //user interactions if ($is_old_agent) { return $job; } $do_interaction = true; $jobstate = new PluginGlpiinventoryTaskjobstate(); //Job has reached the maximum number of retries, do not interact with the user //and execute the job $jobstate->getFromDBByUniqID($job['job']['uuid']); if ( isset($jobstate->fields['nb_retry']) && $jobstate->fields['max_retry'] > 0 ) { if ($jobstate->fields['nb_retry'] >= $jobstate->fields['max_retry']) { $do_interaction = false; } } //If the number of retries has been met, //remove all userinteractions directives if (!$do_interaction) { unset($job['job']['userinteractions']); } elseif (isset($job['job']['userinteractions'])) { $template = new PluginGlpiinventoryDeployUserinteractionTemplate(); foreach ($job['job']['userinteractions'] as $key => $value) { if (isset($value['template']) && $value['template']) { if ($template->getFromDB($value['template'])) { $job['job']['userinteractions'][$key] = $template->addJsonFieldsToArray($job['job']['userinteractions'][$key]); unset($job['job']['userinteractions'][$key]['template']); $job['job']['userinteractions'][$key]['text'] = str_replace( PluginGlpiinventoryDeployUserinteraction::RN_TRANSFORMATION, "\r\n", $job['job']['userinteractions'][$key]['text'] ); } } } } return $job; } /** * Transform \r\n in an userinteraction text * @since 9.2 * @param array $params the input parameters * @return array $params input parameters with text modified */ public function escapeText($params) { //Hack to keep \r\n in the user interaction text //before going to stripslashes_deep if (isset($params['text'])) { $params['text'] = str_replace( '\r\n', PluginGlpiinventoryDeployUserinteraction::RN_TRANSFORMATION, $params['text'] ); } return $params; } }