From acafe278cb2f41377ac74209be2c4d37f619303e Mon Sep 17 00:00:00 2001 From: Adrien Perret Date: Tue, 20 Apr 2021 15:00:28 +0200 Subject: [PATCH] DEL: remove dashboards --- .composer-require-checker.config.json | 2 +- ajax/dashboard.php | 140 -- css/dashboard.scss | 1305 ------------ front/central.php | 15 - front/dashboard_assets.php | 54 - front/dashboard_helpdesk.php | 54 - front/ticket.php | 5 - inc/based_config.php | 1 - inc/central.class.php | 21 - inc/config.class.php | 33 - inc/dashboard/dashboard.class.php | 490 ----- inc/dashboard/filter.class.php | 251 --- inc/dashboard/grid.class.php | 1524 -------------- inc/dashboard/item.class.php | 135 -- inc/dashboard/provider.class.php | 1601 -------------- inc/dashboard/right.class.php | 110 - inc/dashboard/widget.class.php | 1872 ----------------- inc/define.php | 14 +- inc/html.class.php | 12 +- inc/profile.class.php | 5 - install/empty_data.php | 59 - install/mysql/glpi-empty.sql | 46 - install/update_94_95.php | 124 -- install/update_94_95/dashboards.php | 383 ---- js/dashboard.js | 1131 ---------- tests/bootstrap.php | 53 - .../functionnal/Glpi/Dashboard/Dashboard.php | 313 --- tests/functionnal/Glpi/Dashboard/Provider.php | 294 --- tests/functionnal/Session.php | 1 - tests/units/Glpi/Dashboard/Widget.php | 106 - 30 files changed, 6 insertions(+), 10148 deletions(-) delete mode 100644 ajax/dashboard.php delete mode 100644 css/dashboard.scss delete mode 100644 front/dashboard_assets.php delete mode 100644 front/dashboard_helpdesk.php delete mode 100644 inc/dashboard/dashboard.class.php delete mode 100644 inc/dashboard/filter.class.php delete mode 100644 inc/dashboard/grid.class.php delete mode 100644 inc/dashboard/item.class.php delete mode 100644 inc/dashboard/provider.class.php delete mode 100644 inc/dashboard/right.class.php delete mode 100644 inc/dashboard/widget.class.php delete mode 100644 install/update_94_95/dashboards.php delete mode 100644 js/dashboard.js delete mode 100644 tests/functionnal/Glpi/Dashboard/Dashboard.php delete mode 100644 tests/functionnal/Glpi/Dashboard/Provider.php delete mode 100644 tests/units/Glpi/Dashboard/Widget.php diff --git a/.composer-require-checker.config.json b/.composer-require-checker.config.json index 17f8e78ad1..055f65cf83 100644 --- a/.composer-require-checker.config.json +++ b/.composer-require-checker.config.json @@ -19,7 +19,7 @@ "DB", "DBSlave", "// GLPI base constants (they are not detected as they are dynamically declared)", - "GLPI_AJAX_DASHBOARD", "GLPI_ALLOW_IFRAME_IN_RICH_TEXT", "GLPI_CALDAV_IMPORT_STATE", "GLPI_CACHE_DIR", "GLPI_CRON_DIR", "GLPI_CSRF_EXPIRES", "GLPI_CSRF_MAX_TOKENS", "GLPI_USE_IDOR_CHECK", "GLPI_IDOR_EXPIRES", "GLPI_DEMO_MODE", "GLPI_DOC_DIR", "GLPI_DUMP_DIR", "GLPI_FORCE_EMPTY_SQL_MODE", "GLPI_GRAPH_DIR", "GLPI_INSTALL_MODE", "GLPI_LOCAL_I18N_DIR", "GLPI_LOCK_DIR", "GLPI_LOG_DIR", "GLPI_NETWORK_REGISTRATION_API_URL", "GLPI_PICTURE_DIR", "GLPI_PLUGIN_DOC_DIR", "GLPI_RSS_DIR", "GLPI_SESSION_DIR", "GLPI_TELEMETRY_URI", "GLPI_TMP_DIR", "GLPI_UPLOAD_DIR", "GLPI_USE_CSRF_CHECK", "GLPI_USER_AGENT_EXTRA_COMMENTS", "GLPI_VAR_DIR", + "GLPI_ALLOW_IFRAME_IN_RICH_TEXT", "GLPI_CALDAV_IMPORT_STATE", "GLPI_CACHE_DIR", "GLPI_CRON_DIR", "GLPI_CSRF_EXPIRES", "GLPI_CSRF_MAX_TOKENS", "GLPI_USE_IDOR_CHECK", "GLPI_IDOR_EXPIRES", "GLPI_DEMO_MODE", "GLPI_DOC_DIR", "GLPI_DUMP_DIR", "GLPI_FORCE_EMPTY_SQL_MODE", "GLPI_GRAPH_DIR", "GLPI_INSTALL_MODE", "GLPI_LOCAL_I18N_DIR", "GLPI_LOCK_DIR", "GLPI_LOG_DIR", "GLPI_PICTURE_DIR", "GLPI_PLUGIN_DOC_DIR", "GLPI_RSS_DIR", "GLPI_SESSION_DIR", "GLPI_TELEMETRY_URI", "GLPI_TMP_DIR", "GLPI_UPLOAD_DIR", "GLPI_USE_CSRF_CHECK", "GLPI_USER_AGENT_EXTRA_COMMENTS", "GLPI_VAR_DIR", "// GLPI optionnal constants", "GLPI_FORCE_MAIL", "GLPI_LOG_LVL", diff --git a/ajax/dashboard.php b/ajax/dashboard.php deleted file mode 100644 index 0a77aaf888..0000000000 --- a/ajax/dashboard.php +++ /dev/null @@ -1,140 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -include ('../inc/includes.php'); - -use Glpi\Dashboard\Grid; - -if (!isset($_REQUEST["action"])) { - exit; -} - -if (!isset($_REQUEST['embed']) || !$_REQUEST['embed']) { - Session::checkCentralAccess(); - -} else if (!in_array($_REQUEST['action'], [ - 'get_dashboard_items', - 'get_card', - 'get_cards' -])) { - Html::displayRightError(); -} - -$dashboard = new Glpi\Dashboard\Dashboard($_REQUEST['dashboard'] ?? ""); - -switch ($_POST['action'] ?? null) { - case 'save_new_dashboard': - echo $dashboard->saveNew( - $_POST['title'] ?? "", - $_POST['context'] ?? "" - ); - exit; - - case 'save_items': - $dashboard->saveitems($_POST['items'] ?? []); - $dashboard->saveTitle($_POST['title'] ?? ""); - exit; - - case 'save_rights': - echo $dashboard->saveRights($_POST['rights'] ?? []); - exit; - - case 'delete_dashboard': - echo $dashboard->delete(['key' => $_POST['dashboard']]); - exit; - - case 'set_last_dashboard': - $grid = new Grid($_POST['dashboard'] ?? ""); - echo $grid->setLastDashboard($_POST['page'], $_POST['dashboard']); - exit; - - case 'clone_dashboard': - $new_dashboard = $dashboard->cloneCurrent(); - echo json_encode($new_dashboard); - exit; -} - -$grid = new Grid($_REQUEST['dashboard'] ?? ""); - -header("Content-Type: text/html; charset=UTF-8"); -switch ($_REQUEST['action']) { - case 'add_new': - $grid->displayAddDashboardForm(); - break; - - case 'edit_rights': - $grid->displayEditRightsForm(); - break; - - case 'display_edit_widget': - case 'display_add_widget': - $grid->displayWidgetForm($_REQUEST); - break; - - case 'display_embed_form': - $grid->displayEmbedForm(); - break; - - case 'get_card': - session_write_close(); - echo $grid->getCardHtml($_REQUEST['card_id'], $_REQUEST); - break; - - case 'get_cards': - session_write_close(); - header("Content-Type: application/json; charset=UTF-8"); - // Parse stringified JSON payload (Used to preserve integers) - $request_data = array_merge($_REQUEST, json_decode($_UREQUEST['data'], true)); - unset($request_data['data']); - $cards = $request_data['cards']; - unset($request_data['cards']); - $result = []; - foreach ($cards as $card) { - $result[$card['card_id']] = $grid->getCardHtml($card['card_id'], array_merge($request_data, $card)); - } - echo json_encode($result); - break; - - case 'display_add_filter': - $grid->displayFilterForm($_REQUEST); - break; - case 'get_dashboard_filters': - echo $grid->getFiltersSetHtml($_REQUEST['filters'] ?? []); - break; - case 'get_filter': - echo $grid->getFilterHtml($_REQUEST['filter_id']); - break; - - case 'get_dashboard_items': - echo $grid->getGridItemsHtml(true, $_REQUEST['embed'] ?? false); - break; -} diff --git a/css/dashboard.scss b/css/dashboard.scss deleted file mode 100644 index 402fe84724..0000000000 --- a/css/dashboard.scss +++ /dev/null @@ -1,1305 +0,0 @@ -$item_margin: 3px; -$toolbar_height: 40px; -$filter_height: 55px; -$break_phones: 700px; -$break_tablet: 1400px; - -@mixin soft-shadow { - box-shadow: - 0 1px 3px rgba(0,0,0,0.12), - 0 1px 2px rgba(0,0,0,0.24) !important; -} - -@mixin custom-scroll-bar() { - // firefox - scrollbar-width: none; - - // chrome - &::-webkit-scrollbar { - height: 6px; - width: 4px; - } - - &::-webkit-scrollbar-thumb, - &::-webkit-scrollbar-track, - &::-webkit-scrollbar-corner { - background-color: transparent; - } - - &:hover { - // firefox - scrollbar-width: thin; - - // chrome - &::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, .3); - } - - &::-webkit-scrollbar-track { - background-color: rgba(0, 0, 0, .2); - } - } -} - -// replicate ui-tabs display -.default_dashboard { - padding: 1em 1.4em; - border: 1px solid #dddddd; - background: #FFF; - box-shadow: 0px 3px 1px #dfdfdf; - border-radius: 3px; - width: 90%; - margin: auto; -} - -.dashboard { - font-family: Arial, Helvetica, sans-serif/*{ffDefault}*/; // for embed - position: relative; - padding-top: $toolbar_height; - - // reset font weight from other gridstack usage - .grid-stack-item-content a { - font-weight: normal; - } - - &.mini { - padding-top: 0; - width: calc(80% + 28px); - margin: 0 auto; - margin-bottom: -20px; - z-index: 1; - - & + .search_page { - z-index: 2; - } - - .card { - padding: 0 3px; - } - - .grid-guide { - top: 0; - } - - .main-icon { - font-size: 1.5em; - right: 3px; - bottom: 3px; - } - - .grid-stack .grid-stack-item.lock-bottom { - min-height: 0; - height: 0; - } - } - - &.embed { - .glpi_logo { - background: url(../pics/logos/logo-GLPI-100-black.png) no-repeat; - width: 100px; - height: 55px; - position: absolute; - top: 30px; - left: 100px; - } - - &.nightmode { - .glpi_logo { - background: url(../pics/logos/logo-GLPI-100-white.png) no-repeat; - } - } - } - - &.fullscreen, &.embed { - background: white; - padding: 100px; - - &.nightmode { - background: black; - color: rgb(153, 153, 153); - - .toolbar { - i.fas { - color: #EEEEEE; - - &.active { - background: #777; - border: 2px inset #777; - } - } - - select.dashboard_select { - color: rgb(187, 187, 187); - background-color: black; - border: 2px solid rgba(255, 255, 255, .3); - - &:hover { - border: 2px solid rgba(255, 255, 255, .5); - } - } - } - - .grid-stack .grid-stack-item .grid-stack-item-content .card { - background-color: #777; - color: rgba(255, 255, 255, 0.7); - } - } - - .toolbar { - top: 50px; - right: 50px; - - &.left-toolbar { - top: 50px; - left: 104px; - } - - .fs-toggle { - display: none; - } - } - } - - &.edit-mode { - .grid-guide { - display: flex; - flex-wrap: wrap; - - @media screen and (max-width: $break_phones) { - display: none; - } - } - - .grid-stack-item-content { - touch-action: none; - } - - .grid-stack .grid-stack-item:hover { - .grid-stack-item-content:hover { - cursor: grab; - } - - & > .controls { - display: inline-block; - } - - .card { - border-color: currentColor - } - } - - .markdown .html_content { - display: none; - } - .markdown textarea.markdown_content { - display: block; - } - - .g-chart .ct-chart { - [data-clickable=true] { - cursor: grab; - } - } - - .toolbar { - .change-dashboard { - display: none; - } - - .edit-dashboard-properties { - display: inline-block; - } - } - - .filters_toolbar { - .filters { - fieldset.filter { - &.filled { - margin-top: 0.5em; - } - border-color: #f0f0f0 !important; - - input, .flatpickr, .select2, .no-wrap { - display: none; - } - - .delete-filter { - display: inline-block; - } - - legend { - display: inline-block; - cursor: grab; - } - - &:hover { - border-color: #509ee3 !important; - background: rgb(80,158,227, .3); - } - } - - .filter-placeholder { - border: 2px dashed #509ee3; - padding: 1em 1em 0.8em; - margin-top: 1em; - border-radius: 4px; - height: 10px; - width: 50px; - } - } - - .filters-control { - border-color: #f0f0f0; - - .add-filter { - display: inline-block; - } - } - } - - .card.filter-impacted { - border-color: #509ee3; - - &:before { - background: rgb(80,158,227, .3); - content: ''; - position: absolute; - width: inherit; - height: inherit; - margin: -3px; - } - } - } - - .toolbar { - position: absolute; - top: 0; - right: 0; - z-index: 10; - - &.left-toolbar { - top: 0; - right: initial; - left: 5px; - - i.fas { - margin-left: 0; - font-size: 1em; - } - - &:hover { - select.dashboard_select { - border: 2px solid rgba(0, 0, 0, .5); - } - } - } - - select.dashboard_select { - padding: 5px; - border: 2px solid rgba(0, 0, 0, .1); - border-radius: 3px; - min-width: 100px; - font-weight: bold; - color: rgb(70, 70, 70); - } - - i.fas { - font-size: 1.5em; - cursor: pointer; - padding: 5px; - margin-left: 5px; - color: rgba(0, 0, 0, .5); - border: 2px solid transparent; - - @media screen and (max-width: $break_phones) { - display: none; - } - - &:hover { - color: rgb(0, 0, 0); - } - - &.active { - background: #EEEEEE; - border: 2px inset #EEEEEE; - } - - &.fa-moon { - display: none; - } - } - - .edit-dashboard-properties { - display: none; - - input.dashboard-name:not(.submit):not([type=submit]):not([type=reset]):not([type=checkbox]):not([type=radio]):not(.select2-search__field) { - padding: 5px; - border: 2px solid rgba(0, 0, 0, .1); - border-radius: 3px; - min-width: 200px; - font-weight: bold; - color: rgb(70, 70, 70); - resize: horizontal; - } - - i.save-dashboard-name { - font-size: 1.5em; - padding: 1px; - vertical-align: middle; - } - - .display-message { - display: none; - font-weight: bold; - - &.success { - color: rgb(82, 145, 82); - } - - &.fail { - color: rgb(145, 82, 82); - } - } - } - } - - .filters_toolbar { - text-align: left; - height: $filter_height; - margin: 0 5px 5px 2px; - box-sizing: border-box; - padding-top: 0.5em; - - @media screen and (max-width: $break_phones) { - height: inherit; - } - - .filters { - display: inline-flex; - flex-wrap: wrap; - - fieldset.filter { - border: 2px solid #f0f0f0; - border-radius: 4px; - padding: 0.5em 1em 0.4em 1em; - box-sizing: border-box; - text-align: center; - margin-top: 0.5em; - - legend { - display: none; - color: #74838f; - font-weight: bold; - } - - input { - border: none !important; - cursor: pointer; - font-size: 1.1em !important; - font-weight: bold; - } - - .flatpickr { - position: relative; - - a[data-clear] { - display: none; - } - - &:after { - font-weight: 400; - content: "\f133"; - right: 5px; - } - } - - .select2 { - position: relative; - margin-left: -12px; - - & ~ a, & ~ span { // remove icons next select2 - display: none; - } - - .select2-selection.select2-selection--single { - border: none; - } - - .select2-container { - margin-right: 0; - } - - .select2-selection__rendered { - color: #74838f; - font-weight: bold; - font-size: 1.1em; - line-height: 18px; - } - - .select2-selection__arrow { - display: none; - } - - .select2-selection__clear { - color: transparent; - position: relative; - - &:after { - position: absolute; - font-family: "Font Awesome 5 Free"; - color: #222; - font-weight: 900; - content: "\f057"; - opacity: 0.7; - font-size: 11px; - } - - &:hover:after { - opacity: 1; - } - } - - &:after { - font-weight: 900; - content: "\f02b"; - } - } - - .flatpickr, .select2 { - &::placeholder, .select2-selection__placeholder { - color: #74838f; - opacity: .7; - } - - &:after { - font-family: "Font Awesome 5 Free"; - color: #74838f; - opacity: .7; - font-size: 1.3em; - pointer-events: none; - position: absolute; - right: 0; - top: 1px - } - } - - .delete-filter { - display: none; - color: rgba(0, 0, 0, 0.5); - cursor: pointer; - } - - &.filled { - border-color: #509ee3; - margin-top: 0; - padding-top: 0.25em; - - legend { - display: inline-block; - } - - input, .select2-selection__rendered { - color: #509ee3 !important; - } - - .flatpickr, .select2 { - &:after { - display: none; - } - - a[data-clear] { - display: inline-block; - } - } - } - } - } - - .filters-control { - border: 2px dashed transparent; - border-radius: 4px; - box-sizing: border-box; - display: inline-block; - vertical-align: top; - margin-top: 1em; - - .add-filter { - padding: 1em 1em 0.8em; - display: none; - color: rgba(0, 0, 0, 0.5); - cursor: pointer; - - &:hover { - color: rgb(0, 0, 0); - } - - .no-filter, .add-filter-lbl { - padding-left: 0.2em; - font-style: italic; - color: #74838f; - font-family: Arial, Helvetica, sans-serif; - font-weight: bold; - } - } - - &:hover { - border-color: rgba(0, 0, 0, 0.5); - } - } - } - - .grid-guide { - display: none; - position: absolute; - top: calc(#{$toolbar_height} + #{$filter_height} + 5px); - width: calc(100% + 1px); - border: 1px solid #EEEEEE; - border-width: 0 1px 1px 0; - background-image: - linear-gradient(to right, #EEEEEE 1px, transparent 1px), - linear-gradient(to bottom, #EEEEEE 1px, transparent 1px); - - .cell-add { - opacity: 0; - z-index: 2; - position: relative; - cursor: pointer; - border: 2px dashed #777; - box-sizing: border-box; - - &:hover { - opacity: 1; - } - - &:after { - content: "\f067"; - left: 40%; - top: 40%; - font-size: 1em; - color: grey; - font-family: 'Font Awesome\ 5 Free'; - position: absolute; - font-weight: 900; - } - } - } - - .grid-stack { - &.grid-stack-one-column-mode { - max-width: 100%; - - .grid-stack-item { - margin-bottom: 2px; - } - } - - .grid-stack-item { - - &:hover { - cursor: default; - } - - &.lock-bottom { - display: none; - } - - &.ui-draggable-dragging { - .grid-stack-item-content { - box-shadow: none; - } - } - - & > .ui-resizable-se { - bottom: 5px; - right: 5px; - } - - & > .controls { - display: none; - position: absolute; - top: 5px; - right: 10px; - z-index: 11; - cursor: pointer; - - i.fas, i.far { - opacity: .6; - - &:hover { - opacity: 1; - } - } - } - - .placeholder-content { - left: $item_margin; - right: $item_margin; - } - - .loading-card { - height: 100%; - border: 1px solid rgba($color: #000, $alpha: .05); - - .fa-spin { - position: absolute; - top: calc(50% - 16px); - left: calc(50% - 16px); - color: #222; - } - } - - .empty-card { - height: calc(100% - 5px); - margin: $item_margin; - border-radius: 3px; - box-sizing: border-box; - border: 1px solid transparent; - - .fas { - position: absolute; - top: calc(50% - 16px); - left: calc(50% - 16px); - font-size: 2em; - } - } - - .card-error { - border-color: rgba(100, 0, 0, .3); - background: rgba(255, 0, 0, .1); - color: rgba(100, 0, 0, .5); - } - - .card-warning { - border-color: rgba(105, 100, 32, 0.3); - background: rgba(255, 238, 0, 0.178); - color: rgba(105, 100, 32, 0.3); - } - - .no-data { - display: block; - position: relative; - - div { - position: absolute; - top: 50%; - text-align: center; - width: 100%; - } - } - - .ui-resizable-se { - background: none; - text-indent: unset; - - &:before { - content: "\f338"; - font-family: "Font Awesome 5 Free"; - font-weight: 900; - font-size: 13px; - position: absolute; - bottom: -5px; - right: 1px; - width: 20px; - height: 20px; - opacity: .6; - } - - &:hover { - &:before { - opacity: 1; - } - } - } - - .grid-stack-item-content { - left: $item_margin; - right: $item_margin; - cursor: default; - touch-action: initial; - - .debug-card { - z-index: 10; - position: absolute; - color: rgba(255, 0, 0, 0.5); - font-size: 10px; - bottom: 5px; - left: 5px; - } - } - } - } - - .card { - @include soft-shadow; - text-align: left; - box-sizing: border-box; - padding: 5px; - height: calc(100% - 5px); - width: calc(100% - 5px); - margin: 2px 0 0 2px; - display: block; - color: rgba(0, 0, 0, 0.7); - border: 2px solid transparent; - border-radius: 3px; - position: relative; - box-sizing: border-box; - background-color: #DDD; - - img { - max-width: 100%; - } - } - - - .big-number { - .main-icon { - right: 5px; - top: 5px; - } - - .formatted-number { - display: flex; - - .number, .suffix { - font-size: 3em; - font-weight: normal; - - @media screen and (max-width: $break_tablet) { - font-size: 2em; - } - } - } - - .label { - max-width: calc(100%); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - .main-icon { - font-size: 3em; - position: absolute; - right: 5px; - bottom: 5px; - - @media screen and (min-width: $break_phones) and (max-width: $break_tablet) { - font-size: 2em; - } - } - - .main-label { - margin: 5px; - font-size: 1.5em; - font-weight: bold; - display: block; - max-width: calc(100% - 2.5em); - - @media screen and (min-width: $break_phones) and (max-width: $break_tablet) { - font-size: 1.1em; - } - - i { - color: currentColor; - } - } - - .summary-numbers { - display: flex; - flex-direction: column; - - .scrollable { - flex-grow: 1; - display: flex; - - .table { - flex-grow: 1; - display: flex; - text-align: center; - - .line { - flex-grow: 1; - display: flex; - flex-direction: column-reverse; - justify-content: space-evenly; - flex-basis: 100%; - color: #555555; - position: relative; - font-weight: normal; - - .content { - //flex-grow: 1; - display: flex; - - .formatted-number { - flex-grow: 1; - } - } - - .label { - font-size: 1.5em; - } - - &:not(:first-child) { - border-left: 1px solid rgba(125, 125, 125, .2); - } - - &:hover { - background-color: rgba(125, 125, 125, .1); - cursor: pointer; - border-radius: 3px; - border-left-color: transparent; - - & + .line { - border-left-color: transparent; - } - } - } - } - } - } - - .multiple-numbers { - $number_size: 1.9em; - - .scrollable { - overflow: auto; - max-height: calc(100% - 35px); - @include custom-scroll-bar(); - } - - .table { - display: table; - width: 100%; - - .line { - display: table-row; - color: currentColor; - - * { - display: table-cell; - } - - &:nth-child(odd) { - background-color: rgba($color: #000, $alpha: .05) - } - - &:hover { - background-color: rgba($color: #000, $alpha: .2) - } - - .content { - font-size: $number_size; - min-width: 30px; - background-color: rgba($color: #000, $alpha: .1); - padding: 10px; - text-align: center; - min-width: 40px; - white-space: nowrap; - width: 1%; - } - - .label, .icon { - position: inherit; - font-size: inherit; - padding: 10px 0 10px 10px; - line-height: $number_size; - } - - .icon { - color: rgba(0, 0, 0, .5); - } - } - } - - .main-label { - position: absolute; - left: 5px; - bottom: 5px; - } - } - - .articles-list { - .scrollable { - overflow: auto; - max-height: calc(100% - 35px); - @include custom-scroll-bar(); - } - - .line { - color: currentColor; - margin: 5px; - padding: 5px; - font-weight: normal; - position: relative; - - .label { - display: block; - font-weight: bold; - font-size: 1.4em; - margin: 5px 0 10px; - } - - .content { - display: block; - margin-bottom: 3px; - font-size: 1.2em; - overflow: hidden; - text-overflow: ellipsis; - - &.long_text { - max-height: 80px; - } - } - - .date { - float: right - } - - a, i.fa-user { - color: currentColor; - } - - i.fa-user { - font-size: 0.8em; - } - } - - .main-label { - position: absolute; - left: 5px; - bottom: 5px; - } - } - - .g-chart { - display: flex; - flex-direction: column; - - .chart { - flex: 1; - min-height: 0; // force firefox to respect flex-shrink on svg - } - - .main-label { - height: 30px; - } - - .ct-chart-bar:not(.ct-horizontal-bars), - .ct-chart-line { - .ct-circle + .ct-label { - font-size: 0.75rem; - line-height: 1; - paint-order: stroke; - stroke-width: 5px; - } - .ct-labels { - .ct-label:not(.ct-vertical) { - word-break: break-word; - } - } - } - - .ct-chart-line { - .ct-label.ct-horizontal.ct-end { - transform: translateX(-40%); - } - } - - .ct-chart-donut { - fill: none; - - .ct-label { - text-anchor: middle; - font-weight: bold; - - &.fade { - opacity: .2; - } - } - } - - .ct-horizontal-bars { - .ct-label.ct-vertical.ct-start { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - - &:before { - content: ""; - display: inline-block; - height: 100%; - vertical-align: middle; - } - } - } - - .ct-chart-bar { - .ct-barlabel { - font-weight: bold; - } - } - - &.gauge { - .ct-chart { - max-height: calc(100% - 45px); // prevent gauge to overflow - - .ct-series.mouseover { - .ct-slice-pie, .ct-slice-donut-solid { - transform: scale(1.02); // with gauge, scale 5% si too much (we have clipping) - } - } - } - } - - @keyframes slice-fill-opacity { - to { - fill-opacity: 1; - } - } - - .ct-chart-donut, .ct-chart-pie { - .ct-series { - .ct-slice-pie, .ct-slice-donut-solid { - stroke-width: 1px; - transform-origin: center center; - //transition: transform 400ms ease-in-out; - fill-opacity: 0; - animation: slice-fill-opacity 1s ease-in-out forwards; - } - - &.disable-animation { - .ct-slice-pie, .ct-slice-donut-solid { - animation: none; - fill-opacity: 1; - } - } - - &.mouseover { - .ct-slice-pie, .ct-slice-donut-solid { - fill-opacity: 1; - stroke-opacity: 1; - stroke-width: 3px; - } - } - - &.notmouseover { - .ct-slice-pie, .ct-slice-donut-solid { - fill-opacity: .5; - stroke-opacity: 0; - } - } - } - } - - .ct-chart { - [data-clickable=true] { - cursor: pointer; - } - } - - .ct-point:hover { - stroke-width: 13px; - } - - &.pie, &.line.multiple, &.bar.tab10 { - @import 'chartist/palette_d3_tab_10'; - } - - &.bar.tab20 { - @import 'chartist/palette_d3_cat_20'; - } - - .ct-legend { - text-align: left; - padding: 10px 0 10px 38px; - - li { - position: relative; - padding-left: 23px; - margin-bottom: 3px; - } - - li:before { - width: 12px; - height: 12px; - position: absolute; - left: 0; - content: ''; - border: 3px solid transparent; - border-radius: 2px; - } - - li.inactive:before { - background-color: transparent !important; - } - } - } - - .markdown { - $mdmargin_v: 15px; - $editor_border: 1px solid rgba(0, 0, 0, 0.3); - overflow-y: auto; - @include custom-scroll-bar(); - - textarea.markdown_content { - display: none; - width: 100%; - height: calc(100% - #{$mdmargin_v} * 2); - resize: none; - padding: 5px; - margin: $mdmargin_v 0; - box-sizing: border-box; - background-color: rgba(255, 255, 255, .9); - border: 1px solid currentColor; - border-radius: 1px; - outline: none; - font-family: monospace - } - - ul { - list-style-type: disc; - - li { - margin-left: 1.5em; - } - } - - h1 { - border-bottom: 2px solid currentColor - } - h2 { - border-bottom: 1px solid currentColor - } - - img { - max-width: 100%; - max-height: 100%; - } - } - - .search-table { - .table-container { - overflow: auto; - max-height: calc(100% - 40px); - margin-top: 10px; - @include custom-scroll-bar(); - - .tab_cadrehov { - margin: 0; - min-width: 100%; - box-shadow: none; - background-color: transparent; - font-size: 13px; - - th { - position: sticky; - top: 0; - z-index: 10; - padding: 5px 5px; - border-bottom: 0; - color: inherit; - font-size: inherit; - - &.order_ASC:before, - &.order_DESC:before { - color: inherit; - } - } - - td { - border-bottom: 0; - } - - .tab_bg_1 { - background-color: rgba(0, 0, 0, .03); - } - .tab_bg_2 { - background-color: transparent; - } - - a { - color: inherit; - font-size: inherit !important; - - &:hover { - text-decoration: underline; - } - } - } - } - - .main-icon { - font-size: 2em; - right: 5px; - bottom: 5px; - } - - .main-label { - a { - font-size: inherit !important; - color: inherit; - } - } - } -} - -.chartist-tooltip.dashboard-tooltip { - $bg-color: rgba($color: #FFFFFF, $alpha: .65); - - background-color: $bg-color; - @include soft-shadow; - - &:before { - border-top-color: $bg-color; - } -} - -.embed_block { - border: 1px dashed #a0a0a0; - background-color: #e9e9e9; -} - -.widgettype_field { - .widget-list { - max-width: 400px; - } - - input[type=radio].widget-select { - display: none; - - & + label { - background: rgba($color: #000, $alpha: .05); - margin: 5px 5px 0 0; - border: 2px solid transparent; - border-radius: 3px; - display: none; - text-align: center; - font-weight: bold; - - &:hover { - background: rgba($color: #000, $alpha: .1); - border-color: rgba($color: #000, $alpha: .2); - cursor: pointer; - } - } - - &:checked + label { - background: rgba($color: #000, $alpha: .2); - border-color: rgba($color: #000, $alpha: .3); - position: relative; - - &:after { - content: "\f00c"; - right: 5px; - bottom: 3px; - font-size: 1em; - color: rgb(39, 39, 39); - font-family: 'Font Awesome\ 5 Free'; - position: absolute; - font-weight: 900; - } - } - } -} diff --git a/front/central.php b/front/central.php index 022330b638..764bff968e 100644 --- a/front/central.php +++ b/front/central.php @@ -32,21 +32,6 @@ include ('../inc/includes.php'); -if (!(isset($_GET["embed"]) - && isset($_GET["dashboard"]))) { - Session::checkCentralAccess(); -} - -// embed (anonymous) dashboard -if (isset($_GET["embed"]) && isset($_GET["dashboard"])) { - $grid = new Glpi\Dashboard\Grid($_GET["dashboard"]); - $dashboard = $grid->getDashboard(); - Html::popHeader($dashboard->getTitle(), $_SERVER['PHP_SELF'], false, 'central', 'central'); - echo $grid->embed($_REQUEST); - Html::popFooter(); - exit; -} - // Change profile system if (isset($_POST['newprofile'])) { if (isset($_SESSION["glpiprofiles"][$_POST['newprofile']])) { diff --git a/front/dashboard_assets.php b/front/dashboard_assets.php deleted file mode 100644 index be0391ba73..0000000000 --- a/front/dashboard_assets.php +++ /dev/null @@ -1,54 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -/** - * Filename was previously states.php - * @since 0.84 - */ - -include ('../inc/includes.php'); - - -Session::checkCentralAccess(); -$default = Glpi\Dashboard\Grid::getDefaultDashboardForMenu('assets'); - -// Redirect to "/front/computer.php" if no dashboard found -if ($default == "") { - Html::redirect($CFG_GLPI["root_doc"] . "/front/computer.php"); -} - -Html::header(__('Assets Dashboard'), $_SERVER['PHP_SELF'], "assets", "dashboard"); - -$dashboard = new Glpi\Dashboard\Grid($default); -$dashboard->showDefault(); - -Html::footer(); diff --git a/front/dashboard_helpdesk.php b/front/dashboard_helpdesk.php deleted file mode 100644 index fb0f8a1554..0000000000 --- a/front/dashboard_helpdesk.php +++ /dev/null @@ -1,54 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -/** - * Filename was previously states.php - * @since 0.84 - */ - -include ('../inc/includes.php'); - - -Session::checkCentralAccess(); -$default = Glpi\Dashboard\Grid::getDefaultDashboardForMenu('helpdesk'); - -// Redirect to "/front/ticket.php" if no dashboard found -if ($default == "") { - Html::redirect($CFG_GLPI["root_doc"] . "/front/ticket.php"); -} - -Html::header(__('Helpdesk Dashboard'), $_SERVER['PHP_SELF'], "helpdesk", "dashboard"); - -$dashboard = new Glpi\Dashboard\Grid($default); -$dashboard->showDefault(); - -Html::footer(); diff --git a/front/ticket.php b/front/ticket.php index 8bd747197d..44e69f7a21 100644 --- a/front/ticket.php +++ b/front/ticket.php @@ -48,11 +48,6 @@ echo Html::manageRefreshPage(false, $callback); -if ($default = Glpi\Dashboard\Grid::getDefaultDashboardForMenu('mini_ticket', true)) { - $dashboard = new Glpi\Dashboard\Grid($default, 33, 2, 'mini_core'); - $dashboard->show(true); -} - Search::show('Ticket'); if (Session::getCurrentInterface() == "helpdesk") { diff --git a/inc/based_config.php b/inc/based_config.php index f814b606fa..de85dae014 100644 --- a/inc/based_config.php +++ b/inc/based_config.php @@ -84,7 +84,6 @@ 'GLPI_USER_AGENT_EXTRA_COMMENTS' => '', // Extra comment to add to GLPI User-Agent // Other constants - 'GLPI_AJAX_DASHBOARD' => '1', 'GLPI_CALDAV_IMPORT_STATE' => 0, // external events created from a caldav client will take this state by default (0 = Planning::INFO) 'GLPI_DEMO_MODE' => '0', // TODO GLPI_FORCE_EMPTY_SQL_MODE need to be set to 0 after review of all sql queries diff --git a/inc/central.class.php b/inc/central.class.php index 94bf074c1b..9acb779425 100644 --- a/inc/central.class.php +++ b/inc/central.class.php @@ -68,11 +68,6 @@ function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { 4 => _n('RSS feed', 'RSS feeds', Session::getPluralNumber()), ]; - $grid = new Glpi\Dashboard\Grid('central'); - if ($grid->canViewOneDashboard()) { - array_unshift($tabs, __('Dashboard')); - } - return $tabs; } return ''; @@ -84,9 +79,6 @@ static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtem if ($item->getType() == __CLASS__) { switch ($tabnum) { case 0 : - $item->showGlobalDashboard(); - break; - case 1 : $item->showMyView(); break; @@ -107,19 +99,6 @@ static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtem return true; } - public function showGlobalDashboard() { - echo ""; - Plugin::doHook('display_central'); - echo "
"; - - self::showMessages(); - - $default = Glpi\Dashboard\Grid::getDefaultDashboardForMenu('central'); - $dashboard = new Glpi\Dashboard\Grid($default); - $dashboard->show(); - } - - /** * Show the central global view **/ diff --git a/inc/config.class.php b/inc/config.class.php index 7409fbfa12..c58eaf5df7 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -30,7 +30,6 @@ * --------------------------------------------------------------------- */ -use Glpi\Dashboard\Grid; use Glpi\Cache\SimpleCache; use Glpi\Exception\PasswordTooWeakException; use Glpi\System\RequirementsManager; @@ -1403,38 +1402,6 @@ function formatLayout(layout) { echo ""; } - if (Grid::canViewOneDashboard()) { - echo "".__('Dashboards').""; - - echo ""; - echo "" . __('Default for central') . ""; - Grid::dropdownDashboard("default_dashboard_central", [ - 'value' => $data['default_dashboard_central'], - 'display_emptychoice' => true - ]); - echo "". __('Default for Assets'). - ""; - Grid::dropdownDashboard("default_dashboard_assets", [ - 'value' => $data['default_dashboard_assets'], - 'display_emptychoice' => true - ]); - echo ""; - - echo ""; - echo "". __('Default for Assistance') . ""; - Grid::dropdownDashboard("default_dashboard_helpdesk", [ - 'value' => $data['default_dashboard_helpdesk'], - 'display_emptychoice' => true - ]); - echo "". __('Default for tickets (mini dashboard)'). - ""; - Grid::dropdownDashboard("default_dashboard_mini_ticket", [ - 'value' => $data['default_dashboard_mini_ticket'], - 'display_emptychoice' => true - ]); - echo ""; - } - if ((!$userpref && $canedit) || ($userpref && $canedituser)) { echo ""; echo ""; diff --git a/inc/dashboard/dashboard.class.php b/inc/dashboard/dashboard.class.php deleted file mode 100644 index 7227ec1f73..0000000000 --- a/inc/dashboard/dashboard.class.php +++ /dev/null @@ -1,490 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -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; - } -} diff --git a/inc/dashboard/filter.class.php b/inc/dashboard/filter.class.php deleted file mode 100644 index 3f24170d7d..0000000000 --- a/inc/dashboard/filter.class.php +++ /dev/null @@ -1,251 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -use CommonGLPI; -use ITILCategory; -use RequestType; -use Location; -use Manufacturer; -use Session; -use Html; -use Group; -use Plugin; -use User; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -/** - * Filter class -**/ -class Filter extends CommonGLPI { - - /** - * Return all available filters - * Plugins can hooks on this functions to add their own filters - * - * @return array of filters - */ - static function getAll(): array { - $filters = [ - 'dates' => __("Creation date"), - 'dates_mod' => __("Last update"), - 'itilcategory' => ITILCategory::getTypeName(Session::getPluralNumber()), - 'requesttype' => RequestType::getTypeName(Session::getPluralNumber()), - 'location' => Location::getTypeName(Session::getPluralNumber()), - 'manufacturer' => Manufacturer::getTypeName(Session::getPluralNumber()), - 'group_tech' => __("Technician group"), - 'user_tech' => __("Technician"), - ]; - - $more_filters = Plugin::doHookFunction("dashboard_filters"); - if (is_array($more_filters)) { - $filters = array_merge($filters, $more_filters); - } - - return $filters; - } - - /** - * Get HTML for a dates range filter - * - * @param string|array $values init the input with these values, will be a string if empty values - * @param string $fieldname how is named the current date field - * (used to specify creation date or last update) - * - * @return string - */ - static function dates($values = "", string $fieldname = 'dates'): string { - // string mean empty value - if (is_string($values)) { - $values = []; - } - - $rand = mt_rand(); - $label = self::getAll()[$fieldname]; - $field = Html::showDateField('filter-dates', [ - 'value' => $values, - 'rand' => $rand, - 'range' => true, - 'display' => false, - 'calendar_btn' => false, - 'placeholder' => $label, - 'on_change' => "on_change_{$rand}(selectedDates, dateStr, instance)", - ]); - - $js = << 0); - } - - /** - * Get HTML for a dates range filter. Same as date but for last update field - * - * @param string|array $values init the input with these values, will be a string if empty values - * - * @return string - */ - static function dates_mod($values): string { - return self::dates($values, "dates_mod"); - } - - - static function itilcategory(string $value = ""): string { - return self::dropdown($value, 'itilcategory', ItilCategory::class); - } - - static function requesttype(string $value = ""): string { - return self::dropdown($value, 'requesttype', RequestType::class); - } - - static function location(string $value = ""): string { - return self::dropdown($value, 'location', Location::class); - } - - static function manufacturer(string $value = ""): string { - return self::dropdown($value, 'manufacturer', Manufacturer::class); - } - - static function group_tech(string $value = ""): string { - return self::dropdown($value, 'group_tech', Group::class, ['toadd' => [-1 => __("My groups")]]); - } - - static function user_tech(string $value = ""): string { - return self::dropdown($value, 'user_tech', User::class, ['right' => 'own_ticket']); - } - - static function dropdown( - string $value = "", - string $fieldname = "", - string $itemtype = "", - array $add_params = [] - ): string { - $value = !empty($value) ? (int) $value : null; - $rand = mt_rand(); - $label = self::getAll()[$fieldname]; - $field = $itemtype::dropdown([ - 'name' => $fieldname, - 'value' => $value, - 'rand' => $rand, - 'display' => false, - 'display_emptychoice' => false, - 'emptylabel' => '', - 'placeholder' => $label, - 'on_change' => "on_change_{$rand}()", - 'allowClear' => true, - 'width' => '' - ] + $add_params); - - $js = << 0) - }; - -JAVASCRIPT; - $js = Html::scriptBlock($js); - - return $js.self::field($fieldname, $field, $label, $value > 0); - } - - /** - * Get generic HTML for a filter - * - * @param string $id system name of the filter (ex "dates") - * @param string $field html of the filter - * @param string $label displayed label for the filter - * @param bool $filled - * - * @return string the html for the complete field - */ - static function field( - string $id = "", - string $field = "", - string $label = "", - bool $filled = false - ): string { - - $rand = mt_rand(); - $class = $filled ? "filled" : ""; - $html = << - $field - $label - - -HTML; - - $js = << 0) { - $('#filter-{$rand}').addClass('filled'); - } else { - $('#filter-{$rand}').removeClass('filled'); - } - - $(this).width((str_len + 1) * 8 ); - }); - - $('#filter-{$rand}') - .hover(function() { - $('.dashboard .card.filter-{$id}').addClass('filter-impacted'); - }, function() { - $('.dashboard .card.filter-{$id}').removeClass('filter-impacted'); - }); - }); -JAVASCRIPT; - $js = Html::scriptBlock($js); - - return $html.$js; - } -} diff --git a/inc/dashboard/grid.class.php b/inc/dashboard/grid.class.php deleted file mode 100644 index 5d72f1c3c4..0000000000 --- a/inc/dashboard/grid.class.php +++ /dev/null @@ -1,1524 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -use Ramsey\Uuid\Uuid; - -use Config; -use CommonGLPI; -use Dropdown; -use DBConnection; -use Entity; -use Group; -use Html; -use Plugin; -use Profile; -use Session; -use ShareDashboardDropdown; -use Telemetry; -use Toolbox; -use User; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -class Grid extends CommonGLPI { - protected $cell_margin = 6; - protected $grid_cols = 26; - protected $grid_rows = 24; - protected $current = ""; - protected $dashboard = null; - protected $items = []; - - static $embed = false; - static $context = ''; - static $all_dashboards = []; - - public function __construct( - string $dashboard_key = "central", - int $grid_cols = 26, - int $grid_rows = 24, - string $context = "core" - ) { - - $this->current = $dashboard_key; - $this->grid_cols = $grid_cols; - $this->grid_rows = $grid_rows; - - $this->dashboard = new Dashboard($dashboard_key); - self::$context = $context; - } - - - /** - * Return the instance of current dasbhoard - * - * @return Dashboard - */ - public function getDashboard() { - return $this->dashboard; - } - - - /** - * load all existing dashboards from DB into a static property for caching data - * - * @param bool $force, if false, don't use cache - * - * @return bool - */ - static function loadAllDashboards(bool $force = true): bool { - if (!is_array(self::$all_dashboards) - || count(self::$all_dashboards) === 0 - || $force) { - self::$all_dashboards = Dashboard::getAll($force, !self::$embed, self::$context); - } - - return is_array(self::$all_dashboards); - } - - - /** - * Init dashboards cards - * A define.php constant (GLPI_AJAX_DASHBOARD) exists to control how the cards should be loaded - * - if true: only the parent block will be initialized and the content will be load by ajax - * pros: if a widget fails, only this one will crash - * - else: load all html - * pros: better perfs - * - * @return void - */ - public function getCards() { - self::loadAllDashboards(); - - if (!isset(self::$all_dashboards[$this->current]) - || !isset(self::$all_dashboards[$this->current]['items'])) { - self::$all_dashboards[$this->current] = [ - 'items' => [] - ]; - } - - foreach (self::$all_dashboards[$this->current]['items'] as $specs) { - $card_id = $specs['card_id'] ?? $specs['gridstack_id'] ?? $specs['id']; - $gridstack_id = $specs['gridstack_id'] ?? $specs['id']; - $card_options = ($specs['card_options'] ?? []) + [ - 'card_id' => $card_id - ]; - - if (GLPI_AJAX_DASHBOARD) { - $card_html = << - - -HTML; - } else { - $card_html = $this->getCardHtml($card_id, ['args' => $card_options]); - } - - // manage cache - $dashboard_key = $this->current; - $footprint = sha1(serialize($card_options). - ($_SESSION['glpiactiveentities_string'] ?? ""). - ($_SESSION['glpilanguage'])); - $cache_key = "dashboard_card_{$dashboard_key}_{$footprint}"; - $card_options['cache_key'] = $cache_key; - - $this->addGridItem( - $card_html, - $gridstack_id, - $specs['x'] ?? -1, - $specs['y'] ?? -1, - $specs['width'] ?? 2, - $specs['height'] ?? 2, - $card_options - ); - } - } - - - /** - * Do we have the right to view at least one dashboard int the current collection - * - * @return bool - */ - public function canViewCurrent(): bool { - // check global (admin) right - if (Dashboard::canView()) { - return true; - } - - return $this->dashboard->canViewCurrent(); - } - - - /** - * Do we have the right to view at least one dashboard in the current collection - * - * @return bool - */ - static function canViewOneDashboard(): bool { - // check global (admin) right - if (Dashboard::canView()) { - return true; - } - - self::loadAllDashboards(); - - return (count(self::$all_dashboards) > 0); - } - - - /** - * Do we have the right to view the specified dashboard int the current collection - * - * @param string $key the dashboard to check - * - * @return bool - */ - static function canViewSpecificicDashboard($key): bool { - // check global (admin) right - if (Dashboard::canView()) { - return true; - } - - self::loadAllDashboards(); - - return isset(self::$all_dashboards[$key]); - } - - - /** - * Display grid for the current dashboard - * - * @return void display html of the grid - */ - public function show(bool $mini = false) { - $rand = mt_rand(); - - if (!self::$embed && !$this->dashboard->canViewCurrent()) { - return; - } - - self::loadAllDashboards(); - - $this->restoreLastDashboard(); - - if ($mini) { - $this->cell_margin = 3; - } - - $embed_str = self::$embed ? "true" : "false"; - $embed_class = self::$embed ? "embed" : ""; - $mini_class = $mini ? "mini" : ""; - - $nb_dashboards = count(self::$all_dashboards); - - $can_view_all = Session::haveRight('dashboard', READ) || self::$embed; - $can_create = Session::haveRight('dashboard', CREATE); - $can_edit = Session::haveRight('dashboard', UPDATE) && $nb_dashboards; - $can_purge = Session::haveRight('dashboard', PURGE) && $nb_dashboards; - $can_clone = $can_create && $nb_dashboards; - - // prepare html for add controls - $add_controls = ""; - for ($y = 0; $y < $this->grid_rows; $y++) { - for ($x = 0; $x < $this->grid_cols; $x++) { - $add_controls.= "
 
"; - } - } - - // prepare all available cards - $cards = $this->getAllDasboardCards(); - $cards_json = json_encode($cards); - - // prepare all available widgets - $all_widgets = Widget::getAllTypes(); - $all_widgets_json = json_encode($all_widgets); - - // prepare labels - $embed_label = __("Share or embed this dashboard"); - $delete_label = __("Delete this dashboard"); - $history_label = __("Toggle auto-refresh"); - $night_label = __("Toggle night mode"); - $fs_label = __("Toggle fullscreen"); - $clone_label = __("Clone this dashboard"); - $edit_label = __("Toggle edit mode"); - $add_filter_lbl = __("Add filter"); - $add_dash_label = __("Add a new dashboard"); - $save_label = _x('button', "Save"); - - $gridstack_items = $this->getGridItemsHtml(); - - $dropdown_dashboards = ""; - if ($nb_dashboards) { - $dropdown_dashboards = self::dropdownDashboard("", [ - 'value' => $this->current, - 'display' => false, - 'class' => 'dashboard_select', - 'can_view_all' => $can_view_all, - 'noselect2' => true, - ]); - } - - $dashboard_title = $this->dashboard->getTitle(); - - $l_tb_icons = ""; - $r_tb_icons = ""; - $rename = ""; - $left_toolbar = ""; - $grid_guide = ""; - - if (!self::$embed) { - if (!$mini && $can_create) { - $l_tb_icons.= ""; - } - if (!$mini && $can_clone) { - $r_tb_icons.= ""; - } - if (!$mini && $can_edit) { - $r_tb_icons.= ""; - $rename = "
- - - -
"; - } - if (!$mini && $can_purge) { - $r_tb_icons.= ""; - } - if ($can_edit) { - $r_tb_icons.= ""; - } - - if (!$mini) { - $r_tb_icons.= ""; - } - - if (!$mini) { - $left_toolbar = << -
- $dropdown_dashboards - $l_tb_icons -
- $rename - -HTML; - } - - $grid_guide = << - $add_controls - -HTML; - } - - $toolbars = << - - - $r_tb_icons - -HTML; - - $filters = ""; - if (!$mini) { - $filters = << - - - - {$add_filter_lbl} - - - -HTML; - } - - $embed_watermark = ""; - if (self::$embed) { - $embed_watermark = ""; - } - - // display the grid - $html = << - $embed_watermark - $toolbars - $filters - $grid_guide -
- $gridstack_items -
- -HTML; - - $ajax_cards = GLPI_AJAX_DASHBOARD; - $context = self::$context; - $cache_key = sha1($_SESSION['glpiactiveentities_string '] ?? ""); - - $js = <<current}', - cols: {$this->grid_cols}, - rows: {$this->grid_rows}, - cell_margin: {$this->cell_margin}, - rand: '{$rand}', - embed: {$embed_str}, - ajax_cards: {$ajax_cards}, - all_cards: {$cards_json}, - all_widgets: {$all_widgets_json}, - context: "{$context}", - cache_key: "{$cache_key}", - }) - }); -JAVASCRIPT; - $js = Html::scriptBlock($js); - - echo $html.$js; - } - - - public function showDefault() { - echo "
"; - $this->show(); - echo "
"; - } - - - /** - * Show an embeded dashboard. - * We must check token validity to avoid displaying dashboard to invalid users - * - * @param array $params contains theses keys: - * - dashboard: the dashboard system name - * - entities_id: entity to init in session - * - is_recursive: do we need to display sub entities - * - token: the token to check - * - * @return void (display) - */ - public function embed(array $params = []) { - $defaults = [ - 'dashboard' => '', - 'entities_id' => 0, - 'is_recursive' => 0, - 'token' => '' - ]; - $params = array_merge($defaults, $params); - - if (!self::checkToken($params)) { - Html::displayRightError(); - exit; - } - - self::$embed = true; - - // load minimal session - $_SESSION["glpiactive_entity"] = $params['entities_id']; - $_SESSION["glpiactive_entity_recursive"] = $params['is_recursive']; - $_SESSION["glpiname"] = 'embed_dashboard'; - $_SESSION["glpigroups"] = []; - if ($params['is_recursive']) { - $entities = getSonsOf("glpi_entities", $params['entities_id']); - } else { - $entities = [$params['entities_id']]; - } - $_SESSION['glpiactiveentities'] = $entities; - $_SESSION['glpiactiveentities_string'] = "'".implode("', '", $entities)."'"; - - // show embeded dashboard - $this->show(true); - } - - static function getToken(string $dasboard = "", int $entities_id = 0, int $is_recursive = 0): string { - $seed = $dasboard.$entities_id.$is_recursive.Telemetry::getInstanceUuid(); - $uuid = Uuid::uuid5(Uuid::NAMESPACE_OID, $seed); - $token = $uuid->toString(); - - return $token; - } - - /** - * Check token variables (compare it to `dashboard`, `entities_id` and `is_recursive` paramater) - * - * @param array $params contains theses keys: - * - dashboard: the dashboard system name - * - entities_id: entity to init in session - * - is_recursive: do we need to display sub entities - * - token: the token to check - * - * @return bool - */ - static function checkToken(array $params = []):bool { - $defaults = [ - 'dashboard' => '', - 'entities_id' => 0, - 'is_recursive' => 0, - 'token' => '' - ]; - $params = array_merge($defaults, $params); - - $token = self::getToken( - $params['dashboard'], - $params['entities_id'], - $params['is_recursive'] - ); - - if ($token !== $params['token']) { - return false; - Html::displayRightError(); - exit; - } - - return true; - } - - - /** - * Return the html for all items for the current dashboard - * - * @param bool $with_lock if true, return also a locked bottom item (to fix grid height) - * - * @return string html of the grid items - */ - public function getGridItemsHtml(bool $with_lock = true, bool $embed = false): string { - if ($embed) { - self::$embed = true; - } - - $this->getCards(); - - if ($with_lock) { - $this->items[] = << -HTML; - } - - // append all elements to insert them in html - return implode("", $this->items); - } - - - /** - * Add a new grid item - * - * @param string $html content of the card - * @param string $gridstack_id unique id identifying the card (used in gridstack) - * @param int $x position in the grid - * @param int $y position in the grid - * @param int $width size in the grid - * @param int $height size in the grid - * @param array $data_option aditional options passed to the widget, contains at least thses keys: - * - string 'color' - * @return void - */ - public function addGridItem( - string $html = "", - string $gridstack_id = "", - int $x = -1, - int $y = -1, - int $width = 2, - int $height = 2, - array $data_option = []) { - - // let grid-stack to autoposition item - $autoposition = 'data-gs-auto-position="true"'; - $coordinates = ''; - if ((int) $x >= 0 && (int) $y >= 0) { - $autoposition = ""; - $coordinates = "data-gs-x='$x' data-gs-y='$y'"; - } - - $color = $data_option['color'] ?? "#FFFFFF"; - $fg_color = Toolbox::getFgColor($color, 100); - - // add card options in data attribute - $data_option_attr = ""; - if (count($data_option)) { - $data_option_attr = "data-card-options='".json_encode($data_option, JSON_HEX_APOS)."'"; - } - - $refresh_label = __("Refresh this card"); - $edit_label = __("Edit this card"); - $delete_label = __("Delete this card"); - - $this->items[] = << - - - - - -
{$html}
- -HTML; - } - - - /** - * Display a mini form fo adding a new dashboard - * - * @return void (display) - */ - public function displayAddDashboardForm() { - $rand = mt_rand(); - - echo "
"; - - echo "
"; - echo ""; - echo "
"; - echo Html::input('title', ['id' => "title_$rand"]); - echo "
"; - echo "
"; // .field - - echo Html::submit(_x('button', "Add"), [ - 'class' => 'submit vsubmit submit-new-dashboard' - ]); - - echo "
"; // .card.display-widget-form - } - - - /** - * Display mini configuration form to add or edit a widget - * - * @param array $params with these keys: - * - int 'gridstack_id': unique identifier of the card - * - int 'x': position in the grid - * - int 'y: position in the grid - * - int 'width': size in the grid - * - int 'height': size in the grid - * - string 'rand': unique identifier for the dom - * - string 'action': [display_add_widget|display_edit_widget] current action for the form - * - array 'card_options': aditionnal options for the card, contains at least: - * - string 'card_id': identifier return by @see self::getAllDasboardCards - * - string 'color' - * - * @return void - */ - public function displayWidgetForm(array $params = []) { - $gridstack_id = $params['gridstack_id'] ?? ""; - $old_id = $gridstack_id; - $x = (int) ($params['x'] ?? 0); - $y = (int) ($params['y'] ?? 0); - $width = (int) ($params['width'] ?? 0); - $height = (int) ($params['height'] ?? 0); - $cardopt = $params['card_options'] ?? ['color' => "#FAFAFA"]; - $card_id = $cardopt['card_id'] ?? ""; - $widgettypes = Widget::getAllTypes(); - $widgettype = $cardopt['widgettype'] ?? ""; - $widget_def = $widgettypes[$widgettype] ?? []; - $use_gradient = $cardopt['use_gradient'] ?? 0; - $point_labels = $cardopt['point_labels'] ?? 0; - $limit = $cardopt['limit'] ?? 7; - $color = $cardopt['color']; - $edit = $params['action'] === "display_edit_widget"; - $rand = $params['rand'] ?? mt_rand(); - $cards = $this->getAllDasboardCards(); - $card = $cards[$card_id] ?? []; - // append card id to options - if (!isset($cardopt['card_id'] )) { - $cardopt['card_id'] = $card_id; - } - - $list_cards = []; - array_walk($cards, function($data, $index) use (&$list_cards) { - $group = $data['group'] ?? __("others"); - $list_cards[$group][$index] = $data['label'] ?? $data['itemtype']::getTypeName(); - }); - - echo "
"; - - echo "
"; - echo ""; - echo "
"; - Html::showColorField('color', [ - 'rand' => $rand, - 'value' => $color, - ]); - echo "
"; - echo "
"; // .field - - echo "
"; - echo ""; - echo "
"; - Dropdown::showFromArray('card_id', $list_cards, [ - 'display_emptychoice' => true, - 'rand' => $rand, - 'value' => $card_id - ]); - echo "
"; - echo "
"; // .field - - // display widget list - $displayed = ""; - if (!$edit) { - $displayed = "style='display: none'"; - } - echo "
"; - echo ""; - echo "
"; - foreach (Widget::getAllTypes() as $key => $current) { - $selected = ''; - if ($key === $widgettype) { - $selected = 'checked'; - } - $w_diplayed = ""; - if ($edit && isset($card['widgettype']) && in_array($key, $card['widgettype'])) { - $w_diplayed = "style='display: inline-block;'"; - } - echo " - "; - } - echo "
"; - echo "
"; // .field - - // display checkbox to use gradient palette or not - $gradient_displayed = ""; - if (!$edit || !isset($widget_def['gradient']) || !$widget_def['gradient']) { - $gradient_displayed = "style='display: none'"; - } - echo "
"; - echo ""; - echo "
"; - Html::showCheckbox([ - 'label' => " ", - 'name' => "use_gradient", - 'id' => "check_gradient_$rand", - 'checked' => $use_gradient, - ]); - echo "
"; - echo "
"; // .field - - // display checkbox to use point label or not - $point_labels_displayed = ""; - if (!$edit || !isset($widget_def['pointlbl']) || !$widget_def['pointlbl']) { - $point_labels_displayed = "style='display: none'"; - } - echo "
"; - echo ""; - echo "
"; - Html::showCheckbox([ - 'label' => " ", - 'name' => "point_labels", - 'id' => "check_point_labels_$rand", - 'checked' => $point_labels, - ]); - echo "
"; - echo "
"; // .field - - // show limit dropdown - $limit_displayed = ""; - if (!$edit || !isset($widget_def['limit']) || !$widget_def['limit']) { - $limit_displayed = "style='display: none'"; - } - echo "
"; - echo ""; - echo "
"; - Dropdown::showNumber('limit', [ - 'value' => $limit, - 'rand' => $rand, - ]); - echo "
"; - echo "
"; // .field - - $class_submit = "add-widget"; - $label_submit = " "._x('button', "Add"); - if ($edit) { - $class_submit = "edit-widget"; - $label_submit = " "._x('button', "Update"); - } - - // manage autoescaping - if (isset($cardopt['markdown_content'])) { - $cardopt['markdown_content'] = Html::cleanPostForTextArea($cardopt['markdown_content']); - } - - echo Html::submit($label_submit, [ - 'class' => 'submit vsubmit '.$class_submit - ]); - - echo Html::hidden('gridstack_id', ['value' => $gridstack_id]); - echo Html::hidden('old_id', ['value' => $old_id]); - echo Html::hidden('x', ['value' => $x]); - echo Html::hidden('y', ['value' => $y]); - echo Html::hidden('width', ['value' => $width]); - echo Html::hidden('height', ['value' => $height]); - echo Html::hidden('card_options', ['value' => json_encode($cardopt, JSON_HEX_APOS | JSON_HEX_QUOT)]); - - echo "
"; // .card.display-widget-form - } - - - /** - * Display mini form to add filter to the current dashboard - * - * @param array $params default values for - * - 'used' already used filters - * - * @return void - */ - public function displayFilterForm(array $params = []) { - $default_params = [ - 'used' => [], - ]; - $params = array_merge($default_params, $params); - - $used = array_flip($params['used']); - $list_filters = array_diff_key(Filter::getAll(), $used); - - $rand = mt_rand(); - echo "
"; - - echo "
"; - echo ""; - echo "
"; - Dropdown::showFromArray('filter_id', $list_filters, [ - 'display_emptychoice' => true, - 'rand' => $rand, - ]); - echo "
"; - echo "
"; // .field - - echo Html::submit(" "._x('button', "Add"), [ - 'class' => 'submit vsubmit' - ]); - echo "
"; // form.card.display-filter-form - } - - - /** - * Display a mini form for embedding current dashboard in another application. - * Also, display a select for sharing current dashboard to another users/groups/entities/profiles - * - * @return void - */ - public function displayEmbedForm() { - global $CFG_GLPI; - - $entities_id = $_SESSION['glpiactive_entity']; - $is_recursive = $_SESSION['glpiactive_entity_recursive']; - $token = self::getToken($this->current, $entities_id, $is_recursive); - - $embed_url = $CFG_GLPI['url_base']. - "/front/central.php?embed&dashboard=".$this->current. - "&entities_id=$entities_id". - "&is_recursive=$is_recursive". - "&token=$token"; - - echo "
"; - echo "
"; - echo __("Direct link"); - echo "
"; - echo Html::input('direct_link', [ - 'value' => $embed_url, - 'style' => 'width: calc(100% - 38px)' - ]); - echo "

"; - - $iframe = ""; - echo __("Iframe"); - echo "
"; - echo Html::input('iframe_code', [ - 'value' => $iframe, - 'style' => 'width: calc(100% - 38px)' - ]); - echo "
"; - echo "

"; - - $this->displayEditRightsForm(); - } - - - /** - * Display a mini form for sharing current dashboard to another users/groups/entities/profiles. - * - * @return void - */ - public function displayEditRightsForm() { - self::loadAllDashboards(); - $rand = mt_rand(); - $values = []; - - echo "
"; - - echo "
"; - - $values = []; - $raw_values = [ - 'profiles_id' => self::$all_dashboards[$this->current]['rights']['profiles_id'] ?? [], - 'entities_id' => self::$all_dashboards[$this->current]['rights']['entities_id'] ?? [], - 'users_id' => self::$all_dashboards[$this->current]['rights']['users_id'] ?? [], - 'groups_id' => self::$all_dashboards[$this->current]['rights']['groups_id'] ?? [], - ]; - - foreach ($raw_values as $fkey => $ids) { - foreach ($ids as $id) { - $values[] = $fkey . "-" . $id; - } - } - echo ShareDashboardDropdown::show($rand, $values); - echo "

"; - - echo "".__("Save").""; - - Html::closeForm(true); - } - - - /** - * Return the html for the given card_id - * - * @param string $card_id identifier return by @see self::getAllDasboardCards - * @param array $card_options contains these keys: - * - array 'args': - * - string 'gridstack_id' unique identifier of the card in the grid, used to return html by cache - * - bool 'force' if true, cache will be bypassed - * - bool 'embed' is the dashboard emebeded or not - * - * @return string html of the card - */ - public function getCardHtml(string $card_id = "", array $card_options = []): string { - global $GLPI_CACHE; - - $gridstack_id = $card_options['args']['gridstack_id'] ?? $card_id; - $dashboard = $card_options['dashboard'] ?? ""; - - $force = ($card_options['args']['force'] ?? $card_options['force'] ?? false); - - // retrieve card - $notfound_html = "
- ". - __('empty card !')." -
"; - $cards = $this->getAllDasboardCards(); - if (!isset($cards[$card_id])) { - return $notfound_html; - } - $card = $cards[$card_id]; - - // manage cache - $options_footprint = sha1(serialize($card_options). - ($_SESSION['glpiactiveentities_string'] ?? ""). - ($_SESSION['glpilanguage'])); - - $use_cache = !$force - && $_SESSION['glpi_use_mode'] != Session::DEBUG_MODE - && (!isset($card['cache']) || $card['cache'] == true); - $cache_key = "dashboard_card_{$dashboard}_{$options_footprint}"; - $cache_age = 40; - - if ($use_cache) { - // browser cache - if (GLPI_AJAX_DASHBOARD) { - header_remove('Pragma'); - header('Cache-Control: public'); - header('Cache-Control: max-age=' . $cache_age); - header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $cache_age)); - } - - // server cache - if ($GLPI_CACHE->has($cache_key)) { - $dashboard_cards = $GLPI_CACHE->get($cache_key); - if (isset($dashboard_cards[$gridstack_id])) { - return (string) $dashboard_cards[$gridstack_id]; - } - } - } - - $html = ""; - $start = microtime(true); - - // call provider to retrieve data - if (isset($card['provider'])) { - $provider_args = ($card['args'] ?? []) + [ - 'params' => [ - 'label' => $card['label'] ?? "" - ] - ]; - if (isset($card_options['args']['apply_filters'])) { - $provider_args['params']['apply_filters'] = $card_options['args']['apply_filters']; - } - $widget_args = call_user_func_array($card['provider'], $provider_args); - } - $widget_args = array_merge($widget_args ?? [], $card_options['args'] ?? []); - - // call widget function to construct html - $all_widgets = Widget::getAllTypes(); - $widgettype = $card_options['args']['widgettype'] ?? ""; - $widgetfct = $all_widgets[$widgettype]['function'] ?? ""; - if (strlen($widgetfct)) { - // clean urls in embed mode - if (isset($card_options['embed']) && $card_options['embed']) { - unset($widget_args['url']); - - if (isset($widget_args['data'])) { - $unset_url = function (&$array) use(&$unset_url) { - unset($array['url']); - foreach ($array as &$value) { - if (is_array($value)) { - $unset_url($value, 'url'); - } - } - }; - $unset_url($widget_args['data']); - } - } - - if (isset($card['filters'])) { - $widget_args['filters'] = $card['filters']; - } - - // call widget function - $html = call_user_func($widgetfct, $widget_args); - } - - // display a warning for empty card - if (strlen($html) === 0) { - return $notfound_html; - } - - $execution_time = round(microtime(true) - $start, 3); - - // store server cache - if (strlen($dashboard)) { - $dashboard_cards = $GLPI_CACHE->get($cache_key) ?? []; - $dashboard_cards[$gridstack_id] = $html; - $GLPI_CACHE->set($cache_key, $dashboard_cards, new \DateInterval("PT".$cache_age."S")); - } - - if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) { - $html.= << - {$execution_time}s - -HTML; - } - - return $html; - } - - - /** - * Return Html for a provided set of filters - * @param array $filter_names - * - * @return string the html - */ - public function getFiltersSetHtml(array $filters = []): string { - $html = ""; - - foreach ($filters as $filter_id => $filter_values) { - $html.= $this->getFilterHtml($filter_id, $filter_values); - } - - return $html; - } - - - /** - * Return Html for a provided filter name - * - * @param string $filter_id the system name of a filter (ex dates) - * @param string|array $filter_values init the input with these values, - * will be a string if empty values - * - * @return string the html - */ - public function getFilterHtml(string $filter_id = "", $filter_values = ""): string { - if (method_exists("Glpi\Dashboard\Filter", $filter_id)) { - return call_user_func("Glpi\Dashboard\Filter::$filter_id", $filter_values); - } - - return ""; - } - - - /** - * Return all itemtypes possible for constructing cards. - * User in @see self::getAllDasboardCards - * - * @return array [itemtype1, itemtype2] - */ - protected function getMenuItemtypes(): array { - $menu_itemtypes = []; - $exclude = [ - 'Config', - ]; - - $menu = Html::getMenuInfos(); - array_walk($menu, static function($firstlvl) use (&$menu_itemtypes) { - $key = $firstlvl['title']; - if (isset($firstlvl['types'])) { - $menu_itemtypes[$key] = array_merge($menu_itemtypes[$key] ?? [], $firstlvl['types']); - } - }); - - foreach ($menu_itemtypes as &$firstlvl) { - $firstlvl = array_filter($firstlvl, static function($itemtype) use ($exclude) { - if (in_array($itemtype, $exclude) - || !is_subclass_of($itemtype, 'CommonDBTM')) { - return false; - } - - $testClass = new \ReflectionClass($itemtype); - return !$testClass->isAbstract(); - }); - } - - return $menu_itemtypes; - } - - /** - * Construct catalog of all possible cards addable in a dashboard. - * - * @param bool $force Force rebuild the catalog of cards - * - * @return array - */ - public function getAllDasboardCards($force = false): array { - global $CFG_GLPI; - - // anonymous fct for adding relevant filters to cards - $add_filters_fct = static function($itemtable) { - $DB = DBConnection::getReadConnection(); - - $add_filters = []; - if ($DB->fieldExists($itemtable, "ititlcategories_id")) { - $add_filters[] = "itilcategory"; - } - if ($DB->fieldExists($itemtable, "requesttypes_id")) { - $add_filters[] = "requesttype"; - } - if ($DB->fieldExists($itemtable, "locations_id")) { - $add_filters[] = "location"; - } - if ($DB->fieldExists($itemtable, "manufacturers_id")) { - $add_filters[] = "manufacturer"; - } - if ($DB->fieldExists($itemtable, "groups_id_tech")) { - $add_filters[] = "group_tech"; - } - if ($DB->fieldExists($itemtable, "users_id_tech")) { - $add_filters[] = "user_tech"; - } - - return $add_filters; - }; - - static $cards = null; - - if ($cards === null || $force) { - $cards = []; - $menu_itemtypes = $this->getMenuItemtypes(); - - foreach ($menu_itemtypes as $firstlvl => $itemtypes) { - foreach ($itemtypes as $itemtype) { - $clean_itemtype = str_replace('\\', '_', $itemtype); - $cards["bn_count_$clean_itemtype"] = [ - 'widgettype' => ["bigNumber"], - 'group' => $firstlvl, - 'itemtype' => "\\$itemtype", - 'label' => sprintf(__("Number of %s"), $itemtype::getTypeName()), - 'provider' => "Glpi\\Dashboard\\Provider::bigNumber$itemtype", - 'filters' => array_merge([ - 'dates', - 'dates_mod', - ], $add_filters_fct($itemtype::getTable())) - ]; - } - } - - // add multiple width for Assets itemtypes grouped by their foreign keys - $assets = array_merge($CFG_GLPI['asset_types'], ['Software']); - foreach ($assets as $itemtype) { - $fk_itemtypes = [ - 'State', - 'Entity', - 'Manufacturer', - 'Location', - ]; - - if (class_exists($itemtype . 'Type')) { - $fk_itemtypes[] = $itemtype . 'Type'; - } - if (class_exists($itemtype . 'Model')) { - $fk_itemtypes[] = $itemtype . 'Model'; - } - - foreach ($fk_itemtypes as $fk_itemtype) { - $label = sprintf( - __("%s by %s"), - $itemtype::getTypeName(Session::getPluralNumber()), - $fk_itemtype::getFieldLabel() - ); - - $cards["count_" . $itemtype . "_" . $fk_itemtype] = [ - 'widgettype' => ['summaryNumbers', 'multipleNumber', 'pie', 'donut', 'halfpie', 'halfdonut', 'bar', 'hbar'], - 'itemtype' => "\\Computer", - 'group' => __('Assets'), - 'label' => $label, - 'provider' => "Glpi\\Dashboard\\Provider::multipleNumber" . $itemtype . "By" . $fk_itemtype, - 'filters' => array_merge([ - 'dates', - 'dates_mod', - ], $add_filters_fct($itemtype::getTable())) - ]; - } - } - - $tickets_cases = [ - 'late' => __("Late tickets"), - 'waiting_validation' => __("Ticket waiting for your approval"), - 'notold' => __('Not solved tickets'), - 'incoming' => __("New tickets"), - 'waiting' => __('Pending tickets'), - 'assigned' => __('Assigned tickets'), - 'planned' => __('Planned tickets'), - 'solved' => __('Solved tickets'), - 'closed' => __('Closed tickets'), - ]; - foreach ($tickets_cases as $case => $label) { - $cards["bn_count_tickets_$case"] = [ - 'widgettype' => ["bigNumber"], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => sprintf(__("Number of %s"), $label), - 'provider' => "Glpi\\Dashboard\\Provider::nbTicketsGeneric", - 'args' => [ - 'case' => $case, - 'params' => [ - 'validation_check_user' => true, - ] - ], - 'cache' => false, - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - $cards["table_count_tickets_$case"] = [ - 'widgettype' => ["searchShowList"], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => sprintf(__("List of %s"), $label), - 'provider' => "Glpi\\Dashboard\\Provider::nbTicketsGeneric", - 'args' => [ - 'case' => $case, - 'params' => [ - 'validation_check_user' => true, - ] - ], - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - } - - // add specific ticket's cases - $cards["nb_opened_ticket"] = [ - 'widgettype' => ['line', 'area', 'bar'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => __("Number of tickets by month"), - 'provider' => "Glpi\\Dashboard\\Provider::ticketsOpened", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - $cards["ticket_evolution"] = [ - 'widgettype' => ['lines', 'areas', 'bars', 'stackedbars'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => __("Evolution of ticket in the past year"), - 'provider' => "Glpi\\Dashboard\\Provider::getTicketsEvolution", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - $cards["ticket_status"] = [ - 'widgettype' => ['lines', 'areas', 'bars', 'stackedbars'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => __("Tickets status by month"), - 'provider' => "Glpi\\Dashboard\\Provider::getTicketsStatus", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - $cards["ticket_times"] = [ - 'widgettype' => ['lines', 'areas', 'bars', 'stackedbars'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => __("Tickets times (in hours)"), - 'provider' => "Glpi\\Dashboard\\Provider::averageTicketTimes", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - $cards["tickets_summary"] = [ - 'widgettype' => ['summaryNumbers', 'multipleNumber', 'bar', 'hbar'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => __("Tickets summary"), - 'provider' => "Glpi\\Dashboard\\Provider::getTicketSummary", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - - foreach ([ - 'ITILCategory' => __("Top ticket's categories"), - 'Entity' => __("Top ticket's entities"), - 'RequestType' => __("Top ticket's request types"), - 'Location' => __("Top ticket's locations"), - ] as $itemtype => $label) { - $cards["top_ticket_$itemtype"] = [ - 'widgettype' => ['summaryNumbers', 'pie', 'donut', 'halfpie', 'halfdonut', 'multipleNumber', 'bar', 'hbar'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => $label, - 'provider' => "Glpi\\Dashboard\\Provider::multipleNumberTicketBy$itemtype", - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - } - - foreach ([ - 'user_requester' => __("Top ticket's requesters"), - 'group_requester' => __("Top ticket's requester groups"), - 'user_observer' => __("Top ticket's observers"), - 'group_observer' => __("Top ticket's observer groups"), - 'user_assign' => __("Top ticket's assignees"), - 'group_assign' => __("Top ticket's assignee groups"), - ] as $type => $label) { - $cards["top_ticket_$type"] = [ - 'widgettype' => ['pie', 'donut', 'halfpie', 'halfdonut', 'summaryNumbers', 'multipleNumber', 'bar', 'hbar'], - 'itemtype' => "\\Ticket", - 'group' => __('Assistance'), - 'label' => $label, - 'provider' => "Glpi\\Dashboard\\Provider::nbTicketsActor", - 'args' => [ - 'case' => $type, - ], - 'filters' => [ - 'dates', 'dates_mod', 'itilcategory', - 'group_tech', 'user_tech', 'requesttype', 'location' - ] - ]; - } - - $cards["RemindersList"] = [ - 'widgettype' => ["articleList"], - 'label' => __("List of reminders"), - 'group' => __('Tools'), - 'provider' => "Glpi\\Dashboard\\Provider::getArticleListReminder", - 'filters' => ['dates', 'dates_mod'] - ]; - - $cards["markdown_editable"] = [ - 'widgettype' => ["markdown"], - 'label' => __("Editable markdown card"), - 'group' => __('Others'), - 'card_options' => [ - 'content' => __("Toggle edit mode to edit content"), - ] - ]; - - $more_cards = Plugin::doHookFunction("dashboard_cards"); - if (is_array($more_cards)) { - $cards = array_merge($cards, $more_cards); - } - } - - return $cards; - } - - - function getRights($interface = 'central') { - return [ - READ => __('Read'), - UPDATE => __('Update'), - CREATE => __('Create'), - PURGE => [ - 'short' => __('Purge'), - 'long' => _x('button', 'Delete permanently') - ] - ]; - } - - - /** - * Save last dashboard viewed - * - * @param string $page current page - * @param string $dashboard current dashboard - * - * @return void - */ - function setLastDashboard(string $page = "", string $dashboard = "") { - $_SESSION['last_dashboards'][$page] = $dashboard; - } - - - /** - * Restore last viewed dashboard - * - * @return string the dashboard key - */ - function restoreLastDashboard():string { - global $CFG_GLPI; - $new_key = ""; - $target = Toolbox::cleanTarget($_REQUEST['_target'] ?? $_SERVER['REQUEST_URI'] ?? ""); - if (isset($_SESSION['last_dashboards']) && strlen($target) > 0) { - - $target = preg_replace('/^' . preg_quote($CFG_GLPI['root_doc'], '/') . '/', '', $target); - if (!isset($_SESSION['last_dashboards'][$target])) { - return ""; - } - - $new_key = $_SESSION['last_dashboards'][$target]; - $dashboard = new Dashboard($new_key); - if (!$dashboard->canViewCurrent()) { - return ""; - } - - $this->current = $new_key; - } - - return $new_key; - } - - - /** - * Retrieve the default dashboard for a specific menu entry - * First try from session - * then on config - * And Fallback on the first dashboard found - * - * @param string $menu - * @param bool $strict if true, do not provide a fallback - * - * @return string the dashboard key - */ - static function getDefaultDashboardForMenu(string $menu = "", bool $strict = false): string { - $grid = new self; - - if (!$strict) { - $restored = $grid->restoreLastDashboard(); - if (strlen($restored) > 0) { - return $restored; - } - } - - $config_key = 'default_dashboard_'.$menu; - $default = $_SESSION["glpi$config_key"] ?? ""; - if (strlen($default)) { - $dasboard = new Dashboard($default); - - if ($dasboard->load() && $dasboard->canViewCurrent()) { - return $default; - } - } - - // if default not found, return first dashboards - if (!$strict) { - self::loadAllDashboards(); - $first_dashboard = array_shift(self::$all_dashboards); - if (isset($first_dashboard['key'])) { - return $first_dashboard['key']; - } - } - - return ""; - } - - - static function dropdownDashboard(string $name = "", array $params = []): string { - self::loadAllDashboards(); - $can_view_all = $params['can_view_all'] ?? false; - - $options_dashboards = []; - foreach (self::$all_dashboards as $key => $dashboard) { - if ($can_view_all - || self::canViewSpecificicDashboard($key)) { - $options_dashboards[$key] = $dashboard['name'] ?? $key;; - } - } - - return Dropdown::showFromArray($name, $options_dashboards, $params); - } -} diff --git a/inc/dashboard/item.class.php b/inc/dashboard/item.class.php deleted file mode 100644 index afcf7f81ab..0000000000 --- a/inc/dashboard/item.class.php +++ /dev/null @@ -1,135 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -class Item extends \CommonDBChild { - static public $itemtype = "Glpi\\Dashboard\\Dashboard"; - static public $items_id = 'dashboards_dashboards_id'; - - // prevent bad getFromDB when bootstraping tests suite - static public $mustBeAttached = false; - - /** - * Return items for the provided dashboard - * - * @param int $dashboards_id - * - * @return array the items - */ - static function getForDashboard(int $dashboards_id = 0): array { - global $DB; - - $di_iterator = $DB->request([ - 'FROM' => self::getTable(), - 'WHERE' => [ - 'dashboards_dashboards_id' => $dashboards_id - ] - ]); - - $items = []; - foreach ($di_iterator as $item) { - unset($item['id']); - $item['card_options'] = importArrayFromDB($item['card_options']); - $items[] = $item; - } - - return $items; - } - - - /** - * Save items in DB for the provided dashboard - * - * @param int $dashboards_id id (not key) of the 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 - */ - static function addForDashboard(int $dashboards_id = 0, array $items = []) { - global $DB, $_UREQUEST; - - $query_items = $DB->buildInsert( - self::getTable(), - [ - 'dashboards_dashboards_id' => new \QueryParam(), - 'gridstack_id' => new \QueryParam(), - 'card_id' => new \QueryParam(), - 'x' => new \QueryParam(), - 'y' => new \QueryParam(), - 'width' => new \QueryParam(), - 'height' => new \QueryParam(), - 'card_options' => new \QueryParam(), - ] - ); - $stmt = $DB->prepare($query_items); - foreach ($items as $item_key => $item) { - // card_options should be unescaped as they will be json_encoded after - $card_options = $_UREQUEST['items'][$item_key]['card_options'] ?? $item['card_options'] ?? []; - - // clean - unset( - $card_options['force'], - $card_options['card_id'], - $card_options['gridstack_id'] - ); - - // encode for DB - $card_options = exportArrayToDB($card_options); - $gridstack_id = $item['gridstack_id'] ?? $item['gs_id']; - - $stmt->bind_param( - 'issiiiis', - $dashboards_id, - $gridstack_id, - $item['card_id'], - $item['x'], - $item['y'], - $item['width'], - $item['height'], - $card_options - ); - $stmt->execute(); - } - } -} diff --git a/inc/dashboard/provider.class.php b/inc/dashboard/provider.class.php deleted file mode 100644 index 7f3327f394..0000000000 --- a/inc/dashboard/provider.class.php +++ /dev/null @@ -1,1601 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -use CommonGLPI; -use DBConnection; -use QueryExpression; -use Change; -use CommonITILActor; -use CommonITILValidation; -use CommonTreeDropdown; -use CommonDBTM; -use CommonITILObject; -use Group; -use Group_Ticket; -use Problem; -use QuerySubQuery; -use Session; -use Search; -use Stat; -use Ticket; -use Ticket_User; -use Toolbox; -use User; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -/** - * Provider class -**/ -class Provider extends CommonGLPI { - - - /** - * Retrieve the number of element for a given item - * - * @param CommonDBTM|null object to count - * - * @param array $params default values for - * - 'apply_filters' values from dashboard filters - * - * @return array : - * - 'number' - * - 'url' - * - 'label' - * - 'icon' - */ - static function bigNumberItem(CommonDBTM $item = null, array $params = []): array { - $DB = DBConnection::getReadConnection(); - - $default_params = [ - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $i_table = $item::getTable(); - - $where = []; - if (isset($item->fields['is_deleted'])) { - $where['is_deleted'] = 0; - } - - if (isset($item->fields['is_template'])) { - $where['is_template'] = 0; - } - - if ($item->isEntityAssign()) { - $where += getEntitiesRestrictCriteria($item::getTable()); - } - - $criteria = array_merge_recursive( - [ - 'SELECT' => ['COUNT DISTINCT' => $item::getTableField($item::getIndexName()) . ' as cpt'], - 'FROM' => $i_table, - 'WHERE' => $where - ], - self::getFiltersCriteria($i_table, $params['apply_filters']), - $item instanceof Ticket ? Ticket::getCriteriaFromProfile() : [] - ); - $iterator = $DB->request($criteria); - - $result = $iterator->next(); - $nb_items = $result['cpt']; - - $search_criteria = [ - [ - self::getSearchFiltersCriteria($i_table, $params['apply_filters']) - ] - ]; - - $url = $item::getSearchURL()."?".Toolbox::append_params([ - $search_criteria, - 'reset' => 'reset', - ]); - - return [ - 'number' => $nb_items, - 'url' => $url, - 'label' => $item::getTypeName($nb_items), - 'icon' => $item::getIcon(), - ]; - } - - - /** - * @method self::bigNumberItem - * @method self::nbItemByFk - */ - public static function __callStatic(string $name = "", array $arguments = []) { - if (strpos($name, 'bigNumber') !== false) { - $itemtype = str_replace('bigNumber', '', $name); - if (is_subclass_of($itemtype, 'CommonDBTM')) { - $item = new $itemtype; - $item->getEmpty(); - return self::bigNumberItem($item, $arguments[0] ?? []); - } - } - - if (strpos($name, 'multipleNumber') !== false - && strpos($name, 'By') !== false) { - $tmp = str_replace('multipleNumber', '', $name); - $tmp = explode('By', $tmp); - - if (count($tmp) === 2) { - $itemtype = $tmp[0]; - $fk_itemtype = $tmp[1]; - - return self::nbItemByFk( - new $itemtype, - new $fk_itemtype, - $arguments[0] ?? [] - ); - } - } - - if (strpos($name, 'getArticleList') !== false) { - $itemtype = str_replace('getArticleList', '', $name); - if (is_subclass_of($itemtype, 'CommonDBTM')) { - $item = new $itemtype; - $item->getEmpty(); - return self::articleListItem($item, $arguments[0] ?? []); - } - } - } - - - /** - * Count number of tickets for a given case - * - * @param string $case: - * - 'notold': not closed or solved tickets - * - 'late': late tickets - * - 'waiting_validation': tickets waiting validation for connected user - * - 'incoming': ticket with incoming status - * - 'waiting': ticket with waiting status - * - 'assigned': ticket with assigned status - * - 'planned': ticket with planned status - * - 'solved': ticket with solved status - * - 'closed': ticket with closed status - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array : - * - 'number' - * - 'url' - * - 'label' - * - 'icon' - */ - static function nbTicketsGeneric( - string $case = "", - array $params = [] - ):array { - $DBread = DBConnection::getReadConnection(); - - $default_params = [ - 'label' => "", - 'icon' => Ticket::getIcon(), - 'apply_filters' => [], - 'validation_check_user' => false, - ]; - $params = array_merge($default_params, $params); - - $nb_tickets = 0; - $query_criteria = []; - $search_criteria = []; - $skip = false; - - $notold = [ - 'field' => 12, - 'searchtype' => 'equals', - 'value' => 'notold', - ]; - - $table = Ticket::getTable(); - $query_criteria = [ - 'SELECT' => [ - 'COUNT DISTINCT' => "$table.id AS cpt", - ], - 'FROM' => $table, - 'WHERE' => [ - "$table.is_deleted" => 0, - ] + getEntitiesRestrictCriteria($table), - ]; - - $query_criteria = array_merge_recursive( - $query_criteria, - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($table, $params['apply_filters']) - ); - - switch ($case) { - case 'notold': - $search_criteria = [$notold]; - $query_criteria['WHERE']+= [ - "$table.status" => Ticket::getNotSolvedStatusArray(), - ]; - break; - - case 'late': - $params['icon'] = "far fa-clock"; - $params['label'] = __("Late tickets"); - $search_criteria = array_merge([$notold], [ - [ - 'link' => 'AND', - 'criteria' => [ - [ - 'field' => 82, - 'searchtype' => 'equals', - 'value' => 1, - ], [ - 'link' => 'OR', - 'field' => 182, - 'searchtype' => 'equals', - 'value' => 1, - ], [ - 'link' => 'OR', - 'field' => 159, - 'searchtype' => 'equals', - 'value' => 1, - ], [ - 'link' => 'OR', - 'field' => 187, - 'searchtype' => 'equals', - 'value' => 1, - ] - ] - ] - ]); - $query_criteria['WHERE']["$table.status"] = Ticket::getNotSolvedStatusArray(); - $query_criteria['WHERE'][] = [ - 'OR' => [ - CommonITILObject::generateSLAOLAComputation('time_to_resolve', 'glpi_tickets'), - CommonITILObject::generateSLAOLAComputation('internal_time_to_resolve', 'glpi_tickets'), - CommonITILObject::generateSLAOLAComputation('time_to_own', 'glpi_tickets'), - CommonITILObject::generateSLAOLAComputation('internal_time_to_own', 'glpi_tickets'), - ] - ]; - break; - - case 'waiting_validation': - $params['icon'] = "far fa-eye"; - $params['label'] = __("Tickets waiting for validation"); - $search_criteria = [ - [ - 'field' => 55, - 'searchtype' => 'equals', - 'value' => CommonITILValidation::WAITING, - ] - ]; - - if ($params['validation_check_user']) { - $search_criteria[] = [ - 'link' => 'AND', - 'field' => 59, - 'searchtype' => 'equals', - 'value' => Session::getLoginUserID(), - ]; - } - - $where = [ - 'glpi_ticketvalidations.status' => CommonITILValidation::WAITING, - ]; - - if ($params['validation_check_user']) { - $where['glpi_ticketvalidations.users_id_validate'] = Session::getLoginUserID(); - } - - $query_criteria = array_merge_recursive($query_criteria, [ - 'LEFT JOIN' => [ - 'glpi_ticketvalidations' => [ - 'ON' => [ - 'glpi_ticketvalidations' => 'tickets_id', - $table => 'id' - ] - ] - ], - 'WHERE' => $where, - ]); - break; - - // Statuses speciale cases (no break) - case 'incoming': - $status = Ticket::INCOMING; - $params['icon'] = Ticket::getIcon(); - $params['label'] = __("Incoming tickets"); - $skip = true; - case 'waiting': - if (!$skip) { - $status =Ticket::WAITING; - $params['icon'] = "fas fa-pause-circle"; - $params['label'] = __("Pending tickets"); - $skip = true; - } - case 'assigned': - if (!$skip) { - $status = Ticket::ASSIGNED; - $params['icon'] = "fas fa-users"; - $params['label'] = __("Assigned tickets"); - $skip = true; - } - case 'planned': - if (!$skip) { - $status = Ticket::PLANNED; - $params['icon'] = "fas fa-calendar-check"; - $params['label'] = __("Planned tickets"); - $skip = true; - } - case 'solved': - if (!$skip) { - $status = Ticket::SOLVED; - $params['icon'] = "far fa-check-square"; - $params['label'] = __("Solved tickets"); - $skip = true; - } - case 'closed': - if (!$skip) { - $status = Ticket::CLOSED; - $params['icon'] = "fas fa-archive"; - $params['label'] = __("Closed tickets"); - $skip = true; - } - case 'status': - if (!$skip) { - $status = Ticket::INCOMING; - } - $search_criteria = [ - [ - 'field' => 12, - 'searchtype' => 'equals', - 'value' => $status, - ] - ]; - $query_criteria = array_merge_recursive($query_criteria, [ - 'WHERE' => [ - "$table.status" => $status, - ] - ]); - break; - } - - $search_criteria['criteria'] = self::getSearchFiltersCriteria($table, $params['apply_filters']); - - $url = Ticket::getSearchURL()."?".Toolbox::append_params([ - 'criteria' => $search_criteria, - 'reset' => 'reset' - ]); - - $iterator = $DBread->request($query_criteria); - $result = $iterator->next(); - $nb_tickets = $result['cpt']; - - return [ - 'number' => $nb_tickets, - 'url' => $url, - 'label' => $params['label'], - 'icon' => $params['icon'], - 's_criteria' => $search_criteria, - 'itemtype' => 'Ticket', - ]; - } - - - /** - * Get multiple counts of computer by a specific foreign key - * - * @param CommonDBTM $item main item to count - * @param CommonDBTM $fk_item groupby by this item (we will find the foreign key in the main item) - * @param array $params values for: - * - 'title' of the card - * - 'icon' of the card - * - 'searchoption_id' id corresponding to FK search option - * - 'limit' max data to return - * - 'join_key' LEFT, INNER, etc JOIN - * - 'apply_filters' values from dashboard filters - * - * @return array : - * - 'data': [ - * 'url' - * 'number' - * 'label' - * ] - * - 'label' - * - 'icon' - */ - public static function nbItemByFk( - CommonDBTM $item = null, - CommonDBTM $fk_item = null, - array $params = [] - ): array { - $DB = DBConnection::getReadConnection(); - - $c_table = $item::getTable(); - $fk_table = $fk_item::getTable(); - $fk_itemtype = $fk_item::getType(); - - // try to autodetect searchoption id - $searchoptions = $item->rawSearchOptions(); - $found_so = array_filter($searchoptions, function($searchoption) use($fk_table) { - return isset($searchoption['table']) && $searchoption['table'] === $fk_table; - }); - $found_so = array_shift($found_so); - $found_so_id = $found_so['id'] ?? 0; - - $default_params = [ - 'label' => "", - 'searchoption_id' => $found_so_id, - 'icon' => $fk_item::getIcon() ?? $item::getIcon(), - 'limit' => 50, - 'join_key' => 'LEFT JOIN', - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $where = []; - if ($item->maybeDeleted()) { - $where["$c_table.is_deleted"] = 0; - } - if ($item->maybeTemplate()) { - $where["$c_table.is_template"] = 0; - } - - $name = 'name'; - if ($fk_item instanceof CommonTreeDropdown) { - $name = 'completename'; - } - - if ($item->isEntityAssign()) { - $where += getEntitiesRestrictCriteria($c_table, '', '', $item->maybeRecursive()); - } - - $criteria = array_merge_recursive( - [ - 'SELECT' => [ - "$fk_table.$name AS fk_name", - "$fk_table.id AS fk_id", - 'COUNT DISTINCT' => "$c_table.id AS cpt", - ], - 'FROM' => $c_table, - $params['join_key'] => [ - $fk_table => [ - 'ON' => [ - $fk_table => 'id', - $c_table => getForeignKeyFieldForItemType($fk_itemtype), - ] - ] - ], - 'GROUPBY' => "$fk_table.$name", - 'ORDERBY' => "cpt DESC", - 'LIMIT' => $params['limit'], - ], - count($where) ? ['WHERE' => $where] : [], - self::getFiltersCriteria($c_table, $params['apply_filters']), - $item instanceof Ticket ? Ticket::getCriteriaFromProfile() : [] - ); - $iterator = $DB->request($criteria); - - $search_criteria = [ - 'criteria' => [ - [ - 'field' => $params['searchoption_id'], - 'searchtype' => 'equals', - 'value' => 0 - ], - self::getSearchFiltersCriteria($fk_table, $params['apply_filters']) - ], - 'reset' => 'reset', - ]; - - $url = $item::getSearchURL(); - $url .= (strpos($url, '?') !== false ? '&' : '?') . 'reset'; - - $data = []; - foreach ($iterator as $result) { - $search_criteria['criteria'][0]['value'] = $result['fk_id'] ?? 0; - $data[] = [ - 'number' => $result['cpt'], - 'label' => $result['fk_name'] ?? __("without"), - 'url' => $url . '&' . Toolbox::append_params($search_criteria), - ]; - } - - if (count($data) === 0) { - $data = [ - 'nodata' => true - ]; - } - - return [ - 'data' => $data, - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * Get a list of article for an compatible item (with date,name,text fields) - * - * @param CommonDBTM $item the itemtype to list - * @param array $params default values for - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function articleListItem(CommonDBTM $item = null, array $params = []): array { - $DB = DBConnection::getReadConnection(); - - $default_params = [ - 'icon' => $item::getIcon(), - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $i_table = $item::getTable(); - $criteria = array_merge_recursive( - [ - 'SELECT' => "$i_table.*", - 'FROM' => $i_table - ], - self::getFiltersCriteria($i_table, $params['apply_filters']) - ); - $iterator = $DB->request($criteria); - - $data = []; - foreach ($iterator as $line) { - $data[] = [ - 'date' => $line['date'] ?? '', - 'label' => $line['name'] ?? '', - 'content' => $line['text'] ?? '', - 'author' => User::getFriendlyNameById($line['users_id'] ?? 0), - 'url' => $item::getFormURLWithID($line['id']), - ]; - } - - $nb_items = count($data); - if ($nb_items === 0) { - $data = [ - 'nodata' => true - ]; - } - - return [ - 'data' => $data, - 'number' => $nb_items, - 'url' => $item::getSearchURL(), - 'label' => $item::getTypeName($nb_items), - 'icon' => $item::getIcon(), - ]; - } - - - /** - * get multiple count of ticket by month - * - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function ticketsOpened(array $params = []): array { - $DB = DBConnection::getReadConnection(); - $default_params = [ - 'label' => "", - 'icon' => Ticket::getIcon(), - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $t_table = Ticket::getTable(); - $criteria = array_merge_recursive( - [ - 'SELECT' => [ - 'COUNT DISTINCT' => "$t_table.id as nb_tickets", - new QueryExpression("DATE_FORMAT(".$DB->quoteName("date").", '%Y-%m') AS ticket_month") - ], - 'FROM' => $t_table, - 'GROUPBY' => 'ticket_month', - 'ORDER' => 'ticket_month ASC' - ], - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($t_table, $params['apply_filters']) - ); - $iterator = $DB->request($criteria); - - $s_criteria = [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 15, - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 15, - 'searchtype' => 'lessthan', - 'value' => null - ], - ], - 'reset' => 'reset' - ]; - - $data = []; - foreach ($iterator as $result) { - list($start_day, $end_day) = self::formatMonthyearDates($result['ticket_month']); - - $s_criteria['criteria'][0]['value'] = $start_day; - $s_criteria['criteria'][1]['value'] = $end_day; - - $data[] = [ - 'number' => $result['nb_tickets'], - 'label' => $result['ticket_month'], - 'url' => Ticket::getSearchURL()."?".Toolbox::append_params($s_criteria), - ]; - } - - return [ - 'data' => $data, - 'distributed' => false, - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * Get ticket evolution by opened, solved, closed, late series and months group - * - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function getTicketsEvolution(array $params = []): array { - $default_params = [ - 'label' => "", - 'icon' => Ticket::getIcon(), - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $year = date("Y")-15; - $begin = date("Y-m-d", mktime(1, 0, 0, (int)date("m"), (int)date("d"), $year)); - $end = date("Y-m-d"); - - if (isset($params['apply_filters']['dates']) - && count($params['apply_filters']['dates']) == 2) { - $begin = date("Y-m-d", strtotime($params['apply_filters']['dates'][0])); - $end = date("Y-m-d", strtotime($params['apply_filters']['dates'][1])); - unset($params['apply_filters']['dates']); - } - - $t_table = Ticket::getTable(); - - $series = [ - - 'inter_total' => [ - 'name' => _nx('ticket', 'Opened', 'Opened', \Session::getPluralNumber()), - 'search' => [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 15, // creation date - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 15, // creation date - 'searchtype' => 'lessthan', - 'value' => null - ],self::getSearchFiltersCriteria($t_table, $params['apply_filters']), - ], - 'reset' => 'reset' - ] - ], - 'inter_solved' => [ - 'name' => _nx('ticket', 'Solved', 'Solved', \Session::getPluralNumber()), - 'search' => [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 17, // solve date - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 17, // solve date - 'searchtype' => 'lessthan', - 'value' => null - ],self::getSearchFiltersCriteria($t_table, $params['apply_filters']), - ], - 'reset' => 'reset' - ] - ], - 'inter_solved_late' => [ - 'name' => __('Late'), - 'search' => [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 17, // solve date - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 17, // solve date - 'searchtype' => 'lessthan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 82, // time_to_resolve exceed solve date - 'searchtype' => 'equals', - 'value' => 1 - ],self::getSearchFiltersCriteria($t_table, $params['apply_filters']), - ], - 'reset' => 'reset' - ] - ], - 'inter_closed' => [ - 'name' => __('Closed'), - 'search' => [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 16, // close date - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 16, // close date - 'searchtype' => 'lessthan', - 'value' => null - ],self::getSearchFiltersCriteria($t_table, $params['apply_filters']), - ], - 'reset' => 'reset' - ] - ], - ]; - - $filters = array_merge_recursive( - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($t_table, $params['apply_filters']) - ); - - $i = 0; - $monthsyears = []; - foreach ($series as $stat_type => &$serie) { - $values = Stat::constructEntryValues( - 'Ticket', - $stat_type, - $begin, - $end, - "", - "", - "", - $filters - ); - - if ($i === 0) { - $monthsyears = array_keys($values); - } - $values = array_values($values); - - foreach ($values as $index => $number) { - $current_monthyear = $monthsyears[$index]; - list($start_day, $end_day) = self::formatMonthyearDates($current_monthyear); - $serie['search']['criteria'][0]['value'] = $start_day; - $serie['search']['criteria'][1]['value'] = $end_day; - - $serie['data'][$index] = [ - 'value' => $number, - 'url' => Ticket::getSearchURL()."?".Toolbox::append_params($serie['search']), - ]; - } - - $i++; - } - - return [ - 'data' => [ - 'labels' => $monthsyears, - 'series' => array_values($series), - ], - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * get ticket by their curent status and their opening date - * - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function getTicketsStatus(array $params = []): array { - $DB = DBConnection::getReadConnection(); - - $default_params = [ - 'label' => "", - 'icon' => Ticket::getIcon(), - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $statuses = Ticket::getAllStatusArray(); - $t_table = Ticket::getTable(); - - $sub_query = array_merge_recursive( - [ - 'DISTINCT' => true, - 'SELECT' => ["$t_table.*"], - 'FROM' => $t_table, - 'WHERE' => [ - "$t_table.is_deleted" => 0, - ] + getEntitiesRestrictCriteria($t_table), - ], - // limit count for profiles with limited rights - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($t_table, $params['apply_filters']) - ); - - $criteria = [ - 'SELECT' => [ - new QueryExpression( - "FROM_UNIXTIME(UNIX_TIMESTAMP(".$DB->quoteName("{$t_table}_distinct.date")."),'%Y-%m') AS period" - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::INCOMING.", 1, 0)) - as ".$DB->quoteValue(_x('status', 'New')) - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::ASSIGNED.", 1, 0)) - as ".$DB->quoteValue(_x('status', 'Processing (assigned)')) - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::PLANNED.", 1, 0)) - as ".$DB->quoteValue(_x('status', 'Processing (planned)')) - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::WAITING.", 1, 0)) - as ".$DB->quoteValue(__('Pending')) - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::SOLVED.", 1, 0)) - as ".$DB->quoteValue(_x('status', 'Solved')) - ), - new QueryExpression( - "SUM(IF({$t_table}_distinct.status = ".Ticket::CLOSED.", 1, 0)) - as ".$DB->quoteValue(_x('status', 'Closed')) - ), - ], - 'FROM' => new QuerySubQuery($sub_query, "{$t_table}_distinct"), - 'ORDER' => 'period ASC', - 'GROUP' => ['period'] - ]; - - $iterator = $DB->request($criteria); - - $s_criteria = [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => 12, // status - 'searchtype' => 'equals', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 15, // creation date - 'searchtype' => 'morethan', - 'value' => null - ], [ - 'link' => 'AND', - 'field' => 15, // creation date - 'searchtype' => 'lessthan', - 'value' => null - ], - self::getSearchFiltersCriteria($t_table, $params['apply_filters']) - ], - 'reset' => 'reset' - ]; - - $data = [ - 'labels' => [], - 'series' => [] - ]; - foreach ($iterator as $result) { - list($start_day, $end_day) = self::formatMonthyearDates($result['period']); - $s_criteria['criteria'][1]['value'] = $start_day; - $s_criteria['criteria'][2]['value'] = $end_day; - - $data['labels'][] = $result['period']; - $tmp = $result; - unset($tmp['period'], $tmp['nb_tickets']); - - $i = 0; - foreach ($tmp as $label2 => $value) { - $status_key = array_search($label2, $statuses); - $s_criteria['criteria'][0]['value'] = $status_key; - - $data['series'][$i]['name'] = $label2; - $data['series'][$i]['data'][] = [ - 'value' => (int) $value, - 'url' => Ticket::getSearchURL()."?".Toolbox::append_params($s_criteria), - ]; - $i++; - } - } - - return [ - 'data' => $data, - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * Get numbers of tickets grouped by actors - * - * @param string $case cound be: - * - user_requester - * - group_requester - * - user_observer - * - group_observer - * - user_assign - * - group_assign - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function nbTicketsActor( - string $case = "", - array $params = [] - ):array { - $DBread = DBConnection::getReadConnection(); - $default_params = [ - 'label' => "", - 'icon' => null, - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $t_table = Ticket::getTable(); - $li_table = Ticket_User::getTable(); - $ug_table = User::getTable(); - $n_fields = [ - "$ug_table.firstname as first", - "$ug_table.realname as second", - ]; - - $where = [ - "$t_table.is_deleted" => 0, - ]; - - $case_array = explode('_', $case); - if ($case_array[0] == 'user') { - $where["$ug_table.is_deleted"] = 0; - $params['icon'] = $params['icon'] ?? User::getIcon(); - } else if ($case_array[0] == 'group') { - $li_table = Group_Ticket::getTable(); - $ug_table = Group::getTable(); - $n_fields = [ - "$ug_table.completename as first" - ]; - $params['icon'] = $params['icon'] ?? Group::getIcon(); - } - - $type = 0; - switch ($case) { - case "user_requester": - $type = CommonITILActor::REQUESTER; - $soption = 4; - break; - case "group_requester": - $type = CommonITILActor::REQUESTER; - $soption = 71; - break; - case "user_observer": - $type = CommonITILActor::OBSERVER; - $soption = 66; - break; - case "group_observer": - $type = CommonITILActor::OBSERVER; - $soption = 65; - break; - case "user_assign": - $type = CommonITILActor::ASSIGN; - $soption = 5; - break; - case "group_assign": - $type = CommonITILActor::ASSIGN; - $soption = 8; - break; - } - - $criteria = array_merge_recursive( - [ - 'SELECT' => array_merge([ - 'COUNT DISTINCT' => "$t_table.id AS nb_tickets", - "$ug_table.id as actor_id", - ], $n_fields), - 'FROM' => $t_table, - 'INNER JOIN' => [ - $li_table => [ - 'ON' => [ - $li_table => getForeignKeyFieldForItemType("Ticket"), - $t_table => 'id', - [ - 'AND' => [ - "$li_table.type" => $type - ] - ] - ] - ], - $ug_table => [ - 'ON' => [ - $li_table => getForeignKeyFieldForTable($ug_table), - $ug_table => 'id' - ] - ] - ], - 'GROUPBY' => "$ug_table.id", - 'ORDER' => 'nb_tickets DESC', - 'WHERE' => $where + getEntitiesRestrictCriteria($t_table), - ], - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($t_table, $params['apply_filters']) - ); - $iterator = $DBread->request($criteria); - - $s_criteria = [ - 'criteria' => [ - [ - 'link' => 'AND', - 'field' => $soption, - 'searchtype' => 'equals', - 'value' => null - ], - self::getSearchFiltersCriteria($t_table, $params['apply_filters']), - ], - 'reset' => 'reset' - ]; - $data = []; - foreach ($iterator as $result) { - $s_criteria['criteria'][0]['value'] = $result['actor_id']; - $data[] = [ - 'number' => $result['nb_tickets'], - 'label' => $result['first']." ".($result['second'] ?? ""), - 'url' => Ticket::getSearchURL()."?".Toolbox::append_params($s_criteria), - ]; - } - return [ - 'data' => $data, - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * get average stats (takeintoaccoutn, solve/close delay, waiting) of ticket by month - * - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function averageTicketTimes(array $params = []) { - $DBread = DBConnection::getReadConnection(); - $default_params = [ - 'label' => "", - 'icon' => "fas fa-stopwatch", - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $t_table = Ticket::getTable(); - $criteria = array_merge_recursive( - [ - 'SELECT' => [ - new QueryExpression("DATE_FORMAT(".$DBread->quoteName("date").", '%Y-%m') AS period"), - new QueryExpression("AVG(".$DBread->quoteName("takeintoaccount_delay_stat").") AS avg_takeintoaccount_delay_stat"), - new QueryExpression("AVG(".$DBread->quoteName("waiting_duration").") AS avg_waiting_duration"), - new QueryExpression("AVG(".$DBread->quoteName("solve_delay_stat").") AS avg_solve_delay_stat"), - new QueryExpression("AVG(".$DBread->quoteName("close_delay_stat").") AS close_delay_stat"), - ], - 'FROM' => $t_table, - 'WHERE' => [ - 'is_deleted' => 0, - ] + getEntitiesRestrictCriteria($t_table), - 'ORDER' => 'period ASC', - 'GROUP' => ['period'] - ], - Ticket::getCriteriaFromProfile(), - self::getFiltersCriteria($t_table, $params['apply_filters']) - ); - $iterator = $DBread->request($criteria); - - $data = [ - 'labels' => [], - 'series' => [ - [ - 'name' => __("Time to own"), - 'data' => [] - ], [ - 'name' => __("Waiting time"), - 'data' => [] - ], [ - 'name' => __("Time to resolve"), - 'data' => [] - ], [ - 'name' => __("Time to close"), - 'data' => [] - ] - ] - ]; - foreach ($iterator as $r) { - $data['labels'][] = $r['period']; - $tmp = $r; - unset($tmp['period']); - - $data['series'][0]['data'][] = round($r['avg_takeintoaccount_delay_stat'] / HOUR_TIMESTAMP, 1); - $data['series'][1]['data'][] = round($r['avg_waiting_duration'] / HOUR_TIMESTAMP, 1); - $data['series'][2]['data'][] = round($r['avg_solve_delay_stat'] / HOUR_TIMESTAMP, 1); - $data['series'][3]['data'][] = round($r['close_delay_stat'] / HOUR_TIMESTAMP, 1); - } - - return [ - 'data' => $data, - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - /** - * get multiple count of ticket by status and month - * - * @param array $params default values for - * - 'title' of the card - * - 'icon' of the card - * - 'apply_filters' values from dashboard filters - * - * @return array - */ - public static function getTicketSummary(array $params = []) { - $default_params = [ - 'label' => "", - 'icon' => "", - 'apply_filters' => [], - ]; - $params = array_merge($default_params, $params); - - $incoming = self::nbTicketsGeneric('incoming', $params); - $assigned = self::nbTicketsGeneric('assigned', $params); - $waiting = self::nbTicketsGeneric('waiting', $params); - $tovalidate = self::nbTicketsGeneric('waiting_validation', $params); - $closed = self::nbTicketsGeneric('closed', $params); - $solved = self::nbTicketsGeneric('solved', $params); - - return [ - 'data' => [ - [ - 'number' => $incoming['number'], - 'label' => __("New"), - 'url' => $incoming['url'], - 'color' => '#3bc519', - ], [ - 'number' => $assigned['number'], - 'label' => __("Assigned"), - 'url' => $assigned['url'], - 'color' => '#f1cd29', - ], [ - 'number' => $waiting['number'], - 'label' => __("Pending"), - 'url' => $waiting['url'], - 'color' => '#f1a129', - ], [ - 'number' => $tovalidate['number'], - 'label' => __("To validate"), - 'url' => $tovalidate['url'], - 'color' => '#266ae9', - ], [ - 'number' => $solved['number'], - 'label' => __("Solved"), - 'url' => $solved['url'], - 'color' => '#edc949', - ], [ - 'number' => $closed['number'], - 'label' => __("Closed"), - 'url' => $closed['url'], - 'color' => '#555555', - ] - ], - 'label' => $params['label'], - 'icon' => $params['icon'], - ]; - } - - - public static function formatMonthyearDates(string $monthyear): array { - $rawdate = explode('-', $monthyear); - $year = $rawdate[0]; - $month = $rawdate[1]; - $monthtime = mktime(0, 0, 0, $month, 1, $year); - - $start_day = date("Y-m-d H:i:s", strtotime("first day of this month", $monthtime)); - $end_day = date("Y-m-d H:i:s", strtotime("first day of next month", $monthtime)); - - return [$start_day, $end_day]; - } - - private static function getSearchOptionID(string $table, string $name, string $tableToSearch): int { - $data = Search::getOptions(getItemTypeForTable($table), true); - $sort = []; - foreach ($data as $ref => $opt) { - if (isset($opt['field'])) { - $sort[$ref] = $opt['linkfield']."-".$opt['table']; - } - } - return array_search($name."-".$tableToSearch, $sort); - } - - final public static function getSearchFiltersCriteria(string $table = "", array $apply_filters = []) { - $DB = DBConnection::getReadConnection(); - $s_criteria = []; - - if ($DB->fieldExists($table, 'date') - && isset($apply_filters['dates']) - && count($apply_filters['dates']) == 2) { - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date", $table), $apply_filters['dates'], 'begin'); - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date", $table), $apply_filters['dates'], 'end'); - } - - //exclude itilobject already processed with 'date' - if (!in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ]) && $DB->fieldExists($table, 'date_creation') - && isset($apply_filters['dates']) - && count($apply_filters['dates']) == 2) { - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date_creation", $table), $apply_filters['dates'], 'begin'); - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date_creation", $table), $apply_filters['dates'], 'end'); - } - - if ($DB->fieldExists($table, 'date_mod') - && isset($apply_filters['dates_mod']) - && count($apply_filters['dates_mod']) == 2) { - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date_mod", $table), $apply_filters['dates_mod'], 'begin'); - $s_criteria['criteria'][] = self::getDatesSearchCriteria(self::getSearchOptionID($table, "date_mod", $table), $apply_filters['dates_mod'], 'end'); - } - - if ($DB->fieldExists($table, 'itilcategories_id') - && isset($apply_filters['itilcategory']) - && (int) $apply_filters['itilcategory'] > 0) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'itilcategories_id', 'glpi_itilcategories'), // itilcategory - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['itilcategory'] - ]; - } - - if ($DB->fieldExists($table, 'requesttypes_id') - && isset($apply_filters['requesttype']) - && (int) $apply_filters['requesttype'] > 0) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'requesttypes_id', 'glpi_requesttypes'), // request type - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['requesttype'] - ]; - } - - if ($DB->fieldExists($table, 'locations_id') - && isset($apply_filters['location']) - && (int) $apply_filters['location'] > 0) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'locations_id', 'glpi_locations'), // location - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['location'] - ]; - } - - if ($DB->fieldExists($table, 'manufacturers_id') - && isset($apply_filters['manufacturer']) - && (int) $apply_filters['manufacturer'] > 0) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'manufacturers_id', 'glpi_manufacturers'), // manufacturer - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['manufacturer'] - ]; - } - - if (isset($apply_filters['group_tech'])) { - - $groups_id = null; - if ((int) $apply_filters['group_tech'] > 0) { - $groups_id = (int) $apply_filters['group_tech']; - } else if ((int) $apply_filters['group_tech'] == -1) { - $groups_id = 'mygroups'; - } - - if ($groups_id != null) { - if ($DB->fieldExists($table, 'groups_id_tech')) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'groups_id_tech', 'glpi_groups'), // group tech - 'searchtype' => 'equals', - 'value' => $groups_id - ]; - } else if (in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ])) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => 8, // group tech - 'searchtype' => 'equals', - 'value' => $groups_id - ]; - } - } - } - - if (isset($apply_filters['user_tech']) - && (int) $apply_filters['user_tech'] > 0) { - if ($DB->fieldExists($table, 'users_id_tech')) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => self::getSearchOptionID($table, 'users_id_tech', 'glpi_users'),// tech - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['user_tech'] - ]; - } else if (in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ])) { - $s_criteria['criteria'][] = [ - 'link' => 'AND', - 'field' => 5,// tech - 'searchtype' => 'equals', - 'value' => (int) $apply_filters['user_tech'] - ]; - } - } - - return $s_criteria; - } - - private static function getFiltersCriteria(string $table = "", array $apply_filters = []) { - $DB = DBConnection::getReadConnection(); - - $where = []; - $join = []; - - if (($DB->fieldExists($table, 'date')) - && isset($apply_filters['dates']) - && count($apply_filters['dates']) == 2) { - $where += self::getDatesCriteria("$table.date", $apply_filters['dates']); - } - - //exclude itilobject already processed with 'date' - if ((!in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ]) && $DB->fieldExists($table, 'date_creation')) - && isset($apply_filters['dates']) - && count($apply_filters['dates']) == 2) { - $where += self::getDatesCriteria("$table.date_creation", $apply_filters['dates']); - } - - if ($DB->fieldExists($table, 'date_mod') - && isset($apply_filters['dates_mod']) - && count($apply_filters['dates_mod']) == 2) { - $where += self::getDatesCriteria("$table.date_mod", $apply_filters['dates_mod']); - } - - if ($DB->fieldExists($table, 'itilcategories_id') - && isset($apply_filters['itilcategory']) - && (int) $apply_filters['itilcategory'] > 0) { - $where += [ - "$table.itilcategories_id" => (int) $apply_filters['itilcategory'] - ]; - } - - if ($DB->fieldExists($table, 'requesttypes_id') - && isset($apply_filters['requesttype']) - && (int) $apply_filters['requesttype'] > 0) { - $where += [ - "$table.requesttypes_id" => (int) $apply_filters['requesttype'] - ]; - } - - if ($DB->fieldExists($table, 'locations_id') - && isset($apply_filters['location']) - && (int) $apply_filters['location'] > 0) { - $where += [ - "$table.locations_id" => (int) $apply_filters['location'] - ]; - } - - if ($DB->fieldExists($table, 'manufacturers_id') - && isset($apply_filters['manufacturer']) - && (int) $apply_filters['manufacturer'] > 0) { - $where += [ - "$table.manufacturers_id" => (int) $apply_filters['manufacturer'] - ]; - } - - if (isset($apply_filters['group_tech'])) { - - $groups_id = null; - if ((int) $apply_filters['group_tech'] > 0) { - $groups_id = (int) $apply_filters['group_tech']; - } else if ((int) $apply_filters['group_tech'] == -1) { - $groups_id = $_SESSION['glpigroups']; - } - - if ($groups_id != null) { - if ($DB->fieldExists($table, 'groups_id_tech')) { - $where += [ - "$table.groups_id_tech" => $groups_id - ]; - } else if (in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ])) { - $itemtype = getItemTypeForTable($table); - $main_item = getItemForItemtype($itemtype); - $grouplink = $main_item->grouplinkclass; - $gl_table = $grouplink::getTable(); - $fk = $main_item->getForeignKeyField(); - - $join += [ - "$gl_table as gl" => [ - 'ON' => [ - 'gl' => $fk, - $table => 'id', - ] - ] - ]; - $where += [ - "gl.type" => \CommonITILActor::ASSIGN, - "gl.groups_id" => $groups_id - ]; - } - } - } - - if (isset($apply_filters['user_tech']) - && (int) $apply_filters['user_tech'] > 0) { - - if ($DB->fieldExists($table, 'users_id_tech')) { - $where += [ - "$table.users_id_tech" => (int) $apply_filters['user_tech'] - ]; - } else if (in_array($table, [ - Ticket::getTable(), - Change::getTable(), - Problem::getTable(), - ])) { - $itemtype = getItemTypeForTable($table); - $main_item = getItemForItemtype($itemtype); - $userlink = $main_item->userlinkclass; - $ul_table = $userlink::getTable(); - $fk = $main_item->getForeignKeyField(); - - $join += [ - "$ul_table as ul" => [ - 'ON' => [ - 'ul' => $fk, - $table => 'id', - ] - ] - ]; - $where += [ - "ul.type" => \CommonITILActor::ASSIGN, - "ul.users_id" => (int) $apply_filters['user_tech'] - ]; - } - } - - $criteria = []; - if (count($where)) { - $criteria['WHERE'] = $where; - } - if (count($join)) { - $criteria['LEFT JOIN'] = $join; - } - - return $criteria; - } - - private static function getDatesCriteria(string $field = "", array $dates = []): array { - $begin = strtotime($dates[0]); - $end = strtotime($dates[1]); - - return [ - [$field => ['>=', date('Y-m-d', $begin)]], - [$field => ['<=', date('Y-m-d', $end)]], - ]; - } - - private static function getDatesSearchCriteria(int $searchoption_id, array $dates = [], $when = 'begin'): array { - - if ($when == "begin") { - $begin = strtotime($dates[0]); - return [ - 'link' => 'AND', - 'field' => $searchoption_id, // creation date - 'searchtype' => 'morethan', - 'value' => date('Y-m-d 00:00:00', $begin) - ]; - } else { - $end = strtotime($dates[1]); - return [ - 'link' => 'AND', - 'field' => $searchoption_id, // creation date - 'searchtype' => 'lessthan', - 'value' => date('Y-m-d 00:00:00', $end) - ]; - } - } - -} diff --git a/inc/dashboard/right.class.php b/inc/dashboard/right.class.php deleted file mode 100644 index 947610efc5..0000000000 --- a/inc/dashboard/right.class.php +++ /dev/null @@ -1,110 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -class Right extends \CommonDBChild { - static public $itemtype = "Glpi\\Dashboard\\Dashboard"; - static public $items_id = 'dashboards_dashboards_id'; - - // prevent bad getFromDB when bootstraping tests suite - static public $mustBeAttached = false; - - /** - * Return rights for the provided dashboard - * - * @param int $dashboards_id - * - * @return array the rights - */ - static function getForDashboard(int $dashboards_id = 0): array { - global $DB; - - $dr_iterator = $DB->request([ - 'FROM' => self::getTable(), - 'WHERE' => [ - 'dashboards_dashboards_id' => $dashboards_id - ] - ]); - - $rights = []; - foreach ($dr_iterator as $right) { - unset($right['id']); - $rights[] = $right; - } - - return $rights; - } - - - /** - * Save rights in DB for the provided dashboard - * - * @param int $dashboards_id id (not key) of the 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 - */ - static function addForDashboard(int $dashboards_id = 0, array $rights = []) { - global $DB; - - $query_rights = $DB->buildInsert( - self::getTable(), - [ - 'dashboards_dashboards_id' => new \QueryParam(), - 'itemtype' => new \QueryParam(), - 'items_id' => new \QueryParam(), - ] - ); - $stmt = $DB->prepare($query_rights); - foreach ($rights as $fk => $right_line) { - $itemtype = getItemtypeForForeignKeyField($fk); - foreach ($right_line as $items_id) { - $stmt->bind_param( - 'isi', - $dashboards_id, - $itemtype, - $items_id - ); - $stmt->execute(); - } - } - } -} diff --git a/inc/dashboard/widget.class.php b/inc/dashboard/widget.class.php deleted file mode 100644 index 766fa0a0f2..0000000000 --- a/inc/dashboard/widget.class.php +++ /dev/null @@ -1,1872 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace Glpi\Dashboard; - -use Mexitek\PHPColors\Color; -use ScssPhp\ScssPhp\Compiler; -use Michelf\MarkdownExtra; -use CommonGLPI; -use Toolbox; -use Plugin; -use Html; -use Search; - -if (!defined('GLPI_ROOT')) { - die("Sorry. You can't access this file directly"); -} - -/** - * Widget class -**/ -class Widget extends CommonGLPI { - static $animation_duration = 1000; // in millseconds - - - /** - * Define all possible widget types with their $labels/ - * This is used when adding a new card to display optgroups - * This array can be hooked by plugins to add their own definitions. - * - * @return array - */ - public static function getAllTypes(): array { - global $CFG_GLPI; - - $types = [ - 'pie' => [ - 'label' => __("Pie"), - 'function' => 'Glpi\\Dashboard\\Widget::pie', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/pie.png', - 'gradient' => true, - 'limit' => true, - 'width' => 3, - 'height' => 3, - ], - 'donut' => [ - 'label' => __("Donut"), - 'function' => 'Glpi\\Dashboard\\Widget::donut', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/donut.png', - 'gradient' => true, - 'limit' => true, - 'width' => 3, - 'height' => 3, - ], - 'halfpie' => [ - 'label' => __("Half pie"), - 'function' => 'Glpi\\Dashboard\\Widget::halfPie', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/halfpie.png', - 'gradient' => true, - 'limit' => true, - 'width' => 3, - 'height' => 2, - ], - 'halfdonut' => [ - 'label' => __("Half donut"), - 'function' => 'Glpi\\Dashboard\\Widget::halfDonut', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/halfdonut.png', - 'gradient' => true, - 'limit' => true, - 'width' => 3, - 'height' => 2, - ], - 'bar' => [ - 'label' => __("Bars"), - 'function' => 'Glpi\\Dashboard\\Widget::simpleBar', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/bar.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 4, - 'height' => 3, - ], - 'line' => [ - 'label' => \Line::getTypeName(1), - 'function' => 'Glpi\\Dashboard\\Widget::simpleLine', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/line.png', - 'limit' => true, - 'pointlbl' => true, - 'width' => 4, - 'height' => 3, - ], - 'lines' => [ - 'label' => __("Multiple lines"), - 'function' => 'Glpi\\Dashboard\\Widget::multipleLines', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/line.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 4, - 'height' => 3, - ], - 'area' => [ - 'label' => __("Area"), - 'function' => 'Glpi\\Dashboard\\Widget::simpleArea', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/area.png', - 'limit' => true, - 'pointlbl' => true, - 'width' => 4, - 'height' => 3, - ], - 'areas' => [ - 'label' => __("Multiple areas"), - 'function' => 'Glpi\\Dashboard\\Widget::multipleAreas', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/area.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 5, - 'height' => 3, - ], - 'bars' => [ - 'label' => __("Multiple bars"), - 'function' => 'Glpi\\Dashboard\\Widget::multipleBars', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/bar.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 5, - 'height' => 3, - ], - 'stackedbars' => [ - 'label' => __("Stacked bars"), - 'function' => 'Glpi\\Dashboard\\Widget::StackedBars', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/stacked.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 4, - 'height' => 3, - ], - 'hbar' => [ - 'label' => __("Horizontal bars"), - 'function' => 'Glpi\\Dashboard\\Widget::simpleHbar', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/hbar.png', - 'gradient' => true, - 'limit' => true, - 'pointlbl' => true, - 'width' => 3, - 'height' => 4, - ], - 'bigNumber' => [ - 'label' => __("Big number"), - 'function' => 'Glpi\\Dashboard\\Widget::bigNumber', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/bignumber.png', - ], - 'multipleNumber' => [ - 'label' => __("Multiple numbers"), - 'function' => 'Glpi\\Dashboard\\Widget::multipleNumber', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/multiplenumbers.png', - 'limit' => true, - 'gradient' => true, - 'width' => 3, - 'height' => 3, - ], - 'markdown' => [ - 'label' => __("Editable markdown"), - 'function' => 'Glpi\\Dashboard\\Widget::markdown', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/markdown.png', - 'width' => 4, - 'height' => 4, - ], - 'searchShowList' => [ - 'label' => __("Search result"), - 'function' => 'Glpi\\Dashboard\\Widget::searchShowList', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/table.png', - 'limit' => true, - 'width' => 5, - 'height' => 4, - ], - 'summaryNumbers' => [ - 'label' => __("Summary numbers"), - 'function' => 'Glpi\\Dashboard\\Widget::summaryNumber', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/summarynumber.png', - 'limit' => true, - 'gradient' => true, - 'width' => 4, - 'height' => 2, - ], - 'articleList' => [ - 'label' => __("List of articles"), - 'function' => 'Glpi\\Dashboard\\Widget::articleList', - 'image' => $CFG_GLPI['root_doc'].'/pics/charts/articles.png', - 'limit' => true, - 'width' => 3, - 'height' => 4, - ], - ]; - - $more_types = Plugin::doHookFunction("dashboard_types"); - if (is_array($more_types)) { - $types = array_merge($types, $more_types); - } - - return $types; - } - - - /** - * Display a big number widget. - * - * @param array $params contains these keys: - * - int 'number': the number to display - * - string 'url': url to redirect when clicking on the widget - * - string 'label': title of the widget - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - array 'filters': array of filter's id to apply classes on widget html - * - * @return string html of the widget - */ - public static function bigNumber(array $params = []): string { - $default = [ - 'number' => 0, - 'url' => '', - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'id' => 'bn_'.mt_rand(), - 'filters' => [], - ]; - $p = array_merge($default, $params); - - $formatted_number = Toolbox::shortenNumber($p['number']); - $fg_color = Toolbox::getFgColor($p['color']); - $fg_hover_color = Toolbox::getFgColor($p['color'], 15); - $fg_hover_border = Toolbox::getFgColor($p['color'], 30); - - $class = count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $href = strlen($p['url']) - ? "href='{$p['url']}'" - : ""; - - $label = $p['label']; - $html = << - #{$p['id']} { - background-color: {$p['color']}; - color: {$fg_color}; - } - - #{$p['id']}:hover { - background-color: {$fg_hover_color}; - border: 1px solid {$fg_hover_border}; - } - - - $formatted_number -
{$label}
- -
-HTML; - - return $html; - } - - - public static function summaryNumber(array $params = []): string { - $params['class'] = 'summary-numbers'; - return self::multipleNumber($params); - } - - - /** - * Display a multiple big number widget. - * - * @param array $params contains these keys: - * - array 'data': represents the lines to display - * - int 'number': the number to display in the line - * - string 'url': url to redirect when clicking on the line - * - string 'label': title of the line - * - string 'number': number to display in the line - * - string 'icon': font awesome class to display an icon side of the line - * - int 'limit': the numbers of lines diplayed - * - string 'label': global title of the widget - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - array 'filters': array of filter's id to apply classes on widget html - * - * @return string html of the widget - */ - public static function multipleNumber(array $params = []): string { - $default = [ - 'data' => [], - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'limit' => 99999, - 'use_gradient' => false, - 'class' => "multiple-numbers", - 'filters' => [], - 'rand' => mt_rand(), - ]; - $p = array_merge($default, $params); - $default_entry = [ - 'url' => '', - 'icon' => '', - 'label' => '', - 'number' => '', - ]; - - $nb_lines = min($p['limit'], count($p['data'])); - array_splice($p['data'], $nb_lines); - - $fg_color = Toolbox::getFgColor($p['color']); - - $class = $p['class']; - $class.= count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $alphabet = range('a', 'z'); - $numbers_html = ""; - $i = 0; - foreach ($p['data'] as $entry) { - if (!is_array($entry)) { - continue; - } - $entry = array_merge($default_entry, $entry); - - $href = strlen($entry['url']) - ? "href='{$entry['url']}'" - : ""; - - $color = isset($entry['color']) - ? "style=\"color: {$entry['color']};\"" - : ""; - - $color2 = isset($entry['color']) - ? "style=\"color: ".Toolbox::getFgColor($entry['color'], 20).";\"" - : ""; - - $formatted_number = Toolbox::shortenNumber($entry['number']); - - $numbers_html.= << - $formatted_number - - {$entry['label']} - -HTML; - $i++; - } - - $nodata = isset($p['data']['nodata']) && $p['data']['nodata']; - if ($nodata) { - $numbers_html = " - - - - ".__('No data found')." - "; - } - - $palette_style = ""; - if ($p['use_gradient']) { - $palette = self::getGradientPalette($p['color'], $i, false); - foreach ($palette['names'] as $index => $letter) { - $bgcolor = $palette['colors'][$index]; - $bgcolor_h = Toolbox::getFgColor($bgcolor, 10); - $color = Toolbox::getFgColor($bgcolor); - - $palette_style .= " - #chart-{$p['rand']} .line-$letter { - background-color: $bgcolor; - color: $color; - } - - #chart-{$p['rand']} .line-$letter:hover { - background-color: $bgcolor_h; - font-weight: bold; - } - "; - } - } - - $html = << - {$palette_style} - - -
-
-
- {$numbers_html} -
-
- {$p['label']} - -
-HTML; - - return $html; - } - - - /** - * Display a widget with a pie chart - * - * @param array $params contains these keys: - * - array 'data': represents the slices to display - * - int 'number': number of the slice - * - string 'url': url to redirect when clicking on the slice - * - string 'label': title of the slice - * - string 'label': global title of the widget - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - bool 'use_gradient': gradient or generic palette - * - int 'limit': the number of slices - * - bool 'donut': do we want a "holed" pie - * - bool 'gauge': do we want an half pie - * - array 'filters': array of filter's id to apply classes on widget html - * - * @return string html of the widget - */ - public static function pie(array $params = []): string { - $default = [ - 'data' => [], - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'donut' => false, - 'half' => false, - 'use_gradient' => false, - 'limit' => 99999, - 'filters' => [], - 'rand' => mt_rand(), - ]; - $p = array_merge($default, $params); - $default_entry = [ - 'url' => '', - 'icon' => '', - 'label' => '', - 'number' => '', - ]; - - $nb_slices = min($p['limit'], count($p['data'])); - array_splice($p['data'], $nb_slices); - - $nodata = isset($p['data']['nodata']) && $p['data']['nodata']; - $fg_color = Toolbox::getFgColor($p['color']); - - $class = "pie"; - $class.= $p['half'] ? " half": ""; - $class.= $p['donut'] ? " donut": ""; - $class.= count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $no_data_html = ""; - if ($nodata) { - $no_data_html = " -
".__('No data found')."
- "; - } - - $nb_series = min($p['limit'], count($p['data'])); - - $palette_style = ""; - if ($p['use_gradient']) { - $palette_style = self::getCssGradientPalette( - $p['color'], - $nb_series, - ".dashboard #chart-{$p['rand']}", - false - ); - } - - $html = << - #chart-{$p['rand']} .ct-label { - fill: {$fg_color}; - color: {$fg_color}; - } - {$palette_style} - -
-
{$no_data_html}
- {$p['label']} - -
-HTML; - - if ($nodata) { - return $html; - } - - $labels = []; - $series = []; - $total = 0; - foreach ($p['data'] as $entry) { - $entry = array_merge($default_entry, $entry); - $total+= $entry['number']; - - $labels[] = $entry['label']; - $series[] = [ - 'meta' => $entry['label'], - 'value' => $entry['number'], - 'url' => $entry['url'], - ]; - } - $total_txt = Toolbox::shortenNumber($total, 1, false); - - $labels = json_encode($labels); - $series = json_encode($series); - - $chartPadding = 4; - $height_divider = 1; - $half_opts = ""; - if ($p['half']) { - $half_opts = " - startAngle: 270, - total: ".($total*2).", - "; - $chartPadding = 9; - $height_divider = 2; - } - - $donut_opts = " - showLabel: false, - "; - if ($p['donut']) { - $donut_opts = " - donutSolid: true, - showLabel: true, - labelInterpolationFnc: function(value) { - return '{$total_txt}'; - }, - "; - } - - $donut = $p['donut'] ? 'true' : 'false'; - $height = $p['half'] ? '180%' : '100%'; - $animation_duration = self::$animation_duration; - - $js = << 0) { - data.element.attr({ - 'data-clickable': true - }); - data.element._node.onclick = function() { - if (!Dashboard.edit_mode) { - window.location = url; - } - } - } - - // Get the total path length in order to use for dash array animation - var pathLength = data.element._node.getTotalLength(); - - // Set a dasharray that matches the path length as prerequisite to animate dashoffset - data.element.attr({ - 'stroke-dasharray': pathLength + 'px ' + pathLength + 'px' - }); - - // Create animation definition while also assigning an ID to the animation for later sync usage - var animationDefinition = { - 'stroke-dashoffset': { - id: 'anim' + data.index, - dur: {$animation_duration}, - from: -pathLength + 'px', - to: '0px', - easing: Chartist.Svg.Easing.easeOutQuint, - // We need to use `fill: 'freeze'` otherwise our animation will fall back to initial (not visible) - fill: 'freeze' - } - }; - - // We need to set an initial value before the animation starts as we are not in guided mode which would do that for us - data.element.attr({ - 'stroke-dashoffset': -pathLength + 'px' - }); - - // We can't use guided mode as the animations need to rely on setting begin manually - // See http://gionkunz.github.io/chartist-js/api-documentation.html#chartistsvg-function-animate - data.element.animate(animationDefinition, false); - } - - // donut center label - if (data.type === 'label') { - if (data.index === 0) { - var width = data.element.root().width() / 2; - var height = data.element.root().height() / 2; - var fontsize = ((height / {$height_divider}) / (1.3 * "{$total_txt}".length)); - data.element.attr({ - dx: width, - dy: height - ($chartPadding / 2), - 'style': 'font-size: '+fontsize, - }); - - // apend real total - var text = new Chartist.Svg('title'); - text.text("{$total}"); - data.element.append(text); - } else { - data.element.remove(); - } - } - - // fade others bars on one mouseouver - chart.on('created', function(bar) { - $('#chart-{$p['rand']} .ct-series') - .mouseover(function() { - $(this).parent().children().addClass('disable-animation'); - $(this).addClass('mouseover'); - $(this).siblings() - .addClass('notmouseover'); - - $('#chart-{$p['rand']} .ct-label') - .addClass('fade'); - }) - .mouseout(function() { - $(this).removeClass('mouseover'); - $(this).siblings() - .removeClass('notmouseover'); - - $('#chart-{$p['rand']} .ct-label') - .removeClass('fade'); - }); - }); - }); - }); -JAVASCRIPT; - $js = \Html::scriptBlock($js); - - return $html.$js; - } - - - /** - * Display a widget with a donut chart - * @see self::pie for params - * - * @return string html - */ - public static function donut(array $params = []): string { - return self::pie(array_merge($params, [ - 'donut' => true, - ])); - } - - - /** - * Display a widget with a half donut chart - * @see self::pie for params - * - * @return string html - */ - public static function halfDonut(array $params = []): string { - return self::donut(array_merge($params, [ - 'half' => true, - ])); - } - - /** - * Display a widget with a half pie chart - * @see self::pie for params - * - * @return string html - */ - public static function halfPie(array $params = []): string { - return self::pie(array_merge($params, [ - 'half' => true, - ])); - } - - - /** - * Display a widget with a bar chart (with single series) - * @see self::getBarsGraph for params - * - * @return string html - */ - public static function simpleBar(array $params = []): string { - $default = [ - 'data' => [], - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'horizontal' => false, - 'distributed' => true, - 'rand' => mt_rand(), - ]; - $params = array_merge($default, $params); - $default_entry = [ - 'url' => '', - 'icon' => '', - 'label' => '', - 'number' => '', - ]; - - $labels = []; - $series = []; - $total = 0; - foreach ($params['data'] as $entry) { - if (!is_array($entry)) { - continue; - } - $entry = array_merge($default_entry, $entry); - $total+= $entry['number']; - - $labels[] = $entry['label']; - $series[] = [ - 'meta' => $entry['label'], - 'value' => $entry['number'], - 'url' => $entry['url'], - ]; - } - - // chartist bar graphs are always multiple lines - if (!$params['distributed']) { - $series = [$series]; - } - - return self::getBarsGraph($params, $labels, $series); - } - - - /** - * Display a widget with an horizontal bar chart - * @see self::getBarsGraph for params - * - * @return string html - */ - public static function simpleHbar(array $params = []): string { - return self::simpleBar(array_merge($params, [ - 'horizontal' => true, - ])); - } - - /** - * @inheritdoc self::simpleHbar - */ - public static function hbar(array $params = []): string { - return self::simpleHbar($params); - } - - - /** - * Display a widget with a multiple bars chart - * @see self::getBarsGraph for params - * - * @return string html - */ - public static function multipleBars(array $params = []): string { - return self::getBarsGraph( - array_merge($params, [ - 'legend' => true, - 'multiple' => true, - ]), - $params['data']['labels'], - $params['data']['series'] - ); - } - - - /** - * Display a widget with a stacked multiple bars chart - * @see self::getBarsGraph for params - * - * @return string html - */ - public static function StackedBars(array $params = []): string { - return self::multipleBars(array_merge($params, [ - 'stacked' => true, - ])); - } - - - /** - * Display a widget with a bars chart - * - * @param array $params contains these keys: - * - array 'data': represents the bars to display - * - string 'url': url to redirect when clicking on the bar - * - string 'label': title of the bar - * - int 'number': number of the bar - * - string 'label': global title of the widget - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - bool 'horizontal': do we want an horizontal chart - * - bool 'distributed': do we want a distributed chart (see https://gionkunz.github.io/chartist-js/examples.html#example-bar-distributed-series) - * - bool 'legend': do we display a legend for the graph - * - bool 'stacked': do we display multiple bart stacked or grouped - * - bool 'use_gradient': gradient or generic palette - * - bool 'point_labels': display labels (for values) directly on graph - * - int 'limit': the number of bars - * - array 'filters': array of filter's id to apply classes on widget html - * @param array $labels title of the bars (if a single array is given, we have a single bar graph) - * @param array $series values of the bar (if a single array is given, we have a single bar graph) - * - * @return string html of the widget - */ - private static function getBarsGraph( - array $params = [], - array $labels = [], - array $series = []): string { - - $defaults = [ - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'legend' => false, - 'multiple' => false, - 'stacked' => false, - 'horizontal' => false, - 'distributed' => false, - 'use_gradient' => false, - 'point_labels' => false, - 'limit' => 99999, - 'filters' => [], - 'rand' => mt_rand(), - ]; - $p = array_merge($defaults, $params); - - $nb_series = count($series); - $nb_labels = min($p['limit'], count($labels)); - if ($p['distributed']) { - array_splice($labels, $nb_labels); - } else { - array_splice($labels, 0, -$nb_labels); - } - if ($p['multiple']) { - foreach ($series as &$serie) { - if (isset($serie['data'])) { - array_splice($serie['data'], 0, -$nb_labels); - } - } - } else { - if ($p['distributed']) { - array_splice($series, $nb_labels); - } else { - array_splice($series[0], 0, -$nb_labels); - } - } - - $json_labels = json_encode($labels); - $json_series = json_encode($series); - - $fg_color = Toolbox::getFgColor($p['color']); - $line_color = Toolbox::getFgColor($p['color'], 10); - - $animation_duration = self::$animation_duration; - - $class = "bar"; - $class.= $p['horizontal'] ? " horizontal": ""; - $class.= $p['distributed'] ? " distributed": ""; - $class.= $nb_series <= 10 ? " tab10": ""; - $class.= $nb_series > 10 ? " tab20": ""; - $class.= count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $palette_style = ""; - if ($p['use_gradient']) { - $nb_gradients = $p['distributed'] ? $nb_labels : $nb_series; - $palette_style = self::getCssGradientPalette($p['color'], $nb_gradients, "#chart-{$p['rand']}"); - } - - $nodata = isset($p['data']['nodata']) && $p['data']['nodata'] - || count($series) == 0; - $no_data_html = ""; - if ($nodata) { - $no_data_html = " -
".__('No data found')."
- "; - } - - $height = "calc(100% - 5px)"; - $legend_options = ""; - if ($p['legend']) { - $height = "calc(100% - 40px)"; - $legend_options = " - Chartist.plugins.legend(),"; - } - - $html = << - #chart-{$p['rand']} .ct-label { - color: {$fg_color}; - } - #chart-{$p['rand']} .ct-grid { - stroke: {$line_color}; - } - - /** fix chrome resizing height when animating svg (don't know why) **/ - #chart-{$p['rand']} .ct-chart-bar { - min-height: $height; - } - {$palette_style} - - -
-
$no_data_html
- {$p['label']} - -
-HTML; - - $horizontal_options = ""; - $vertical_options = ""; - $is_horizontal = "false"; - if ($p['horizontal']) { - $is_horizontal = "true"; - $horizontal_options = " - horizontalBars: true, - axisY: { - offset: 100 - }, - axisX: { - onlyInteger: true - }, - "; - } else { - $vertical_options = " - axisX: { - offset: 50, - }, - axisY: { - onlyInteger: true - }, - "; - } - - $stack_options = ""; - if ($p['stacked']) { - $stack_options = " - stackBars: true,"; - } - - $distributed_options = ""; - if ($p['distributed']) { - $distributed_options = " - distributeSeries: true,"; - } - - // just to avoid issues with syntax coloring - $point_labels = $p['point_labels'] ? "true" : "false;"; - $is_multiple = $p['multiple'] ? "true" : "false;"; - - $js = << 0 - && chart.data.series[0].hasOwnProperty('data')) { - nb_elements = nb_elements * chart.data.series.length; - bar_margin += 1; - } - - chart.on('draw', function(data) { - if (data.type === 'bar') { - // set url redirecting on bar - var url = _.get(data, 'series['+data.index+'].url') - || _.get(data, 'series.data['+data.index+'].url') - || _.get(data, 'series.url') - || ""; - if (url.length > 0) { - data.element.attr({ - 'data-clickable': true - }); - data.element._node.onclick = function() { - if (!Dashboard.edit_mode) { - window.location = url; - } - } - } - - var chart_height = data.chartRect.height(); - var chart_width = data.chartRect.width(); - - var stroke_width = chart_width / nb_elements; - if (is_horizontal) { - stroke_width = chart_height / nb_elements; - } - - if (!chart.options.stackBars - && chart.data.series.length > 0 && is_vertical) { - stroke_width -= bar_margin * nb_elements; - } else { - stroke_width -= bar_margin; - } - data.element.attr({ - 'style': 'stroke-width: '+stroke_width+'px' - }); - - var axis_anim = 'y'; - if ({$is_horizontal}) { - axis_anim = 'x'; - } - - var animate_properties = { - opacity: { - dur: {$animation_duration}, - from: 0, - to: 1, - easing: Chartist.Svg.Easing.easeOutQuint - } - }; - animate_properties[axis_anim+'2'] = { - dur: {$animation_duration}, - from: data[axis_anim+'1'], - to: data[axis_anim+'2'], - easing: Chartist.Svg.Easing.easeOutQuint - }; - data.element.animate(animate_properties); - - // append labels - var display_labels = true; - var labelX = 0; - var labelY = 0; - var value = data.element.attr('ct:value').toString(); - var text_anchor = 'middle'; - - if (is_vertical) { - labelX = data.x2; - labelY = data.y2 + 15; - - if (is_multiple) { - labelY = data.y2 - 5; - } else if (data.y1 - data.y2 < 18) { - display_labels = false; - } - } - - if (is_horizontal) { - var word_width = value.length * 5 + 5; - labelX = data.x2 - word_width; - labelY = data.y2; - - // don't display label if width too short - if (data.x2 - data.x1 < word_width) { - display_labels = false; - } - } - - if (is_stacked) { - labelY = data.y2 + 15; - - // don't display label if height too short - if (data.y1 - data.y2 < 15) { - display_labels = false; - } - } - - // don't display label if value is not relevant - if (value == 0 || !point_labels) { - display_labels = false; - } - - if (display_labels) { - label = new Chartist.Svg('text'); - label.text(value); - label.addClass("ct-barlabel"); - label.attr({ - x: labelX, - y: labelY, - 'text-anchor': text_anchor - }); - return data.group.append(label); - } - } - }); - - chart.on('created', function(bar) { - $('#chart-{$p['rand']} .ct-series') - .mouseover(function() { - $(this).siblings().children().css('stroke-opacity', "0.2"); - }) - .mouseout(function() { - $(this).siblings().children().css('stroke-opacity', "1"); - }); - }); - }); -JAVASCRIPT; - $js = \Html::scriptBlock($js); - - return $html.$js; - } - - - /** - * Display a widget with a line chart (with single series) - * @see self::getLinesGraph for params - * - * @return string html - */ - public static function simpleLine(array $params = []): string { - $default_entry = [ - 'url' => '', - 'icon' => '', - 'label' => '', - 'number' => '', - ]; - - $labels = []; - $series = []; - foreach ($params['data'] as $entry) { - $entry = array_merge($default_entry, $entry); - - $labels[] = $entry['label']; - $series[] = [ - 'meta' => $entry['label'], - 'value' => $entry['number'], - 'url' => $entry['url'], - ]; - } - - // chartist line graphs are always multiple lines - $series = [$series]; - - return self::getLinesGraph($params, $labels, $series); - } - - - /** - * Display a widget with a area chart (with single serie) - * @see self::getLinesGraph for params - * - * @return string html - */ - public static function simpleArea(array $params = []): string { - return self::simpleLine(array_merge($params, [ - 'area' => true, - ])); - } - - - /** - * Display a widget with a multiple line chart (with multiple series) - * @see self::getLinesGraph for params - * - * @return string html - */ - public static function multipleLines(array $params = []): string { - return self::getLinesGraph( - array_merge($params, [ - 'legend' => true, - 'multiple' => true, - ]), - $params['data']['labels'], - $params['data']['series'] - ); - } - - - /** - * Display a widget with a multiple area chart (with multiple series) - * @see self::getLinesGraph for params - * - * @return string html - */ - public static function multipleAreas(array $params = []): string { - return self::multipleLines(array_merge($params, [ - 'area' => true, - ])); - } - - - /** - * Display a widget with a lines chart - * - * @param array $params contains these keys: - * - array 'data': represents the lines to display - * - string 'url': url to redirect when clicking on the line - * - string 'label': title of the line - * - int 'number': number of the line - * - string 'label': global title of the widget - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - bool 'area': do we want an area chart - * - bool 'legend': do we display a legend for the graph - * - bool 'use_gradient': gradient or generic palette - * - bool 'point_labels': display labels (for values) directly on graph - * - int 'limit': the number of lines - * - array 'filters': array of filter's id to apply classes on widget html - * @param array $labels title of the lines (if a single array is given, we have a single line graph) - * @param array $series values of the line (if a single array is given, we have a single line graph) - * - * @return string html of the widget - */ - private static function getLinesGraph( - array $params = [], - array $labels = [], - array $series = []): string { - - $defaults = [ - 'data' => [], - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 'area' => false, - 'legend' => false, - 'multiple' => false, - 'use_gradient' => false, - 'point_labels' => false, - 'limit' => 99999, - 'filters' => [], - 'rand' => mt_rand(), - ]; - $p = array_merge($defaults, $params); - - $nb_series = count($series); - $nb_labels = min($p['limit'], count($labels)); - array_splice($labels, 0, -$nb_labels); - if ($p['multiple']) { - foreach ($series as &$serie) { - if (isset($serie['data'])) { - array_splice($serie['data'], 0, -$nb_labels); - } - } - } else { - array_splice($series[0], 0, -$nb_labels); - } - - $json_labels = json_encode($labels); - $json_series = json_encode($series); - - $fg_color = Toolbox::getFgColor($p['color']); - $line_color = Toolbox::getFgColor($p['color'], 10); - $class = "line"; - $class.= $p['area'] ? " area": ""; - $class.= $p['multiple'] ? " multiple": ""; - $class.= count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $animation_duration = self::$animation_duration; - - $palette_style = ""; - if (!$p['multiple'] || $p['use_gradient']) { - $palette_style = self::getCssGradientPalette($p['color'], $nb_series, "#chart-{$p['rand']}"); - } - - $pointlabels_plugins = ""; - if ($p['point_labels']) { - $pointlabels_plugins = ", - Chartist.plugins.ctPointLabels({ - textAnchor: 'middle', - labelInterpolationFnc: function(value) { - if (value == undefined) { - return '' - } - return value; - } - })"; - } - - $height = "calc(100% - 1px)"; - $legend_options = ""; - if ($p['legend']) { - $height = "calc(100% - 40px)"; - $legend_options = " - Chartist.plugins.legend(),"; - } - - $html = << - /** fix chrome resizing height when animating svg (don't know why) **/ - #chart-{$p['rand']} .ct-chart-line { - min-height: $height; - } - #chart-{$p['rand']} .ct-label { - color: {$fg_color}; - } - - #chart-{$p['rand']} .ct-grid { - stroke: {$line_color}; - } - - #chart-{$p['rand']} .ct-circle { - stroke: {$p['color']}; - stroke-width: 3; - } - #chart-{$p['rand']} .ct-circle + .ct-label { - stroke: {$p['color']}; - } - {$palette_style} - - -
-
- {$p['label']} - -
-HTML; - - $area_options = ""; - if ($p['area']) { - $area_options = " - showArea: true,"; - } - - $js = << 0; - - var circle = new Chartist.Svg('circle', { - cx: [data.x], - cy: [data.y], - r: data.value.y > 0 ? [5] : [0], - "ct:value": data.value.y, - "data-clickable": clickable - }, 'ct-circle'); - var circle = data.element.replace(circle); - - if (clickable) { - circle.getNode().onclick = function() { - if (!Dashboard.edit_mode) { - window.location = url; - } - } - } - } - }); - - // hide other lines when hovering a point - chart.on('created', function(bar) { - $('#chart-{$p['rand']} .ct-series .ct-circle, #chart-{$p['rand']} .ct-series .ct-circle + .ct-label') - .mouseover(function() { - $(this) - .attr('r', "9") - .parent(".ct-series") - .siblings().children() - .css('stroke-opacity', "0.05") - .filter(".ct-circle, .ct-label").css('fill-opacity', "0.1"); - }) - .mouseout(function() { - $(this) - .attr('r', "5") - .parent(".ct-series") - .siblings().children() - .css('stroke-opacity', "1") - .filter(".ct-circle, .ct-label").css('fill-opacity', "1"); - }); - }); - }); -JAVASCRIPT; - $js = Html::scriptBlock($js); - - return $html.$js; - } - - - /** - * Display an editable markdown widget - * - * @param array $params with these keys: - * - string 'color': hex color - * - string 'markdown_content': text content formatted with warkdown - * - * @return string html - */ - public static function markdown(array $params = []): string { - $default = [ - 'color' => '', - 'markdown_content' => '', - ]; - $p = array_merge($default, $params); - - // fix auto-escaping - if (isset($p['markdown_content'])) { - $p['markdown_content'] = \Html::cleanPostForTextArea($p['markdown_content']); - } - - $ph = __("Type markdown text here"); - $fg_color = Toolbox::getFgColor($p['color']); - $border_color = Toolbox::getFgColor($p['color'], 10); - $md = new MarkdownExtra(); - // Prevent escaping as code is already escaped by GLPI sanityze - $md->code_span_content_func = function ($code) { return $code; }; - $md->code_block_content_func = function ($code) { return $code; }; - - $html = << - -
{$md->transform($p['markdown_content'])}
- - - -HTML; - - return $html; - } - - - /** - * Display an html table from a \Search result - * - * @param array $params contains these keys: - * - string 'itemtype': Glpi oObject to search - * - array 's_criteria': parameters to pass to the search engine (@see \Search::manageParams) - * - string 'label': global title of the widget - * - string 'url': link to the full search result - * - string 'alt': tooltip - * - string 'color': hex color of the widget - * - string 'icon': font awesome class to display an icon side of the label - * - string 'id': unique dom identifier - * - int 'limit': the number of displayed lines - * - array 'filters': array of filter's id to apply classes on widget html - * - * @return string html of the widget - */ - public static function searchShowList(array $params = []): string { - $default = [ - 'url' => '', - 'label' => '', - 'alt' => '', - 'color' => '', - 'icon' => '', - 's_criteria' => '', - 'itemtype' => '', - 'limit' => $_SESSION['glpilist_limit'], - 'rand' => mt_rand(), - 'filters' => [], - ]; - $p = array_merge($default, $params); - - $id = "search-table-".$p['rand']; - - $color = new Color($p['color']); - $is_light = $color->isLight(); - - $fg_color = Toolbox::getFgColor($p['color'], $is_light ? 65 : 40); - $fg_color2 = Toolbox::getFgColor($p['color'], 5); - - $href = strlen($p['url']) - ? "href='{$p['url']}'" - : ""; - - $class = count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - // prepare search data - $_GET['_in_modal'] = true; - $params = [ - 'criteria' => $p['s_criteria'], - 'reset' => 'reset', - ]; - - ob_start(); - $params = Search::manageParams($p['itemtype'], $params); - // remove parts of search list - $params = array_merge($params, [ - 'showmassiveactions' => false, - 'dont_flush' => true, - 'show_pager' => false, - 'show_footer' => false, - 'no_sort' => true, - 'list_limit' => $p['limit'] - ]); - Search::showList($p['itemtype'], $params); - $search_result = ob_get_clean(); - - $html = << - #{$id} .tab_cadrehov th { - background: {$fg_color2}; - } - -
-
- $search_result -
- - {$p['label']} - - -
-HTML; - - return $html; - } - - - public static function articleList(array $params): string { - $default = [ - 'data' => [], - 'label' => '', - 'alt' => '', - 'url' => '', - 'color' => '', - 'icon' => '', - 'limit' => 99999, - 'class' => "articles-list", - 'rand' => mt_rand(), - 'filters' => [], - ]; - $p = array_merge($default, $params); - $default_entry = [ - 'url' => '', - 'icon' => '', - 'label' => '', - 'number' => '', - ]; - - $nb_lines = min($p['limit'], count($p['data'])); - array_splice($p['data'], $nb_lines); - $fg_color = Toolbox::getFgColor($p['color']); - $bg_color_2 = Toolbox::getFgColor($p['color'], 5); - - $class = $p['class']; - $class.= count($p['filters']) > 0 ? " filter-".implode(' filter-', $p['filters']) : ""; - - $i = 0; - $list_html = ""; - foreach ($p['data'] as $entry) { - if (!is_array($entry)) { - continue; - } - - $entry = array_merge($default_entry, $entry); - - $href = strlen($entry['url']) - ? "href='{$entry['url']}'" - : ""; - - $author = strlen($entry['author']) - ? " {$entry['author']}" - : ""; - - $content_size = strlen($entry['content']); - $content = strlen($entry['content']) - ? Toolbox::getHtmlToDisplay($entry['content']). - ($content_size > 300 - ? "

...

" - : "" - ) - : ""; - - $list_html.= << - {$entry['label']} -
{$content}
- $author - {$entry['date']} -
-HTML; - $i++; - } - - $nodata = isset($p['data']['nodata']) && $p['data']['nodata']; - if ($nodata) { - $list_html = " - - - - ".__('No data found')." - "; - } - - $view_all = strlen($p['url']) - ? "" - : ""; - - $html = << - #chart-{$p['rand']} .line { - background-color: $bg_color_2; - } - - #chart-{$p['rand']} .fa-eye { - color: {$fg_color}; - } - - -
-
-
    - {$list_html} -
-
- - {$p['label']} - $view_all - - -
-HTML; - - $js = << [...] - * 'colors' => [...] - * ] - */ - public static function getGradientPalette( - string $bgcolor = "", - int $nb_series = 1, - bool $revert = true - ) { - if ($nb_series == 0) { - return [ - 'names' => [], - 'colors' => [], - ]; - } - - if ($nb_series == 1) { - return [ - 'names' => ['a'], - 'colors' => [Toolbox::getFgColor($bgcolor)], - ]; - } - - $alphabet = range('a', 'z'); - $min_l = 20; // min for luminosity - $max_l = 20; // max ... - $min_s = 30; // min for saturation - $max_s = 50; // max ... - $step_l = (100 - ($min_l + $max_l)) / ($nb_series * 100); - $step_s = (100 - ($min_s + $max_s)) / ($nb_series * 100); - - $color_instance = new Color($bgcolor); - $hsl = $color_instance->getHsl(); - - $names = []; - $colors = []; - - for ($i = 1; $i <= $nb_series; $i++) { - $names[$i - 1] = $alphabet[$i - 1]; - - // adjust luminosity - $i_l_step = $i * $step_l + $min_l / 100; - $hsl['L'] = min(1, $revert - ? 1 - $i_l_step - : $i_l_step); - // adjust saturation - if ($hsl['H'] != 0 && $hsl['H'] != 1) { - $i_s_step = $i * $step_s + $min_s / 100; - $hsl['S'] = min(1, $revert - ? $i_s_step - : 1 - $i_s_step); - } - - $colors[$i - 1] = "#".Color::hslToHex($hsl); - } - - return [ - 'names' => $names, - 'colors' => $colors, - ]; - } - - - /** - * Generate a css ruleset for chartist given a starting background color - * Based on @see self::getGradientPalette - */ - public static function getCssGradientPalette( - string $bgcolor = "", - int $nb_series = 1, - string $css_dom_parent = "", - bool $revert = true - ) { - global $GLPI_CACHE; - - $palette = self::getGradientPalette($bgcolor, $nb_series, $revert); - - $series_names = implode(',', $palette['names']); - $series_colors = implode(',', $palette['colors']); - - $hash = sha1($series_names.$series_colors); - if (($palette_css = $GLPI_CACHE->get($hash)) !== null) { - return $palette_css; - } - - $scss = new Compiler(); - $scss->addImportPath(GLPI_ROOT); - - $palette_css = $scss->compile("{$css_dom_parent} { - \$ct-series-names: ({$series_names}); - \$ct-series-colors: ({$series_colors}); - - @import 'css/chartist/generate'; - }"); - - $GLPI_CACHE->set($hash, $palette_css); - - return $palette_css; - } -} diff --git a/inc/define.php b/inc/define.php index d96022f629..12ed68a87c 100644 --- a/inc/define.php +++ b/inc/define.php @@ -410,8 +410,7 @@ 'set_default_requester', 'show_count_on_tabs', 'show_jobs_at_login', 'task_private', 'task_state', 'use_flat_dropdowntree', 'layout', 'palette', - 'highcontrast_css', 'default_dashboard_central', 'default_dashboard_assets', - 'default_dashboard_helpdesk', 'default_dashboard_mini_ticket']; + 'highcontrast_css']; $CFG_GLPI['layout_excluded_pages'] = ["profile.form.php", "knowbaseitem.php", @@ -468,27 +467,20 @@ $CFG_GLPI['appliance_relation_types'] = ['Location', 'Network', 'Domain']; -$dashboard_libs = [ - 'dashboard', 'gridstack', - 'charts', 'clipboard', 'sortable' -]; - $CFG_GLPI['javascript'] = [ 'central' => [ 'central' => array_merge([ 'fullcalendar', 'planning', 'tinymce', - ], $dashboard_libs) + ]) ], 'assets' => [ - 'dashboard' => $dashboard_libs, 'rack' => ['gridstack', 'rack'] ], 'helpdesk' => [ - 'dashboard' => $dashboard_libs, 'planning' => ['clipboard', 'fullcalendar', 'tinymce', 'planning'], - 'ticket' => array_merge(['rateit', 'tinymce', 'photoswipe'], $dashboard_libs), + 'ticket' => ['rateit', 'tinymce', 'photoswipe'], 'problem' => ['tinymce', 'photoswipe'], 'change' => ['tinymce', 'photoswipe'], 'stat' => ['charts'] diff --git a/inc/html.class.php b/inc/html.class.php index 19d3a3db3c..8b3011b171 100644 --- a/inc/html.class.php +++ b/inc/html.class.php @@ -1297,11 +1297,6 @@ static function includeHeader($title = '', $sector = 'none', $item = 'none', $op Html::requireJs('rateit'); } - if (in_array('dashboard', $jslibs)) { - echo Html::scss('css/dashboard'); - Html::requireJs('dashboard'); - } - if (in_array('rack', $jslibs)) { Html::requireJs('rack'); } @@ -1486,7 +1481,7 @@ static function getMenuInfos() { 'CartridgeItem', 'ConsumableItem', 'Phone', 'Rack', 'Enclosure', 'PDU', 'PassiveDCEquipment' ], $CFG_GLPI['devices_in_menu']), - 'default' => '/front/dashboard_assets.php' + 'default' => '/front/computer.php' ], 'helpdesk' => [ 'title' => __('Assistance'), @@ -1494,7 +1489,7 @@ static function getMenuInfos() { 'Ticket', 'Problem', 'Change', 'Planning', 'Stat', 'TicketRecurrent' ], - 'default' => '/front/dashboard_helpdesk.php' + 'default' => '/front/ticket.php' ], 'management' => [ 'title' => __('Management'), @@ -6556,9 +6551,6 @@ static public function requireJs($name) { $_SESSION['glpi_js_toload'][$name][] = 'public/lib/fuzzy.js'; $_SESSION['glpi_js_toload'][$name][] = 'js/fuzzysearch.js'; break; - case 'dashboard': - $_SESSION['glpi_js_toload'][$name][] = 'js/dashboard.js'; - break; case 'gridstack': $_SESSION['glpi_js_toload'][$name][] = 'public/lib/gridstack.js'; break; diff --git a/inc/profile.class.php b/inc/profile.class.php index a7f404782c..c822c795a8 100644 --- a/inc/profile.class.php +++ b/inc/profile.class.php @@ -1563,11 +1563,6 @@ function showFormSetup($openform = true, $closeform = true) { 'label' => __('Personalization'), 'field' => 'personalization' ], - [ - 'itemtype' => 'Glpi\Dashboard\Grid', - 'label' => __('All dashboards'), - 'field' => 'dashboard' - ], [ 'itemtype' => 'DisplayPreference', 'label' => __('Search result display'), diff --git a/install/empty_data.php b/install/empty_data.php index f605c79734..5232652a3b 100644 --- a/install/empty_data.php +++ b/install/empty_data.php @@ -299,10 +299,6 @@ 'password_expiration_delay' => '-1', 'password_expiration_notice' => '-1', 'password_expiration_lock_delay' => '-1', - 'default_dashboard_central' => 'central', - 'default_dashboard_assets' => 'assets', - 'default_dashboard_helpdesk' => 'assistance', - 'default_dashboard_mini_ticket' => 'mini_tickets', 'admin_email_noreply' => '', 'admin_email_noreply_name' => '', Impact::CONF_ENABLED => exportArrayToDB(Impact::getDefaultItemtypes()) @@ -670,29 +666,6 @@ ], ]; -$dashboards_data = include_once __DIR__."/update_94_95/dashboards.php"; -$tables['glpi_dashboards_dashboards'] = []; -$tables['glpi_dashboards_items'] = []; -$i = $j = 1; -foreach ($dashboards_data as $default_dashboard) { - $items = $default_dashboard['_items']; - unset($default_dashboard['_items']); - $tables['glpi_dashboards_dashboards'][] = array_merge([ - 'id' => $i - ], $default_dashboard); - - foreach ($items as $item) { - $tables['glpi_dashboards_items'][] = array_merge([ - 'id' => $j, - 'dashboards_dashboards_id' => $i, - ], $item); - - $j++; - } - - $i++; -} - $tables['glpi_devicememorytypes'] = [ [ 'id' => 1, @@ -7340,38 +7313,6 @@ 'profiles_id' => '8', 'name' => 'externalevent', 'rights' => 1, - ], [ - 'profiles_id' => '1', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '2', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '3', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '4', - 'name' => 'dashboard', - 'rights' => 23, - ], [ - 'profiles_id' => '5', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '6', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '7', - 'name' => 'dashboard', - 'rights' => 0, - ], [ - 'profiles_id' => '8', - 'name' => 'dashboard', - 'rights' => 0, ], [ 'profiles_id' => '1', 'name' => 'appliance', diff --git a/install/mysql/glpi-empty.sql b/install/mysql/glpi-empty.sql index e7721469c8..a34e190647 100644 --- a/install/mysql/glpi-empty.sql +++ b/install/mysql/glpi-empty.sql @@ -1553,48 +1553,6 @@ CREATE TABLE `glpi_crontasks` ( KEY `date_creation` (`date_creation`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Task run by internal / external cron.'; -### Dump table glpi_dashboards_dashboards - -DROP TABLE IF EXISTS `glpi_dashboards_dashboards`; -CREATE TABLE `glpi_dashboards_dashboards` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `key` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `name` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `context` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'core', - PRIMARY KEY (`id`), - UNIQUE KEY `key` (`key`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - -### Dump table glpi_dashboards_items - -DROP TABLE IF EXISTS `glpi_dashboards_items`; -CREATE TABLE `glpi_dashboards_items` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `dashboards_dashboards_id` int(11) NOT NULL, - `gridstack_id` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `card_id` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `x` int(11) DEFAULT NULL, - `y` int(11) DEFAULT NULL, - `width` int(11) DEFAULT NULL, - `height` int(11) DEFAULT NULL, - `card_options` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `dashboards_dashboards_id` (`dashboards_dashboards_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - -### Dump table glpi_dashboards_rights - -DROP TABLE IF EXISTS `glpi_dashboards_rights`; -CREATE TABLE `glpi_dashboards_rights` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `dashboards_dashboards_id` int(11) NOT NULL, - `itemtype` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `items_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `dashboards_dashboards_id` (`dashboards_dashboards_id`), - UNIQUE KEY `unicity` (`dashboards_dashboards_id`, `itemtype`,`items_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - ### Dump table glpi_devicecasemodels DROP TABLE IF EXISTS `glpi_devicecasemodels`; @@ -7076,10 +7034,6 @@ CREATE TABLE `glpi_users` ( `groups_id` int(11) NOT NULL DEFAULT '0', `users_id_supervisor` int(11) NOT NULL DEFAULT '0', `timezone` varchar(50) DEFAULT NULL, - `default_dashboard_central` varchar(100) DEFAULT NULL, - `default_dashboard_assets` varchar(100) DEFAULT NULL, - `default_dashboard_helpdesk` varchar(100) DEFAULT NULL, - `default_dashboard_mini_ticket` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unicityloginauth` (`name`, `authtype`, `auths_id`), KEY `firstname` (`firstname`), diff --git a/install/update_94_95.php b/install/update_94_95.php index 96b6af61a3..0e6c2ead5e 100644 --- a/install/update_94_95.php +++ b/install/update_94_95.php @@ -948,130 +948,6 @@ function update94to95() { $migration->addField("glpi_entities", "altitude", "string"); /** Add geolocation to entity */ - /** Dashboards */ - $migration->addRight('dashboard', READ | UPDATE | CREATE | PURGE, [ - 'config' => UPDATE - ]); - if (!$DB->tableExists('glpi_dashboards_dashboards')) { - $query = "CREATE TABLE `glpi_dashboards_dashboards` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `key` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `name` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `context` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'core', - PRIMARY KEY (`id`), - UNIQUE KEY `key` (`key`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;"; - $DB->queryOrDie($query, "add table glpi_dashboards_dashboards"); - } - if (!$DB->tableExists('glpi_dashboards_items')) { - $query = "CREATE TABLE `glpi_dashboards_items` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `dashboards_dashboards_id` int(11) NOT NULL, - `gridstack_id` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `card_id` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `x` int(11) DEFAULT NULL, - `y` int(11) DEFAULT NULL, - `width` int(11) DEFAULT NULL, - `height` int(11) DEFAULT NULL, - `card_options` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `dashboards_dashboards_id` (`dashboards_dashboards_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci"; - $DB->queryOrDie($query, "add table glpi_dashboards_items"); - } - if (!$DB->tableExists('glpi_dashboards_rights')) { - $query = "CREATE TABLE `glpi_dashboards_rights` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `dashboards_dashboards_id` int(11) NOT NULL, - `itemtype` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `items_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `dashboards_dashboards_id` (`dashboards_dashboards_id`), - UNIQUE KEY `unicity` (`dashboards_dashboards_id`, `itemtype`,`items_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci"; - $DB->queryOrDie($query, "add table glpi_dashboards_rights"); - } - - // migration from previous development versions - $dashboards = Config::getConfigurationValues('core', ['dashboards']); - if (count($dashboards)) { - $dashboards = $dashboards['dashboards']; - \Glpi\Dashboard\Dashboard::importFromJson($dashboards); - Config::deleteConfigurationValues('core', ['dashboards']); - } - - //delete prevous dashboards configuration (remove partial dev versions) - Config::deleteConfigurationValues('core', [ - 'default_dashboard_central', - 'default_dashboard_assets', - 'default_dashboard_helpdesk', - 'default_dashboard_mini_ticket', - ]); - - // add default dashboards - $migration->addConfig([ - 'default_dashboard_central' => 'central', - 'default_dashboard_assets' => 'assets', - 'default_dashboard_helpdesk' => 'assistance', - 'default_dashboard_mini_ticket' => 'mini_tickets', - ]); - - if (!$DB->fieldExists('glpi_users', 'default_dashboard_central')) { - $migration->addField("glpi_users", "default_dashboard_central", "varchar(100) DEFAULT NULL"); - } - if (!$DB->fieldExists('glpi_users', 'default_dashboard_assets')) { - $migration->addField("glpi_users", "default_dashboard_assets", "varchar(100) DEFAULT NULL"); - } - if (!$DB->fieldExists('glpi_users', 'default_dashboard_helpdesk')) { - $migration->addField("glpi_users", "default_dashboard_helpdesk", "varchar(100) DEFAULT NULL"); - } - if (!$DB->fieldExists('glpi_users', 'default_dashboard_mini_ticket')) { - $migration->addField("glpi_users", "default_dashboard_mini_ticket", "varchar(100) DEFAULT NULL"); - } - - // default dashboards - if (countElementsInTable("glpi_dashboards_dashboards") === 0) { - $dashboard_obj = new \Glpi\Dashboard\Dashboard(); - $dashboards_data = include_once __DIR__."/update_94_95/dashboards.php"; - foreach ($dashboards_data as $default_dashboard) { - $items = $default_dashboard['_items']; - unset($default_dashboard['_items']); - - // add current dashboard - $dashboard_id = $dashboard_obj->add($default_dashboard); - - // add items to this new dashboard - $query = $DB->buildInsert( - \Glpi\Dashboard\Item::getTable(), - [ - 'dashboards_dashboards_id' => new QueryParam(), - 'gridstack_id' => new QueryParam(), - 'card_id' => new QueryParam(), - 'x' => new QueryParam(), - 'y' => new QueryParam(), - 'width' => new QueryParam(), - 'height' => new QueryParam(), - 'card_options' => new QueryParam(), - ] - ); - $stmt = $DB->prepare($query); - foreach ($items as $item) { - $stmt->bind_param('issiiiis', - $dashboard_id, - $item['gridstack_id'], - $item['card_id'], - $item['x'], - $item['y'], - $item['width'], - $item['height'], - $item['card_options'] - ); - $stmt->execute(); - } - } - } - /** /Dashboards */ - /** Domains */ if (!$DB->tableExists('glpi_domaintypes')) { $query = "CREATE TABLE `glpi_domaintypes` ( diff --git a/install/update_94_95/dashboards.php b/install/update_94_95/dashboards.php deleted file mode 100644 index 60dc33ab65..0000000000 --- a/install/update_94_95/dashboards.php +++ /dev/null @@ -1,383 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -return [ - [ - 'key' => 'central', - 'name' => __("Central"), - 'context' => 'core', - '_items' => [ - [ - "x" => 3, "y" => 0, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Computer_4a315743-151c-40cb-a20b-762250668dac", - "card_id" => "bn_count_Computer", - "card_options" => "{\"color\":\"#e69393\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 0, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Software_0690f524-e826-47a9-b50a-906451196b83", - "card_id" => "bn_count_Software", - "card_options" => "{\"color\":\"#aaddac\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 6, "y" => 2, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Rack_c6502e0a-5991-46b4-a771-7f355137306b", - "card_id" => "bn_count_Rack", - "card_options" => "{\"color\":\"#0e87a0\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 2, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_SoftwareLicense_e755fd06-283e-4479-ba35-2d548f8f8a90", - "card_id" => "bn_count_SoftwareLicense", - "card_options" => "{\"color\":\"#27ab3c\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 3, "y" => 2, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Monitor_7059b94c-583c-4ba7-b100-d40461165318", - "card_id" => "bn_count_Monitor", - "card_options" => "{\"color\":\"#b52d30\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 14, "y" => 7, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Ticket_a74c0903-3387-4a07-9111-b0938af8f1e7", - "card_id" => "bn_count_Ticket", - "card_options" => "{\"color\":\"#ffdc64\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 20, "y" => 7, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Problem_c1cf5cfb-f626-472e-82a1-49c3e200e746", - "card_id" => "bn_count_Problem", - "card_options" => "{\"color\":\"#f08d7b\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 4, "width" => 5, "height" => 4, - "gridstack_id" => "count_Computer_Manufacturer_6129c451-42b5-489d-b693-c362adf32d49", - "card_id" => "count_Computer_Manufacturer", - "card_options" => "{\"color\":\"#f8faf9\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 14, "y" => 9, "width" => 6, "height" => 5, - "gridstack_id" => "top_ticket_user_requester_c74f52a8-046a-4077-b1a6-c9f840d34b82", - "card_id" => "top_ticket_user_requester", - "card_options" => "{\"color\":\"#f9fafb\",\"widgettype\":\"hbar\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 17, "y" => 7, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_late_04c47208-d7e5-4aca-9566-d46e68c45c67", - "card_id" => "bn_count_tickets_late", - "card_options" => "{\"color\":\"#f8911f\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 14, "y" => 0, "width" => 12, "height" => 7, - "gridstack_id" => "ticket_status_2e4e968b-d4e6-4e33-9ce9-a1aaff53dfde", - "card_id" => "ticket_status", - "card_options" => "{\"color\":\"#fafafa\",\"widgettype\":\"stackedbars\",\"use_gradient\":\"0\",\"limit\":\"12\"}", - ], [ - "x" => 20, "y" => 9, "width" => 6, "height" => 5, - "gridstack_id" => "top_ticket_ITILCategory_37736ba9-d429-4cb3-9058-ef4d111d9269", - "card_id" => "top_ticket_ITILCategory", - "card_options" => "{\"color\":\"#fbf9f9\",\"widgettype\":\"hbar\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 9, "y" => 2, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Printer_517684b0-b064-49dd-943e-fcb6f915e453", - "card_id" => "bn_count_Printer", - "card_options" => "{\"color\":\"#365a8f\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 9, "y" => 0, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Phone_f70c489f-02c1-46e5-978b-94a95b5038ee", - "card_id" => "bn_count_Phone", - "card_options" => "{\"color\":\"#d5e1ec\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 23, "y" => 7, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Change_ab950dbd-cd25-466d-8dff-7dcaca386564", - "card_id" => "bn_count_Change", - "card_options" => "{\"color\":\"#cae3c4\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 4, "y" => 8, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_Group_b84a93f2-a26c-49d7-82a4-5446697cc5b0", - "card_id" => "bn_count_Group", - "card_options" => "{\"color\":\"#e0e0e0\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 4, "y" => 10, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_Profile_770b35e8-68e9-4b4f-9e09-5a11058f069f", - "card_id" => "bn_count_Profile", - "card_options" => "{\"color\":\"#e0e0e0\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 8, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Supplier_36ff9011-e4cf-4d89-b9ab-346b9857d734", - "card_id" => "bn_count_Supplier", - "card_options" => "{\"color\":\"#c9c9c9\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 8, "y" => 10, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_KnowbaseItem_a3785a56-bed4-4a30-8387-f251f5365b3b", - "card_id" => "bn_count_KnowbaseItem", - "card_options" => "{\"color\":\"#c9c9c9\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 10, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_Entity_9b82951a-ba52-45cc-a2d3-1d238ec37adf", - "card_id" => "bn_count_Entity", - "card_options" => "{\"color\":\"#f9f9f9\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 11, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Document_7dc7f4b8-61ff-4147-b994-5541bddd7b66", - "card_id" => "bn_count_Document", - "card_options" => "{\"color\":\"#b4b4b4\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 11, "y" => 10, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Project_4d412ee2-8b79-469b-995f-4c0a05ab849d", - "card_id" => "bn_count_Project", - "card_options" => "{\"color\":\"#b3b3b3\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 6, "y" => 0, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_NetworkEquipment_c537e334-d584-43bc-b6de-b4a939143e89", - "card_id" => "bn_count_NetworkEquipment", - "card_options" => "{\"color\":\"#bfe7ea\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 8, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_User_ac0cbe52-3593-43c1-8ecc-0eb115de494d", - "card_id" => "bn_count_User", - "card_options" => "{\"color\":\"#fafafa\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 5, "y" => 4, "width" => 5, "height" => 4, - "gridstack_id" => "count_Monitor_MonitorModel_5a476ff9-116e-4270-858b-c003c20841a9", - "card_id" => "count_Monitor_MonitorModel", - "card_options" => "{\"color\":\"#f5fafa\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 10, "y" => 4, "width" => 4, "height" => 4, - "gridstack_id" => "count_NetworkEquipment_State_81f2ae35-b366-4065-ac26-02ea4e3704a6", - "card_id" => "count_NetworkEquipment_State", - "card_options" => "{\"color\":\"#f5f3ef\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ] - ] - ], [ - 'key' => 'assets', - 'name' => __("Assets"), - 'context' => 'core', - '_items' => [ - [ - "x" => 0, "y" => 0, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Computer_34cfbaf9-a471-4852-b48c-0dadea7644de", - "card_id" => "bn_count_Computer", - "card_options" => "{\"color\":\"#f3d0d0\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 4, "y" => 0, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Software_60091467-2137-49f4-8834-f6602a482079", - "card_id" => "bn_count_Software", - "card_options" => "{\"color\":\"#d1f1a8\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 8, "y" => 3, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Printer_c9a385d4-76a3-4971-ad0e-1470efeafacc", - "card_id" => "bn_count_Printer", - "card_options" => "{\"color\":\"#5da8d6\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 12, "y" => 3, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_PDU_60053eb6-8dda-4416-9a4b-afd51889bd09", - "card_id" => "bn_count_PDU", - "card_options" => "{\"color\":\"#ffb62f\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 12, "y" => 0, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Rack_0fdc196f-20d2-4f63-9ddb-b75c165cc664", - "card_id" => "bn_count_Rack", - "card_options" => "{\"color\":\"#f7d79a\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 16, "y" => 3, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Phone_c31fde2d-510a-4482-b17d-2f65b61eae08", - "card_id" => "bn_count_Phone", - "card_options" => "{\"color\":\"#a0cec2\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 16, "y" => 0, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Enclosure_c21ce30a-58c3-456a-81ec-3c5f01527a8f", - "card_id" => "bn_count_Enclosure", - "card_options" => "{\"color\":\"#d7e8e4\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 8, "y" => 0, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_NetworkEquipment_76f1e239-777b-4552-b053-ae5c64190347", - "card_id" => "bn_count_NetworkEquipment", - "card_options" => "{\"color\":\"#c8dae4\",\"widgettype\":\"bigNumber\"}", - ], [ - "x" => 4, "y" => 3, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_SoftwareLicense_576e58fe-a386-480f-b405-1c2315b8ab47", - "card_id" => "bn_count_SoftwareLicense", - "card_options" => "{\"color\":\"#9bc06b\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 3, "width" => 4, "height" => 3, - "gridstack_id" => "bn_count_Monitor_890e16d3-b121-48c6-9713-d9c239d9a970", - "card_id" => "bn_count_Monitor", - "card_options" => "{\"color\":\"#dc6f6f\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 4, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "count_Computer_Manufacturer_986e92e8-32e8-4a6f-806f-6f5383acbb3f", - "card_id" => "count_Computer_Manufacturer", - "card_options" => "{\"color\":\"#f3f5f1\",\"widgettype\":\"hbar\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 0, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "count_Computer_State_290c5920-9eab-4db8-8753-46108e60f1d8", - "card_id" => "count_Computer_State", - "card_options" => "{\"color\":\"#fbf7f7\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 8, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "count_Computer_ComputerType_c58f9c7e-22d5-478b-8226-d2a752bcbb09", - "card_id" => "count_Computer_ComputerType", - "card_options" => "{\"color\":\"#f5f9fa\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ], [ - "x" => 12, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "count_NetworkEquipment_Manufacturer_8132b21c-6f7f-4dc1-af54-bea794cb96e9", - "card_id" => "count_NetworkEquipment_Manufacturer", - "card_options" => "{\"color\":\"#fcf8ed\",\"widgettype\":\"hbar\",\"use_gradient\":\"0\",\"limit\":\"5\"}", - ], [ - "x" => 16, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "count_Monitor_Manufacturer_43b0c16b-af82-418e-aac1-f32b39705c0d", - "card_id" => "count_Monitor_Manufacturer", - "card_options" => "{\"color\":\"#f9fbfb\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"5\"}", - ] - ] - ], [ - 'key' => 'assistance', - 'name' => __("Assistance"), - 'context' => 'core', - '_items' => [ - [ - "x" => 0, "y" => 0, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Ticket_344e761b-f7e8-4617-8c90-154b266b4d67", - "card_id" => "bn_count_Ticket", - "card_options" => "{\"color\":\"#ffdc64\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 4, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Problem_bdb4002b-a674-4493-820f-af85bed44d2a", - "card_id" => "bn_count_Problem", - "card_options" => "{\"color\":\"#f0967b\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 6, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_Change_b9b87513-4f40-41e6-8621-f51f9a30fb19", - "card_id" => "bn_count_Change", - "card_options" => "{\"color\":\"#cae3c4\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 2, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_late_1e9ae481-21b4-4463-a830-dec1b68ec5e7", - "card_id" => "bn_count_tickets_late", - "card_options" => "{\"color\":\"#f8911f\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 3, "y" => 6, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_incoming_336a36d9-67fe-4475-880e-447bd766b8fe", - "card_id" => "bn_count_tickets_incoming", - "card_options" => "{\"color\":\"#a0e19d\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 9, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_closed_e004bab5-f2b6-4060-a401-a2a8b9885245", - "card_id" => "bn_count_tickets_closed", - "card_options" => "{\"color\":\"#515151\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 6, "y" => 6, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_assigned_7455c855-6df8-4514-a3d9-8b0fce52bd63", - "card_id" => "bn_count_tickets_assigned", - "card_options" => "{\"color\":\"#eaf5f7\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 9, "y" => 6, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_solved_5e9759b3-ee7e-4a14-b68f-1ac024ef55ee", - "card_id" => "bn_count_tickets_solved", - "card_options" => "{\"color\":\"#d8d8d8\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 3, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_waiting_102b2c2a-6ac6-4d73-ba47-8b09382fe00e", - "card_id" => "bn_count_tickets_waiting", - "card_options" => "{\"color\":\"#ffcb7d\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_TicketRecurrent_13f79539-61f6-45f7-8dde-045706e652f2", - "card_id" => "bn_count_TicketRecurrent", - "card_options" => "{\"color\":\"#fafafa\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 6, "y" => 8, "width" => 3, "height" => 2, - "gridstack_id" => "bn_count_tickets_planned_267bf627-9d5e-4b6c-b53d-b8623d793ccf", - "card_id" => "bn_count_tickets_planned", - "card_options" => "{\"color\":\"#6298d5\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 12, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "top_ticket_ITILCategory_0cba0c84-6c62-4cd8-8564-18614498d8e4", - "card_id" => "top_ticket_ITILCategory", - "card_options" => "{\"color\":\"#f1f5ef\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"7\"}", - ], [ - "x" => 16, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "top_ticket_RequestType_b9e43f34-8e94-4a6e-9023-c5d1e2ce7859", - "card_id" => "top_ticket_RequestType", - "card_options" => "{\"color\":\"#f9fafb\",\"widgettype\":\"hbar\",\"use_gradient\":\"1\",\"limit\":\"4\"}", - ], [ - "x" => 20, "y" => 6, "width" => 4, "height" => 4, - "gridstack_id" => "top_ticket_Entity_a8e65812-519c-488e-9892-9adbe22fbd5c", - "card_id" => "top_ticket_Entity", - "card_options" => "{\"color\":\"#f7f1f0\",\"widgettype\":\"donut\",\"use_gradient\":\"1\",\"limit\":\"7\"}", - ], [ - "x" => 3, "y" => 0, "width" => 12, "height" => 6, - "gridstack_id" => "ticket_evolution_76fd4926-ee5e-48db-b6d6-e2947c190c5e", - "card_id" => "ticket_evolution", - "card_options" => "{\"color\":\"#f3f7f8\",\"widgettype\":\"areas\",\"use_gradient\":\"0\",\"limit\":\"12\"}", - ], [ - "x" => 15, "y" => 0, "width" => 11, "height" => 6, - "gridstack_id" => "ticket_status_5b256a35-b36b-4db5-ba11-ea7c125f126e", - "card_id" => "ticket_status", - "card_options" => "{\"color\":\"#f7f3f2\",\"widgettype\":\"stackedbars\",\"use_gradient\":\"0\",\"limit\":\"12\"}", - ] - ] - ], [ - 'key' => 'mini_tickets', - 'name' => __("Mini tickets dashboard"), - 'context' => 'mini_core', - '_items' => [ - [ - "x" => 24, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_closed_ccf7246b-645a-40d2-8206-fa33c769e3f5", - "card_id" => "bn_count_tickets_closed", - "card_options" => "{\"color\":\"#fafafa\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 0, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_Ticket_d5bf3576-5033-40fb-bbdb-292294a7698e", - "card_id" => "bn_count_Ticket", - "card_options" => "{\"color\":\"#ffd957\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 4, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_incoming_055e813c-b0ce-4687-91ef-559249e8ddd8", - "card_id" => "bn_count_tickets_incoming", - "card_options" => "{\"color\":\"#6fd169\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 8, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_waiting_793c665b-b620-4b3a-a5a8-cf502defc008", - "card_id" => "bn_count_tickets_waiting", - "card_options" => "{\"color\":\"#ffcb7d\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 12, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_assigned_d3d2f697-52b4-435e-9030-a760dd649085", - "card_id" => "bn_count_tickets_assigned", - "card_options" => "{\"color\":\"#eaf4f7\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 16, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_planned_0c7f3569-c23b-4ee3-8e85-279229b23e70", - "card_id" => "bn_count_tickets_planned", - "card_options" => "{\"color\":\"#6298d5\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ], [ - "x" => 20, "y" => 0, "width" => 4, "height" => 2, - "gridstack_id" => "bn_count_tickets_solved_ae2406cf-e8e8-410b-b355-46e3f5705ee8", - "card_id" => "bn_count_tickets_solved", - "card_options" => "{\"color\":\"#d7d7d7\",\"widgettype\":\"bigNumber\",\"use_gradient\":\"0\",\"limit\":\"7\"}", - ] - ] - ] -]; diff --git a/js/dashboard.js b/js/dashboard.js deleted file mode 100644 index f3019a9dc6..0000000000 --- a/js/dashboard.js +++ /dev/null @@ -1,1131 +0,0 @@ -/** - * --------------------------------------------------------------------- - * GLPI - Gestionnaire Libre de Parc Informatique - * Copyright (C) 2015-2021 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 . - * --------------------------------------------------------------------- - */ - -/* global GoInFullscreen, GoOutFullscreen, EasyMDE, getUuidV4, _, sortable */ - -var Dashboard = { - grid: null, - elem_id: "", - element: null, - elem_dom: null, - rand: null, - interval: null, - current_name: null, - markdown_editors: [], - all_cards: [], - all_widgets: [], - edit_mode: false, - embed: false, - ajax_cards: false, - context: "core", - markdown_contents: [], - dash_width: 0, - cell_margin: 3, - cols: 26, - cache_key: "", - - display: function(params) { - - // get passed options and merge it with default ones - var options = (typeof params !== 'undefined') - ? params: {}; - var default_options = { - cols: 24, - rows: 24, - cell_length: 40, - cell_margin: 5, - rand: '', - embed: false, - ajax_cards: true, - all_cards: [], - context: "core" - }; - options = Object.assign({}, default_options, options); - - this.rand = options.rand; - this.elem_id = "#dashboard-"+options.rand; - this.element = $(Dashboard.elem_id); - this.elem_dom = Dashboard.element[0]; - this.current_name = $(this.elem_id+' .dashboard_select').val() || options.current; - this.embed = options.embed; - this.ajax_cards = options.ajax_cards; - this.all_cards = options.all_cards; - this.all_widgets = options.all_widgets; - this.context = options.context; - this.dash_width = this.element.width(); - this.cell_margin = options.cell_margin; - this.cols = options.cols; - this.cache_key = options.cache_key || ""; - - $('#grid-stack-'+options.rand).gridstack({ - column: options.cols, - maxRow: (options.rows + 1), // +1 for a hidden item at bottom (to fix height) - verticalMargin: this.cell_margin, - float: true, // widget can be placed anywhere on the grid, not only on top - animate: false, // as we don't move widget automatically, we don't need animation - draggable: { // override jquery ui draggable options - 'cancel': 'textarea' // avoid draggable on some child elements - } - }); - Dashboard.grid = $('#grid-stack-'+options.rand).data('gridstack'); - - // set grid in static to prevent edition (unless user click on edit button) - // previously in option, but current version of gridstack has a bug with one column mode (responsive) - // see https://github.com/gridstack/gridstack.js/issues/1229 - Dashboard.grid.setStatic(true); - - // generate the css based on the grid width - Dashboard.generateCss(); - - // init filters from storage - Dashboard.initFilters(); - - // retieve cards content by ajax - if (Dashboard.ajax_cards) { - Dashboard.getCardsAjax(); - } - - // animate the dashboards - if (!Dashboard.ajax_cards) { - Dashboard.fitNumbers(); - Dashboard.animateNumbers(); - } - - // change dashboard - $("#dashboard-"+options.rand+" .toolbar .dashboard_select").change(function() { - Dashboard.current_name = $(this).val(); - var selected_label = $(this).find("option:selected").text(); - $(".dashboard-name").val(selected_label); - Dashboard.refreshDashboard(); - Dashboard.setLastDashboard(); - Dashboard.initFilters(); - }); - - // add dashboard - $("#dashboard-"+options.rand+" .toolbar .add-dashboard").click(function() { - Dashboard.addForm(); - }); - $(document).on('submit', '.display-add-dashboard-form', function(event) { - event.preventDefault(); - - $(".ui-dialog-content").dialog("close"); - var button = $(this); - var form_data = {}; - $.each(button.closest('.display-add-dashboard-form').serializeArray(), function() { - form_data[this.name] = this.value; - }); - - Dashboard.addNew(form_data); - }); - - // delete dashboard - $("#dashboard-"+options.rand+" .toolbar .delete-dashboard").click(function() { - Dashboard.delete(); - }); - - //clone dashboard - $("#dashboard-"+options.rand+" .toolbar .clone-dashboard").click(function() { - Dashboard.clone(); - }); - - // embed mode toggle - $("#dashboard-"+options.rand+" .toolbar .open-embed").click(function() { - $('
') - .load(CFG_GLPI.root_doc+"/ajax/dashboard.php", { - action: 'display_embed_form', - dashboard: Dashboard.current_name - }, function() { - $(this).dialog({ - width: 300, - modal: true, - open: function() { - $(this).find('input').first().focus(); - } - }); - }); - }); - - // edit mode toggle - $("#dashboard-"+options.rand+" .toolbar .edit-dashboard").click(function() { - var activate = !$(this).hasClass('active'); - - Dashboard.setEditMode(activate); - }); - - // fullscreen mode toggle - var expand_selector = "#dashboard-"+options.rand+" .toggle-fullscreen"; - $(expand_selector).click(function() { - Dashboard.toggleFullscreenMode($(this)); - }); - // trigger fullscreen off (by esc key) - $(document).on('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', function() { - if (!document.webkitIsFullScreen - && !document.mozFullScreen - && !document.msFullscreenElement !== null) { - Dashboard.disableFullscreenMode(); - } - }); - - // night mode toggle - $("#dashboard-"+options.rand+" .toolbar .night-mode").click(function() { - $(this).toggleClass('active'); - Dashboard.element.toggleClass('nightmode'); - }); - - // refresh mode toggle - $("#dashboard-"+options.rand+" .toolbar .auto-refresh").click(function() { - $(this).toggleClass('active'); - var active = $(this).hasClass('active'); - - if (active) { - var seconds = parseInt(CFG_GLPI.refresh_ticket_list) * 60 || 30; - Dashboard.interval = setInterval(function() { - Dashboard.refreshDashboard(); - }, seconds * 1000); - } else { - clearInterval(Dashboard.interval); - } - }); - - // browser resized (use debounce to delay generation of css) - var debounce; - $(window).on('resize', function(event) { - if (event.target.constructor.name !== "Window") { - return; - } - - window.clearTimeout(debounce); - debounce = window.setTimeout(function() { - Dashboard.generateCss(); - - // fit again numbers - Dashboard.fitNumbers(); - Dashboard.animateNumbers(); - }, 200); - }); - - // publish rights - $(document).on('click', '.display-rights-form .save_rights', function() { - $(".ui-dialog-content").dialog("close"); - - var button = $(this); - var form_data = {}; - $.each(button.closest('.display-rights-form').serializeArray(), function() { - var current_val = this.value.split('-'); - var right_name = current_val[0]; - var value = current_val[1]; - if (!(right_name in form_data)) { - form_data[right_name] = []; - } - form_data[right_name].push(value); - }); - - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'save_rights', - dashboard: Dashboard.current_name, - rights: form_data, - } - }); - }); - - // event: moving item - $('#grid-stack-'+options.rand).on('dragstop', function() { - Dashboard.saveDashboard(); - }); - - // event: resize item - $('#grid-stack-'+options.rand).on('gsresizestop', function(event, elem) { - Dashboard.saveDashboard(); - - // resize also chart if exists - var chart = $(elem).find('.ct-chart'); - if (chart.length > 0) { - chart[0].__chartist__.update(); - } - - // animate the number - Dashboard.fitNumbers($(elem)); - Dashboard.animateNumbers($(elem)); - }); - - // delete item - $(document).on('click', "#dashboard-"+options.rand+" .delete-item", function() { - var del_ctrl = $(this); - var item = del_ctrl.closest('.grid-stack-item'); - - Dashboard.grid.removeWidget(item); - Dashboard.saveDashboard(); - }); - - // refresh item - $(document).on('click', "#dashboard-"+options.rand+" .refresh-item", function() { - var refresh_ctrl = $(this); - var item = refresh_ctrl.closest('.grid-stack-item'); - var id = item.data('gs-id'); - - Dashboard.getCardsAjax("[data-gs-id="+id+"]"); - }); - - // edit item - $(document).on('click', "#dashboard-"+options.rand+" .edit-item", function() { - var edit_ctrl = $(this); - var item = edit_ctrl.parent().parent('.grid-stack-item'); - var card_opt = item.data('card-options'); - - $(".ui-dialog-content").dialog("close"); - $('
') - .load(CFG_GLPI.root_doc+"/ajax/dashboard.php", { - action: 'display_edit_widget', - gridstack_id: item.data('gs-id'), - card_id: card_opt.card_id, - x: item.data('gs-x'), - y: item.data('gs-y'), - width: item.data('gs-width'), - height: item.data('gs-height'), - card_options: card_opt, - }, function() { - $(this).dialog({ - width: 'auto', - modal: true, - open: function() { - $(this).find('input[type=submit]').first().focus(); - } - }); - }); - }); - - // add new widget form - $(document).on("click", "#dashboard-"+options.rand+" .cell-add", function() { - var add_ctrl = $(this); - - $(".ui-dialog-content").dialog("close"); - $('
') - .load(CFG_GLPI.root_doc+"/ajax/dashboard.php", { - action: 'display_add_widget', - x: add_ctrl.data('x'), - y: add_ctrl.data('y') - }, function() { - $(this).dialog({ - width: 'auto', - modal: true, - open: function() { - $(this).find('input[type=submit]').first().focus(); - } - }); - }); - }); - - // save new or existing widget (submit form) - $(document).on('submit', '.display-widget-form ', function(event) { - event.preventDefault(); - - var form = $(this); - var edit = form.has('.edit-widget').length > 0; - - Dashboard.setWidgetFromForm(form, edit); - }); - - // add new filter - $(document).on("click", "#dashboard-"+options.rand+" .filters_toolbar .add-filter", function() { - $(".ui-dialog-content").dialog("close"); - - var filters = Dashboard.getFiltersFromStorage(); - var filter_names = Object.keys(filters); - - $('
') - .load(CFG_GLPI.root_doc+"/ajax/dashboard.php", { - action: 'display_add_filter', - used: filter_names - }, function() { - $(this).dialog({ - width: 'auto', - modal: true, - open: function() { - $(this).find('input[type=submit]').first().focus(); - } - }); - }); - }); - - // save new filter (submit form) - $(document).on('submit', '.display-filter-form ', function(event) { - event.preventDefault(); - - var form = $(this); - - Dashboard.setFilterFromForm(form); - }); - - // delete existing filter - $(document).on("click", "#dashboard-"+options.rand+" .filters_toolbar .delete-filter", function() { - var filter = $(this).closest('.filter'); - var filter_id = filter.data('filter-id'); - - // remove filter from dom - filter.remove(); - - // remove filter from storage and refresh cards - var filters = Dashboard.getFiltersFromStorage(); - delete filters[filter_id]; - Dashboard.setFiltersInStorage(filters); - Dashboard.refreshCardsImpactedByFilter(filter_id); - }); - - // rename dashboard - $(document).on('click', '.save-dashboard-name ', function(event) { - event.preventDefault(); - // change in selector - $('.dashboard_select option[value='+Dashboard.current_name+']') - .text($(".dashboard-name").val()); - Dashboard.saveDashboard(); - - $('.display-message') - .addClass('success') - .text(__("Saved")) - .show('fade').delay(2000).hide('fade'); - }); - - // display widget types after selecting a card - $(document).on('select2:select', '.display-widget-form select[name=card_id]', function(event) { - var select2_data = event.params.data; - var selected = select2_data.id; - var widgettype_field = $(this).closest('.field').siblings('.widgettype_field'); - var available_widgets = Dashboard.all_cards[selected].widgettype; - var force_checked = available_widgets.length === 1; - - widgettype_field - .show() - .find('input[type=radio]') - .next('label').css('display', 'none').end() - .filter("[value='"+available_widgets.join("'],[value='")+"']") - .prop("checked", force_checked) - .trigger('change') - .next('label').css('display', 'inline-block'); - }); - - // display gradient and limit after selecting a widget - $(document).on('change', '.display-widget-form [name=widgettype]', function() { - var widgetdom = $(this); - var widgettype = widgetdom.val(); - var widget = Dashboard.all_widgets[widgettype]; - var usegradient = widget.gradient || false; - var pointlabels = widget.pointlbl || false; - var uselimit = widget.limit || false; - var width = widget.width || 2; - var height = widget.height || 2; - - widgetdom - .closest('.field') - .siblings('.gradient_field') - .hide() - .toggle(usegradient).end() - .siblings('.pointlbl_field') - .hide() - .toggle(pointlabels).end() - .siblings('.limit_field') - .hide() - .toggle(uselimit).end(); - - var width_field = widgetdom - .closest('.field') - .siblings('[name="width"]'); - var height_field = widgetdom - .closest('.field') - .siblings('[name="height"]'); - - if (width_field.val() == 0) { - width_field.val(width); - } - if (height_field.val() == 0) { - height_field.val(height); - } - }); - - // markdown textarea edited - $(document).on('input', '.card.markdown textarea.markdown_content', function() { - Dashboard.saveMarkdown($(this)); - }); - }, - - saveMarkdown:function(textarea) { - var item = textarea.closest('.grid-stack-item'); - var content = textarea.val(); - var gs_id = item.data('gs-id'); - - item.addClass('dirty'); - Dashboard.markdown_contents[gs_id] = content; - }, - - setWidgetFromForm: function(form, edit_item) { - edit_item = edit_item || false; - - $(".ui-dialog-content").dialog("close"); - var form_data = {}; - - $.each(form.serializeArray(), function() { - form_data[this.name] = this.value; - }); - - // no card selected - if (form_data.card_id === "0") { - return false; - } - - form_data.card_options = form_data.card_options || {}; - if (typeof form_data.card_options === "string") { - form_data.card_options = JSON.parse(form_data.card_options); - } - - // prepare options - form_data.card_options.color = form_data.color || null; - form_data.card_options.widgettype = form_data.widgettype || null; - form_data.card_options.use_gradient = form_data.use_gradient || 0; - form_data.card_options.point_labels = form_data.point_labels || 0; - form_data.card_options.limit = form_data.limit || 7; - - // specific case for markdown - if (form_data.card_id === "markdown_editable" - && !('markdown_content' in form_data.card_options)) { - form_data.card_options.markdown_content = ""; - } - - // id edit mode remove old item before adding the new - if (edit_item === true) { - if (form_data.old_id === "0") { - return false; - } - var item = $('.grid-stack-item[data-gs-id='+form_data.old_id+']'); - Dashboard.grid.removeWidget(item); - } - - // complete ajax data - var uuid = getUuidV4(); - form_data.gridstack_id = form_data.card_id+"_"+uuid; - form_data.card_options.card_id = form_data.card_id; - form_data.card_options.gridstack_id = form_data.gridstack_id; - - var args = form_data.card_options; - args.force = true; - - // add the new widget - var widget = Dashboard.addWidget(form_data); - - // get the html of the new card and save dashboard - $.get({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'get_card', - dashboard: Dashboard.current_name, - card_id: form_data.card_id, - cache_key: Dashboard.cache_key, - args: args, - } - }).done(function(card_html) { - widget - .children('.grid-stack-item-content') - .append(card_html); - Dashboard.fitNumbers(widget); - Dashboard.animateNumbers(widget); - Dashboard.saveDashboard(); - }); - }, - - addWidget: function(p) { - var gridstack_id = p.gridstack_id; - var x = p.x || -1; - var y = p.y || -1; - var width = p.width || 2; - var height = p.height || 2; - var options = p.card_options || {}; - - var html = ' \ -
\ - \ - \ - \ - \ - \ -
\ -
\ -
'; - - // add the widget to the grid - var widget = Dashboard.grid.addWidget( - html, - x, - y, - width, - height, - x < 0 || y < 0, - undefined, undefined, undefined, undefined, // min, max dimensions - gridstack_id - ); - - // append options - widget.data('card-options', options); - - return widget; - }, - - setFilterFromForm: function(form) { - $(".ui-dialog-content").dialog("close"); - var form_data = {}; - - $.each(form.serializeArray(), function() { - form_data[this.name] = this.value; - }); - - // get the html of the new card and save dashboard - $.get({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'get_filter', - filter_id: form_data.filter_id, - } - }).done(function(filter_html) { - $('.filters').append(filter_html); - Dashboard.saveFilter(form_data.filter_id, []); - }); - }, - - refreshDashboard: function() { - var gridstack = $(Dashboard.elem_id+" .grid-stack"); - Dashboard.grid.removeAll(); - - $.get({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - dashboard: Dashboard.current_name, - action: 'get_dashboard_items', - embed: (Dashboard.embed ? 1 : 0), - } - }).done(function(html) { - gridstack.prepend(html); - gridstack.find('.grid-stack-item').each(function() { - Dashboard.grid.makeWidget($(this)); - }); - - if (Dashboard.ajax_cards) { - Dashboard.getCardsAjax(); - } - }); - }, - - setLastDashboard: function() { - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - dashboard: Dashboard.current_name, - page: (location.origin+location.pathname) - .replace(CFG_GLPI.url_base, ''), - action: 'set_last_dashboard', - } - }); - }, - - saveFilter: function(filter_id, value) { - // store current filter in localStorage - var filters = Dashboard.getFiltersFromStorage(); - filters[filter_id] = value; - Dashboard.setFiltersInStorage(filters); - - // refresh all card impacted by the changed filter - Dashboard.refreshCardsImpactedByFilter(filter_id); - }, - - refreshCardsImpactedByFilter: function(filter_id) { - $('.dashboard .card.filter-'+filter_id).each(function () { - var gridstack_item = $(this).closest(".grid-stack-item"); - var card_id = gridstack_item.data('gs-id'); - Dashboard.getCardsAjax("[data-gs-id="+card_id+"]"); - }); - }, - - saveDashboard: function(force_refresh) { - force_refresh = force_refresh | false; - - var serializedData = $.makeArray( - Dashboard.element.find('.grid-stack-item:visible:not(.grid-stack-placeholder)') - ) .map(function (v) { - var n = $(v).data('_gridstack_node'); - var options = $(v).data('card-options'); - - // replace markdown content (this to avoid unwanted slashing) - if (_.keys(Dashboard.markdown_contents).length > 0 - && n.id in Dashboard.markdown_contents) { - options.markdown_content = Dashboard.markdown_contents[n.id]; - } - - return n ? { - gridstack_id: n.id, - card_id: options.card_id, - x: n.x, - y: n.y, - width: n.width, - height: n.height, - card_options: options - } : null; - }); - - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'save_items', - dashboard: Dashboard.current_name, - items: serializedData, - title: $(".dashboard-name").val() - } - }).done(function() { - if (force_refresh) { - Dashboard.refreshDashboard(); - } - }); - }, - - fitNumbers: function(parent_item) { - parent_item = parent_item || $('body'); - - var text_offset = 0.96; - - // responsive mode - if (this.dash_width <= 700 - || this.grid.container.hasClass('grid-stack-one-column-mode')) { - text_offset = 1.8; - } - - parent_item - .find('.big-number') - .find('.formatted-number').fitText(text_offset); - - parent_item - .find('.summary-numbers') - .find('.formatted-number').fitText(text_offset-0.65); - parent_item - .find('.summary-numbers') - .find('.line .label').fitText(text_offset-0.2); - - parent_item - .find('.big-number') - .find('.label').fitText(text_offset - 0.2); - }, - - animateNumbers: function(parent_item) { - parent_item = parent_item || $('body'); - - parent_item - .find('.multiple-numbers, .summary-numbers, .big-number') - .find('.formatted-number') - .each(function () { - var count = $(this); - var precision = count.data('precision'); - var number = count.children('.number'); - var suffix = count.children('.suffix').text(); - - // Some custom formats may contain text in the number field, no animation in this case - if (isNaN(number.text())) { - return true; - } - - jQuery({ Counter: 0 }).animate({ Counter: number.text() }, { - duration: 800, - easing: 'swing', - step: function () { - number.text(this.Counter.toFixed(precision))+suffix; - } - }); - }); - }, - - setEditMode: function(activate) { - Dashboard.edit_mode = typeof activate == "undefined" ? true : activate; - - var edit_ctrl = $(Dashboard.elem_id+" .toolbar .fa-edit"); - edit_ctrl.toggleClass('active', activate); - Dashboard.element.toggleClass('edit-mode', activate); - Dashboard.grid.setStatic(!activate); - - // set filters as sortable (draggable) or not - sortable('.filters', activate ? 'enable' : 'disable'); - - if (!Dashboard.edit_mode) { - // save markdown textareas set as dirty - var dirty_textareas = $(".grid-stack-item.dirty"); - if (dirty_textareas.length > 0) { - Dashboard.saveDashboard(true); - } - } - }, - - toggleFullscreenMode: function(fs_ctrl) { - var fs_enabled = !fs_ctrl.hasClass('active'); - - Dashboard.element.toggleClass('fullscreen') - .find('.night-mode').toggle(fs_enabled); - fs_ctrl.toggleClass('active'); - - // desactivate edit mode - if (fs_enabled) { - Dashboard.setEditMode(false); - } - - // fullscreen browser api - if (fs_enabled) { - GoInFullscreen(Dashboard.elem_dom); - } else { - GoOutFullscreen(); - } - }, - - disableFullscreenMode: function() { - Dashboard.element - .removeClass('fullscreen') - .find('.night-mode').hide().end() - .find('.toggle-fullscreen').removeClass('active'); - - GoOutFullscreen(); - }, - - /** - * Clone current dashboard - * (clean all previous gridstack_id in cards) - */ - clone: function() { - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - dashboard: Dashboard.current_name, - action: 'clone_dashboard', - }, - dataType: 'json' - }).done(function(new_dash) { - Dashboard.addNewDashbardInSelect(new_dash.title, new_dash.key); - }); - }, - - /** - * Delete current dashboard - */ - delete: function() { - var confirm_msg = __("Are you sure you want to delete the dashboard %s ?") - .replace('%s', Dashboard.current_name); - if (window.confirm(confirm_msg, __("Delete this dashboard"))) { - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'delete_dashboard', - dashboard: Dashboard.current_name, - } - }).done(function() { - $("#dashboard-"+Dashboard.rand+" .toolbar .dashboard_select") - .find("option[value='"+Dashboard.current_name+"']").remove() - .end() // reset find filtering - .prop("selectedIndex", 0) - .trigger('change'); - }); - } - }, - - /** - * Display form to add a new dashboard - */ - addForm: function() { - $(".ui-dialog-content").dialog("close"); - $('
') - .load(CFG_GLPI.root_doc+"/ajax/dashboard.php", { - action: 'add_new', - }, function() { - $(this).dialog({ - width: 'auto', - modal: true, - open: function() { - $(this).find('input').first().focus(); - } - }); - }); - }, - - addNew: function(form_data) { - $.post({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - action: 'save_new_dashboard', - title: form_data.title, - context: Dashboard.context, - } - }).done(function(dashboard_key) { - Dashboard.addNewDashbardInSelect(form_data.title, dashboard_key); - Dashboard.setEditMode(true); - }); - }, - - /** - * Add a new option to top left dashboard select - */ - addNewDashbardInSelect: function(label, value) { - var newOption = new Option(label, value, false, true); - $("#dashboard-"+Dashboard.rand+" .toolbar .dashboard_select") - .append(newOption) - .trigger('change'); - }, - - getCardsAjax: function(specific_one) { - specific_one = specific_one || ""; - - var filters = Dashboard.getFiltersFromStorage(); - - let requested_cards = []; - let card_ajax_data = []; - $(".grid-stack-item:not(.lock-bottom)"+specific_one).each(function() { - var card = $(this); - var card_opt = card.data('card-options'); - var gridstack_id = card.data('gs-id'); - var card_id = card_opt.card_id || card.data('gs-id'); - - card_opt.gridstack_id = gridstack_id; - - // store markdown after card reload - if ("markdown_content" in card_opt) { - Dashboard.markdown_contents[gridstack_id] = card_opt.markdown_content; - } - - // append filters - card_opt.apply_filters = filters; - - card_ajax_data.push({ - 'card_id': card_id, - 'force': (specific_one.length > 0 ? 1 : 0), - 'args': card_opt, - 'c_cache_key': card_opt.cache_key || "" - }); - requested_cards.push({ - 'card_el': card, - 'card_id': card_id - }); - }); - - return $.ajax({ - url:CFG_GLPI.root_doc+"/ajax/dashboard.php", - method: 'POST', - data: { - 'action': 'get_cards', - data: JSON.stringify({ //Preserve integers - 'dashboard': Dashboard.current_name, - 'force': (specific_one.length > 0 ? 1 : 0), - 'embed': (Dashboard.embed ? 1 : 0), - 'd_cache_key': Dashboard.cache_key, - 'cards': card_ajax_data - }) - } - }).then(function(results) { - $.each(requested_cards, (i2, crd) => { - let has_result = false; - const card = crd.card_el; - $.each(results, (card_id, card_result) => { - if (crd.card_id === card_id) { - const html = card_result; - has_result = true; - card.children('.grid-stack-item-content').html(html); - - Dashboard.fitNumbers(card); - Dashboard.animateNumbers(card); - } - }); - if (!has_result) { - card.html("
"); - } - }); - }).fail(function() { - $.each(requested_cards, (i2, crd) => { - const card = crd.card_el; - card.html("
"); - }); - }); - }, - - easter: function() { - var items = $(Dashboard.elem_id+" .grid-stack .grid-stack-item .card"); - - setInterval(function() { - var color = "#"+((1<<24)*Math.random()|0).toString(16); - var no_item = Math.floor(Math.random() * items.length) + 1; - var item = items[no_item]; - $(item).css('background-color', color); - }, 10); - }, - - generateCss: function() { - var dash_width = Math.floor(this.element.width()); - var cell_length = dash_width / this.cols; - var cell_height = cell_length; - var cell_fullsize = (dash_width / this.cols) + this.cell_margin; - var width_percent = 100 / this.cols; - - var style = " \ - "+this.elem_id+" .cell-add { \ - width: "+cell_length+"px; \ - height: "+cell_fullsize+"px; \ - } \ - "+this.elem_id+" .grid-guide { \ - background-size: "+cell_length+"px "+cell_fullsize+"px; \ - bottom: "+cell_fullsize+"px; \ - }"; - - for (var i = 0; i < this.cols; i++) { - var left = i * width_percent; - var width = (i+1) * width_percent; - - style+= this.elem_id+" .grid-stack > .grid-stack-item[data-gs-x='"+i+"'] { \ - left: "+left+"%; \ - } \ - "+this.elem_id+" .grid-stack > .grid-stack-item[data-gs-width='"+(i+1)+"'] { \ - min-width: "+width_percent+"%; \ - width: "+width+"%; \ - }"; - } - - // remove old inline styles - $("#gs_inline_css_"+this.rand).remove(); - - // add new style - if (dash_width > 700) { - $("") - .prop("type", "text/css") - .html(style) - .appendTo("head"); - } else { - cell_height = 60; - } - - // apply new height to gridstack - this.grid.cellHeight(cell_height); - }, - - /** - * init filters of the dashboard - */ - initFilters: function() { - if ($(".filters").length === 0) { - return; - } - - var filters = Dashboard.getFiltersFromStorage(); - - // replace empty array by empty string to avoid jquery remove the corresponding key - // when sending ajax query - $.each(filters, function( index, value ) { - if (Array.isArray(value) && value.length == 0) { - filters[index] = ""; - } - }); - - // get html of provided filters - $.get({ - url: CFG_GLPI.root_doc+"/ajax/dashboard.php", - data: { - "action": "get_dashboard_filters", - "filters": filters, - } - }).done(function(html) { - $('.filters').html(html); - - // we must emit an event to all filters to say them dashboard is ready - $(document).trigger("glpiDasbhoardInitFilter"); - - // start sortable on filter but disable it by default, - // we will enable it when edit mode will be toggled on - sortable('.filters', { - placeholderClass: 'filter-placeholder', - })[0].addEventListener('sortupdate', function(e) { - // after drag, save the order of filters in storage - var items_after = $(e.detail.destination.items).filter('.filter'); - var filters = Dashboard.getFiltersFromStorage(); - var new_filters = {}; - $.each(items_after, function() { - var filter_id = $(this).data('filter-id'); - new_filters[filter_id] = filters[filter_id]; - }); - - Dashboard.setFiltersInStorage(new_filters); - }); - sortable('.filters', 'disable'); - }); - }, - - /** - * Return saved filter from LocalStorage - * - * @param {boolean} all_filters: do we return all filters - * or only those for the current dashboard (default) - */ - getFiltersFromStorage: function(all_filters) { - all_filters = all_filters || false; - - var filters = JSON.parse(localStorage.getItem('glpi_dashboard_filters')); - var save = false; - if (filters == null) { - filters = {}; - save = true; - } - - if ('current_name' in Dashboard - && Dashboard.current_name != null - && !(Dashboard.current_name in filters)) { - filters[Dashboard.current_name] = {}; - save = true; - } - - if (save) { - localStorage.setItem('glpi_dashboard_filters', JSON.stringify(filters)); - } - - if (all_filters) { - return filters; - } - - return filters[Dashboard.current_name]; - }, - - /** - * Save an object of filters for the current dashboard into LocalStorage - * - * @param {Object} sub_filters - */ - setFiltersInStorage: function(sub_filters) { - var filters = Dashboard.getFiltersFromStorage(true); - if (Dashboard.current_name.length > 0) { - filters[Dashboard.current_name] = sub_filters; - } - return localStorage.setItem('glpi_dashboard_filters', JSON.stringify(filters)); - }, - -}; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 893f87fc46..767bb134a4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -571,59 +571,6 @@ function loadDataset() { 'begin_date' => '2018-12-29', 'end_date' => '2019-01-06' ] - ], 'Glpi\\Dashboard\\Dashboard' => [ - [ - 'key' => 'test_dashboard', - 'name' => 'Test_Dashboard', - 'context' => 'core', - ], [ - 'key' => 'test_dashboard2', - 'name' => 'Test_Dashboard_2', - 'context' => 'core' - ], [ - 'key' => 'test_dashboard3', - 'name' => 'Test_Dashboard_3', - 'context' => 'oustide_core', - ] - ], 'Glpi\\Dashboard\\Item' => [ - [ - 'dashboards_dashboards_id' => 'Test_Dashboard', - 'gridstack_id' => 'bn_count_Computer_1', - 'card_id' => 'bn_count_Computer', - 'x' => 0, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => '{"color": "#FFFFFF"}' - ], [ - 'dashboards_dashboards_id' => 'Test_Dashboard', - 'gridstack_id' => 'bn_count_Computer_2', - 'card_id' => 'bn_count_Computer', - 'x' => 2, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => '{"color": "#FFFFFF"}' - ], [ - 'dashboards_dashboards_id' => 'Test_Dashboard', - 'gridstack_id' => 'bn_count_Computer_3', - 'card_id' => 'bn_count_Computer', - 'x' => 4, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => '{"color": "#FFFFFF"}' - ] - ], 'Glpi\\Dashboard\\Right' => [ - [ - 'dashboards_dashboards_id' => 'Test_Dashboard', - 'itemtype' => 'Entity', - 'items_id' => 0, - ], [ - 'dashboards_dashboards_id' => 'Test_Dashboard', - 'itemtype' => 'Profile', - 'items_id' => 3, - ] ], 'Plugin' => [ [ 'directory' => 'tester', diff --git a/tests/functionnal/Glpi/Dashboard/Dashboard.php b/tests/functionnal/Glpi/Dashboard/Dashboard.php deleted file mode 100644 index d05f6f772d..0000000000 --- a/tests/functionnal/Glpi/Dashboard/Dashboard.php +++ /dev/null @@ -1,313 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace tests\units\Glpi\Dashboard; - -use DbTestCase; - -/* Test for inc/dashboard/dashboard.class.php */ - -class Dashboard extends DbTestCase { - private $dashboard = null; - - public function beforeTestMethod($method) { - $this->dashboard = new \Glpi\Dashboard\Dashboard('test_dashboard'); - - parent::beforeTestMethod($method); - } - - public function testLoad() { - $d_key = $this->dashboard->load(true); - $this->integer($d_key)->isGreaterThan(0); - - $items = $this->getPrivateProperty('items'); - $this->array($items)->hasSize(3); - - $rights = $this->getPrivateProperty('rights'); - $this->array($rights)->hasSize(2); - } - - - public function testGetFromDB() { - // we need to test we get the dashboard by it's key and not it's id - $this->boolean($this->dashboard->getFromDB(1))->isFalse(); - $this->boolean($this->dashboard->getFromDB('test_dashboard'))->isTrue(); - $this->string($this->getPrivateProperty('key'))->isEqualTo('test_dashboard'); - $this->array($this->getPrivateProperty('fields'))->isNotEmpty(); - } - - - public function testGetTitle() { - $this->string($this->dashboard->getTitle())->isEqualTo("Test_Dashboard"); - } - - - public function testSaveNew() { - $this->string($this->dashboard->saveNew( - "New Dashboard", - 'my_context', [ - [ - 'gridstack_id' => 'bn_count_Computer_4', - 'card_id' => 'bn_count_Computer', - 'x' => 0, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => [ - 'color' => '#FFFFFF', - ] - ], [ - 'gridstack_id' => 'bn_count_Computer_5', - 'card_id' => 'bn_count_Computer', - 'x' => 2, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => [ - 'color' => '#FFFFFF', - ] - ], - ], [ - [ - 'itemtype' => 'Entity', - 'items_id' => 0, - ] - ] - ))->isEqualTo("new-dashboard"); - - $items = $this->getPrivateProperty('items'); - $this->array($items)->hasSize(2); - - $rights = $this->getPrivateProperty('rights'); - $this->array($rights)->hasSize(1); - } - - - public function testSaveTitle() { - $new_title = "new Title"; - $this->dashboard->saveTitle($new_title); - $this->string($this->dashboard->getTitle())->isEqualTo($new_title); - - // key of dashboard should not have changed - $this->string($this->getPrivateProperty('key'))->isEqualTo('test_dashboard'); - } - - - public function testClone() { - $clone_name = sprintf(__('Copy of %s'), "Test_Dashboard"); - $clone_key = \Toolbox::slugify($clone_name); - $this->array($this->dashboard->cloneCurrent())->isEqualTo([ - 'title' => $clone_name, - 'key' => $clone_key - ]); - - $this->boolean($this->dashboard->getFromDB($clone_key))->isTrue(); - $this->string($this->dashboard->fields['context'])->isEqualTo('core'); - - $this->string($this->getPrivateProperty('key'))->isEqualTo($clone_key); - $this->string($this->getPrivateProperty('key'))->isEqualTo($clone_key); - $items = $this->getPrivateProperty('items'); - $this->array($items)->hasSize(3); - $this->array($this->getPrivateProperty('rights'))->hasSize(4); - - foreach ($items as $item) { - $this->array($item)->hasKey('card_options'); - $this->array($item['card_options'])->hasSize(1); - } - } - - - public function testGetAll() { - // get "core" dashboards - $dasboards = $this->dashboard::getAll(true, false); - $this->array($dasboards) - ->hasSize(5) - ->hasKey('test_dashboard') - ->hasKey('test_dashboard2'); - - $this->array($dasboards['test_dashboard']) - ->hasKey('items') - ->hasKey('rights'); - $this->array($dasboards['test_dashboard2']) - ->hasKey('items') - ->hasKey('rights'); - - $this->array($dasboards['test_dashboard']['items'])->hasSize(3); - $this->array($dasboards['test_dashboard']['rights'])->hasSize(4); - $this->array($dasboards['test_dashboard2']['items'])->hasSize(0); - $this->array($dasboards['test_dashboard2']['rights'])->hasSize(4); - - $dasboards = $this->dashboard::getAll(true, false, "inexistent_context"); - $this->array($dasboards) - ->hasSize(0); - } - - - public function testDelete() { - global $DB; - - $this->dashboard->getFromDB('test_dashboard'); - $dashboards_id = $this->dashboard->fields['id']; - - $this->boolean($this->dashboard->delete([ - 'key' => 'test_dashboard' - ]))->isTrue(); - - $items = iterator_to_array($DB->request([ - 'FROM' => \Glpi\Dashboard\Item::getTable(), - 'WHERE' => [ - 'dashboards_dashboards_id' => $dashboards_id - ] - ])); - $this->array($items)->isEmpty(); - $rights = iterator_to_array($DB->request([ - 'FROM' => \Glpi\Dashboard\Right::getTable(), - 'WHERE' => [ - 'dashboards_dashboards_id' => $dashboards_id - ] - ])); - $this->array($rights)->isEmpty(); - } - - - public function getPrivateProperty(string $propertyName) { - $reflector = new \ReflectionClass("Glpi\Dashboard\Dashboard"); - $property = $reflector->getProperty( $propertyName ); - $property->setAccessible(true); - - return $property->getValue($this->dashboard); - } - - - public function testImportFromJson() { - $title = 'Test Import'; - $key = \Toolbox::slugify($title); - $import = [ - $key => [ - 'title' => $title, - 'context' => 'core', - 'items' => [ - [ - 'gridstack_id' => 'bn_count_Computer_4', - 'card_id' => 'bn_count_Computer', - 'x' => 0, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => [] - ], [ - 'gridstack_id' => 'bn_count_Computer_5', - 'card_id' => 'bn_count_Computer', - 'x' => 2, - 'y' => 0, - 'width' => 2, - 'height' => 2, - 'card_options' => [] - ], - ], - 'rights' => [ - [ - 'itemtype' => 'Entity', - 'items_id' => 0, - ] - ], - ] - ]; - - $this->boolean(\Glpi\Dashboard\Dashboard::importFromJson($import))->isTrue(); - $this->boolean($this->dashboard->getFromDB($key))->isTrue(); - $this->string($this->dashboard->getTitle())->isEqualTo($title); - $this->string($this->getPrivateProperty('key'))->isEqualTo($key); - $this->array($this->getPrivateProperty('items'))->hasSize(2); - $this->array($this->getPrivateProperty('rights'))->hasSize(1); - } - - public function testConvertRights() { - $raw = [ - [ - 'itemtype' => 'Entity', - 'items_id' => 0, - ], [ - 'itemtype' => 'Profile', - 'items_id' => 3, - ], [ - 'itemtype' => 'Profile', - 'items_id' => 4, - ], [ - 'itemtype' => 'User', - 'items_id' => 2, - ] - ]; - - $this->array(\Glpi\Dashboard\Dashboard::convertRights($raw))->isEqualTo([ - 'entities_id' => [0], - 'profiles_id' => [3, 4], - 'users_id' => [2], - 'groups_id' => [], - ]); - } - - - public function testCheckRights() { - $rights = [ - 'entities_id' => [0], - 'profiles_id' => [3 => 3, 4 => 4], - 'users_id' => [2], - 'groups_id' => [3], - ]; - - $_SESSION['glpiactiveentities'] = []; - $_SESSION['glpiprofiles'] = []; - $_SESSION['glpigroups'] = []; - $_SESSION['glpiID'] = 1; - - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isFalse(); - - $_SESSION['glpiactiveentities'] = [0]; - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isTrue(); - - $_SESSION['glpiactiveentities'] = []; - $_SESSION['glpiprofiles'] = [3 => 3]; - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isTrue(); - - $_SESSION['glpiprofiles'] = []; - $_SESSION['glpiID'] = 2; - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isTrue(); - - $_SESSION['glpiID'] = 1; - $_SESSION['glpigroups'] = [3]; - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isTrue(); - - $_SESSION['glpigroups'] = []; - $this->boolean(\Glpi\Dashboard\Dashboard::checkRights($rights))->isFalse(); - } -} diff --git a/tests/functionnal/Glpi/Dashboard/Provider.php b/tests/functionnal/Glpi/Dashboard/Provider.php deleted file mode 100644 index 941b32e73f..0000000000 --- a/tests/functionnal/Glpi/Dashboard/Provider.php +++ /dev/null @@ -1,294 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace tests\units\Glpi\Dashboard; - -use DbTestCase; - -/* Test for inc/dashboard/provider.class.php */ - -class Provider extends DbTestCase { - - public function itemProvider() { - return [ - ['item' => new \Computer], - ['item' => new \Ticket], - ]; - } - - /** - * @dataProvider itemProvider - */ - public function testBigNumber(\CommonDBTM $item) { - $this->login(); - - $itemtype = $item->getType(); - $data = [ - \Glpi\Dashboard\Provider::bigNumberItem($item), - call_user_func(['\\Glpi\\Dashboard\\Provider', "bigNumber$itemtype"]) - ]; - - foreach ($data as $result) { - $this->array($result) - ->hasKeys([ - 'number', - 'url', - 'label', - 'icon', - ]); - $this->integer($result['number'])->isGreaterThan(0); - $this->string($result['url'])->contains($item::getSearchURL()); - $this->string($result['label'])->isNotEmpty(); - $this->string($result['icon'])->isEqualTo($item::getIcon()); - } - } - - - public function ticketsCaseProvider() { - return [ - ['case' => 'notold'], - ['case' => 'late'], - ['case' => 'waiting_validation'], - ['case' => 'incoming'], - ['case' => 'waiting'], - ['case' => 'assigned'], - ['case' => 'planned'], - ['case' => 'solved'], - ['case' => 'closed'], - ['case' => 'status'], - ]; - } - - - /** - * @dataProvider ticketsCaseProvider - */ - public function testNbTicketsGeneric(string $case) { - $result = \Glpi\Dashboard\Provider::nbTicketsGeneric($case); - - $this->array($result) - ->hasKeys([ - 'number', - 'url', - 'label', - 'icon', - 's_criteria', - 'itemtype', - ]); - $this->integer($result['number']); - $this->string($result['url'])->contains(\Ticket::getSearchURL()); - $this->string($result['icon']); - $this->string($result['label']); - $this->array($result['s_criteria'])->size->isGreaterThan(0); - $this->string($result['itemtype'])->isEqualTo('Ticket'); - } - - - public function itemFKProvider() { - return [ - ['item' => new \Computer, 'fk_item' => new \Entity], - ['item' => new \Software, 'fk_item' => new \Entity], - ]; - } - - - /** - * @dataProvider itemFKProvider - */ - public function testNbItemByFk(\CommonDBTM $item, \CommonDBTM $fk_item) { - $this->login(); - - $result = \Glpi\Dashboard\Provider::nbItemByFk($item, $fk_item); - $this->array($result) - ->hasKeys([ - 'data', - 'label', - 'icon', - ]); - - foreach ($result['data'] as $data) { - $this->array($data) - ->hasKeys([ - 'number', - 'label', - 'url', - ]); - - $this->integer($data['number'])->isGreaterThan(0); - $this->string($data['label']); - $this->string($data['url'])->contains($item::getSearchURL()); - } - } - - - public function testTicketsOpened() { - $result = \Glpi\Dashboard\Provider::ticketsOpened(); - $this->array($result) - ->hasKeys([ - 'data', - 'distributed', - 'label', - 'icon', - ]); - - $this->boolean($result['distributed'])->isFalse(); - $this->string($result['icon']); - $this->string($result['label']); - - foreach ($result['data'] as $data) { - $this->array($data) - ->hasKeys([ - 'number', - 'label', - 'url', - ]); - - $this->integer($data['number'])->isGreaterThan(0); - $this->string($data['label']); - $this->string($data['url'])->contains(\Ticket::getSearchURL()); - } - } - - - public function testGetTicketsEvolution() { - $result = \Glpi\Dashboard\Provider::getTicketsEvolution(); - $this->array($result) - ->hasKeys([ - 'data', - 'label', - 'icon', - ]); - - $this->string($result['icon']); - $this->string($result['label']); - $this->array($result['data'])->hasKeys(['labels', 'series']); - $this->array($result['data']['labels'])->isNotEmpty(); - $this->array($result['data']['series'])->isNotEmpty(); - - $nb_labels = count($result['data']['labels']); - foreach ($result['data']['series'] as $serie) { - $this->array($serie)->hasKey('data'); - $this->integer(count($serie['data']))->isEqualTo($nb_labels); - - foreach ($serie['data'] as $serie_data) { - $this->integer($serie_data['value']); - $this->string($serie_data['url'])->contains(\Ticket::getSearchURL()); - } - } - } - - - public function testGetTicketsStatus() { - $this->login(); - - $result = \Glpi\Dashboard\Provider::getTicketsStatus(); - $this->array($result) - ->hasKeys([ - 'data', - 'label', - 'icon', - ]); - - $this->string($result['icon']); - $this->string($result['label']); - $this->array($result['data'])->hasKeys(['labels', 'series']); - $this->array($result['data']['labels'])->isNotEmpty(); - $this->array($result['data']['series'])->isNotEmpty(); - - $nb_labels = count($result['data']['labels']); - foreach ($result['data']['series'] as $serie) { - $this->array($serie)->hasKey('data'); - $this->integer(count($serie['data']))->isEqualTo($nb_labels); - - foreach ($serie['data'] as $serie_data) { - $this->integer($serie_data['value']); - $this->string($serie_data['url'])->contains(\Ticket::getSearchURL()); - } - } - } - - - public function testTopTicketsCategories() { - $this->login(); - - $result = \Glpi\Dashboard\Provider::multipleNumberTicketByITILCategory(); - $this->array($result) - ->hasKeys([ - 'data', - 'label', - 'icon', - ]); - - $this->string($result['icon']); - $this->string($result['label']); - - foreach ($result['data'] as $data) { - $this->array($data) - ->hasKeys([ - 'number', - 'label', - 'url', - ]); - - $this->integer($data['number'])->isGreaterThan(0); - $this->string($data['label']); - $this->string($data['url'])->contains(\Ticket::getSearchURL()); - } - } - - public function monthYearProvider() { - return [ - [ - 'monthyear' => '2019-01', - 'expected' => [ - '2019-01-01 00:00:00', - '2019-02-01 00:00:00' - ] - ], [ - 'monthyear' => '2019-12', - 'expected' => [ - '2019-12-01 00:00:00', - '2020-01-01 00:00:00' - ] - ] - ]; - } - - - /** - * @dataProvider monthYearProvider - */ - public function testFormatMonthyearDates(string $monthyear, array $expected) { - $this->array(\Glpi\Dashboard\Provider::formatMonthyearDates($monthyear)) - ->isEqualTo($expected); - } -} diff --git a/tests/functionnal/Session.php b/tests/functionnal/Session.php index b6588a43e1..358ab1ceb3 100644 --- a/tests/functionnal/Session.php +++ b/tests/functionnal/Session.php @@ -373,7 +373,6 @@ protected function idorProvider() { return [ ['itemtype' => 'Computer'], ['itemtype' => 'Ticket'], - ['itemtype' => 'Glpi\\Dashboard\\Item'], ['itemtype' => 'User', 'add_params' => ['right' => 'all']], ['itemtype' => 'User', 'add_params' => ['entity_restrict' => 0]], ]; diff --git a/tests/units/Glpi/Dashboard/Widget.php b/tests/units/Glpi/Dashboard/Widget.php deleted file mode 100644 index 3212ee4c93..0000000000 --- a/tests/units/Glpi/Dashboard/Widget.php +++ /dev/null @@ -1,106 +0,0 @@ -. - * --------------------------------------------------------------------- - */ - -namespace tests\units\Glpi\Dashboard; - -use DbTestCase; - -/* Test for inc/dashboard/widget.class.php */ - -class Widget extends DbTestCase { - - public function testGetAllTypes() { - $types = \Glpi\Dashboard\Widget::getAllTypes(); - - $this->array($types)->isNotEmpty(); - foreach ($types as $specs) { - $this->array($specs) - ->hasKeys(['label', 'function', 'image']); - } - } - - - protected function palettes() { - return [ - [ - 'bg_color' => "#FFFFFF", - 'nb_series' => 4, - 'revert' => true, - 'expected' => [ - 'names' => ['a', 'b', 'c', 'd'], - 'colors' => [ - '#a5a5a5', - '#7f7f7f', - '#595959', - '#323232', - ], - ] - ], [ - 'bg_color' => "#FFFFFF", - 'nb_series' => 4, - 'revert' => false, - 'expected' => [ - 'names' => ['a', 'b', 'c', 'd'], - 'colors' => [ - '#595959', - '#7f7f7f', - '#a5a5a5', - '#cccccc', - ], - ] - ], [ - 'bg_color' => "#FFFFFF", - 'nb_series' => 1, - 'revert' => true, - 'expected' => [ - 'names' => ['a'], - 'colors' => [ - '#999999', - ], - ] - ], - ]; - } - - /** - * @dataProvider palettes - */ - public function testGetGradientPalette( - string $bg_color, - int $nb_series, - bool $revert, - array $expected - ) { - $this->array(\Glpi\Dashboard\Widget::getGradientPalette($bg_color, $nb_series, $revert)) - ->isEqualTo($expected); - } -} \ No newline at end of file