490 lines
12 KiB
PHP
490 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* ---------------------------------------------------------------------
|
|
* GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2015-2020 Teclib' and contributors.
|
|
*
|
|
* http://glpi-project.org
|
|
*
|
|
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
|
*
|
|
* ---------------------------------------------------------------------
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This file is part of GLPI.
|
|
*
|
|
* GLPI is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GLPI is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
|
|
namespace Glpi\Dashboard;
|
|
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
class Dashboard extends \CommonDBTM {
|
|
protected $id = 0;
|
|
protected $key = "";
|
|
protected $title = "";
|
|
protected $embed = false;
|
|
protected $items = null;
|
|
protected $rights = null;
|
|
|
|
static $all_dashboards = [];
|
|
static $rightname = 'dashboard';
|
|
|
|
|
|
public function __construct(string $dashboard_key = "") {
|
|
$this->key = $dashboard_key;
|
|
}
|
|
|
|
|
|
static function getIndexName() {
|
|
return "key";
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve the current dashboard from the DB (or from cache)
|
|
* with its rights and items
|
|
*
|
|
* @param bool $force if true, don't use cache
|
|
*
|
|
* @return int
|
|
*/
|
|
public function load(bool $force = false): int {
|
|
$loaded = true;
|
|
if ($force
|
|
|| count($this->fields) == 0
|
|
|| $this->fields['id'] == 0
|
|
|| strlen($this->fields['name']) == 0) {
|
|
$loaded = $this->getFromDB($this->key);
|
|
}
|
|
|
|
if ($loaded) {
|
|
if ($force || $this->items === null) {
|
|
$this->items = Item::getForDashboard($this->fields['id']);
|
|
}
|
|
|
|
if ($force || $this->rights === null) {
|
|
$this->rights = Right::getForDashboard($this->fields['id']);
|
|
}
|
|
}
|
|
|
|
return $this->fields['id'] ?? false;
|
|
}
|
|
|
|
|
|
public function getFromDB($ID) {
|
|
global $DB;
|
|
|
|
$iterator = $DB->request([
|
|
'FROM' => self::getTable(),
|
|
'WHERE' => [
|
|
'key' => $ID
|
|
],
|
|
'LIMIT' => 1
|
|
]);
|
|
if (count($iterator) == 1) {
|
|
$this->fields = $iterator->next();
|
|
$this->key = $ID;
|
|
$this->post_getFromDB();
|
|
return true;
|
|
} else if (count($iterator) > 1) {
|
|
\Toolbox::logWarning(
|
|
sprintf(
|
|
'getFromDB expects to get one result, %1$s found!',
|
|
count($iterator)
|
|
)
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the title of the current dasbhoard
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getTitle(): string {
|
|
$this->load();
|
|
return $this->fields['name'] ?? "";
|
|
}
|
|
|
|
/**
|
|
* Do we have the right to view the current dashboard
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function canViewCurrent(): bool {
|
|
// check global (admin) right
|
|
if (self::canView()) {
|
|
return true;
|
|
}
|
|
|
|
$this->load();
|
|
|
|
//check shared rights
|
|
$rights = self::convertRights($this->rights ?? []);
|
|
return self::checkRights($rights);
|
|
}
|
|
|
|
|
|
/**
|
|
* Save the current dashboard instance to DB
|
|
*
|
|
* @param string $title label of the dasbhoard, will be suglified to have a corresponding key
|
|
* @param string $context of the dasbhoard, filter the dasboard collection by a key
|
|
* @param array $items cards for the dashboard
|
|
* @param array $rights for the dasbhoard
|
|
*
|
|
* @return string
|
|
*/
|
|
public function saveNew(
|
|
string $title = "",
|
|
string $context = "core",
|
|
array $items = [],
|
|
array $rights = []
|
|
): string {
|
|
$this->fields['name'] = $title;
|
|
$this->fields['context'] = $context;
|
|
$this->key = \Toolbox::slugify($title);
|
|
$this->items = $items;
|
|
$this->rights = $rights;
|
|
|
|
$this->save();
|
|
|
|
return $this->key;
|
|
}
|
|
|
|
|
|
/**
|
|
* Save current dashboard
|
|
*
|
|
* @param bool $skip_child skip saving rights and items
|
|
*
|
|
* @return void
|
|
*/
|
|
public function save(bool $skip_child = false) {
|
|
global $DB, $GLPI_CACHE;
|
|
|
|
$DB->updateOrInsert(self::getTable(), [
|
|
'key' => $this->key,
|
|
'name' => $this->fields['name'],
|
|
'context' => $this->fields['context']
|
|
], [
|
|
'key' => $this->key
|
|
]);
|
|
|
|
// reload dashboard
|
|
$this->getFromDB($this->key);
|
|
|
|
//save items
|
|
if (!$skip_child && count($this->items) > 0) {
|
|
$this->saveItems($this->items);
|
|
}
|
|
|
|
//save rights
|
|
if (!$skip_child && count($this->rights) > 0) {
|
|
$this->saveRights($this->rights);
|
|
}
|
|
|
|
// invalidate dashboard cache
|
|
$cache_key = "dashboard_card_".$this->key;
|
|
$GLPI_CACHE->delete($cache_key);
|
|
}
|
|
|
|
|
|
function cleanDBonPurge() {
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Item::class,
|
|
Right::class,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Save items in DB for the current dashboard
|
|
*
|
|
* @param array $items cards of the dashboard, contains:
|
|
* - gridstack_id: unique id of the card in the grid, usually build like card_id.uuidv4
|
|
* - card_id: key of array return by getAllDasboardCards
|
|
* - x: position in grid
|
|
* - y: position in grid
|
|
* - width: size in grid
|
|
* - height: size in grid
|
|
* - card_options, sub array, depends on the card, contains at least a key color
|
|
*
|
|
* @return void
|
|
*/
|
|
public function saveItems(array $items = []) {
|
|
$this->load();
|
|
$this->items = $items;
|
|
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Item::class,
|
|
]);
|
|
|
|
Item::addForDashboard($this->fields['id'], $items);
|
|
}
|
|
|
|
/**
|
|
* Save title DB for the current dashboard
|
|
*
|
|
* @param string $title of the current dashboard
|
|
*
|
|
* @return void
|
|
*/
|
|
public function saveTitle(string $title = "") {
|
|
if (!strlen($title)) {
|
|
return;
|
|
}
|
|
|
|
$this->load();
|
|
$this->fields['name'] = $title;
|
|
$this->save(true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Save rights (share) in DB for the current dashboard
|
|
*
|
|
* @param array $rights contains these data:
|
|
* - 'users_id' => [items_id]
|
|
* - 'groups_id' => [items_id]
|
|
* - 'entities_id' => [items_id]
|
|
* - 'profiles_id' => [items_id]
|
|
*
|
|
* @return void
|
|
*/
|
|
public function saveRights(array $rights = []) {
|
|
$this->load();
|
|
$this->rights = $rights;
|
|
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Right::class,
|
|
]);
|
|
|
|
Right::addForDashboard($this->fields['id'], $rights);
|
|
}
|
|
|
|
|
|
/**
|
|
* Clone current Dashboard.
|
|
* (Clean gridstack_id-id in new one)
|
|
*
|
|
* @return array with [title, key]
|
|
*/
|
|
function cloneCurrent(): array {
|
|
$this->load();
|
|
|
|
$this->fields['name'] = sprintf(__('Copy of %s'), $this->fields['name']);
|
|
$this->key = \Toolbox::slugify($this->fields['name']);
|
|
|
|
// replace gridstack_id (with uuid V4) in the copy, to avoid cache issue
|
|
$this->items = array_map(function(array $item) {
|
|
$item['gridstack_id'] = $item['card_id'].Uuid::uuid4();
|
|
|
|
return $item;
|
|
}, $this->items);
|
|
|
|
// convert right to the good format
|
|
$this->rights = self::convertRights($this->rights);
|
|
|
|
$this->save();
|
|
|
|
return [
|
|
'title' => $this->fields['name'],
|
|
'key' => $this->key
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve all dashboards and store them into a static var
|
|
*
|
|
* @param bool $force don't check dashboard are already loaded and force their load
|
|
* @param bool $check_rights use to remove rights checking (use in embed)
|
|
* @param string $context only dashboard for given context
|
|
*
|
|
* @return array dasboards
|
|
*/
|
|
static function getAll(
|
|
bool $force = false,
|
|
bool $check_rights = true,
|
|
string $context = 'core'
|
|
): array {
|
|
global $DB;
|
|
|
|
if (!$force && count(self::$all_dashboards) > 0) {
|
|
return self::$all_dashboards;
|
|
}
|
|
|
|
// empty previous data
|
|
self::$all_dashboards = [];
|
|
|
|
$dashboard_criteria = [];
|
|
if (strlen($context)) {
|
|
$dashboard_criteria['context'] = $context;
|
|
}
|
|
|
|
$dashboards = iterator_to_array($DB->request(self::getTable(), ['WHERE' => $dashboard_criteria]));
|
|
$items = iterator_to_array($DB->request(Item::getTable()));
|
|
$rights = iterator_to_array($DB->request(Right::getTable()));
|
|
|
|
foreach ($dashboards as $dashboard) {
|
|
$key = $dashboard['key'];
|
|
$id = $dashboard['id'];
|
|
|
|
$d_rights = array_filter($rights, function($right_line) use($id) {
|
|
return $right_line['dashboards_dashboards_id'] == $id;
|
|
});
|
|
if ($check_rights && !self::checkRights(self::convertRights($d_rights))) {
|
|
continue;
|
|
}
|
|
$dashboard['rights'] = self::convertRights($d_rights);
|
|
|
|
$d_items = array_filter($items, function($item) use($id) {
|
|
return $item['dashboards_dashboards_id'] == $id;
|
|
});
|
|
$d_items = array_map(function($item) {
|
|
$item['card_options'] = importArrayFromDB($item['card_options']);
|
|
return $item;
|
|
}, $d_items);
|
|
$dashboard['items'] = $d_items;
|
|
|
|
self::$all_dashboards[$key] = $dashboard;
|
|
}
|
|
|
|
return self::$all_dashboards;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert right from DB entries to a array with type foreign keys.
|
|
* Ex:
|
|
* IN
|
|
* [
|
|
* [
|
|
* 'itemtype' => 'Entity'
|
|
* 'items_id' => yyy
|
|
* ], [
|
|
* ...
|
|
* ],
|
|
* ]
|
|
*
|
|
* OUT
|
|
* [
|
|
* 'entities_id' => [...]
|
|
* 'profiles_id' => [...]
|
|
* 'users_id' => [...]
|
|
* 'groups_id' => [...]
|
|
* ]
|
|
*
|
|
* @param array $raw_rights right from DB
|
|
*
|
|
* @return array converter rights
|
|
*/
|
|
static function convertRights(array $raw_rights = []): array {
|
|
$rights = [
|
|
'entities_id' => [],
|
|
'profiles_id' => [],
|
|
'users_id' => [],
|
|
'groups_id' => [],
|
|
];
|
|
foreach ($raw_rights as $right_line) {
|
|
$fk = getForeignKeyFieldForItemType($right_line['itemtype']);
|
|
$rights[$fk][] = $right_line['items_id'];
|
|
}
|
|
|
|
return $rights;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check a current set of rights
|
|
*
|
|
* @param array $rights
|
|
*
|
|
* @return bool
|
|
*/
|
|
static function checkRights(array $rights = []): bool {
|
|
// check global (admin) right
|
|
if (self::canView()) {
|
|
return true;
|
|
}
|
|
|
|
$default_rights = [
|
|
'entities_id' => [],
|
|
'profiles_id' => [],
|
|
'users_id' => [],
|
|
'groups_id' => [],
|
|
];
|
|
$rights = array_merge_recursive($default_rights, $rights);
|
|
|
|
// check specific rights
|
|
if (count(array_intersect($rights['entities_id'], $_SESSION['glpiactiveentities']))
|
|
|| count(array_intersect($rights['profiles_id'], array_keys($_SESSION['glpiprofiles'])))
|
|
|| in_array($_SESSION['glpiID'], $rights['users_id'])
|
|
|| count(array_intersect($rights['groups_id'], $_SESSION['glpigroups']))) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Import dashboards from a variable
|
|
|
|
* @param string|array $import json or php array representing the dashboards collection
|
|
* [
|
|
* dashboard_key => [
|
|
* 'title' => '...',
|
|
* 'items' => [...],
|
|
* 'rights' => [...],
|
|
* ], [
|
|
* ...
|
|
* ]
|
|
* ]
|
|
*
|
|
* @return bool
|
|
*/
|
|
static function importFromJson($import = null) {
|
|
if (!is_array($import)) {
|
|
$import = json_decode($import, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
foreach ($import as $key => $dashboard) {
|
|
$dash_object = new self($key);
|
|
$dash_object->saveNew(
|
|
$dashboard['title'] ?? $key,
|
|
$dashboard['context'] ?? "core",
|
|
$dashboard['items'] ?? [],
|
|
$dashboard['rights'] ?? []
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} |