From cc1d4a14f0e01f4c9f009365a1e211a73043c668 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 30 Apr 2025 05:37:13 +0400 Subject: [PATCH 01/16] deps de dev --- composer.json | 18 ++++++++++++++--- composer.lock | 54 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 514ceb3..b86627d 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,18 @@ "type": "library", "description": "espace de maturation pour les librairies", "repositories": [ + { + "type": "path", + "url": "../nulib" + }, + { + "type": "path", + "url": "../nulib-spout" + }, + { + "type": "path", + "url": "../nulib-phpss" + }, { "type": "composer", "url": "https://repos.univ-reunion.fr/composer" @@ -18,9 +30,9 @@ "php": "^8.2" }, "require-dev": { - "nulib/php": "^0.5.0p82", - "nulib/spout": "^0.5.0p82", - "nulib/phpss": "^0.5.0p82", + "nulib/php": "^8.2-dev", + "nulib/spout": "^8.2-dev", + "nulib/phpss": "^8.2-dev", "nulib/tests": "^8.2", "ext-posix": "*", "ext-pcntl": "*", diff --git a/composer.lock b/composer.lock index adbbf00..546a729 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "20d80ba2a043b1e083f352300c19da0f", + "content-hash": "614c12e46a043d8be2eb5f049c76c653", "packages": [], "packages-dev": [ { @@ -391,11 +391,11 @@ }, { "name": "nulib/php", - "version": "0.5.0p82", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib.git", - "reference": "9f8b6545e68079728bb7349487b99024bd7d5090" + "version": "dev-dev82", + "dist": { + "type": "path", + "url": "../nulib", + "reference": "a8094658d8ae21c9e66fba3120b9efede4d54ce7" }, "require": { "ext-json": "*", @@ -435,18 +435,20 @@ } ], "description": "fonctions et classes essentielles", - "time": "2025-04-30T00:33:36+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/phpss", - "version": "0.5.0p82", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git", - "reference": "620e1b8eab68afc8bae94b5a936b7929c03bc734" + "version": "dev-dev82", + "dist": { + "type": "path", + "url": "../nulib-phpss", + "reference": "19ef9cae64dd860492055845779262c236c29556" }, "require": { - "nulib/php": "^0.5.0p82", + "nulib/php": "^8.2-dev", "php": "^8.2", "phpoffice/phpspreadsheet": "^3.3.0" }, @@ -477,15 +479,17 @@ } ], "description": "wrapper pour phpoffice/phpspreadsheet", - "time": "2025-04-30T00:48:34+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/spout", - "version": "0.5.0p82", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git", - "reference": "32fbed599e2e3cfc616df70fe51ba8ec4cb3aefd" + "version": "dev-dev82", + "dist": { + "type": "path", + "url": "../nulib-spout", + "reference": "d52f9a0ebfe4fc2bab145209178ce2d4570c2e75" }, "require": { "ext-dom": "*", @@ -494,7 +498,7 @@ "ext-libxml": "*", "ext-xmlreader": "*", "ext-zip": "*", - "nulib/php": "^0.5.0p82", + "nulib/php": "^8.2-dev", "php": "^8.2" }, "replace": { @@ -535,7 +539,9 @@ } ], "description": "wrapper pour openspout/openspout", - "time": "2025-04-30T00:43:21+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/tests", @@ -2618,7 +2624,11 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "nulib/php": 20, + "nulib/spout": 20, + "nulib/phpss": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From ea68906b8a495656c611d1ab4def0390370d02c8 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 30 Apr 2025 05:35:22 +0400 Subject: [PATCH 02/16] Init changelog & version 0.5.0p82 --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9a2877c..83072ce 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,5 @@ +## Release 0.5.0p82 du 30/04/2025-05:35 + ## Release 0.5.0p74 du 30/04/2025-05:30 * `2d73f4d` documenter showmorePlugin From 5862d3583aa7d61caf3f6e7b650c4ebb7db700d5 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 30 Apr 2025 05:47:34 +0400 Subject: [PATCH 03/16] maj doc --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b6942e --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# nur-ture + + +## Release + +Exemple: release de la version 0.6.0 + +Avant de faire une release majeure sur nur/ture, faire d'abord la release +majeure correspondante sur +* nulib/php +* nulib/spout +* nulib/phpss + +~~~sh +version=0.6.0 + +major="${version%.*}.0" + +## branche dev74 +git checkout dev74 + +sed -ri "\ +/nulib\/.*:/s/[0-9]+.[0-9]+.0p74/${major}p74/ +" .composer.pman.yml +pci "maj projet" + +prel -v$version + +# en cas de conflit, sélectionner HEAD +_merge82 + +## branche dev82 +git checkout dev82 + +sed -ri "\ +/nulib\/.*:/s/[0-9]+.[0-9]+.0p82/${major}p82/ +" .composer.pman.yml +pci "maj projet" + +prel -C + +commit="$(git log --grep="Init changelog . version ${version}p82" --format=%H)" +git checkout dev74 +git cherry-pick "$commit" +pp -a +~~~ + +-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary \ No newline at end of file From 5dd9ebbf656f2d15614b76954f4e1bbd41efe216 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 30 Apr 2025 07:53:34 +0400 Subject: [PATCH 04/16] modifs.mineures sans commentaires --- .idea/php.xml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.idea/php.xml b/.idea/php.xml index 9891bef..d678acf 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -21,20 +21,16 @@ - - - - @@ -44,7 +40,6 @@ - @@ -64,8 +59,10 @@ + + From fea5cb8b878ce8394a966b722b60a2e36e8d9996 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 30 Apr 2025 16:01:18 +0400 Subject: [PATCH 05/16] corrections tarray --- src/schema/Schema.php | 29 +++++++++++++---- src/schema/types/tarray.php | 45 ++++++++++++++++++++----- tests/schema/types/arrayTest.php | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 tests/schema/types/arrayTest.php diff --git a/src/schema/Schema.php b/src/schema/Schema.php index fbb441b..2b96626 100644 --- a/src/schema/Schema.php +++ b/src/schema/Schema.php @@ -127,7 +127,6 @@ abstract class Schema implements ArrayAccess { } } # type - $types = []; $deftype = $definition["type"]; $nullable = $definition["nullable"] ?? false; if ($deftype === null) { @@ -138,7 +137,17 @@ abstract class Schema implements ArrayAccess { if (!is_string($deftype)) throw SchemaException::invalid_type($deftype); $deftype = explode("|", $deftype); } - foreach ($deftype as $type) { + $types = []; + $unionTypes = []; + $index = 0; + foreach ($deftype as $key => $type) { + $args = null; + if ($key === $index) { + $index++; + } else { + $args = $type; + $type = $key; + } if ($type !== null) $type = trim($type); if ($type === null || $type === "null") { $nullable = true; @@ -151,10 +160,18 @@ abstract class Schema implements ArrayAccess { } if ($type === "") throw SchemaException::invalid_type($type); $type = cl::get(ref_types::ALIASES, $type, $type); - $types = array_merge($types, explode("|", $type)); + if ($args === null) { + $unionTypes = array_merge($unionTypes, explode("|", $type)); + } else { + $types = array_merge($types, [$type => $args]); + } + } + if (!$types && !$unionTypes) throw SchemaException::invalid_schema("scalar: type is required"); + foreach ($unionTypes as $type) { + if (!array_key_exists($type, $types)) { + $types[] = $type; + } } - if (!$types) throw SchemaException::invalid_schema("scalar: type is required"); - $types = array_keys(array_fill_keys($types, true)); } $definition["type"] = $types; $definition["nullable"] = $nullable; @@ -224,7 +241,7 @@ abstract class Schema implements ArrayAccess { $types = $definition["type"]; $nullable = $definition["nullable"]; # s'il n'y a qu'une seul type, l'instancier tout de suite - if (is_array($types) && count($types) == 1 && $types[0] !== null) { + if (is_array($types) && count($types) == 1 && cl::first($types) !== null) { foreach ($types as $key => $name) { if ($key === 0) { $args = null; diff --git a/src/schema/types/tarray.php b/src/schema/types/tarray.php index 05cfe7c..d53cddc 100644 --- a/src/schema/types/tarray.php +++ b/src/schema/types/tarray.php @@ -5,19 +5,45 @@ use nulib\cl; use nulib\schema\_scalar\ScalarSchema; use nulib\schema\Result; use nulib\schema\Schema; +use nulib\str; class tarray extends _tstring { const NAME = "array"; - const SPLIT_PATTERN = '/\s+/'; - const FORMAT = " "; + const TRIM = true; + const NORM_NL = true; + + const SEP_MAP = [ + "space" => "spaces", + "lines" => "line", + ]; + const STD_SEPS = [ + "spaces" => [" ", '/\s+/', true], + "line" => ["\n", "\n", false], + ]; + + const DEFAULT_SEP = self::STD_SEPS["spaces"]; + const DEFAULT_PARSE_SEP = self::DEFAULT_SEP[1]; + const DEFAULT_FORMAT_SEP = self::DEFAULT_SEP[0]; public static function get_params_from_definition(?array $definition): ?array { $params = parent::get_params_from_definition($definition); - $splitPattern = $definition["split_pattern"] ?? null; - if ($splitPattern !== null) $params["split_pattern"] = $splitPattern; - $format = $definition["format"] ?? null; - if ($format !== null) $params["format"] = $format; + $sep = $definition["sep"] ?? null; + if ($sep !== null) { + if (!is_array($sep)) { + $sep = cl::get(self::SEP_MAP, $sep, $sep); + $sep = self::STD_SEPS[$sep] ?? [$sep, $sep, false]; + } + $params["parse_sep"] = $sep[1] ?? $sep[0]; + $params["format_sep"] = $sep[0]; + $params["trim"] ??= $sep[2] ?? static::TRIM; + $params["norm_nl"] ??= $sep[3] ?? static::NORM_NL; + } else { + $parseSep = $definition["parse_sep"] ?? null; + if ($parseSep !== null) $params["parse_sep"] = $parseSep; + $formatSep = $definition["format_sep"] ?? null; + if ($formatSep !== null) $params["format_sep"] = $formatSep; + } return $params; } @@ -43,8 +69,9 @@ class tarray extends _tstring { } function parse(string $value) { - $pattern = $this->params["split_pattern"] ?? static::SPLIT_PATTERN; - return preg_split($pattern, $value); + $sep = $this->params["parse_sep"] ?? static::DEFAULT_PARSE_SEP; + if ($sep !== false) $value = str::split($sep, $value); + return $value; } /** @@ -63,7 +90,7 @@ class tarray extends _tstring { function format($value, $format=null): string { if ($value === null) return ""; - $format ??= $this->params["format"] ?? static::FORMAT; + $format ??= $this->params["format"] ?? static::DEFAULT_FORMAT_SEP; return implode($format, $value); } } diff --git a/tests/schema/types/arrayTest.php b/tests/schema/types/arrayTest.php new file mode 100644 index 0000000..95a7e9a --- /dev/null +++ b/tests/schema/types/arrayTest.php @@ -0,0 +1,56 @@ + "spaces", + ]); + self::assertSame(["first", "second"], $value); + + $value = " first second "; + Schema::nw($value, null, [ + "array", + "sep" => "spaces", "trim" => false, + ]); + self::assertSame(["", "first", "second", ""], $value); + + $value = " first second "; + Schema::nw($value, null, [ + "array", + "sep" => "line", + ]); + self::assertSame([" first second "], $value); + + $value = " first second "; + Schema::nw($value, null, [ + "array", + "sep" => "line", "trim" => true, + ]); + self::assertSame(["first second"], $value); + } + + function testxxx() { + $value = " first second "; + Schema::nw($value, null, [ + "array", "sep" => "spaces", + ]); + self::assertSame(["first", "second"], $value); + + # équivalent à... + $value = " first second "; + Schema::nw($value, null, [ + "type" => ["array" => [["sep" => "spaces"]]], + "" => ["scalar"], + ]); + self::assertSame(["first", "second"], $value); + } +} From 3754ed745454fa0e3be5dcaf5b091cc3163f68a6 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Tue, 6 May 2025 17:01:54 +0400 Subject: [PATCH 06/16] modifs.mineures sans commentaires --- .idea/php.xml | 9 ++++++++- nur_bin/sqlite-storage.php | 28 ++++++++-------------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/.idea/php.xml b/.idea/php.xml index d678acf..3c96682 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -60,9 +60,16 @@ - + + + + + + + + diff --git a/nur_bin/sqlite-storage.php b/nur_bin/sqlite-storage.php index d81b72f..a636ce3 100755 --- a/nur_bin/sqlite-storage.php +++ b/nur_bin/sqlite-storage.php @@ -63,21 +63,14 @@ Application::run(new class extends Application { $storage = new SqliteStorage($dbfile); $db = $storage->db(); - $haveChannels = $storage->tableExists("_channels"); - $name = $this->name; $channelClass = $this->channelClass; $tableName = $this->tableName; if ($name !== null) { - $row = null; - if ($haveChannels) { - $row = $db->one([ - "select from _channels", - "where" => ["name" => $name], - ]); + if (!$storage->channelExists($name, $row)) { + self::die("$name: nom de canal de données introuvable"); } - if ($row === null) self::die("$name: nom de canal de données introuvable"); - if ($row["class"] !== "class@anonymous") $channelClass = $row["class"]; + if ($row["class_name"] !== "class@anonymous") $channelClass = $row["class_name"]; else $tableName = $row["table_name"]; } if ($channelClass !== null) { @@ -92,17 +85,12 @@ Application::run(new class extends Application { }; } else { $found = false; - if ($haveChannels) { - $rows = $db->all([ - "select from _channels", - ]); - foreach ($rows as $row) { - msg::print($row["name"]); - $found = true; - } + foreach ($storage->getChannels() as $row) { + msg::print($row["name"]); + $found = true; } - if (!$found) self::die("Vous devez spécifier le canal de données"); - else self::exit(); + if ($found) self::exit(); + self::die("Vous devez spécifier le canal de données"); } $capacitor = new Capacitor($storage, $channel); From a1201437941d9086657dd044551760fffb239219 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 7 May 2025 17:36:31 +0400 Subject: [PATCH 07/16] =?UTF-8?q?possibilit=C3=A9=20de=20sauter=20une=20ce?= =?UTF-8?q?llule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nur_src/v/bs3/vc/CTable.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nur_src/v/bs3/vc/CTable.php b/nur_src/v/bs3/vc/CTable.php index 226c2f9..1ff1716 100644 --- a/nur_src/v/bs3/vc/CTable.php +++ b/nur_src/v/bs3/vc/CTable.php @@ -633,14 +633,15 @@ class CTable extends ComponentPrintable implements IParametrable { /** @var string|int clé de la colonne courante */ protected $col; - function colTd($value): array { + function colTd($value): ?array { $vs = $this->col($value); if ($this->colCtx !== null) { $vs = func::_call($this->colCtx, [$vs, $value, $this->col, $this->index, $this->row, $this->rawRow]); + if ($vs === false) return null; } else { $result = A::get($this->results, $this->col); $valid = $result === null || $result["valid"]; - $vs= [ + $vs = [ "class" => ["danger" => !$valid], $vs, ]; From 0c4076961952d9c6619ebeb26559857229363dab Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 7 May 2025 23:06:49 +0400 Subject: [PATCH 08/16] =?UTF-8?q?am=C3=A9liorer=20Cursor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/php/coll/Cursor.php | 233 ++++++++++++++++++++++++---------------- 1 file changed, 142 insertions(+), 91 deletions(-) diff --git a/src/php/coll/Cursor.php b/src/php/coll/Cursor.php index 4834b50..ec09831 100644 --- a/src/php/coll/Cursor.php +++ b/src/php/coll/Cursor.php @@ -11,6 +11,10 @@ use Traversable; /** * Class Cursor: parcours des lignes itérable * + * XXX si on spécifie $cols ou $colsFunc, il y a une possibilité que les clés de + * $row ne soient pas dans le bon ordre, ou que les clés de $cols ne soient pas + * présentes dans $row. ajouter les paramètres ensure_keys et order_keys + * * @property-read array|null $value alias pour $row * @property-read iterable|null $rows la source des lignes */ @@ -18,14 +22,53 @@ class Cursor implements Iterator { const PARAMS_SCHEMA = [ "rows" => ["?iterable"], "rows_func" => ["?callable"], - "cols" => ["?array"], - "cols_func" => ["?callable"], - "map" => ["?array"], - "map_func" => ["?callable"], "filter" => ["?array"], "filter_func" => ["?callable"], + "map" => ["?array"], + "map_func" => ["?callable"], + "cols" => ["?array"], + "cols_func" => ["?callable"], ]; + /** + * tester si $row satisfait les conditions de $filter + * - $filter est un scalaire, le transformer en [$filter] + * - sinon $filter doit être un tableau de scalaires + * + * les règles des conditions sont les suivantes: + * - une valeur séquentielle $key est équivalente à la valeur associative + * $key => true + * - une valeur associative $key => bool indique que la clé correspondante ne + * doit pas (resp. doit) exister selon que bool vaut false (resp. true) + * - une valeur associative $key => $value indique que la clé correspondante + * doit exiter avec la valeur spécifiée + */ + static function filter_row(?array $row, $filter): bool { + if ($filter === null) return false; + if (!is_array($filter)) $filter = [$filter]; + if (!$filter) return false; + + $index = 0; + foreach ($filter as $key => $value) { + if ($key === $index) { + $index++; + if ($row === null) return false; + if (!array_key_exists($value, $row)) return false; + } elseif (is_bool($value)) { + if ($value) { + if ($row === null || !array_key_exists($key, $row)) return false; + } else { + if ($row !== null && array_key_exists($key, $row)) return false; + } + } else { + if ($row === null) return false; + if (!array_key_exists($key, $row)) return false; + if ($row[$key] !== $value) return false; + } + } + return true; + } + /** * mapper le tableau source $row selon les règles suivantes illustrées dans * l'exemple suivant: @@ -33,8 +76,11 @@ class Cursor implements Iterator { * $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null] * alors retourner le tableau * ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null] + * + * si une fonction est utilisée, sa signature est + * function(mixed $value, string|int $key, ?array $row) */ - protected static function map_row(array $row, ?array $map): array { + static function map_row(?array $row, ?array $map): array { if ($map === null) return $row; $index = 0; $mapped = []; @@ -55,43 +101,6 @@ class Cursor implements Iterator { return $mapped; } - /** - * tester si $row satisfait les conditions de $filter - * - $filter est un scalaire, le transformer en [$filter] - * - sinon $filter doit être un tableau de scalaires - * - * les règles des conditions sont les suivantes: - * - une valeur séquentielle $key est équivalente à la valeur associative - * $key => true - * - une valeur associative $key => bool indique que la clé correspondante ne - * doit pas (resp. doit) exister selon que bool vaut false (resp. true) - * - une valeur associative $key => $value indique que la clé correspondante - * doit exiter avec la valeur spécifiée - */ - protected static function filter_row(array $row, $filter): bool { - if ($filter === null) return false; - if (!is_array($filter)) $filter = [$filter]; - if (!$filter) return false; - - $index = 0; - foreach ($filter as $key => $value) { - if ($key === $index) { - $index++; - if (!array_key_exists($value, $row)) return false; - } elseif (is_bool($value)) { - if ($value) { - if (!array_key_exists($key, $row)) return false; - } else { - if (array_key_exists($key, $row)) return false; - } - } else { - if (!array_key_exists($key, $row)) return false; - if ($row[$key] !== $value) return false; - } - } - return true; - } - function __construct(?iterable $rows=null, ?array $params=null) { if ($rows !== null) $params["rows"] = $rows; @@ -103,7 +112,7 @@ class Cursor implements Iterator { $rowsGenerator = $rowsFunc; $rowsFunc = null; } else { - $rowsFunc = func::with($rowsFunc, [$rows]); + $rowsFunc = func::with($rowsFunc, [$rows, $this]); } } elseif ($rows instanceof Traversable) { $rowsGenerator = $rows; @@ -115,61 +124,95 @@ class Cursor implements Iterator { $this->rowsGenerator = $rowsGenerator; $this->rowsFunc = $rowsFunc; - $this->cols = $params["cols"] ?? null; - $colsFunc = $params["cols_func"] ?? null; - if ($colsFunc !== null) $colsFunc = func::with($colsFunc); - $this->colsFunc = $colsFunc; + $filter = $params["filter"] ?? null; + $filterFunc = $params["filter_func"] ?? null; + if ($filterFunc !== null) $this->setFilterFunc($filterFunc); + elseif ($filter !== null) $this->setFilter($filter); $map = $params["map"] ?? null; $mapFunc = $params["map_func"] ?? null; - if ($mapFunc !== null) { - $mapFunc = func::with($mapFunc); - } elseif ($map !== null) { - $mapFunc = func::with(function(array $row) use ($map) { - return self::map_row($row, $map); - }); - } - $this->mapFunc = $mapFunc; + if ($mapFunc !== null) $this->setMapFunc($mapFunc); + elseif ($map !== null) $this->setMap($map); - $filter = $params["filter"] ?? null; - $filterFunc = $params["filter_func"] ?? null; - if ($filterFunc !== null) { - $filterFunc = func::with($filterFunc); - } elseif ($filter !== null) { - $filterFunc = func::with(function(array $row) use ($filter) { - return self::filter_row($row, $filter); - }); - } - $this->filterFunc = $filterFunc; + $this->cols = $params["cols"] ?? null; + $this->setColsFunc($params["cols_func"] ?? null); } /** un générateur de lignes */ private ?Traversable $rowsGenerator; - /** une fonction de signature function(Cursor): ?iterable */ + /** une fonction de signature function(mixed $rows, Cursor): ?iterable */ private ?func $rowsFunc; - /** une fonction de signature function(Cursor): ?array */ - private ?func $colsFunc; + /** une fonction de signature function(?array $row, Cursor): bool */ + private ?func $filterFunc = null; - /** une fonction de signature function(Cursor): ?array */ - private ?func $mapFunc; + function setFilter(array $filter): self { + $this->filterFunc = func::with(function(?array $row) use ($filter) { + return self::filter_row($row, $filter); + }); + return $this; + } - /** une fonction de signature function(Cursor): bool */ - private ?func $filterFunc; + function setFilterFunc(?callable $func): self { + if ($func === null) $this->filterFunc = null; + else $this->filterFunc = func::with($func)->bind($this); + return $this; + } + /** une fonction de signature function(?array $row, Cursor): ?array */ + private ?func $mapFunc = null; + + function setMap(array $map): self { + $this->mapFunc = func::with(function(?array $row) use ($map) { + return self::map_row($row, $map); + }); + return $this; + } + + function setMapFunc(?callable $func): self { + if ($func === null) $this->mapFunc = null; + else $this->mapFunc = func::with($func)->bind($this); + return $this; + } + + /** une fonction de signature function(?array $row, Cursor): ?array */ + private ?func $colsFunc = null; + + function setColsFunc(?callable $func): self { + $this->cols = null; + if ($func === null) $this->colsFunc = null; + else $this->colsFunc = func::with($func)->bind($this); + return $this; + } + + /** @var iterable|null source des éléments */ protected ?iterable $rows; + /** @var array|null listes des colonnes de chaque enregistrement */ public ?array $cols; + /** + * @var int index de l'enregistrement (en ne comptant pas les éléments filtrés) + */ public int $index; + /** + * @var int index original de l'enregistrement (en tenant compte des éléments + * filtrés) + */ public int $origIndex; + /** @var string|int clé de l'enregistrement */ public $key; + /** @var mixed élément original récupéré depuis la source */ public $raw; + /** + * @var array|null enregistrement après conversion en tableau et application + * du mapping + */ public ?array $row; function __get($name) { @@ -179,18 +222,22 @@ class Cursor implements Iterator { return null; } - protected function cols(): ?array { - return $this->row !== null? array_keys($this->row): null; + protected function convertToRow($raw): ?array { + return cl::withn($raw); } - protected function filter(): bool { + protected function filterFunc(?array $row): bool { return false; } - protected function map(): ?array { + protected function mapFunc(?array $row): ?array { return $this->row; } + protected function colsFunc(?array $row): ?array { + return $this->row !== null? array_keys($this->row): null; + } + ############################################################################# # Iterator @@ -212,26 +259,30 @@ class Cursor implements Iterator { } function valid(): bool { - $cols = $this->colsFunc; - $filter = $this->filterFunc; - $map = $this->mapFunc; + $colsFunc = $this->colsFunc; + $filterFunc = $this->filterFunc; + $mapFunc = $this->mapFunc; while ($valid = iter::valid($this->rows)) { $this->raw = iter::current($this->rows, $this->key); + # conversion en enregistrement $this->key ??= $this->origIndex; - $this->row = cl::withn($this->raw); - if ($filter === null) $filtered = $this->filter(); - else $filtered = $filter->invoke([$this]); - if (!$filtered) { - if ($map === null) $this->row = $this->map(); - else $this->row = $map->invoke([$this]); - if ($this->cols === null) { - if ($cols === null) $this->cols = $this->cols(); - else $this->cols = $cols->invoke([$this]); - } - break; - } else { + $this->row = $this->convertToRow($this->raw); + # filtrage + if ($filterFunc === null) $filtered = $this->filterFunc($this->row); + else $filtered = $filterFunc->invoke([$this->row, $this]); + if ($filtered) { iter::next($this->rows); $this->origIndex++; + } else { + # l'enregistrement n'as pas été filtré: faire le mapping + if ($mapFunc === null) $this->row = $this->mapFunc($this->row); + else $this->row = $mapFunc->invoke([$this->row, $this]); + # calculer la liste des colonnes le cas échéant + if ($this->cols === null) { + if ($colsFunc === null) $this->cols = $this->colsFunc($this->row); + else $this->cols = $colsFunc->invoke([$this->row, $this]); + } + break; } } if (!$valid) { From f7e692df0c7afd13a7db20e672e83c6ffebba913 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 9 May 2025 10:28:58 +0400 Subject: [PATCH 09/16] =?UTF-8?q?d=C3=A9placer=20les=20fonctions=20vers=20?= =?UTF-8?q?nulib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/php/access/KeyAccess.php | 76 +++-------------------------------- src/php/coll/Cursor.php | 75 +--------------------------------- tests/php/coll/CursorTest.php | 4 +- 3 files changed, 9 insertions(+), 146 deletions(-) diff --git a/src/php/access/KeyAccess.php b/src/php/access/KeyAccess.php index 9afd1a2..1f445ca 100644 --- a/src/php/access/KeyAccess.php +++ b/src/php/access/KeyAccess.php @@ -2,6 +2,7 @@ namespace nulib\php\access; use ArrayAccess; +use nulib\A; use nulib\cl; /** @@ -118,85 +119,18 @@ class KeyAccess extends AbstractAccess { } function ensureAssoc(array $keys, ?array $params=null): void { - $dest =& $this->dest; - $prefix = $params["key_prefix"] ?? null; - $suffix = $params["key_suffix"] ?? null; - $index = 0; - foreach ($keys as $key) { - if ($prefix !== null || $suffix !== null) { - $destKey = "$prefix$key$suffix"; - } else { - # préserver les clés numériques - $destKey = $key; - } - if ($dest !== null && array_key_exists($destKey, $dest)) continue; - while (in_array($index, $keys, true)) { - $index++; - } - if ($dest !== null && array_key_exists($index, $dest)) { - $dest[$destKey] = $dest[$index]; - unset($dest[$index]); - $index++; - } - } + A::ensure_assoc($this->dest, $keys, $params); } function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void { - $dest =& $this->dest; - $keys = array_keys($defaults); - $prefix = $params["key_prefix"] ?? null; - $suffix = $params["key_suffix"] ?? null; - foreach ($keys as $key) { - $destKey = "$prefix$key$suffix"; - $haveMissing = $missings !== null && array_key_exists($key, $missings); - if ($dest === null || !array_key_exists($destKey, $dest)) { - $dest[$destKey] = $defaults[$key]; - } elseif ($haveMissing && $dest[$destKey] === $missings[$key]) { - $dest[$destKey] = $defaults[$key]; - } - } + A::ensure_keys($this->dest, $defaults, $missings, $params); } function deleteMissings(array $missings, ?array $params=null): void { - $dest =& $this->dest; - $prefix = $params["key_prefix"] ?? null; - $suffix = $params["key_suffix"] ?? null; - foreach ($missings as $key => $missing) { - $destKey = "$prefix$key$suffix"; - if (array_key_exists($destKey, $dest) && $dest[$destKey] === $missing) { - unset($dest[$destKey]); - } - } + A::delete_missings($this->dest, $missings, $params); } function ensureOrder(array $keys, ?array $params=null): void { - $dest =& $this->dest; - if ($dest === null) return; - - $prefix = $params["key_prefix"] ?? null; - $suffix = $params["key_suffix"] ?? null; - if ($prefix !== null || $suffix !== null) { - foreach ($keys as &$key) { - $key = "$prefix$key$suffix"; - }; unset($key); - } - - $destKeys = array_keys($dest); - $keyCount = count($keys); - if (array_slice($destKeys, 0, $keyCount) === $keys) { - # si le tableau a déjà les bonnes clés dans le bon ordre, rien à faire - return; - } - - $ordered = []; - foreach ($keys as $key) { - if (array_key_exists($key, $dest)) { - $ordered[$key] = $dest[$key]; - unset($dest[$key]); - } - } - $preserveKeys = $params["preserve_keys"] ?? false; - if ($preserveKeys) $dest = cl::merge2($ordered, $dest); - else $dest = array_merge($ordered, $dest); + A::ensure_order($this->dest, $keys, $params); } } diff --git a/src/php/coll/Cursor.php b/src/php/coll/Cursor.php index ec09831..f9a2bdb 100644 --- a/src/php/coll/Cursor.php +++ b/src/php/coll/Cursor.php @@ -30,77 +30,6 @@ class Cursor implements Iterator { "cols_func" => ["?callable"], ]; - /** - * tester si $row satisfait les conditions de $filter - * - $filter est un scalaire, le transformer en [$filter] - * - sinon $filter doit être un tableau de scalaires - * - * les règles des conditions sont les suivantes: - * - une valeur séquentielle $key est équivalente à la valeur associative - * $key => true - * - une valeur associative $key => bool indique que la clé correspondante ne - * doit pas (resp. doit) exister selon que bool vaut false (resp. true) - * - une valeur associative $key => $value indique que la clé correspondante - * doit exiter avec la valeur spécifiée - */ - static function filter_row(?array $row, $filter): bool { - if ($filter === null) return false; - if (!is_array($filter)) $filter = [$filter]; - if (!$filter) return false; - - $index = 0; - foreach ($filter as $key => $value) { - if ($key === $index) { - $index++; - if ($row === null) return false; - if (!array_key_exists($value, $row)) return false; - } elseif (is_bool($value)) { - if ($value) { - if ($row === null || !array_key_exists($key, $row)) return false; - } else { - if ($row !== null && array_key_exists($key, $row)) return false; - } - } else { - if ($row === null) return false; - if (!array_key_exists($key, $row)) return false; - if ($row[$key] !== $value) return false; - } - } - return true; - } - - /** - * mapper le tableau source $row selon les règles suivantes illustrées dans - * l'exemple suivant: - * si - * $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null] - * alors retourner le tableau - * ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null] - * - * si une fonction est utilisée, sa signature est - * function(mixed $value, string|int $key, ?array $row) - */ - static function map_row(?array $row, ?array $map): array { - if ($map === null) return $row; - $index = 0; - $mapped = []; - foreach ($map as $key => $value) { - if ($key === $index) { - $index++; - if ($value === null) $mapped[] = null; - else $mapped[$value] = cl::get($row, $value); - } elseif (is_callable($value)) { - $func = func::with($value); - $value = cl::get($row, $key); - $mapped[$key] = $func->invoke([$value, $key, $row]); - } else { - if ($value === null) $mapped[$key] = null; - else $mapped[$key] = cl::get($row, $value); - } - } - return $mapped; - } - function __construct(?iterable $rows=null, ?array $params=null) { if ($rows !== null) $params["rows"] = $rows; @@ -149,7 +78,7 @@ class Cursor implements Iterator { function setFilter(array $filter): self { $this->filterFunc = func::with(function(?array $row) use ($filter) { - return self::filter_row($row, $filter); + return cl::filter($row, $filter); }); return $this; } @@ -165,7 +94,7 @@ class Cursor implements Iterator { function setMap(array $map): self { $this->mapFunc = func::with(function(?array $row) use ($map) { - return self::map_row($row, $map); + return cl::map($row, $map); }); return $this; } diff --git a/tests/php/coll/CursorTest.php b/tests/php/coll/CursorTest.php index af76c0a..9d9a218 100644 --- a/tests/php/coll/CursorTest.php +++ b/tests/php/coll/CursorTest.php @@ -16,7 +16,7 @@ class CursorTest extends TestCase { function test_map_row() { $cursor = new class extends Cursor { function mapRow(array $row, ?array $map): array { - return self::map_row($row, $map); + return cl::map($row, $map); } }; $row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99]; @@ -32,7 +32,7 @@ class CursorTest extends TestCase { function test_filter_row() { $cursor = new class extends Cursor { function filterRow(array $row, $filter): bool { - return self::filter_row($row, $filter); + return cl::filter($row, $filter); } }; $row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99]; From 33a10a37c2d0cf6617bf7549a704e98a900a2760 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 06:21:28 +0400 Subject: [PATCH 10/16] ajout de table-sticky --- nur_public/nur-v-bs3/base.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nur_public/nur-v-bs3/base.css b/nur_public/nur-v-bs3/base.css index 890de9a..c13ccf3 100644 --- a/nur_public/nur-v-bs3/base.css +++ b/nur_public/nur-v-bs3/base.css @@ -34,6 +34,12 @@ .left-gap { margin-left: 1em;} .right-gap { margin-right: 1em;} +table.table-sticky tr th { + position: sticky; + top: 0; + background-color: white; +} + /* si un navbar-form contient des btn-sm ou des btn-xs, utiliser les classes ci-dessous. */ .navbar-form-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-form-xs { margin-top: 14px; margin-bottom: 14px; } From 39b2fa58be2a62a221f93d7866ae2c0d454fd8c8 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:12:22 +0400 Subject: [PATCH 11/16] =?UTF-8?q?g=C3=A9n=C3=A9rifier=20la=20m=C3=A9thode?= =?UTF-8?q?=20d'authentification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nur_src/b/authnz/AuthzManager.php | 122 ++++++++++++++++++++++++--- nur_src/b/authnz/CasAuthzManager.php | 99 ++++++++++++++++++++++ nur_src/b/authnz/ExtAuthzManager.php | 78 +++++++++++++++++ nur_src/b/authnz/ExtUserManager.php | 8 ++ nur_src/v/vp/AInitAuthzPage.php | 4 +- nur_src/v/vp/AInitPage.php | 5 ++ nur_src/v/vp/AppExtauthPage.php | 68 +++++++++++++++ nur_src/v/vp/BasicPage.php | 2 +- nur_src/v/vp/NavigablePage.php | 2 +- nur_src/v/vp/TAuthzLoginPage.php | 19 +++++ nur_src/v/vp/TFormLoginPage.php | 2 +- 11 files changed, 394 insertions(+), 15 deletions(-) create mode 100644 nur_src/b/authnz/ExtAuthzManager.php create mode 100644 nur_src/b/authnz/ExtUserManager.php create mode 100644 nur_src/v/vp/AppExtauthPage.php create mode 100644 nur_src/v/vp/TAuthzLoginPage.php diff --git a/nur_src/b/authnz/AuthzManager.php b/nur_src/b/authnz/AuthzManager.php index 12b8129..f188bc2 100644 --- a/nur_src/b/authnz/AuthzManager.php +++ b/nur_src/b/authnz/AuthzManager.php @@ -1,12 +1,22 @@ getCookieKey(), false); if ($value === false) return false; - if (!preg_match('/^(cas|form|):(.*)$/', $value, $ms)) return false; + if (!preg_match('/^(cas|form|ext|):(.*)$/', $value, $ms)) return false; $authType = $ms[1]; $username = $ms[2]; return true; @@ -191,6 +201,22 @@ class AuthzManager { page::redirect(page::bu($loginUrl, $params)); } + function extLogin(string $username): bool { + # l'utilisateur doit exister + $user = $this->getUserManager()->getAuthzUser($username, null); + # ce doit être un utilisateur valide + if ($user !== null && $user->isValid()) { + $this->setCookie($username, "ext"); + $this->initSession(self::STATUS_INITIAL); + session::set(self::SESSION_KEY_USERNAME, $username); + $this->onAuthOk($username); + session::set(self::SESSION_KEY_USER, $user); + $this->onAuthzOk($user); + return true; + } + return false; + } + function formLogin(string $username, string $password): bool { # l'utilisateur doit exister $user = $this->getUserManager()->getAuthzUser($username, null); @@ -202,7 +228,7 @@ class AuthzManager { if (config::is_devel() && !$password) $password = null; if ($password === null || $user->validatePassword($password)) { # c'est bon - $this->initSession(self::STATUS_INITIAL, null); + $this->initSession(self::STATUS_INITIAL); session::set(self::SESSION_KEY_USERNAME, $username); $this->onAuthOk($username); session::set(self::SESSION_KEY_USER, $user); @@ -221,14 +247,11 @@ class AuthzManager { $this->onAuthOk($username); # l'utilisateur doit exister $user = $this->getUserManager()->getAuthzUser($username, $overrides); - if ($user !== null) { - # ce doit être un utilisteur valide - if ($user->isValid()) { - # c'est bon - session::set(self::SESSION_KEY_USER, $user); - $this->onAuthzOk($user); - return true; - } + # ce doit être un utilisteur valide + if ($user !== null && $user->isValid()) { + session::set(self::SESSION_KEY_USER, $user); + $this->onAuthzOk($user); + return true; } return false; } @@ -317,4 +340,83 @@ class AuthzManager { /** Traiter le cas où l'utilisateur a été autorisé avec succès. */ function onAuthzOk(IAuthzUser $authz): void { } + + ############################################################################# + # Page login + + private $username = null; + private $authType = null; + + function beforeSetup(AInitAuthzPage $page): void { + # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), + # setup() et afterSetup(), la session est disponible + $username = P::get("u"); + $password = P::get("p"); + $destPage = F::get("d", $page->getMainUrl()); + $page->_ensureFormLoginAndRedirect($username, $password, $destPage); + + $this->checkSession($this->username, $this->authType); + } + + function print(AInitAuthzPage $page): void { + page::no_cache(); + $username = P::get("u"); + $password = P::get("p"); + + ly::row(); + vo::h1(["class" => "text-center", q($page->TITLE())]); + + ly::col(["sm" => 6, "sm-push" => 3]); + $status = $this->getStatus(); + switch ($status) { + case authz::DISCONNECTED: + msg::warning("Vous avez été déconnecté. Veuillez vous reconnecter"); + break; + case authz::UNAUTHORIZED: + msg::error(["user" => [ + "Connecté en tant que ", + v::b($this->getAuth()), + ", vous n'êtes pas autorisé à accéder à la page que vous avez demandé.", + ]]); + break; + } + + ly::panel("Connexion par identifiant/mot de passe"); + fo::start([ + "type" => "basic", + "action" => "", + "method" => "post", + ]); + fo::text("Identifiant", "u", $username?: $this->username, [ + "accesskey" => "q", + "placeholder" => "Votre identifiant", + ]); + fo::password("Mot de passe", "p", $password, [ + "placeholder" => "Votre mot de passe", + ]); + if ($username || $password) { + msg::error("$username: Votre identifiant et/ou votre mot de passe sont incorrects"); + } elseif ($username === "") { + msg::error("Vous devez saisir votre identifiant"); + } elseif ($password === "") { + msg::error("Vous devez saisir votre mot de passe"); + } + fo::submit(["Connexion", "accesskey" => "r"]); + if ($this->isAuth() && $this->authType === "form") { + if ($status != authz::UNAUTHORIZED) { + msg::warning(["user" => [ + "Connecté en tant que ", + v::b($this->getAuth()), + ", vous n'êtes pas autorisé à accéder à cette application.", + ]]); + } + fo::submit([ + "Vous déconnecter", "accesskey" => "z", + "formmethod" => "get", "formaction" => $page->getLogoutUrl(), + ]); + } + fo::end(); + + ly::end(); + } } diff --git a/nur_src/b/authnz/CasAuthzManager.php b/nur_src/b/authnz/CasAuthzManager.php index da544e0..ec02b97 100644 --- a/nur_src/b/authnz/CasAuthzManager.php +++ b/nur_src/b/authnz/CasAuthzManager.php @@ -1,8 +1,18 @@ userManager; } + + private $destPage = null; + + function beforeSetup(AInitAuthzPage $page): void { + # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), + # setup() et afterSetup(), la session est disponible + $this->destPage = F::get("d", $page->getMainUrl()); + $this->checkSession($username, $authType); + + if ($authType === "cas" && F::get("a")) { + # autologin + $casauthUrl = config::k("url")."/".$page->getCasauthUrl(); + page::redirect(page::bu($page->getCasLoginUrl(), [ + "service" => page::bu($casauthUrl, [ + "r" => $page->getLoginUrl(), + "d" => $this->destPage, + ]) + ])); + } + } + + function print(AInitAuthzPage $page): void { + page::no_cache(); + ly::row(); + vo::h1(["class" => "text-center", q($page->TITLE())]); + + ly::col(["sm" => 6, "sm-push" => 3]); + $status = $this->getStatus(); + switch ($status) { + case authz::DISCONNECTED: + msg::warning("Vous avez été déconnecté. Veuillez vous reconnecter"); + break; + case authz::UNAUTHORIZED: + msg::error(["user" => [ + "Connecté en tant que ", + v::b($this->getAuth()), + ", vous n'êtes pas autorisé à accéder à la page que vous avez demandé.", + ]]); + break; + } + + ly::panel("Connexion par CAS"); + if ($page->isDevauthAllowed()) { + fo::start([ + "type" => "basic", + "action" => $page->getCasauthUrl(), + "method" => "get", + ]); + fo::hidden("r", $page->getLoginUrl()); + fo::hidden("d", $this->destPage); + } else { + fo::start([ + "type" => "basic", + "action" => $page->getCasLoginUrl(), + "method" => "get", + ]); + $casauthUrl = config::k("url")."/".$page->getCasauthUrl(); + fo::hidden("service", page::bu($casauthUrl, [ + "r" => $page->getLoginUrl(), + "d" => $this->destPage, + ])); + } + #fo::p("Si vous avez un compte à l'université, vous pouvez vous connecter via CAS"); + vo::p("Si vous avez un compte à l'université, vous pouvez vous connecter via CAS"); + if ($this->isAuth()) { + if ($status != authz::UNAUTHORIZED) { + msg::warning(["user" => [ + "Connecté en tant que ", + v::b(authz::get_auth()), + ", vous n'êtes pas autorisé à accéder à cette application.", + ]]); + } + fo::submit([ + icon::logout("Vous déconnecter"), + "formaction" => $page->getLogoutUrl(), + "accesskey" => "z", + ]); + fo::hidden("renew", "true"); + fo::submit([ + icon::login("Changer de compte"), + "accesskey" => "r", + ]); + } else { + fo::submit(["Connexion par CAS", "accesskey" => "r"]); + } + fo::end(); + + ly::end(); + } } diff --git a/nur_src/b/authnz/ExtAuthzManager.php b/nur_src/b/authnz/ExtAuthzManager.php new file mode 100644 index 0000000..20e28d3 --- /dev/null +++ b/nur_src/b/authnz/ExtAuthzManager.php @@ -0,0 +1,78 @@ +destPage = F::get("d", $page->getMainUrl()); + $this->checkSession($username, $authType); + + if ($authType === "ext" && F::get("a")) { + # autologin + $extauthUrl = config::k("url")."/".$page->getExtauthUrl(); + page::redirect(page::bu($extauthUrl, [ + "d" => $this->destPage, + ])); + } + } + + function print(AInitAuthzPage $page): void { + page::no_cache(); + ly::row(); + vo::h1(["class" => "text-center", q($page->TITLE())]); + + ly::col(["sm" => 6, "sm-push" => 3]); + $status = $this->getStatus(); + switch ($status) { + case authz::DISCONNECTED: + msg::warning("Vous avez été déconnecté. Veuillez vous reconnecter"); + break; + case authz::UNAUTHORIZED: + msg::error(["user" => [ + "Connecté en tant que ", + v::b($this->getAuth()), + ", vous n'êtes pas autorisé à accéder à la page que vous avez demandé.", + ]]); + break; + } + + ly::panel("Connexion"); + fo::start([ + "type" => "basic", + "action" => $page->getExtauthUrl(), + "method" => "get", + ]); + fo::hidden("d", $this->destPage); + vo::p("Si vous avez un compte à l'université, vous pouvez vous connecter"); + if ($this->isAuth()) { + if ($status != authz::UNAUTHORIZED) { + msg::warning(["user" => [ + "Connecté en tant que ", + v::b($this->getAuth()), + ", vous n'êtes pas autorisé à accéder à cette application.", + ]]); + } + } else { + fo::submit(["Connexion", "accesskey" => "r"]); + } + fo::end(); + + ly::end(); + } +} diff --git a/nur_src/b/authnz/ExtUserManager.php b/nur_src/b/authnz/ExtUserManager.php new file mode 100644 index 0000000..4ca8f19 --- /dev/null +++ b/nur_src/b/authnz/ExtUserManager.php @@ -0,0 +1,8 @@ + $username]; + } +} diff --git a/nur_src/v/vp/AInitAuthzPage.php b/nur_src/v/vp/AInitAuthzPage.php index f316bba..bdafa02 100644 --- a/nur_src/v/vp/AInitAuthzPage.php +++ b/nur_src/v/vp/AInitAuthzPage.php @@ -64,7 +64,7 @@ class AInitAuthzPage extends AInitPage implements IBasicPage { } } - protected function ensureAuthOrRedirect(bool $requireAuth, bool $requireAuthz, $requireRole, $requirePerm): void { + protected function _ensureAuthOrRedirect(bool $requireAuth, bool $requireAuthz, $requireRole, $requirePerm): void { $am = authz::manager(); $loginUrl = $this->getLoginUrl(); $destUrl = page::self(true); @@ -88,7 +88,7 @@ class AInitAuthzPage extends AInitPage implements IBasicPage { if ($am->isAuth()) $am->setConnected(); } - protected function ensureFormLoginAndRedirect(?string $username, ?string $password, string $destUrl): void { + function _ensureFormLoginAndRedirect(?string $username, ?string $password, string $destUrl): void { if ($username === null && $password === null) return; if (authz::manager()->formLogin($username, $password)) { page::redirect($destUrl); diff --git a/nur_src/v/vp/AInitPage.php b/nur_src/v/vp/AInitPage.php index 08fd920..e497785 100644 --- a/nur_src/v/vp/AInitPage.php +++ b/nur_src/v/vp/AInitPage.php @@ -40,6 +40,11 @@ abstract class AInitPage extends AbstractPage { } } + /** obtenir l'url d'authentification externe. cet url doit être *relatif* */ + function getExtauthUrl(): string { + return page::bu(config::k("extauth_page", "_extauth.php")); + } + function getLogoutUrl(): string { return page::bu(config::k("logout_page","_logout.php")); } diff --git a/nur_src/v/vp/AppExtauthPage.php b/nur_src/v/vp/AppExtauthPage.php new file mode 100644 index 0000000..75a4653 --- /dev/null +++ b/nur_src/v/vp/AppExtauthPage.php @@ -0,0 +1,68 @@ +extLogin($user)) { + $destUrl = F::get("d"); + if ($destUrl && !$this->isDebug()) page::redirect($destUrl); + } + } else { + $user = "NONE"; + } + + $this->user = $user; + $this->destUrl = $destUrl; + } + + private $destUrl; + + function print(): void { + page::content_type("text/plain"); + page::no_cache(); + if ($this->isDebug()) { + echo "destUrl: $this->destUrl\n"; + echo "--- \$_SERVER\n\n"; + foreach ($_SERVER as $name => $value) { + echo "$name: $value\n"; + } + echo "\n--- \$_REQUEST\n\n"; + foreach ($_REQUEST as $name => $value) { + echo "$name: $value\n"; + } + echo "\n--- \$_SESSION\n\n"; + foreach ($_SESSION as $name => $value) { + echo "$name: ".var_export($value, true)."\n"; + } + } else { + echo $this->user; + } + } +} diff --git a/nur_src/v/vp/BasicPage.php b/nur_src/v/vp/BasicPage.php index 6eb4ef6..c83ca4a 100644 --- a/nur_src/v/vp/BasicPage.php +++ b/nur_src/v/vp/BasicPage.php @@ -13,7 +13,7 @@ class BasicPage extends AInitAuthzPage { function afterConfig(): void { # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), # setup() et afterSetup(), la session est disponible - $this->ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); + $this->_ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); parent::afterConfig(); } } diff --git a/nur_src/v/vp/NavigablePage.php b/nur_src/v/vp/NavigablePage.php index 23212c3..5225b6f 100644 --- a/nur_src/v/vp/NavigablePage.php +++ b/nur_src/v/vp/NavigablePage.php @@ -32,7 +32,7 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage { function afterConfig(): void { # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), # setup() et afterSetup(), la session est disponible - $this->ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); + $this->_ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); parent::afterConfig(); } diff --git a/nur_src/v/vp/TAuthzLoginPage.php b/nur_src/v/vp/TAuthzLoginPage.php new file mode 100644 index 0000000..5517e56 --- /dev/null +++ b/nur_src/v/vp/TAuthzLoginPage.php @@ -0,0 +1,19 @@ +beforeSetup($this); + } + + function beforeSetup(): void { + $this->TLoginPage_beforeSetup(); + parent::beforeSetup(); + } + + function print(): void { + authz::manager()->print($this); + } +} diff --git a/nur_src/v/vp/TFormLoginPage.php b/nur_src/v/vp/TFormLoginPage.php index 088f1a7..387f776 100644 --- a/nur_src/v/vp/TFormLoginPage.php +++ b/nur_src/v/vp/TFormLoginPage.php @@ -17,7 +17,7 @@ trait TFormLoginPage { $username = P::get("u"); $password = P::get("p"); $destPage = F::get("d", $this->getMainUrl()); - $this->ensureFormLoginAndRedirect($username, $password, $destPage); + $this->_ensureFormLoginAndRedirect($username, $password, $destPage); authz::manager()->checkSession($flcUsername, $flcAuthType); $this->flcUsername = $flcUsername; From 4a15dc66bdc7df3a8204c96d13bf09a4b3e9039f Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:34:40 +0400 Subject: [PATCH 12/16] Init changelog & version 0.5.1p74 --- CHANGES.md | 10 ++++++++++ VERSION.txt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 83072ce..88437f3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +## Release 0.5.1p74 du 12/05/2025-15:34 + +* `39b2fa5` générifier la méthode d'authentification +* `33a10a3` ajout de table-sticky +* `f7e692d` déplacer les fonctions vers nulib +* `0c40769` améliorer Cursor +* `a120143` possibilité de sauter une cellule +* `fea5cb8` corrections tarray +* `5862d35` maj doc + ## Release 0.5.0p82 du 30/04/2025-05:35 ## Release 0.5.0p74 du 30/04/2025-05:30 diff --git a/VERSION.txt b/VERSION.txt index 8f0916f..4b9fcbe 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -0.5.0 +0.5.1 From dbfeb602e58f7a9f2d4385b5b26d4bd1a3954b26 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:34:56 +0400 Subject: [PATCH 13/16] deps de dist --- composer.json | 18 ++-------- composer.lock | 93 +++++++++++++++++++++++++-------------------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/composer.json b/composer.json index 1ba08c0..acaf711 100644 --- a/composer.json +++ b/composer.json @@ -3,18 +3,6 @@ "type": "library", "description": "espace de maturation pour les librairies", "repositories": [ - { - "type": "path", - "url": "../nulib" - }, - { - "type": "path", - "url": "../nulib-spout" - }, - { - "type": "path", - "url": "../nulib-phpss" - }, { "type": "composer", "url": "https://repos.univ-reunion.fr/composer" @@ -30,9 +18,9 @@ "php": "^7.4" }, "require-dev": { - "nulib/php": "^7.4-dev", - "nulib/spout": "^7.4-dev", - "nulib/phpss": "^7.4-dev", + "nulib/php": "^0.5.0p74", + "nulib/spout": "^0.5.0p74", + "nulib/phpss": "^0.5.0p74", "nulib/tests": "^7.4", "ext-posix": "*", "ext-pcntl": "*", diff --git a/composer.lock b/composer.lock index 5ab7b92..48205d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0b1e015d12aecf1cdfbdc6a702d83d25", + "content-hash": "b4bd340f94d33a320d66b249b1c21edb", "packages": [], "packages-dev": [ { @@ -585,11 +585,11 @@ }, { "name": "nulib/php", - "version": "dev-dev74", - "dist": { - "type": "path", - "url": "../nulib", - "reference": "a371a68ee2d388d6970eec8ccf83b51cbc8e4e37" + "version": "0.5.1p74", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib.git", + "reference": "80943463586f501b033a90a1a28df857b2c67f1c" }, "require": { "ext-json": "*", @@ -629,20 +629,18 @@ } ], "description": "fonctions et classes essentielles", - "transport-options": { - "relative": true - } + "time": "2025-05-12T11:28:47+00:00" }, { "name": "nulib/phpss", - "version": "dev-dev74", - "dist": { - "type": "path", - "url": "../nulib-phpss", - "reference": "e68672917d2c51d5cf559835a71d49b66facd081" + "version": "0.5.0p74", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git", + "reference": "26b4bfddf5646f9313d419e568cd930efb9353eb" }, "require": { - "nulib/php": "^7.4-dev", + "nulib/php": "^0.5.0p74", "php": "^7.4", "phpoffice/phpspreadsheet": "^1.0" }, @@ -673,17 +671,15 @@ } ], "description": "wrapper pour phpoffice/phpspreadsheet", - "transport-options": { - "relative": true - } + "time": "2025-04-30T00:46:31+00:00" }, { "name": "nulib/spout", - "version": "dev-dev74", - "dist": { - "type": "path", - "url": "../nulib-spout", - "reference": "853f747bad42718f58aa0c1760f4d2536a165d3d" + "version": "0.5.0p74", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git", + "reference": "e650e27abe571553424524633deada32747d33a6" }, "require": { "ext-dom": "*", @@ -691,7 +687,7 @@ "ext-libxml": "*", "ext-xmlreader": "*", "ext-zip": "*", - "nulib/php": "^7.4-dev", + "nulib/php": "^0.5.0p74", "php": "^7.4" }, "replace": { @@ -729,9 +725,7 @@ } ], "description": "wrapper pour openspout/openspout", - "transport-options": { - "relative": true - } + "time": "2025-04-30T00:40:11+00:00" }, { "name": "nulib/tests", @@ -1316,16 +1310,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.22", + "version": "9.6.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", "shasum": "" }, "require": { @@ -1336,7 +1330,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1399,7 +1393,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" }, "funding": [ { @@ -1410,12 +1404,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-12-05T13:48:26+00:00" + "time": "2025-05-02T06:40:34+00:00" }, { "name": "psr/http-client", @@ -2660,7 +2662,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -2719,7 +2721,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -2739,19 +2741,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -2799,7 +2802,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -2815,7 +2818,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/yaml", @@ -2945,11 +2948,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "nulib/php": 20, - "nulib/spout": 20, - "nulib/phpss": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From f40461a324bf525f9086fcc262f55b09afa32630 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:35:04 +0400 Subject: [PATCH 14/16] deps de dev --- composer.json | 18 ++++++++++++++--- composer.lock | 54 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index acaf711..1ba08c0 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,18 @@ "type": "library", "description": "espace de maturation pour les librairies", "repositories": [ + { + "type": "path", + "url": "../nulib" + }, + { + "type": "path", + "url": "../nulib-spout" + }, + { + "type": "path", + "url": "../nulib-phpss" + }, { "type": "composer", "url": "https://repos.univ-reunion.fr/composer" @@ -18,9 +30,9 @@ "php": "^7.4" }, "require-dev": { - "nulib/php": "^0.5.0p74", - "nulib/spout": "^0.5.0p74", - "nulib/phpss": "^0.5.0p74", + "nulib/php": "^7.4-dev", + "nulib/spout": "^7.4-dev", + "nulib/phpss": "^7.4-dev", "nulib/tests": "^7.4", "ext-posix": "*", "ext-pcntl": "*", diff --git a/composer.lock b/composer.lock index 48205d6..2f9dc25 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b4bd340f94d33a320d66b249b1c21edb", + "content-hash": "0b1e015d12aecf1cdfbdc6a702d83d25", "packages": [], "packages-dev": [ { @@ -585,11 +585,11 @@ }, { "name": "nulib/php", - "version": "0.5.1p74", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib.git", - "reference": "80943463586f501b033a90a1a28df857b2c67f1c" + "version": "dev-dev74", + "dist": { + "type": "path", + "url": "../nulib", + "reference": "c8dcc6fe279fe3589ca20d13bcbbdc9be2b50285" }, "require": { "ext-json": "*", @@ -629,18 +629,20 @@ } ], "description": "fonctions et classes essentielles", - "time": "2025-05-12T11:28:47+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/phpss", - "version": "0.5.0p74", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git", - "reference": "26b4bfddf5646f9313d419e568cd930efb9353eb" + "version": "dev-dev74", + "dist": { + "type": "path", + "url": "../nulib-phpss", + "reference": "6fd59afb257e8add4d6b5f524ab2f90a6531c9c7" }, "require": { - "nulib/php": "^0.5.0p74", + "nulib/php": "^7.4-dev", "php": "^7.4", "phpoffice/phpspreadsheet": "^1.0" }, @@ -671,15 +673,17 @@ } ], "description": "wrapper pour phpoffice/phpspreadsheet", - "time": "2025-04-30T00:46:31+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/spout", - "version": "0.5.0p74", - "source": { - "type": "git", - "url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git", - "reference": "e650e27abe571553424524633deada32747d33a6" + "version": "dev-dev74", + "dist": { + "type": "path", + "url": "../nulib-spout", + "reference": "242abe737b6cfe186c6d376e904a45a3d3f6f8cd" }, "require": { "ext-dom": "*", @@ -687,7 +691,7 @@ "ext-libxml": "*", "ext-xmlreader": "*", "ext-zip": "*", - "nulib/php": "^0.5.0p74", + "nulib/php": "^7.4-dev", "php": "^7.4" }, "replace": { @@ -725,7 +729,9 @@ } ], "description": "wrapper pour openspout/openspout", - "time": "2025-04-30T00:40:11+00:00" + "transport-options": { + "relative": true + } }, { "name": "nulib/tests", @@ -2948,7 +2954,11 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "nulib/php": 20, + "nulib/spout": 20, + "nulib/phpss": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From b335fd5fa249a599e3fd753c500ab2a8f34d137f Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:37:43 +0400 Subject: [PATCH 15/16] Init changelog & version 0.5.1p82 --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 88437f3..6140834 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,5 @@ +## Release 0.5.1p82 du 12/05/2025-15:37 + ## Release 0.5.1p74 du 12/05/2025-15:34 * `39b2fa5` générifier la méthode d'authentification From 02e5ab29ab459e94a52717cf7d3da6e1cca6baba Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 12 May 2025 15:37:59 +0400 Subject: [PATCH 16/16] deps de dist --- composer.json | 18 ++---------- composer.lock | 78 ++++++++++++++++++++++----------------------------- 2 files changed, 37 insertions(+), 59 deletions(-) diff --git a/composer.json b/composer.json index b86627d..514ceb3 100644 --- a/composer.json +++ b/composer.json @@ -3,18 +3,6 @@ "type": "library", "description": "espace de maturation pour les librairies", "repositories": [ - { - "type": "path", - "url": "../nulib" - }, - { - "type": "path", - "url": "../nulib-spout" - }, - { - "type": "path", - "url": "../nulib-phpss" - }, { "type": "composer", "url": "https://repos.univ-reunion.fr/composer" @@ -30,9 +18,9 @@ "php": "^8.2" }, "require-dev": { - "nulib/php": "^8.2-dev", - "nulib/spout": "^8.2-dev", - "nulib/phpss": "^8.2-dev", + "nulib/php": "^0.5.0p82", + "nulib/spout": "^0.5.0p82", + "nulib/phpss": "^0.5.0p82", "nulib/tests": "^8.2", "ext-posix": "*", "ext-pcntl": "*", diff --git a/composer.lock b/composer.lock index b3d1834..2fc77b7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "614c12e46a043d8be2eb5f049c76c653", + "content-hash": "20d80ba2a043b1e083f352300c19da0f", "packages": [], "packages-dev": [ { @@ -391,11 +391,11 @@ }, { "name": "nulib/php", - "version": "dev-dev82", - "dist": { - "type": "path", - "url": "../nulib", - "reference": "a8094658d8ae21c9e66fba3120b9efede4d54ce7" + "version": "0.5.1p82", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib.git", + "reference": "bd8cdcafbeee9a786200672a022493441582b177" }, "require": { "ext-json": "*", @@ -435,20 +435,18 @@ } ], "description": "fonctions et classes essentielles", - "transport-options": { - "relative": true - } + "time": "2025-05-12T11:31:32+00:00" }, { "name": "nulib/phpss", - "version": "dev-dev82", - "dist": { - "type": "path", - "url": "../nulib-phpss", - "reference": "19ef9cae64dd860492055845779262c236c29556" + "version": "0.5.0p82", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git", + "reference": "620e1b8eab68afc8bae94b5a936b7929c03bc734" }, "require": { - "nulib/php": "^8.2-dev", + "nulib/php": "^0.5.0p82", "php": "^8.2", "phpoffice/phpspreadsheet": "^3.3.0" }, @@ -479,17 +477,15 @@ } ], "description": "wrapper pour phpoffice/phpspreadsheet", - "transport-options": { - "relative": true - } + "time": "2025-04-30T00:48:34+00:00" }, { "name": "nulib/spout", - "version": "dev-dev82", - "dist": { - "type": "path", - "url": "../nulib-spout", - "reference": "d52f9a0ebfe4fc2bab145209178ce2d4570c2e75" + "version": "0.5.0p82", + "source": { + "type": "git", + "url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git", + "reference": "32fbed599e2e3cfc616df70fe51ba8ec4cb3aefd" }, "require": { "ext-dom": "*", @@ -498,7 +494,7 @@ "ext-libxml": "*", "ext-xmlreader": "*", "ext-zip": "*", - "nulib/php": "^8.2-dev", + "nulib/php": "^0.5.0p82", "php": "^8.2" }, "replace": { @@ -539,9 +535,7 @@ } ], "description": "wrapper pour openspout/openspout", - "transport-options": { - "relative": true - } + "time": "2025-04-30T00:43:21+00:00" }, { "name": "nulib/tests", @@ -1127,16 +1121,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.45", + "version": "10.5.46", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" + "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", + "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", "shasum": "" }, "require": { @@ -1208,7 +1202,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" }, "funding": [ { @@ -1232,7 +1226,7 @@ "type": "tidelift" } ], - "time": "2025-02-06T16:08:12+00:00" + "time": "2025-05-02T06:46:24+00:00" }, { "name": "psr/http-client", @@ -2509,16 +2503,16 @@ }, { "name": "symfony/yaml", - "version": "v7.2.5", + "version": "v7.2.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" + "reference": "0feafffb843860624ddfd13478f481f4c3cd8b23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", - "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0feafffb843860624ddfd13478f481f4c3cd8b23", + "reference": "0feafffb843860624ddfd13478f481f4c3cd8b23", "shasum": "" }, "require": { @@ -2561,7 +2555,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.2.5" + "source": "https://github.com/symfony/yaml/tree/v7.2.6" }, "funding": [ { @@ -2577,7 +2571,7 @@ "type": "tidelift" } ], - "time": "2025-03-03T07:12:39+00:00" + "time": "2025-04-04T10:10:11+00:00" }, { "name": "theseer/tokenizer", @@ -2632,11 +2626,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "nulib/php": 20, - "nulib/spout": 20, - "nulib/phpss": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": {