Backup état complet après enregistrement vidéo démo de bout en bout. À utiliser comme point de référence pour la consolidation post-démo. Changements majeurs de la session 18-19 mai : - AIVA-URGENCE : page autonome avec preset URL + auto-focus chain - Workflow Demo_urgence_3_db : merge linux_db + steps AIVA + pause humaine NoMachine - Bypass LLM (static_result / static_text) dans replay_engine pour démos déterministes sans appel Ollama - Fix api_stream:3013 — replay_paused au premier polling /next - dag_execute : lift duration_ms vers top-level pour wait runtime - NPM bypass auth /aiva-urgence/ via location ^~ (proxy_host/10.conf hors git) - scripts/cancel-replays.sh — workaround Stop VWB qui ne purge pas la queue Anchors visuels (468) forcés dans le commit pour garantir restorabilité. DB workflows actuelle + ~12 .bak DB de la journée incluses. Sujets identifiés pour consolidation post-démo (TODO) : 1. Bug VWB recapture anchor ne régénère pas le PNG 2. Léa client accumule état mémoire (restart périodique requis) 3. Stop VWB ne purge pas la queue serveur (lien manquant vers /replay/cancel) 4. Bug coord client mss tronqué 2560x60 → mapping Y cassé 5. delay_before/delay_after ignorés au runtime (fix partiel duration_ms) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
294 lines
8.5 KiB
PHP
294 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require '/var/www/glpi/inc/includes.php';
|
|
|
|
date_default_timezone_set('Europe/Paris');
|
|
|
|
$target_itemtypes = [
|
|
'Computer',
|
|
'NetworkEquipment',
|
|
'Printer',
|
|
'Peripheral',
|
|
'Phone',
|
|
'Monitor',
|
|
'Appliance',
|
|
];
|
|
|
|
$tag_definitions = [
|
|
[
|
|
'name' => 'Production',
|
|
'color' => '#b91c1c',
|
|
'comment' => 'Element en production',
|
|
],
|
|
[
|
|
'name' => 'Sante',
|
|
'color' => '#15803d',
|
|
'comment' => 'Element du perimetre sante',
|
|
],
|
|
[
|
|
'name' => 'Critique',
|
|
'color' => '#dc2626',
|
|
'comment' => 'Element critique pour l activite',
|
|
],
|
|
[
|
|
'name' => 'Biomedical',
|
|
'color' => '#0f766e',
|
|
'comment' => 'Equipement biomedical ou associe',
|
|
],
|
|
[
|
|
'name' => 'DMZ',
|
|
'color' => '#0ea5e9',
|
|
'comment' => 'Element expose dans une zone DMZ',
|
|
],
|
|
[
|
|
'name' => 'Expose Internet',
|
|
'color' => '#ea580c',
|
|
'comment' => 'Element directement ou indirectement expose a Internet',
|
|
],
|
|
[
|
|
'name' => 'A auditer',
|
|
'color' => '#ca8a04',
|
|
'comment' => 'Element a verifier dans le cadre de la certification',
|
|
],
|
|
[
|
|
'name' => 'Hors perimetre',
|
|
'color' => '#6b7280',
|
|
'comment' => 'Element hors perimetre de la certification',
|
|
],
|
|
];
|
|
|
|
$container_label = 'Conformite et cartographie';
|
|
|
|
$field_definitions = [
|
|
[
|
|
'label' => 'Proprietaire metier',
|
|
'type' => 'text',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Responsable technique',
|
|
'type' => 'text',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Criticite de service',
|
|
'type' => 'dropdown',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Donnees de sante',
|
|
'type' => 'yesno',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Exposition Internet',
|
|
'type' => 'yesno',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Sauvegarde requise',
|
|
'type' => 'yesno',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'PCA PRA requis',
|
|
'type' => 'yesno',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Niveau de confidentialite',
|
|
'type' => 'dropdown',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Date de validation de la cartographie',
|
|
'type' => 'date',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
[
|
|
'label' => 'Commentaire de certification',
|
|
'type' => 'textarea',
|
|
'mandatory' => 0,
|
|
'is_readonly' => 0,
|
|
],
|
|
];
|
|
|
|
$dropdown_values = [
|
|
'Criticite de service' => [
|
|
'Faible',
|
|
'Moyenne',
|
|
'Elevee',
|
|
'Critique',
|
|
],
|
|
'Niveau de confidentialite' => [
|
|
'Interne',
|
|
'Sensible',
|
|
'Confidentiel',
|
|
'Tres confidentiel',
|
|
],
|
|
];
|
|
|
|
function out(string $message): void
|
|
{
|
|
fwrite(STDOUT, $message . PHP_EOL);
|
|
}
|
|
|
|
function find_single_by_criteria(CommonDBTM $item, array $criteria): ?array
|
|
{
|
|
$rows = $item->find($criteria);
|
|
if ($rows === []) {
|
|
return null;
|
|
}
|
|
|
|
return reset($rows) ?: null;
|
|
}
|
|
|
|
$container = new PluginFieldsContainer();
|
|
$existing_container = find_single_by_criteria($container, ['label' => $container_label]);
|
|
|
|
if ($existing_container === null) {
|
|
$container_id = $container->add([
|
|
'label' => $container_label,
|
|
'type' => 'dom',
|
|
'itemtypes' => $target_itemtypes,
|
|
'entities_id' => 0,
|
|
'is_recursive' => 1,
|
|
'is_active' => 1,
|
|
]);
|
|
if ($container_id === false) {
|
|
throw new RuntimeException('Failed to create Fields container.');
|
|
}
|
|
$container->getFromDB((int) $container_id);
|
|
out('Created container: ' . $container_label);
|
|
} else {
|
|
$container->getFromDB((int) $existing_container['id']);
|
|
$current_itemtypes = json_decode((string) $container->fields['itemtypes'], true) ?: [];
|
|
$wanted_itemtypes = $target_itemtypes;
|
|
sort($current_itemtypes);
|
|
sort($wanted_itemtypes);
|
|
if ($current_itemtypes !== $wanted_itemtypes) {
|
|
$container->update([
|
|
'id' => $container->fields['id'],
|
|
'itemtypes' => json_encode($target_itemtypes, JSON_UNESCAPED_SLASHES),
|
|
'is_active' => 1,
|
|
'entities_id' => 0,
|
|
'is_recursive' => 1,
|
|
], true);
|
|
PluginFieldsContainer::create($container->fields);
|
|
}
|
|
out('Reused container: ' . $container_label);
|
|
}
|
|
|
|
$container_id = (int) $container->fields['id'];
|
|
|
|
$field = new PluginFieldsField();
|
|
$created_fields = [];
|
|
|
|
foreach ($field_definitions as $definition) {
|
|
$existing_field = find_single_by_criteria($field, [
|
|
'plugin_fields_containers_id' => $container_id,
|
|
'label' => $definition['label'],
|
|
]);
|
|
|
|
if ($existing_field === null) {
|
|
$field_id = $field->add([
|
|
'plugin_fields_containers_id' => $container_id,
|
|
'label' => $definition['label'],
|
|
'type' => $definition['type'],
|
|
'mandatory' => $definition['mandatory'],
|
|
'is_active' => 1,
|
|
'is_readonly' => $definition['is_readonly'],
|
|
'multiple' => 0,
|
|
]);
|
|
if ($field_id === false) {
|
|
throw new RuntimeException('Failed to create field: ' . $definition['label']);
|
|
}
|
|
$field->getFromDB((int) $field_id);
|
|
$created_fields[$definition['label']] = $field->fields;
|
|
out('Created field: ' . $definition['label']);
|
|
} else {
|
|
$field->getFromDB((int) $existing_field['id']);
|
|
$created_fields[$definition['label']] = $field->fields;
|
|
out('Reused field: ' . $definition['label']);
|
|
}
|
|
}
|
|
|
|
foreach ($dropdown_values as $label => $values) {
|
|
if (!isset($created_fields[$label])) {
|
|
continue;
|
|
}
|
|
|
|
$field_name = (string) $created_fields[$label]['name'];
|
|
$dropdown_class = PluginFieldsDropdown::getClassname($field_name);
|
|
$dropdown_file = PLUGINFIELDS_CLASS_PATH . '/' . $field_name . 'dropdown.class.php';
|
|
|
|
if (!class_exists($dropdown_class) && file_exists($dropdown_file)) {
|
|
require_once $dropdown_file;
|
|
}
|
|
|
|
if (!class_exists($dropdown_class)) {
|
|
throw new RuntimeException('Missing dropdown class for field: ' . $label);
|
|
}
|
|
|
|
$dropdown = new $dropdown_class();
|
|
foreach ($values as $value) {
|
|
$existing_value = find_single_by_criteria($dropdown, ['name' => $value]);
|
|
if ($existing_value === null) {
|
|
$dropdown_id = $dropdown->add([
|
|
'name' => $value,
|
|
'entities_id' => 0,
|
|
'is_recursive' => 1,
|
|
]);
|
|
if ($dropdown_id === false) {
|
|
throw new RuntimeException('Failed to create dropdown value: ' . $value);
|
|
}
|
|
out('Added dropdown value: ' . $label . ' -> ' . $value);
|
|
}
|
|
}
|
|
}
|
|
|
|
$tag = new PluginTagTag();
|
|
foreach ($tag_definitions as $definition) {
|
|
$existing_tag = find_single_by_criteria($tag, ['name' => $definition['name']]);
|
|
|
|
if ($existing_tag === null) {
|
|
$tag_id = $tag->add([
|
|
'name' => $definition['name'],
|
|
'comment' => $definition['comment'],
|
|
'color' => $definition['color'],
|
|
'type_menu' => $target_itemtypes,
|
|
'entities_id' => 0,
|
|
'is_recursive' => 1,
|
|
'is_active' => 1,
|
|
]);
|
|
if ($tag_id === false) {
|
|
throw new RuntimeException('Failed to create tag: ' . $definition['name']);
|
|
}
|
|
out('Created tag: ' . $definition['name']);
|
|
} else {
|
|
$tag->update([
|
|
'id' => $existing_tag['id'],
|
|
'comment' => $definition['comment'],
|
|
'color' => $definition['color'],
|
|
'type_menu' => $target_itemtypes,
|
|
'entities_id' => 0,
|
|
'is_recursive' => 1,
|
|
'is_active' => 1,
|
|
], true);
|
|
out('Updated tag: ' . $definition['name']);
|
|
}
|
|
}
|
|
|
|
out('Cartography configuration completed.');
|