From fc62d8c704b7f0402427ffb683a26da0fcff3b2a Mon Sep 17 00:00:00 2001 From: Rom1-B <8530352+Rom1-B@users.noreply.github.com> Date: Tue, 18 Aug 2026 15:15:23 +0200 Subject: [PATCH 1/4] feature/glpi-12.0 --- .github/workflows/continuous-integration.yml | 2 +- CHANGELOG.md | 6 + composer.json | 3 - composer.lock | 876 +------------------ inc/config.class.php | 4 +- setup.php | 4 +- 6 files changed, 13 insertions(+), 882 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 5fa188f..4996570 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -49,7 +49,7 @@ jobs: name: "Generate CI matrix" uses: "glpi-project/plugin-ci-workflows/.github/workflows/generate-ci-matrix.yml@v1" with: - glpi-version: "11.0.x" + glpi-version: "12.0.x" ci: name: "GLPI ${{ matrix.glpi-version }} - php:${{ matrix.php-version }} - ${{ matrix.db-image }}" needs: "generate-ci-matrix" diff --git a/CHANGELOG.md b/CHANGELOG.md index c1ef1d1..ff30f3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [UNRELEASED] + +### Added + +- GLPI 12 compatibility + ## [2.6.1] - 2026-08-04 ### Fixed diff --git a/composer.json b/composer.json index 8338bce..5881de2 100644 --- a/composer.json +++ b/composer.json @@ -2,9 +2,6 @@ "require": { "php": ">=8.2" }, - "require-dev": { - "glpi-project/tools": "^0.8.3" - }, "config": { "optimize-autoloader": true, "platform": { diff --git a/composer.lock b/composer.lock index 76dc6e4..e798ec8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,881 +4,9 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3b0805ca810a8f2c1092a852c12f1234", + "content-hash": "4cacb822b12bf0d2a0dbcf952b20ef7b", "packages": [], - "packages-dev": [ - { - "name": "glpi-project/tools", - "version": "0.8.3", - "source": { - "type": "git", - "url": "https://github.com/glpi-project/tools.git", - "reference": "8ea2a7d4702a858f4b0360ba7d4f1841a5e77026" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/glpi-project/tools/zipball/8ea2a7d4702a858f4b0360ba7d4f1841a5e77026", - "reference": "8ea2a7d4702a858f4b0360ba7d4f1841a5e77026", - "shasum": "" - }, - "require": { - "symfony/console": "^5.4 || ^6.0", - "twig/twig": "^3.3" - }, - "require-dev": { - "nikic/php-parser": "^4.13", - "phpstan/phpstan-src": "^1.10" - }, - "bin": [ - "bin/extract-locales", - "bin/licence-headers-check", - "tools/plugin-release" - ], - "type": "library", - "autoload": { - "psr-4": { - "GlpiProject\\Tools\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-3.0-or-later" - ], - "authors": [ - { - "name": "Teclib'", - "email": "glpi@teclib.com", - "homepage": "http://teclib-group.com" - } - ], - "description": "Various tools for GLPI and its plugins", - "keywords": [ - "glpi", - "plugins", - "tools" - ], - "support": { - "issues": "https://github.com/glpi-project/tools/issues", - "source": "https://github.com/glpi-project/tools" - }, - "time": "2025-10-14T10:26:06+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "symfony/console", - "version": "v6.4.43", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3b643aa587acbc42f967a429af088a56ed8f046d", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v6.4.43" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-07-26T14:44:19+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", - "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.7-dev" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-06-05T06:23:12+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.37.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T16:19:22+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-07-28T08:25:59+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.38.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", - "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-05-25T13:48:31+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.38.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-05-27T06:59:30+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", - "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.7-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-06-16T09:55:08+00:00" - }, - { - "name": "symfony/string", - "version": "v7.4.15", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v7.4.15" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-07-28T07:33:02+00:00" - }, - { - "name": "twig/twig", - "version": "v3.28.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", - "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", - "shasum": "" - }, - "require": { - "php": ">=8.1.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "php-cs-fixer/shim": "^3.0@stable", - "phpstan/phpstan": "^2.0@stable", - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/Resources/core.php", - "src/Resources/debug.php", - "src/Resources/escaper.php", - "src/Resources/string_loader.php" - ], - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.28.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2026-07-03T20:44:34+00:00" - } - ], + "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": {}, diff --git a/inc/config.class.php b/inc/config.class.php index f98b7e2..6622c5f 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -35,9 +35,9 @@ class PluginSccmConfig extends CommonDBTM { - public static $rightname = 'config'; + public static string $rightname = 'config'; - public $dohistory = true; + public bool $dohistory = true; public static function canCreate(): bool { diff --git a/setup.php b/setup.php index cbbb709..c0158d8 100644 --- a/setup.php +++ b/setup.php @@ -65,9 +65,9 @@ /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */ define('PLUGIN_SCCM_VERSION', '2.6.1'); /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */ -define("PLUGIN_SCCM_MIN_GLPI", "11.0.0"); +define("PLUGIN_SCCM_MIN_GLPI", "12.0.0"); /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */ -define("PLUGIN_SCCM_MAX_GLPI", "11.0.99"); +define("PLUGIN_SCCM_MAX_GLPI", "12.0.99"); function plugin_init_sccm() { From d99306376e227f8a07d4999ce584f2eeb6e431e3 Mon Sep 17 00:00:00 2001 From: mbressy Date: Tue, 1 Sep 2026 11:00:42 +0000 Subject: [PATCH 2/4] add local GLPI 12 dev environment and migration test notes --- .dev/Dockerfile | 59 ++++++ .dev/README.md | 127 +++++++++++++ .dev/docker-compose.sccm.yaml | 51 ++++++ .dev/install-sqlsrv.sh | 61 +++++++ .dev/sccm-schema.sql | 331 ++++++++++++++++++++++++++++++++++ MIGRATION_NOTES.md | 200 ++++++++++++++++++++ Makefile | 47 ++++- README.md | 16 +- 8 files changed, 881 insertions(+), 11 deletions(-) create mode 100644 .dev/Dockerfile create mode 100644 .dev/README.md create mode 100644 .dev/docker-compose.sccm.yaml create mode 100755 .dev/install-sqlsrv.sh create mode 100644 .dev/sccm-schema.sql create mode 100644 MIGRATION_NOTES.md diff --git a/.dev/Dockerfile b/.dev/Dockerfile new file mode 100644 index 0000000..0beed2c --- /dev/null +++ b/.dev/Dockerfile @@ -0,0 +1,59 @@ +# ------------------------------------------------------------------------- +# SCCM plugin for GLPI - local development image (OPTIONAL / NOT wired in) +# ------------------------------------------------------------------------- +# GLPI development image + Microsoft SQL Server client stack: +# - msodbcsql18 (Microsoft ODBC Driver 18 for SQL Server) +# - sqlsrv / pdo_sqlsrv PHP extensions +# +# This mirrors GLPI core's .docker/app/Dockerfile and adds the pieces the +# SCCM plugin needs to talk to a real MSSQL instance. +# +# NOTE: the default workflow no longer builds a custom `app` image - that +# recreated the `app` container and broke VS Code Dev Containers. Instead, +# `make sccm-env-up` installs the same stack into the *running* container +# via plugins/sccm/.dev/install-sqlsrv.sh. This Dockerfile is kept only for +# building a standalone image outside a dev container (e.g. CI): +# docker build -t glpi-sccm-app --build-arg PHP_VERSION=8.4 plugins/sccm/.dev +# ------------------------------------------------------------------------- + +ARG PHP_VERSION=8.4 + +FROM ghcr.io/glpi-project/glpi-development-env:${PHP_VERSION} + +USER root + +# --- verbatim from GLPI core .docker/app/Dockerfile ----------------------- +ENV PHP_INI_SCAN_DIR="/usr/local/etc/php/conf.d/:/usr/local/etc/php/custom_conf.d/" +RUN mkdir -p /usr/local/etc/php/custom_conf.d/ + +ARG HOST_GROUP_ID=1000 +ARG HOST_USER_ID=1000 +RUN groupmod --gid ${HOST_GROUP_ID} www-data \ + && usermod --uid ${HOST_USER_ID} www-data \ + && usermod --shell /bin/bash www-data \ + && echo "www-data ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \ + && mkdir --parents /home/www-data \ + && chown www-data:www-data /home/www-data \ + && usermod --home /home/www-data www-data + +# --- Microsoft ODBC driver 18 + sqlsrv/pdo_sqlsrv PHP extensions --------- +# NOTE: the base image is Debian 12 (bookworm). If it ever switches distro +# or release, adjust the packages.microsoft.com path below. +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg apt-transport-https unixodbc-dev; \ + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg; \ + echo "deb [signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" \ + > /etc/apt/sources.list.d/mssql-release.list; \ + apt-get update; \ + ACCEPT_EULA=Y apt-get install -y --no-install-recommends msodbcsql18; \ + pecl install sqlsrv pdo_sqlsrv; \ + docker-php-ext-enable sqlsrv pdo_sqlsrv; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/* + +VOLUME /home/www-data + +USER www-data diff --git a/.dev/README.md b/.dev/README.md new file mode 100644 index 0000000..6ff1ffd --- /dev/null +++ b/.dev/README.md @@ -0,0 +1,127 @@ +# SCCM plugin — local test environment + +The plugin talks to a **Microsoft SQL Server** database (the SCCM / MECM +source) through the `sqlsrv` PHP extension, then pushes FusionInventory-style +XML to GLPI's **native** inventory endpoint (`/front/inventory.php`). + +A full SCCM/MECM install is not reproducible in a dev environment, so this +folder ships a **fixture MSSQL database** containing only the tables/views the +plugin actually queries, plus two sample machines. + +## What's here + +| File | Purpose | +|------|---------| +| `docker-compose.sccm.yaml` | Purely additive: declares only the `mssql` service (never touches `app`) | +| `install-sqlsrv.sh` | Installs `msodbcsql18` + `sqlsrv` / `pdo_sqlsrv` into the **running** `app` container | +| `Dockerfile` | Optional standalone image (CI); **not** used by the default `make` workflow | +| `sccm-schema.sql` | Idempotent fixture schema + data (database `CM_TST`) | + +> **Why no image rebuild?** When `app` is a VS Code Dev Container, running +> `docker compose … up --build` recreates it *without* the Dev Container +> overrides (`/vscode` mount, keep-alive command) and the running container +> breaks. So the MSSQL client stack is installed into the live container +> instead. It lives in the container's writable layer — **re-run +> `make sccm-env-up` (or `make install-ext`) after every container rebuild.** + +## Requirements + +- The GLPI core docker stack (`docker-compose.yaml` at the GLPI root). +- `x86_64` host. On Apple Silicon / ARM, swap the image in + `docker-compose.sccm.yaml` for `mcr.microsoft.com/azure-sql-edge`. +- The base image is assumed to be Debian 12. If GLPI core changes it, adjust + the `packages.microsoft.com` line in `Dockerfile`. + +## Usage + +All commands are `make` targets defined in `plugins/sccm/Makefile`; run them +from the **plugin** directory (`plugins/sccm`). + +The `app` (dev) container must already be running. + +```bash +# 1. start the MSSQL service + install sqlsrv/ODBC 18 into the running app container +make sccm-env-up + +# 2. wait ~20-30s for MSSQL to become healthy, then load the fixtures +make sccm-db-seed + +# 3. sanity check: the extension is loaded +make sccm-verify-ext +``` + +Then install / enable the plugin as usual: + +```bash +make install +make enable +``` + +### 1. Enable GLPI's native inventory + +*Setup → Inventory → General setup* → check **"Enable inventory"**. Disabled by +default on a fresh instance → the SCCM push would otherwise get a `403 +Forbidden` from `/front/inventory.php` regardless of any plugin setting. + +Note the **"Authorization header"** selector on that same page: as soon as +inventory is enabled it defaults to **Basic Authentication**, which the push +will need to satisfy (step 2 below) — otherwise you get a `401 Authorization +header required to send an inventory`. Set a login/password there, e.g. +`sccm-agent` / `Glpi_Sccm_2026!`. + +### 2. Plugin configuration to enter in GLPI + +*Setup → SCCM → add a configuration* + +| Field | Value | +|-------|-------| +| Enable synchronization | ✅ | +| Server hostname (MSSQL) | `mssql` | +| Database name | `CM_TST` | +| Username | `sa` | +| Password | `Glpi_Sccm_2026!` | +| Collection name | *(empty = all machines)* — or `Workstations`, or `Site Bordeaux - O'Brien` to exercise the quote-escaping path (only matches `PC-DEV-01`) | +| **Verify SSL certificate** | ❌ **must be unchecked** — MSSQL uses a self-signed cert; ODBC Driver 18 forces encryption and would otherwise reject the connection | +| Inventory server base URL | `http://localhost` — **not** the value of `url_base` if it points at the host-side published port (e.g. `http://localhost:12080`): that port mapping isn't reachable *from inside* the `app` container, only from the host. `http://localhost` (no port = 80) reaches GLPI's own Apache, in-container. | +| Utiliser des informations d'authentification spécifique | ✅ — required as soon as GLPI's inventory "Authorization header" is set to Basic Auth (the default, see step 1) | +| Value for specific authentication | `sccm-agent:Glpi_Sccm_2026!` — **`login:password`**, matching step 1 exactly. This is the raw string passed to cURL's `CURLOPT_USERPWD`, not two separate fields. | + +Use the **Test connection** button to confirm MSSQL connectivity (this only +tests the `sqlsrv` side, not the inventory push). + +### 3. Running the sync + +*Setup → Automatic actions → `SCCMCollect` → Execute*, then `SCCMPush`. +Generated XML lands in `files/_plugins/sccm/xml//`. +Expected result: computer `PC-DEV-01` (and `PC-DEV-02` if the collection scope +is left empty) imported as an asset. Check `files/_log/sccm.log` for +`Push OK` / `Push KO` lines — a `KO` line includes GLPI's own inventory error +response, which is usually the fastest way to diagnose a failure. + +### Inspecting / resetting the fixture DB + +```bash +make sccm-db-shell # interactive sqlcmd on CM_TST +make sccm-db-seed # re-run to reset fixture data (idempotent) +``` + +### Teardown + +```bash +make sccm-env-down # remove the mssql container, keep its volume +make sccm-env-destroy # remove the mssql container AND its data volume +``` + +`app` never depends on `mssql`, so teardown only affects the MSSQL container. +The `sqlsrv` extension stays in the `app` container until its next rebuild. + +## Notes + +- `docker-compose.sccm.yaml` is **layered** on core via `-f` (see the Makefile), + so it never edits a GLPI core file and needs no `docker-compose.override.yaml`. + It is purely additive (only the `mssql` service), so it never recreates `app`. +- The MSSQL port is published on `localhost:12433` for external GUI tools. +- `make install-ext` runs `install-sqlsrv.sh` on its own — use it to reinstall + the client stack after a container rebuild without restarting MSSQL. +- The extension is picked up immediately by any new `php` CLI process (the test + runner); the web (Apache) side is reloaded by the script. diff --git a/.dev/docker-compose.sccm.yaml b/.dev/docker-compose.sccm.yaml new file mode 100644 index 0000000..cac6a92 --- /dev/null +++ b/.dev/docker-compose.sccm.yaml @@ -0,0 +1,51 @@ +# ------------------------------------------------------------------------- +# SCCM plugin for GLPI - local test environment +# ------------------------------------------------------------------------- +# Adds a Microsoft SQL Server instance (the "SCCM source database") to the +# running GLPI development stack. +# +# This file is *purely additive*: it declares only the `mssql` service and +# its volume. It deliberately does NOT redefine the `app` service, so +# layering it on top of core's docker-compose.yaml can never recreate the +# `app` container - which matters when `app` is a VS Code Dev Container +# (recreating it there drops the /vscode mount + keep-alive command and +# breaks the running dev container). +# +# The `sqlsrv` / `pdo_sqlsrv` PHP extensions + Microsoft ODBC Driver 18 are +# installed into the *running* `app` container by `make sccm-env-up` +# (see plugins/sccm/.dev/install-sqlsrv.sh), not baked into an image. +# +# Layer it on core: +# cd /var/www/glpi +# docker compose -f docker-compose.yaml \ +# -f plugins/sccm/.dev/docker-compose.sccm.yaml up -d mssql +# +# All relative paths below are resolved from the GLPI root (the directory of +# the first -f file), NOT from this file's directory. +# ------------------------------------------------------------------------- + +services: + mssql: + container_name: "glpi-sccm-mssql-v12" + image: "mcr.microsoft.com/mssql/server:2022-latest" + restart: "unless-stopped" + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "Glpi_Sccm_2026!" + MSSQL_PID: "Developer" + ports: + - "12433:1433" + volumes: + - "sccm_mssql:/var/opt/mssql" + - "./plugins/sccm/.dev/sccm-schema.sql:/sql/sccm-schema.sql:ro" + healthcheck: + test: + - "CMD-SHELL" + - "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P \"$${MSSQL_SA_PASSWORD}\" -Q 'SELECT 1' || exit 1" + interval: "10s" + timeout: "5s" + retries: 12 + start_period: "30s" + +volumes: + sccm_mssql: diff --git a/.dev/install-sqlsrv.sh b/.dev/install-sqlsrv.sh new file mode 100755 index 0000000..ff0ce36 --- /dev/null +++ b/.dev/install-sqlsrv.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# ------------------------------------------------------------------------- +# SCCM plugin for GLPI - MSSQL client stack installer +# ------------------------------------------------------------------------- +# Installs, into a *running* GLPI dev container (no image rebuild): +# - Microsoft ODBC Driver 18 for SQL Server (msodbcsql18) +# - sqlsrv / pdo_sqlsrv PHP extensions +# +# Run as root inside the `app` container. Idempotent - safe to re-run. +# Must be re-run after every container rebuild: the changes live in the +# container's writable layer, not in an image. +# +# Driven by `make sccm-env-up` / `make install-ext` (plugins/sccm/Makefile). +# +# NOTE: the base image is Debian 12 (bookworm). If GLPI core switches distro +# or release, adjust the packages.microsoft.com path below. +# ------------------------------------------------------------------------- +set -euo pipefail + +if php -m | grep -qx 'sqlsrv' && php -m | grep -qx 'pdo_sqlsrv'; then + echo "sqlsrv + pdo_sqlsrv already loaded - nothing to do." + exit 0 +fi + +export DEBIAN_FRONTEND=noninteractive + +apt-get update +apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg apt-transport-https unixodbc-dev + +if [ ! -f /usr/share/keyrings/microsoft-prod.gpg ]; then + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg +fi +echo "deb [signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" \ + > /etc/apt/sources.list.d/mssql-release.list + +apt-get update +ACCEPT_EULA=Y apt-get install -y --no-install-recommends msodbcsql18 + +pecl list 2>/dev/null | grep -qi '^sqlsrv' || pecl install sqlsrv +pecl list 2>/dev/null | grep -qi '^pdo_sqlsrv' || pecl install pdo_sqlsrv +docker-php-ext-enable sqlsrv pdo_sqlsrv + +apt-get clean +rm -rf /var/lib/apt/lists/* + +# Best-effort reload so a running Apache picks up the extension. +# (A fresh `php` CLI - i.e. the test runner - sees it with no reload.) +if command -v apache2ctl >/dev/null 2>&1; then + apache2ctl -k restart 2>/dev/null && echo "Apache reloaded." || true +fi + +echo +if php -m | grep -qE '^(pdo_)?sqlsrv$'; then + echo "sqlsrv stack installed OK:" + php -m | grep -E '^(pdo_)?sqlsrv$' +else + echo "ERROR: sqlsrv extension still not loaded." >&2 + exit 1 +fi diff --git a/.dev/sccm-schema.sql b/.dev/sccm-schema.sql new file mode 100644 index 0000000..421f4b6 --- /dev/null +++ b/.dev/sccm-schema.sql @@ -0,0 +1,331 @@ +-- ------------------------------------------------------------------------- +-- SCCM plugin for GLPI - fixture database for local functional testing +-- ------------------------------------------------------------------------- +-- Re-creates a minimal subset of the Microsoft SCCM / MECM database: only +-- the tables and views the plugin actually queries (see inc/sccm.class.php), +-- created as plain tables, plus a couple of fixture machines. +-- +-- Idempotent: safe to run again to reset the fixture data. +-- +-- docker compose exec -T mssql /opt/mssql-tools18/bin/sqlcmd \ +-- -C -S localhost -U sa -P 'Glpi_Sccm_2026!' -i /sql/sccm-schema.sql +-- +-- or simply: make sccm-db-seed +-- ------------------------------------------------------------------------- + +IF DB_ID('CM_TST') IS NULL +BEGIN + CREATE DATABASE CM_TST; +END +GO + +USE CM_TST; +GO + +-- ---- schema -------------------------------------------------------------- + +DROP TABLE IF EXISTS Computer_System_DATA; +DROP TABLE IF EXISTS Motherboard_DATA; +DROP TABLE IF EXISTS Operating_System_DATA; +DROP TABLE IF EXISTS PC_BIOS_DATA; +DROP TABLE IF EXISTS Processor_DATA; +DROP TABLE IF EXISTS Network_DATA; +DROP TABLE IF EXISTS System_DISC; +DROP TABLE IF EXISTS System_DATA; +DROP TABLE IF EXISTS v_R_System; +DROP TABLE IF EXISTS v_GS_WORKSTATION_STATUS; +DROP TABLE IF EXISTS v_GS_NETWORK_ADAPTER; +DROP TABLE IF EXISTS v_GS_ADD_REMOVE_PROGRAMS; +DROP TABLE IF EXISTS v_GS_ADD_REMOVE_PROGRAMS_64; +DROP TABLE IF EXISTS v_GS_PHYSICAL_MEMORY; +DROP TABLE IF EXISTS v_GS_VIDEO_CONTROLLER; +DROP TABLE IF EXISTS v_GS_SOUND_DEVICE; +DROP TABLE IF EXISTS v_GS_LOGICAL_DISK; +DROP TABLE IF EXISTS v_gs_Disk; +DROP TABLE IF EXISTS v_GS_CDROM; +DROP TABLE IF EXISTS v_FullCollectionMembership; +DROP TABLE IF EXISTS v_Collection; +GO + +CREATE TABLE Computer_System_DATA ( + MachineID int NOT NULL, + Description00 nvarchar(255) NULL, + Domain00 nvarchar(255) NULL, + Manufacturer00 nvarchar(255) NULL, + Model00 nvarchar(255) NULL, + Roles00 nvarchar(512) NULL, + SystemType00 nvarchar(64) NULL, + UserName00 nvarchar(255) NULL, + TimeKey nvarchar(64) NULL +); + +CREATE TABLE Motherboard_DATA ( + MachineID int NOT NULL, + SystemName00 nvarchar(255) NULL +); + +CREATE TABLE Operating_System_DATA ( + MachineID int NOT NULL, + BuildNumber00 nvarchar(64) NULL, + Caption00 nvarchar(255) NULL, + CSDVersion00 nvarchar(128) NULL, + BootDevice00 nvarchar(255) NULL, + InstallDate00 nvarchar(64) NULL, + LastBootUpTime00 nvarchar(64) NULL, + Manufacturer00 nvarchar(255) NULL, + Name00 nvarchar(255) NULL, + Organization00 nvarchar(255) NULL, + RegisteredUser00 nvarchar(255) NULL, + TotalVirtualMemorySize00 nvarchar(64) NULL, + TotalVisibleMemorySize00 nvarchar(64) NULL, + Version00 nvarchar(64) NULL +); + +CREATE TABLE PC_BIOS_DATA ( + MachineID int NOT NULL, + SerialNumber00 nvarchar(128) NULL, + ReleaseDate00 datetime NULL, + Name00 nvarchar(255) NULL, + SMBIOSBIOSVersion00 nvarchar(128) NULL, + Version00 nvarchar(128) NULL, + Manufacturer00 nvarchar(255) NULL +); + +CREATE TABLE Processor_DATA ( + MachineID int NOT NULL, + Manufacturer00 nvarchar(128) NULL, + Name00 nvarchar(255) NULL, + NormSpeed00 nvarchar(32) NULL, + AddressWidth00 nvarchar(16) NULL, + CPUKey00 nvarchar(64) NULL, + NumberOfCores00 nvarchar(16) NULL, + NumberOfLogicalProcessors00 nvarchar(16) NULL +); + +CREATE TABLE Network_DATA ( + MachineID int NOT NULL, + IPAddress00 nvarchar(512) NULL, + MACAddress00 nvarchar(64) NULL, + IPSubnet00 nvarchar(128) NULL, + DefaultIPGateway00 nvarchar(128) NULL, + DHCPServer00 nvarchar(64) NULL, + DNSDomain00 nvarchar(255) NULL, + ServiceName00 nvarchar(128) NULL +); + +CREATE TABLE System_DISC ( + ItemKey int NOT NULL, + User_Name0 nvarchar(255) NULL +); + +CREATE TABLE System_DATA ( + MachineID int NOT NULL, + SMSID0 nvarchar(64) NULL, + SystemRole0 nvarchar(64) NULL +); + +CREATE TABLE v_R_System ( + ResourceID int NOT NULL, + User_Name0 nvarchar(255) NULL, + Name0 nvarchar(255) NULL +); + +CREATE TABLE v_GS_WORKSTATION_STATUS ( + ResourceID int NOT NULL, + LastHWScan datetime NULL +); + +CREATE TABLE v_GS_NETWORK_ADAPTER ( + ResourceID int NOT NULL, + Name0 nvarchar(255) NULL, + ServiceName0 nvarchar(128) NULL +); + +CREATE TABLE v_GS_ADD_REMOVE_PROGRAMS ( + ResourceID int NOT NULL, + DisplayName0 nvarchar(255) NULL, + InstallDate0 nvarchar(32) NULL, + Version0 nvarchar(64) NULL, + Publisher0 nvarchar(255) NULL +); + +CREATE TABLE v_GS_ADD_REMOVE_PROGRAMS_64 ( + ResourceID int NOT NULL, + DisplayName0 nvarchar(255) NULL, + InstallDate0 nvarchar(32) NULL, + Version0 nvarchar(64) NULL, + Publisher0 nvarchar(255) NULL +); + +CREATE TABLE v_GS_PHYSICAL_MEMORY ( + ResourceID int NOT NULL, + Capacity0 nvarchar(32) NULL, + Caption0 nvarchar(128) NULL, + Description0 nvarchar(255) NULL, + FormFactor0 nvarchar(32) NULL, + Manufacturer0 nvarchar(128) NULL, + Removable0 nvarchar(16) NULL, + Speed0 nvarchar(32) NULL, + BankLabel0 nvarchar(64) NULL, + GroupID int NULL +); + +CREATE TABLE v_GS_VIDEO_CONTROLLER ( + ResourceID int NOT NULL, + VideoProcessor0 nvarchar(255) NULL, + AdapterRAM0 bigint NULL, + Name0 nvarchar(255) NULL, + CurrentHorizontalResolution0 nvarchar(16) NULL, + CurrentVerticalResolution0 nvarchar(16) NULL, + GroupID int NULL +); + +CREATE TABLE v_GS_SOUND_DEVICE ( + ResourceID int NOT NULL, + Description0 nvarchar(255) NULL, + Manufacturer0 nvarchar(128) NULL, + Name0 nvarchar(255) NULL +); + +CREATE TABLE v_GS_LOGICAL_DISK ( + ResourceID int NOT NULL, + Description0 nvarchar(255) NULL, + DeviceID0 nvarchar(16) NULL, + FileSystem0 nvarchar(32) NULL, + Size0 bigint NULL, + FreeSpace0 bigint NULL, + VolumeName0 nvarchar(128) NULL, + GroupID int NULL +); + +CREATE TABLE v_gs_Disk ( + ResourceID int NOT NULL, + GroupID int NULL, + Caption0 nvarchar(255) NULL +); + +CREATE TABLE v_GS_CDROM ( + ResourceID int NOT NULL, + Description0 nvarchar(255) NULL, + Manufacturer0 nvarchar(128) NULL, + Caption0 nvarchar(255) NULL, + Name0 nvarchar(255) NULL, + SCSITargetID0 nvarchar(16) NULL, + MediaType0 nvarchar(64) NULL +); + +CREATE TABLE v_FullCollectionMembership ( + ResourceID int NOT NULL, + CollectionID nvarchar(16) NOT NULL +); + +CREATE TABLE v_Collection ( + CollectionID nvarchar(16) NOT NULL, + Name nvarchar(255) NULL +); +GO + +-- ---- fixture data ----------------------------------------------------- +-- Two machines: 16777220 (PC-DEV-01, full hardware) and 16777221 (PC-DEV-02). + +INSERT INTO Computer_System_DATA + (MachineID, Description00, Domain00, Manufacturer00, Model00, Roles00, SystemType00, UserName00, TimeKey) VALUES + (16777220, N'AT/AT COMPATIBLE', N'GLPI', N'LENOVO', N'20XW00 ThinkPad T14', N'SMS Client', N'x64-based PC', N'GLPI\alice', N'2026-08-30 04:12:00'), + (16777221, N'AT/AT COMPATIBLE', N'GLPI', N'Dell Inc.', N'OptiPlex 7090', N'SMS Client', N'x64-based PC', N'GLPI\bob', N'2026-08-30 04:12:30'); + +INSERT INTO Motherboard_DATA (MachineID, SystemName00) VALUES + (16777220, N'PC-DEV-01'), + (16777221, N'PC-DEV-02'); + +INSERT INTO Operating_System_DATA + (MachineID, BuildNumber00, Caption00, CSDVersion00, BootDevice00, InstallDate00, LastBootUpTime00, + Manufacturer00, Name00, Organization00, RegisteredUser00, TotalVirtualMemorySize00, TotalVisibleMemorySize00, Version00) VALUES + (16777220, N'22631', N'Microsoft Windows 11 Enterprise', N'', N'\Device\HarddiskVolume1', N'20240102', N'20260830060000', + N'Microsoft Corporation', N'Microsoft Windows 11 Enterprise|C:\WINDOWS|\Device\Harddisk0\Partition2', N'GLPI', N'alice', + N'33554432', N'16777216', N'10.0.22631'), + (16777221, N'19045', N'Microsoft Windows 10 Pro', N'', N'\Device\HarddiskVolume1', N'20230510', N'20260830055000', + N'Microsoft Corporation', N'Microsoft Windows 10 Pro|C:\WINDOWS|\Device\Harddisk0\Partition2', N'GLPI', N'bob', + N'16777216', N'8388608', N'10.0.19045'); + +INSERT INTO PC_BIOS_DATA + (MachineID, SerialNumber00, ReleaseDate00, Name00, SMBIOSBIOSVersion00, Version00, Manufacturer00) VALUES + (16777220, N'PF3ABCDE', '2023-02-01T00:00:00', N'N3AET42W (1.28)', N'N3AET42W', N'LENOVO - 1280', N'LENOVO'), + (16777221, N'7ABCDX3', '2022-11-15T00:00:00', N'2.14.0', N'2.14.0', N'DELL - 20220101', N'Dell Inc.'); + +INSERT INTO System_DATA (MachineID, SMSID0, SystemRole0) VALUES + (16777220, N'GUID:0AA1B2C3-1111-2222-3333-444455556666', N'Workstation'), + (16777221, N'GUID:0BB2C3D4-2222-3333-4444-555566667777', N'Workstation'); + +INSERT INTO System_DISC (ItemKey, User_Name0) VALUES + (16777220, N'GLPI\alice'), + (16777221, N'GLPI\bob'); + +INSERT INTO v_R_System (ResourceID, User_Name0, Name0) VALUES + (16777220, N'GLPI\alice', N'PC-DEV-01'), + (16777221, N'GLPI\bob', N'PC-DEV-02'); + +INSERT INTO v_GS_WORKSTATION_STATUS (ResourceID, LastHWScan) VALUES + (16777220, '2026-08-30T03:55:00'), + (16777221, '2026-08-30T02:40:00'); + +-- --- machine 16777220: full hardware ------------------------------------- +INSERT INTO Processor_DATA + (MachineID, Manufacturer00, Name00, NormSpeed00, AddressWidth00, CPUKey00, NumberOfCores00, NumberOfLogicalProcessors00) VALUES + (16777220, N'GenuineIntel', N'12th Gen Intel(R) Core(TM) i7-1265U', N'2700', N'64', N'CPU0', N'10', N'12'); + +INSERT INTO Network_DATA + (MachineID, IPAddress00, MACAddress00, IPSubnet00, DefaultIPGateway00, DHCPServer00, DNSDomain00, ServiceName00) VALUES + (16777220, N'10.20.30.40, fe80::1', N'AA:BB:CC:11:22:33', N'255.255.255.0', N'10.20.30.1', N'10.20.30.2', N'glpi.lan', N'e1dexpress'); + +INSERT INTO v_GS_NETWORK_ADAPTER (ResourceID, Name0, ServiceName0) VALUES + (16777220, N'Intel(R) Ethernet Connection I219-V', N'e1dexpress'); + +INSERT INTO v_GS_ADD_REMOVE_PROGRAMS_64 (ResourceID, DisplayName0, InstallDate0, Version0, Publisher0) VALUES + (16777220, N'7-Zip 23.01 (x64)', N'20240210', N'23.01', N'Igor Pavlov'), + (16777220, N'Mozilla Firefox (x64 en-US)', N'20260805', N'128.2.0', N'Mozilla'), + (16777220, N'Kaspersky Endpoint Security 12.5', N'20260101', N'12.5.0.539', N'AO Kaspersky Lab'); + +INSERT INTO v_GS_ADD_REMOVE_PROGRAMS (ResourceID, DisplayName0, InstallDate0, Version0, Publisher0) VALUES + (16777220, N'Notepad++ (32-bit x86)', N'20240115', N'8.6.2', N'Notepad++ Team'); + +-- Capacity0 in MB (matches what GLPI's native inventory expects for +-- MEMORIES/CAPACITY - no unit conversion is applied by the plugin, see +-- PluginSccmSccmxml::setMemories()). 8192 MB = 8 GB per stick. +INSERT INTO v_GS_PHYSICAL_MEMORY + (ResourceID, Capacity0, Caption0, Description0, FormFactor0, Manufacturer0, Removable0, Speed0, BankLabel0, GroupID) VALUES + (16777220, N'8192', N'Physical Memory', N'Physical Memory', N'12', N'Samsung', N'0', N'4800', N'Controller0-ChannelA', 1), + (16777220, N'8192', N'Physical Memory', N'Physical Memory', N'12', N'Samsung', N'0', N'4800', N'Controller0-ChannelB', 2); + +INSERT INTO v_GS_VIDEO_CONTROLLER + (ResourceID, VideoProcessor0, AdapterRAM0, Name0, CurrentHorizontalResolution0, CurrentVerticalResolution0, GroupID) VALUES + (16777220, N'Intel(R) Iris(R) Xe Graphics', 1073741824, N'Intel(R) Iris(R) Xe Graphics', N'1920', N'1080', 1); + +INSERT INTO v_GS_SOUND_DEVICE (ResourceID, Description0, Manufacturer0, Name0) VALUES + (16777220, N'Realtek(R) Audio', N'Realtek', N'Realtek(R) Audio'); + +INSERT INTO v_GS_LOGICAL_DISK + (ResourceID, Description0, DeviceID0, FileSystem0, Size0, FreeSpace0, VolumeName0, GroupID) VALUES + (16777220, N'Local Fixed Disk', N'C:', N'NTFS', 486000, 210000, N'Windows', 1); + +INSERT INTO v_gs_Disk (ResourceID, GroupID, Caption0) VALUES + (16777220, 1, N'KBG50ZNV512G KIOXIA'); + +INSERT INTO v_GS_CDROM (ResourceID, Description0, Manufacturer0, Caption0, Name0, SCSITargetID0, MediaType0) VALUES + (16777220, N'CD-ROM Drive', N'(Standard CD-ROM drives)', N'HL-DT-ST DVDRAM', N'HL-DT-ST DVDRAM GUD1N', N'0', N'DVD Writer'); + +-- --- collections ------------------------------------------------------- +INSERT INTO v_Collection (CollectionID, Name) VALUES + (N'SMS00001', N'All Systems'), + (N'GLP00010', N'Workstations'), + (N'GLP00011', N'Site Bordeaux - O''Brien'); + +INSERT INTO v_FullCollectionMembership (ResourceID, CollectionID) VALUES + (16777220, N'SMS00001'), + (16777221, N'SMS00001'), + (16777220, N'GLP00010'), + (16777221, N'GLP00010'), + (16777220, N'GLP00011'); +GO + +PRINT 'SCCM fixture database CM_TST ready.'; +GO diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md new file mode 100644 index 0000000..ea325a6 --- /dev/null +++ b/MIGRATION_NOTES.md @@ -0,0 +1,200 @@ +# Notes de migration GLPI 12 — plugin SCCM + +Suivi des tests réalisés sur `feature/glpi-12.0`, dans le cadre de la +procédure de test des plugins GLPI 12. Complète [.dev/README.md](.dev/README.md) +(mise en place de l'environnement) avec les **résultats** des tests et les +constats à connaître avant de rejouer cette migration ailleurs. + +Environnement : GLPI core `main` (12.0.0-dev), plugin `feature/glpi-12.0` +(2.6.1), stack Docker locale (voir `.dev/`). + +## Constats importants pour toute migration de plugin vers GLPI 12 + +### 1. Suspension automatique de l'exécution des plugins après une mise à jour majeure + +Dès qu'une mise à jour majeure du core est détectée (`Update::isUpdateMandatory()`), +GLPI 12 **suspend automatiquement l'exécution de tous les plugins** +(`Config` `core.plugins_execution_mode = suspended_by_update`, +voir `src/Plugin.php::checkStates()`). Ce verrou : + +* bloque le chargement du plugin **côté web uniquement** — toute URL + `/plugins//front/*.php` renvoie **404 "Item not found"** + (`PluginsRouterListener` → `Plugin::isPluginLoaded()` = false) ; +* **n'est pas levé automatiquement** après un `bin/console database:update` + réussi, dès lors que la mise à jour change de version majeure/intermédiaire + (`src/Update.php:415` — la reprise auto ne s'applique qu'entre versions de + même ligne, ex. 12.0.0 → 12.0.1) ; +* n'affecte pas le CLI : `bin/console plugin:list/install/enable` continuent + d'afficher le plugin comme « Enabled » (l'état `glpi_plugins.state` en base + n'est pas modifié — seul un flag de config global bloque le chargement). + +**Sans le savoir, on peut donc croire une migration "cassée" côté web alors +que tout est correct côté CLI/DB.** À lever explicitement après toute +migration majeure : + +```bash +docker compose exec app php bin/console plugin:resume_execution +``` + +*(commande liée à `front/plugin.php`, qui exige elle-même une réauthentification +— redirection `/ReAuth/Prompt` observée lors des tests — cohérent avec le +caractère sensible de l'action.)* + +### 2. CSRF : mécanisme entièrement remplacé (confirme la consigne de migration) + +* **GLPI 11** : champ caché `_glpi_csrf_token`, valeur vérifiée via + `Session::checkCSRF()` sur chaque formulaire (confirmé en observant le + code source HTML du formulaire de connexion GLPI 11 — token présent). +* **GLPI 12** : plus aucun champ caché. Validation faite par un listener + Symfony central (`Glpi\Kernel\Listener\ControllerListener\CheckCsrfListener`) + qui compare les en-têtes `Sec-Fetch-Site` / `Origin` au `Host` de la requête, + sur **toute requête non-GET**, automatiquement, sans rien à faire côté plugin. +* Le plugin SCCM n'ajoute **aucun champ `CSRF_token` manuel** dans son + template (`templates/config.html.twig` s'appuie entièrement sur + `generic_show_form.html.twig` du core) → **conforme par construction**, + aucun correctif nécessaire sur ce point. +* Testé et confirmé fonctionnel : une requête POST cross-site forgée + (`Origin` différent du `Host`) sur `config.form.php` (action purge) est + bloquée avec 403 ; la requête légitime same-origin aboutit normalement. + +## Phase 1 — Environnement de test + +Voir [.dev/README.md](.dev/README.md) : image `app` + extension `sqlsrv`/ODBC 18 +installée à chaud (non baked dans l'image, pour ne pas casser le Dev +Container), service `mssql` additif, base fixture `CM_TST` (2 machines, +`PC-DEV-01`/`PC-DEV-02`). + +Prérequis GLPI découverts en cours de route, absents de la doc initiale et +maintenant documentés dans `.dev/README.md` : + +* l'inventaire natif GLPI doit être activé explicitement + (*Configuration → Inventaire → Configuration générale*), sinon 403 sur + `/front/inventory.php` quelle que soit la config du plugin ; +* dès l'inventaire activé, l'authentification basique est active par défaut + → nécessite de renseigner *Utiliser des informations d'authentification + spécifique* côté plugin (`login:mot_de_passe`), le plugin gère déjà ce cas + via `CURLOPT_USERPWD`. + +## Phase 2 — Migration GLPI 11 → 12 + +Rejouée dans un environnement dédié (`git worktree` séparé, stack Docker sur +ports `11xxx`, indépendante de la stack de dev principale) : + +1. Install + config du plugin `main` (2.6.1) sur GLPI 11.0.9-dev fraîchement + installé, config `MigrationTest` avec mot de passe MSSQL et `auth_info` + réels (chiffrés). +2. Bascule du code : core `11.0/bugfixes` → `main` (12.0.0-dev), plugin + `main` → `feature/glpi-12.0`. +3. `bin/console database:update --no-interaction` : OK, sans erreur. +4. `bin/console plugin:resume_execution` (cf. constat n°1 ci-dessus). + +**Résultat** : table, cron tasks et **secrets chiffrés préservés et +déchiffrables** après migration — `Test connection` post-migration retourne +`Login successful` / `Connection successful!` (connexion réelle à MSSQL avec +le mot de passe créé avant migration). Aucune erreur dans les logs après +reprise de l'exécution des plugins. + +## Phase 3 — Tests fonctionnels (sur la stack de dev GLPI 12 principale) + +| Test | Résultat | +|---|---| +| Installation / activation / désactivation / désinstallation | ✅ | +| Configuration (CRUD, multi-config, Test connection) | ✅ | +| Synchro complète (SCCMCollect → XML → SCCMPush → inventaire natif) | ✅ multi-machines | +| Idempotence (re-run sans doublon) | ✅ | +| Option `use_lasthwscan` | ✅ — `last_inventory_update` remplacé par le `LastHWScan` SCCM, pas l'heure du push | +| Scope de collection avec apostrophe (`Site Bordeaux - O'Brien`) | ✅ pas d'injection, échappement correct | +| Droits/profils (profil Technician sans droit `config`) | ✅ 403 sur GET/POST, menu masqué, rien créé en base | +| CSRF (cf. constat n°2) | ✅ | +| Purge (avec test CSRF forgé au préalable) | ✅ | +| Désinstallation complète | ✅ table, cron, display prefs, dossier `files/_plugins/sccm/` tous nettoyés ; ordinateurs importés conservés (assets natifs, non liés au plugin) | +| Logs (`php-errors.log`, `sql-errors.log`, mode debug) | propres à chaque étape | + +## Bugs / points à corriger avant la PR + +### 🐛 Exception non catchée dans `executePush()` (à corriger) + +[inc/sccm.class.php:584](inc/sccm.class.php#L584) : + +```php +$REP_XML = realpath(GLPI_PLUGIN_DOC_DIR . '/sccm/xml/' . $config_id . '/' . $tab['CSD-MachineID'] . '.ocs'); +if ($REP_XML === '0') { // mort : Safe\realpath() ne renvoie jamais '0', il lève une exception +``` + +`Safe\realpath()` **lève une exception** (`Safe\Exceptions\FilesystemException`) +quand le chemin n'existe pas, au lieu de renvoyer `false` comme la fonction +native. Le test `=== '0'` est donc du code mort. Reproduit en conditions +réelles : `SCCMPush` lancé avant que `SCCMCollect` n'ait produit le `.ocs` +correspondant → exception non catchée (`executePush()` n'a **aucun** +`try/catch`, contrairement à `executeCollect()` qui catch `Throwable` par +config) → toute la tâche plante pour toute la config, rien loggé dans +`sccm.log`. Un `.ocs` manquant/purgé en prod (device supprimé de SCCM entre +temps, nettoyage manuel du dossier) provoquerait le même crash. + +**Correctif suggéré** : `file_exists()` avant `realpath()`, ou `try/catch` +autour de l'appel, avec log + `continue` comme le reste de la boucle. + +### ❓ À vérifier : unité de `Capacity0` (mémoire) + +[inc/sccmxml.class.php `setMemories()`](inc/sccmxml.class.php) ne fait +**aucune conversion d'unité** sur `Capacity0` avant de l'injecter dans +``, contrairement à `setStorages()` qui fait `* 1024` sur +`gld-TotalSize`/`gld-FreeSpace`. GLPI/FusionInventory attend `CAPACITY` en +**Mo**. Si `v_GS_PHYSICAL_MEMORY.Capacity0` de SCCM est en Ko (à confirmer +sur une vraie base SCCM — non vérifiable avec le fixture, qui a été codé en +Mo pour ce test), les tailles mémoire remontées en prod seraient ~1024× +trop grandes. Impact : affichage erroné, pas de crash (la colonne `size` en +DB est un `int`, largement suffisant pour ce facteur d'erreur). À trancher +avec un accès à une vraie base SCCM ou la doc Microsoft (`Win32_PhysicalMemory`). + +## Phase 5 — Lint / analyse statique + +Tous outils exécutés depuis le conteneur `app`, binaires partagés du core +(`../../vendor/bin/*`, pas de `vendor/` local au plugin pour ces outils sauf +PHPUnit qui a besoin de son propre `composer install` pour générer +`vendor/autoload.php`, cf. `tests/bootstrap.php`). + +| Outil | Résultat | +|---|---| +| Parallel-lint | ✅ aucune erreur | +| PHP-CS-Fixer | ✅ 0 finding | +| Rector | 🔧 1 finding corrigé — voir ci-dessous | +| PHPStan (level 5) | ✅ aucune erreur | +| Psalm (taint analysis) | ✅ aucune erreur | +| PHPUnit | 🔧 62/62 tests passaient déjà ; 41 warnings PHPUnit 12 corrigés — voir ci-dessous. `OK (62 tests, 273 assertions)` | +| Twig CS | ✅ aucune violation | +| License headers (`tools:licence_headers_check`) | ✅ valides | +| ESLint | N/A — aucun JS dans ce plugin (pas de `package.json`) | + +Correctifs appliqués : + +* **`setup.php`** — Rector (`ReplaceHardcodedRightnameByCommonDBTMRightnamePropertyRector`) : + remplace le littéral `"config"` par `Config::$rightname` dans le + `Session::haveRight()` de `plugin_init_sccm()`. +* **`tests/PluginSccmSccmxmlTest.php`** — ajout de l'attribut + `#[AllowMockObjectsWithoutExpectations]` au niveau classe. PHPUnit 12 + signale par défaut tout mock (`getMockBuilder(...)->getMock()`) jamais + vérifié via `->expects()`, ce qui est le cas ici par construction : les + mocks de `PluginSccmSccmxml`/`PluginSccmSccm`/`PluginSccmSccmdb` ne + servent qu'à fournir des données de test (usage "stub"), pas à vérifier + des appels. Motif déjà utilisé dans les tests du core GLPI + (`tests/functional/ConfigTest.php` et autres) pour ce cas exact. + +L'installation d'un environnement de test dédié a été nécessaire au préalable +(non documentée ailleurs, à refaire si besoin) : + +```bash +php bin/console database:install -r -f --db-host=db --db-port=3306 \ + --db-name=glpi_test --db-user=root --db-password=glpi \ + --no-interaction --no-telemetry --env=testing +php bin/console plugin:install --env=testing sccm -u glpi --force +php bin/console plugin:enable --env=testing sccm +cd plugins/sccm && composer install # génère vendor/autoload.php, requis par tests/bootstrap.php +``` + +## Reste à faire + +* [ ] Corriger le bug `realpath()` (voir ci-dessus) +* [ ] Trancher le point `Capacity0` +* [ ] Mettre à jour `sccm.xml` (`12.0.0`) et le CHANGELOG avant la PR +* [ ] Commit des correctifs (`setup.php`, `tests/PluginSccmSccmxmlTest.php`) et de ce fichier diff --git a/Makefile b/Makefile index b0bcc58..9e6a067 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,42 @@ include ../../PluginsMakefile.mk -install-ext: ## Install PHP extensions required by this plugin (sqlsrv). Re-run after any container rebuild. - $(COMPOSE) exec --user root app bash -c "\ - apt-get update -yq \ - && apt-get install -yq --no-install-recommends unixodbc-dev \ - && rm -rf /var/lib/apt/lists/* \ - && pecl install sqlsrv \ - && docker-php-ext-enable sqlsrv" +install-ext: ## Install the MSSQL client stack (ODBC 18 + sqlsrv/pdo_sqlsrv) into the running `app` container. Re-run after any container rebuild. + $(COMPOSE) exec --user root app bash /var/www/glpi/plugins/$(PLUGIN_DIR)/.dev/install-sqlsrv.sh + +##—— SCCM local test environment (MSSQL) ——————————————————————————————————————— +# See plugins/sccm/.dev/README.md. These targets drive docker compose from the +# GLPI root, layering plugins/sccm/.dev/docker-compose.sccm.yaml on top of core. + +SCCM_COMPOSE_FILES = -f docker-compose.yaml -f plugins/sccm/.dev/docker-compose.sccm.yaml +SCCM_SA_PASSWORD ?= Glpi_Sccm_2026! +SCCM_MSSQL_VOLUME = $(notdir $(realpath $(GLPI_DIR)))_sccm_mssql + +sccm-env-up: ## Start the MSSQL service and install the client stack into the running `app` container (no image rebuild) + @$(COMPOSE) ps --status running --services 2>/dev/null | grep -qx app \ + || { echo "The 'app' container is not running - start the dev container first (VS Code: 'Dev Containers: Rebuild Container'), then re-run."; exit 1; } + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) up -d --no-recreate mssql + @$(MAKE) --no-print-directory install-ext + +sccm-env-down: ## Stop and remove the MSSQL service (keeps its data volume) + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) rm -sf mssql + +sccm-env-destroy: ## Stop the MSSQL service and delete its data volume + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) rm -sf mssql + docker volume rm $(SCCM_MSSQL_VOLUME) 2>/dev/null || true + +sccm-db-seed: ## (Re)load the fixture schema plugins/sccm/.dev/sccm-schema.sql into MSSQL + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) exec -T mssql bash -lc '\ + SQLCMD=$$(command -v sqlcmd || echo /opt/mssql-tools18/bin/sqlcmd); \ + [ -x "$$SQLCMD" ] || SQLCMD=/opt/mssql-tools/bin/sqlcmd; \ + "$$SQLCMD" -C -S localhost -U sa -P "$(SCCM_SA_PASSWORD)" -i /sql/sccm-schema.sql' + +sccm-db-shell: ## Open an interactive sqlcmd shell on the MSSQL container (database CM_TST) + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) exec mssql bash -lc '\ + SQLCMD=$$(command -v sqlcmd || echo /opt/mssql-tools18/bin/sqlcmd); \ + [ -x "$$SQLCMD" ] || SQLCMD=/opt/mssql-tools/bin/sqlcmd; \ + "$$SQLCMD" -C -S localhost -U sa -P "$(SCCM_SA_PASSWORD)" -d CM_TST' + +sccm-verify-ext: ## Check that the sqlsrv extension is loaded in the app container + cd $(GLPI_DIR) && $(COMPOSE) $(SCCM_COMPOSE_FILES) exec -T app php -m | grep -E '^(pdo_)?sqlsrv$$' || (echo "sqlsrv NOT loaded" && exit 1) + +.PHONY: sccm-env-up sccm-env-down sccm-env-destroy sccm-db-seed sccm-db-shell sccm-verify-ext diff --git a/README.md b/README.md index 42f7d15..e2c7f5f 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,23 @@ Plugin to synchronize computers from SCCM (version 1802) to GLPI. ## Development setup -This plugin requires the `sqlsrv` PHP extension (PECL), which is not included in the base GLPI development image. +This plugin needs a Microsoft SQL Server database (the SCCM source) and the +`sqlsrv` PHP extension, neither of which ships with the base GLPI development +image. -After starting the containers (`make` from the GLPI root), install it once: +A ready-to-use dockerised environment (MSSQL service + `sqlsrv` + `msodbcsql18` ++ a fixture SCCM database) is provided in [`.dev/`](.dev/README.md): ```bash -make install-ext +cd plugins/sccm +make sccm-env-up # build app image + start stack incl. MSSQL +make sccm-db-seed # load the fixture schema (database CM_TST) +make sccm-verify-ext # check the sqlsrv extension is loaded ``` -Re-run this command after any container rebuild. +See [`.dev/README.md`](.dev/README.md) for the plugin configuration values and +the full workflow. `make install-ext` remains as a minimal fallback that only +installs the extension into the running base container. ## Documentation From f2a6d0180f2b60d888c738d8400b606258dc055b Mon Sep 17 00:00:00 2001 From: mbressy Date: Tue, 1 Sep 2026 11:01:44 +0000 Subject: [PATCH 3/4] align rightname reference and PHPUnit 12 mock expectations --- setup.php | 2 +- tests/PluginSccmSccmxmlTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.php b/setup.php index c0158d8..820269a 100644 --- a/setup.php +++ b/setup.php @@ -76,7 +76,7 @@ function plugin_init_sccm() $plugin = new Plugin(); - if ($plugin->isActivated("sccm") && Session::getLoginUserID() && Session::haveRight("config", UPDATE)) { + if ($plugin->isActivated("sccm") && Session::getLoginUserID() && Session::haveRight(Config::$rightname, UPDATE)) { $PLUGIN_HOOKS[Hooks::CONFIG_PAGE]['sccm'] = "front/config.php"; $PLUGIN_HOOKS[Hooks::MENU_TOADD]['sccm'] = ['config' => PluginSccmMenu::class]; } diff --git a/tests/PluginSccmSccmxmlTest.php b/tests/PluginSccmSccmxmlTest.php index fea7025..0b8696c 100644 --- a/tests/PluginSccmSccmxmlTest.php +++ b/tests/PluginSccmSccmxmlTest.php @@ -30,8 +30,10 @@ */ use Glpi\Tests\GLPITestCase; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use PHPUnit\Framework\Attributes\DataProvider; +#[AllowMockObjectsWithoutExpectations] class PluginSccmSccmxmlTest extends GLPITestCase { private PluginSccmSccmxml $xml; From 54e329f9b86813108819113a18bede6450049fb2 Mon Sep 17 00:00:00 2001 From: mbressy Date: Tue, 1 Sep 2026 11:19:40 +0000 Subject: [PATCH 4/4] update version --- sccm.xml | 5 +++++ setup.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sccm.xml b/sccm.xml index 6a9c9c4..464029e 100644 --- a/sccm.xml +++ b/sccm.xml @@ -47,6 +47,11 @@ Prerequisite : TECLIB' + + 2.7.0 + 12.0.0 + https://github.com/pluginsGLPI/sccm/releases/download/2.7.0/glpi-sccm-2.7.0.tar.bz2 + 2.6.1 11.0.0 diff --git a/setup.php b/setup.php index 820269a..90c05d2 100644 --- a/setup.php +++ b/setup.php @@ -63,7 +63,7 @@ use Glpi\Plugin\Hooks; /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */ -define('PLUGIN_SCCM_VERSION', '2.6.1'); +define('PLUGIN_SCCM_VERSION', '2.7.0'); /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */ define("PLUGIN_SCCM_MIN_GLPI", "12.0.0"); /** @phpstan-ignore theCodingMachineSafe.function (safe to assume this isn't already defined) */