Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
25cf1c5282 | |||
3a6fd67471 | |||
f40461a324 | |||
bba7bc8a01 | |||
dbfeb602e5 | |||
4a15dc66bd | |||
39b2fa58be | |||
33a10a37c2 | |||
f7e692df0c | |||
0c40769619 | |||
a120143794 | |||
3754ed7454 | |||
fea5cb8b87 | |||
5dd9ebbf65 | |||
5862d3583a | |||
ea68906b8a | |||
309375c297 | |||
d3950f5519 |
14
.idea/php.xml
generated
14
.idea/php.xml
generated
@ -21,20 +21,16 @@
|
|||||||
<path value="$PROJECT_DIR$/vendor/nulib/phpss" />
|
<path value="$PROJECT_DIR$/vendor/nulib/phpss" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
|
||||||
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
|
||||||
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
||||||
<path value="$PROJECT_DIR$/vendor/myclabs/php-enum" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
|
||||||
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
|
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
|
||||||
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
|
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
|
||||||
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
|
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
|
||||||
@ -44,7 +40,6 @@
|
|||||||
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
|
||||||
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
|
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
|
||||||
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
|
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
|
||||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
|
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
|
||||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
|
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
|
||||||
@ -64,6 +59,15 @@
|
|||||||
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
|
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
|
||||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||||
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/myclabs/php-enum" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
<path value="$PROJECT_DIR$/vendor/nulib/php" />
|
<path value="$PROJECT_DIR$/vendor/nulib/php" />
|
||||||
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
<path value="$PROJECT_DIR$/vendor/nulib/spout" />
|
||||||
</include_path>
|
</include_path>
|
||||||
|
14
CHANGES.md
14
CHANGES.md
@ -1,3 +1,17 @@
|
|||||||
|
## 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
|
||||||
|
* `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
|
## Release 0.5.0p74 du 30/04/2025-05:30
|
||||||
|
|
||||||
* `2d73f4d` documenter showmorePlugin
|
* `2d73f4d` documenter showmorePlugin
|
||||||
|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# 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)" &&
|
||||||
|
echo "commit=$commit"
|
||||||
|
|
||||||
|
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
|
@ -1 +1 @@
|
|||||||
0.5.0
|
0.5.1
|
||||||
|
@ -3,6 +3,18 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"description": "espace de maturation pour les librairies",
|
"description": "espace de maturation pour les librairies",
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../nulib"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../nulib-spout"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../nulib-phpss"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "composer",
|
"type": "composer",
|
||||||
"url": "https://repos.univ-reunion.fr/composer"
|
"url": "https://repos.univ-reunion.fr/composer"
|
||||||
@ -18,9 +30,9 @@
|
|||||||
"php": "^7.4"
|
"php": "^7.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nulib/php": "^0.5.0p74",
|
"nulib/php": "^7.4-dev",
|
||||||
"nulib/spout": "^0.5.0p74",
|
"nulib/spout": "^7.4-dev",
|
||||||
"nulib/phpss": "^0.5.0p74",
|
"nulib/phpss": "^7.4-dev",
|
||||||
"nulib/tests": "^7.4",
|
"nulib/tests": "^7.4",
|
||||||
"ext-posix": "*",
|
"ext-posix": "*",
|
||||||
"ext-pcntl": "*",
|
"ext-pcntl": "*",
|
||||||
|
93
composer.lock
generated
93
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "b4bd340f94d33a320d66b249b1c21edb",
|
"content-hash": "0b1e015d12aecf1cdfbdc6a702d83d25",
|
||||||
"packages": [],
|
"packages": [],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
@ -585,11 +585,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nulib/php",
|
"name": "nulib/php",
|
||||||
"version": "0.5.0p74",
|
"version": "dev-dev74",
|
||||||
"source": {
|
"dist": {
|
||||||
"type": "git",
|
"type": "path",
|
||||||
"url": "https://git.univ-reunion.fr/sda-php/nulib.git",
|
"url": "../nulib",
|
||||||
"reference": "4037bf20424eb48708e5fdf9fc8e10f2ef71d134"
|
"reference": "c8dcc6fe279fe3589ca20d13bcbbdc9be2b50285"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
@ -629,18 +629,20 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "fonctions et classes essentielles",
|
"description": "fonctions et classes essentielles",
|
||||||
"time": "2025-04-30T00:32:10+00:00"
|
"transport-options": {
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nulib/phpss",
|
"name": "nulib/phpss",
|
||||||
"version": "0.5.0p74",
|
"version": "dev-dev74",
|
||||||
"source": {
|
"dist": {
|
||||||
"type": "git",
|
"type": "path",
|
||||||
"url": "https://git.univ-reunion.fr/sda-php/nulib-phpss.git",
|
"url": "../nulib-phpss",
|
||||||
"reference": "26b4bfddf5646f9313d419e568cd930efb9353eb"
|
"reference": "6fd59afb257e8add4d6b5f524ab2f90a6531c9c7"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"nulib/php": "^0.5.0p74",
|
"nulib/php": "^7.4-dev",
|
||||||
"php": "^7.4",
|
"php": "^7.4",
|
||||||
"phpoffice/phpspreadsheet": "^1.0"
|
"phpoffice/phpspreadsheet": "^1.0"
|
||||||
},
|
},
|
||||||
@ -671,15 +673,17 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "wrapper pour phpoffice/phpspreadsheet",
|
"description": "wrapper pour phpoffice/phpspreadsheet",
|
||||||
"time": "2025-04-30T00:46:31+00:00"
|
"transport-options": {
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nulib/spout",
|
"name": "nulib/spout",
|
||||||
"version": "0.5.0p74",
|
"version": "dev-dev74",
|
||||||
"source": {
|
"dist": {
|
||||||
"type": "git",
|
"type": "path",
|
||||||
"url": "https://git.univ-reunion.fr/sda-php/nulib-spout.git",
|
"url": "../nulib-spout",
|
||||||
"reference": "e650e27abe571553424524633deada32747d33a6"
|
"reference": "242abe737b6cfe186c6d376e904a45a3d3f6f8cd"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
@ -687,7 +691,7 @@
|
|||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlreader": "*",
|
"ext-xmlreader": "*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"nulib/php": "^0.5.0p74",
|
"nulib/php": "^7.4-dev",
|
||||||
"php": "^7.4"
|
"php": "^7.4"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
@ -725,7 +729,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "wrapper pour openspout/openspout",
|
"description": "wrapper pour openspout/openspout",
|
||||||
"time": "2025-04-30T00:40:11+00:00"
|
"transport-options": {
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nulib/tests",
|
"name": "nulib/tests",
|
||||||
@ -1310,16 +1316,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "9.6.22",
|
"version": "9.6.23",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c"
|
"reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95",
|
||||||
"reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
|
"reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1330,7 +1336,7 @@
|
|||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"myclabs/deep-copy": "^1.12.1",
|
"myclabs/deep-copy": "^1.13.1",
|
||||||
"phar-io/manifest": "^2.0.4",
|
"phar-io/manifest": "^2.0.4",
|
||||||
"phar-io/version": "^3.2.1",
|
"phar-io/version": "^3.2.1",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
@ -1393,7 +1399,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"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": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1404,12 +1410,20 @@
|
|||||||
"url": "https://github.com/sebastianbergmann",
|
"url": "https://github.com/sebastianbergmann",
|
||||||
"type": "github"
|
"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",
|
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-05T13:48:26+00:00"
|
"time": "2025-05-02T06:40:34+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/http-client",
|
"name": "psr/http-client",
|
||||||
@ -2654,7 +2668,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.31.0",
|
"version": "v1.32.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
@ -2713,7 +2727,7 @@
|
|||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2733,19 +2747,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.31.0",
|
"version": "v1.32.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
"ext-iconv": "*",
|
||||||
"php": ">=7.2"
|
"php": ">=7.2"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
@ -2793,7 +2808,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2809,7 +2824,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2024-12-23T08:48:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
@ -2939,7 +2954,11 @@
|
|||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"stability-flags": [],
|
"stability-flags": {
|
||||||
|
"nulib/php": 20,
|
||||||
|
"nulib/spout": 20,
|
||||||
|
"nulib/phpss": 20
|
||||||
|
},
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
|
@ -63,21 +63,14 @@ Application::run(new class extends Application {
|
|||||||
$storage = new SqliteStorage($dbfile);
|
$storage = new SqliteStorage($dbfile);
|
||||||
$db = $storage->db();
|
$db = $storage->db();
|
||||||
|
|
||||||
$haveChannels = $storage->tableExists("_channels");
|
|
||||||
|
|
||||||
$name = $this->name;
|
$name = $this->name;
|
||||||
$channelClass = $this->channelClass;
|
$channelClass = $this->channelClass;
|
||||||
$tableName = $this->tableName;
|
$tableName = $this->tableName;
|
||||||
if ($name !== null) {
|
if ($name !== null) {
|
||||||
$row = null;
|
if (!$storage->channelExists($name, $row)) {
|
||||||
if ($haveChannels) {
|
self::die("$name: nom de canal de données introuvable");
|
||||||
$row = $db->one([
|
|
||||||
"select from _channels",
|
|
||||||
"where" => ["name" => $name],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
if ($row === null) self::die("$name: nom de canal de données introuvable");
|
if ($row["class_name"] !== "class@anonymous") $channelClass = $row["class_name"];
|
||||||
if ($row["class"] !== "class@anonymous") $channelClass = $row["class"];
|
|
||||||
else $tableName = $row["table_name"];
|
else $tableName = $row["table_name"];
|
||||||
}
|
}
|
||||||
if ($channelClass !== null) {
|
if ($channelClass !== null) {
|
||||||
@ -92,17 +85,12 @@ Application::run(new class extends Application {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
$found = false;
|
$found = false;
|
||||||
if ($haveChannels) {
|
foreach ($storage->getChannels() as $row) {
|
||||||
$rows = $db->all([
|
msg::print($row["name"]);
|
||||||
"select from _channels",
|
$found = true;
|
||||||
]);
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
msg::print($row["name"]);
|
|
||||||
$found = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!$found) self::die("Vous devez spécifier le canal de données");
|
if ($found) self::exit();
|
||||||
else self::exit();
|
self::die("Vous devez spécifier le canal de données");
|
||||||
}
|
}
|
||||||
$capacitor = new Capacitor($storage, $channel);
|
$capacitor = new Capacitor($storage, $channel);
|
||||||
|
|
||||||
|
@ -34,6 +34,12 @@
|
|||||||
.left-gap { margin-left: 1em;}
|
.left-gap { margin-left: 1em;}
|
||||||
.right-gap { margin-right: 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. */
|
/* 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-sm { margin-top: 10px; margin-bottom: 10px; }
|
||||||
.navbar-form-xs { margin-top: 14px; margin-bottom: 14px; }
|
.navbar-form-xs { margin-top: 14px; margin-bottom: 14px; }
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\b\authnz;
|
namespace nur\b\authnz;
|
||||||
|
|
||||||
|
use nur\authz;
|
||||||
use nur\b\IllegalAccessException;
|
use nur\b\IllegalAccessException;
|
||||||
use nur\b\ValueException;
|
use nur\b\ValueException;
|
||||||
use nur\config;
|
use nur\config;
|
||||||
use nur\cookie;
|
use nur\cookie;
|
||||||
|
use nur\F;
|
||||||
|
use nur\msg;
|
||||||
|
use nur\P;
|
||||||
use nur\session;
|
use nur\session;
|
||||||
|
use nur\v\fo;
|
||||||
|
use nur\v\ly;
|
||||||
use nur\v\page;
|
use nur\v\page;
|
||||||
|
use nur\v\v;
|
||||||
|
use nur\v\vo;
|
||||||
|
use nur\v\vp\AInitAuthzPage;
|
||||||
|
use nur\v\vp\AInitPage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface IAuthzManager: gestionnaire d'authentification et d'autorisation
|
* Interface IAuthzManager: gestionnaire d'authentification et d'autorisation
|
||||||
@ -53,7 +63,7 @@ class AuthzManager {
|
|||||||
function checkCookie(?string &$username=null, ?string &$authType=null): bool {
|
function checkCookie(?string &$username=null, ?string &$authType=null): bool {
|
||||||
$value = cookie::get($this->getCookieKey(), false);
|
$value = cookie::get($this->getCookieKey(), false);
|
||||||
if ($value === false) return 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];
|
$authType = $ms[1];
|
||||||
$username = $ms[2];
|
$username = $ms[2];
|
||||||
return true;
|
return true;
|
||||||
@ -191,6 +201,22 @@ class AuthzManager {
|
|||||||
page::redirect(page::bu($loginUrl, $params));
|
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 {
|
function formLogin(string $username, string $password): bool {
|
||||||
# l'utilisateur doit exister
|
# l'utilisateur doit exister
|
||||||
$user = $this->getUserManager()->getAuthzUser($username, null);
|
$user = $this->getUserManager()->getAuthzUser($username, null);
|
||||||
@ -202,7 +228,7 @@ class AuthzManager {
|
|||||||
if (config::is_devel() && !$password) $password = null;
|
if (config::is_devel() && !$password) $password = null;
|
||||||
if ($password === null || $user->validatePassword($password)) {
|
if ($password === null || $user->validatePassword($password)) {
|
||||||
# c'est bon
|
# c'est bon
|
||||||
$this->initSession(self::STATUS_INITIAL, null);
|
$this->initSession(self::STATUS_INITIAL);
|
||||||
session::set(self::SESSION_KEY_USERNAME, $username);
|
session::set(self::SESSION_KEY_USERNAME, $username);
|
||||||
$this->onAuthOk($username);
|
$this->onAuthOk($username);
|
||||||
session::set(self::SESSION_KEY_USER, $user);
|
session::set(self::SESSION_KEY_USER, $user);
|
||||||
@ -221,14 +247,11 @@ class AuthzManager {
|
|||||||
$this->onAuthOk($username);
|
$this->onAuthOk($username);
|
||||||
# l'utilisateur doit exister
|
# l'utilisateur doit exister
|
||||||
$user = $this->getUserManager()->getAuthzUser($username, $overrides);
|
$user = $this->getUserManager()->getAuthzUser($username, $overrides);
|
||||||
if ($user !== null) {
|
# ce doit être un utilisteur valide
|
||||||
# ce doit être un utilisteur valide
|
if ($user !== null && $user->isValid()) {
|
||||||
if ($user->isValid()) {
|
session::set(self::SESSION_KEY_USER, $user);
|
||||||
# c'est bon
|
$this->onAuthzOk($user);
|
||||||
session::set(self::SESSION_KEY_USER, $user);
|
return true;
|
||||||
$this->onAuthzOk($user);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -317,4 +340,83 @@ class AuthzManager {
|
|||||||
/** Traiter le cas où l'utilisateur a été autorisé avec succès. */
|
/** Traiter le cas où l'utilisateur a été autorisé avec succès. */
|
||||||
function onAuthzOk(IAuthzUser $authz): void {
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\b\authnz;
|
namespace nur\b\authnz;
|
||||||
|
|
||||||
|
use nur\authz;
|
||||||
use nur\config;
|
use nur\config;
|
||||||
|
use nur\F;
|
||||||
use nur\func;
|
use nur\func;
|
||||||
|
use nur\msg;
|
||||||
|
use nur\v\fo;
|
||||||
|
use nur\v\icon;
|
||||||
|
use nur\v\ly;
|
||||||
|
use nur\v\page;
|
||||||
|
use nur\v\v;
|
||||||
|
use nur\v\vo;
|
||||||
|
use nur\v\vp\AInitAuthzPage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CasAuthzManager: un utilisateur authentifié par CAS v3
|
* Class CasAuthzManager: un utilisateur authentifié par CAS v3
|
||||||
@ -28,4 +38,93 @@ class CasAuthzManager extends AuthzManager {
|
|||||||
}
|
}
|
||||||
return $this->userManager;
|
return $this->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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
78
nur_src/b/authnz/ExtAuthzManager.php
Normal file
78
nur_src/b/authnz/ExtAuthzManager.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
namespace nur\b\authnz;
|
||||||
|
|
||||||
|
use nur\authz;
|
||||||
|
use nur\config;
|
||||||
|
use nur\F;
|
||||||
|
use nur\msg;
|
||||||
|
use nur\v\fo;
|
||||||
|
use nur\v\ly;
|
||||||
|
use nur\v\page;
|
||||||
|
use nur\v\v;
|
||||||
|
use nur\v\vo;
|
||||||
|
use nur\v\vp\AInitAuthzPage;
|
||||||
|
|
||||||
|
class ExtAuthzManager extends AuthzManager {
|
||||||
|
const USER_MANAGER_CLASS = ExtUserManager::class;
|
||||||
|
|
||||||
|
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 === "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();
|
||||||
|
}
|
||||||
|
}
|
8
nur_src/b/authnz/ExtUserManager.php
Normal file
8
nur_src/b/authnz/ExtUserManager.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace nur\b\authnz;
|
||||||
|
|
||||||
|
class ExtUserManager extends SimpleUserManager {
|
||||||
|
function _getUser(string $username): ?array {
|
||||||
|
return ["username" => $username];
|
||||||
|
}
|
||||||
|
}
|
@ -633,14 +633,15 @@ class CTable extends ComponentPrintable implements IParametrable {
|
|||||||
/** @var string|int clé de la colonne courante */
|
/** @var string|int clé de la colonne courante */
|
||||||
protected $col;
|
protected $col;
|
||||||
|
|
||||||
function colTd($value): array {
|
function colTd($value): ?array {
|
||||||
$vs = $this->col($value);
|
$vs = $this->col($value);
|
||||||
if ($this->colCtx !== null) {
|
if ($this->colCtx !== null) {
|
||||||
$vs = func::_call($this->colCtx, [$vs, $value, $this->col, $this->index, $this->row, $this->rawRow]);
|
$vs = func::_call($this->colCtx, [$vs, $value, $this->col, $this->index, $this->row, $this->rawRow]);
|
||||||
|
if ($vs === false) return null;
|
||||||
} else {
|
} else {
|
||||||
$result = A::get($this->results, $this->col);
|
$result = A::get($this->results, $this->col);
|
||||||
$valid = $result === null || $result["valid"];
|
$valid = $result === null || $result["valid"];
|
||||||
$vs= [
|
$vs = [
|
||||||
"class" => ["danger" => !$valid],
|
"class" => ["danger" => !$valid],
|
||||||
$vs,
|
$vs,
|
||||||
];
|
];
|
||||||
|
@ -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();
|
$am = authz::manager();
|
||||||
$loginUrl = $this->getLoginUrl();
|
$loginUrl = $this->getLoginUrl();
|
||||||
$destUrl = page::self(true);
|
$destUrl = page::self(true);
|
||||||
@ -88,7 +88,7 @@ class AInitAuthzPage extends AInitPage implements IBasicPage {
|
|||||||
if ($am->isAuth()) $am->setConnected();
|
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 ($username === null && $password === null) return;
|
||||||
if (authz::manager()->formLogin($username, $password)) {
|
if (authz::manager()->formLogin($username, $password)) {
|
||||||
page::redirect($destUrl);
|
page::redirect($destUrl);
|
||||||
|
@ -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 {
|
function getLogoutUrl(): string {
|
||||||
return page::bu(config::k("logout_page","_logout.php"));
|
return page::bu(config::k("logout_page","_logout.php"));
|
||||||
}
|
}
|
||||||
|
68
nur_src/v/vp/AppExtauthPage.php
Normal file
68
nur_src/v/vp/AppExtauthPage.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
namespace nur\v\vp;
|
||||||
|
|
||||||
|
use nur\A;
|
||||||
|
use nur\authz;
|
||||||
|
use nur\config;
|
||||||
|
use nur\F;
|
||||||
|
use nur\v\base\AbstractPage;
|
||||||
|
use nur\v\page;
|
||||||
|
|
||||||
|
class AppExtauthPage extends AbstractPage {
|
||||||
|
/**
|
||||||
|
* @var bool faut-il afficher les variables au lieu de rediriger vers
|
||||||
|
* $ret_url?
|
||||||
|
*/
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
function isDebug(): bool {
|
||||||
|
if (!config::is_devel()) return false;
|
||||||
|
return static::DEBUG || F::get("D");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string nom de l'utilisateur connecté */
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
function setup(): void {
|
||||||
|
$destUrl = null;
|
||||||
|
$user = false;
|
||||||
|
if ($user === false) $user = A::get($_SERVER, "REMOTE_USER", false);
|
||||||
|
if ($user === false) $user = A::get($_SERVER, "HTTP_REMOTE_USER", false);
|
||||||
|
if ($user === false) $user = A::get($_SERVER, "HTTP_X_REMOTE_USER", false);
|
||||||
|
if ($user) {
|
||||||
|
if (authz::manager()->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ class BasicPage extends AInitAuthzPage {
|
|||||||
function afterConfig(): void {
|
function afterConfig(): void {
|
||||||
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
|
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
|
||||||
# setup() et afterSetup(), la session est disponible
|
# 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();
|
parent::afterConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage {
|
|||||||
function afterConfig(): void {
|
function afterConfig(): void {
|
||||||
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
|
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
|
||||||
# setup() et afterSetup(), la session est disponible
|
# 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();
|
parent::afterConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
nur_src/v/vp/TAuthzLoginPage.php
Normal file
19
nur_src/v/vp/TAuthzLoginPage.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
namespace nur\v\vp;
|
||||||
|
|
||||||
|
use nur\authz;
|
||||||
|
|
||||||
|
trait TAuthzLoginPage {
|
||||||
|
function TLoginPage_beforeSetup(): void {
|
||||||
|
authz::manager()->beforeSetup($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeSetup(): void {
|
||||||
|
$this->TLoginPage_beforeSetup();
|
||||||
|
parent::beforeSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function print(): void {
|
||||||
|
authz::manager()->print($this);
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ trait TFormLoginPage {
|
|||||||
$username = P::get("u");
|
$username = P::get("u");
|
||||||
$password = P::get("p");
|
$password = P::get("p");
|
||||||
$destPage = F::get("d", $this->getMainUrl());
|
$destPage = F::get("d", $this->getMainUrl());
|
||||||
$this->ensureFormLoginAndRedirect($username, $password, $destPage);
|
$this->_ensureFormLoginAndRedirect($username, $password, $destPage);
|
||||||
|
|
||||||
authz::manager()->checkSession($flcUsername, $flcAuthType);
|
authz::manager()->checkSession($flcUsername, $flcAuthType);
|
||||||
$this->flcUsername = $flcUsername;
|
$this->flcUsername = $flcUsername;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace nulib\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
|
use nulib\A;
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,85 +119,18 @@ class KeyAccess extends AbstractAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ensureAssoc(array $keys, ?array $params=null): void {
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
$dest =& $this->dest;
|
A::ensure_assoc($this->dest, $keys, $params);
|
||||||
$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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void {
|
function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void {
|
||||||
$dest =& $this->dest;
|
A::ensure_keys($this->dest, $defaults, $missings, $params);
|
||||||
$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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteMissings(array $missings, ?array $params=null): void {
|
function deleteMissings(array $missings, ?array $params=null): void {
|
||||||
$dest =& $this->dest;
|
A::delete_missings($this->dest, $missings, $params);
|
||||||
$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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureOrder(array $keys, ?array $params=null): void {
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
$dest =& $this->dest;
|
A::ensure_order($this->dest, $keys, $params);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ use Traversable;
|
|||||||
/**
|
/**
|
||||||
* Class Cursor: parcours des lignes itérable
|
* 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 array|null $value alias pour $row
|
||||||
* @property-read iterable|null $rows la source des lignes
|
* @property-read iterable|null $rows la source des lignes
|
||||||
*/
|
*/
|
||||||
@ -18,80 +22,14 @@ class Cursor implements Iterator {
|
|||||||
const PARAMS_SCHEMA = [
|
const PARAMS_SCHEMA = [
|
||||||
"rows" => ["?iterable"],
|
"rows" => ["?iterable"],
|
||||||
"rows_func" => ["?callable"],
|
"rows_func" => ["?callable"],
|
||||||
"cols" => ["?array"],
|
|
||||||
"cols_func" => ["?callable"],
|
|
||||||
"map" => ["?array"],
|
|
||||||
"map_func" => ["?callable"],
|
|
||||||
"filter" => ["?array"],
|
"filter" => ["?array"],
|
||||||
"filter_func" => ["?callable"],
|
"filter_func" => ["?callable"],
|
||||||
|
"map" => ["?array"],
|
||||||
|
"map_func" => ["?callable"],
|
||||||
|
"cols" => ["?array"],
|
||||||
|
"cols_func" => ["?callable"],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* 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]
|
|
||||||
*/
|
|
||||||
protected 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
function __construct(?iterable $rows=null, ?array $params=null) {
|
||||||
if ($rows !== null) $params["rows"] = $rows;
|
if ($rows !== null) $params["rows"] = $rows;
|
||||||
|
|
||||||
@ -103,7 +41,7 @@ class Cursor implements Iterator {
|
|||||||
$rowsGenerator = $rowsFunc;
|
$rowsGenerator = $rowsFunc;
|
||||||
$rowsFunc = null;
|
$rowsFunc = null;
|
||||||
} else {
|
} else {
|
||||||
$rowsFunc = func::with($rowsFunc, [$rows]);
|
$rowsFunc = func::with($rowsFunc, [$rows, $this]);
|
||||||
}
|
}
|
||||||
} elseif ($rows instanceof Traversable) {
|
} elseif ($rows instanceof Traversable) {
|
||||||
$rowsGenerator = $rows;
|
$rowsGenerator = $rows;
|
||||||
@ -115,61 +53,95 @@ class Cursor implements Iterator {
|
|||||||
$this->rowsGenerator = $rowsGenerator;
|
$this->rowsGenerator = $rowsGenerator;
|
||||||
$this->rowsFunc = $rowsFunc;
|
$this->rowsFunc = $rowsFunc;
|
||||||
|
|
||||||
$this->cols = $params["cols"] ?? null;
|
$filter = $params["filter"] ?? null;
|
||||||
$colsFunc = $params["cols_func"] ?? null;
|
$filterFunc = $params["filter_func"] ?? null;
|
||||||
if ($colsFunc !== null) $colsFunc = func::with($colsFunc);
|
if ($filterFunc !== null) $this->setFilterFunc($filterFunc);
|
||||||
$this->colsFunc = $colsFunc;
|
elseif ($filter !== null) $this->setFilter($filter);
|
||||||
|
|
||||||
$map = $params["map"] ?? null;
|
$map = $params["map"] ?? null;
|
||||||
$mapFunc = $params["map_func"] ?? null;
|
$mapFunc = $params["map_func"] ?? null;
|
||||||
if ($mapFunc !== null) {
|
if ($mapFunc !== null) $this->setMapFunc($mapFunc);
|
||||||
$mapFunc = func::with($mapFunc);
|
elseif ($map !== null) $this->setMap($map);
|
||||||
} elseif ($map !== null) {
|
|
||||||
$mapFunc = func::with(function(array $row) use ($map) {
|
|
||||||
return self::map_row($row, $map);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$this->mapFunc = $mapFunc;
|
|
||||||
|
|
||||||
$filter = $params["filter"] ?? null;
|
$this->cols = $params["cols"] ?? null;
|
||||||
$filterFunc = $params["filter_func"] ?? null;
|
$this->setColsFunc($params["cols_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** un générateur de lignes */
|
/** un générateur de lignes */
|
||||||
private ?Traversable $rowsGenerator;
|
private ?Traversable $rowsGenerator;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?iterable</code> */
|
/** une fonction de signature <code>function(mixed $rows, Cursor): ?iterable</code> */
|
||||||
private ?func $rowsFunc;
|
private ?func $rowsFunc;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
/** une fonction de signature <code>function(?array $row, Cursor): bool</code> */
|
||||||
private ?func $colsFunc;
|
private ?func $filterFunc = null;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
function setFilter(array $filter): self {
|
||||||
private ?func $mapFunc;
|
$this->filterFunc = func::with(function(?array $row) use ($filter) {
|
||||||
|
return cl::filter($row, $filter);
|
||||||
|
});
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): bool</code> */
|
function setFilterFunc(?callable $func): self {
|
||||||
private ?func $filterFunc;
|
if ($func === null) $this->filterFunc = null;
|
||||||
|
else $this->filterFunc = func::with($func)->bind($this);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** une fonction de signature <code>function(?array $row, Cursor): ?array</code> */
|
||||||
|
private ?func $mapFunc = null;
|
||||||
|
|
||||||
|
function setMap(array $map): self {
|
||||||
|
$this->mapFunc = func::with(function(?array $row) use ($map) {
|
||||||
|
return cl::map($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 <code>function(?array $row, Cursor): ?array</code> */
|
||||||
|
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;
|
protected ?iterable $rows;
|
||||||
|
|
||||||
|
/** @var array|null listes des colonnes de chaque enregistrement */
|
||||||
public ?array $cols;
|
public ?array $cols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int index de l'enregistrement (en ne comptant pas les éléments filtrés)
|
||||||
|
*/
|
||||||
public int $index;
|
public int $index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int index original de l'enregistrement (en tenant compte des éléments
|
||||||
|
* filtrés)
|
||||||
|
*/
|
||||||
public int $origIndex;
|
public int $origIndex;
|
||||||
|
|
||||||
|
/** @var string|int clé de l'enregistrement */
|
||||||
public $key;
|
public $key;
|
||||||
|
|
||||||
|
/** @var mixed élément original récupéré depuis la source */
|
||||||
public $raw;
|
public $raw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array|null enregistrement après conversion en tableau et application
|
||||||
|
* du mapping
|
||||||
|
*/
|
||||||
public ?array $row;
|
public ?array $row;
|
||||||
|
|
||||||
function __get($name) {
|
function __get($name) {
|
||||||
@ -179,18 +151,22 @@ class Cursor implements Iterator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cols(): ?array {
|
protected function convertToRow($raw): ?array {
|
||||||
return $this->row !== null? array_keys($this->row): null;
|
return cl::withn($raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function filter(): bool {
|
protected function filterFunc(?array $row): bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function map(): ?array {
|
protected function mapFunc(?array $row): ?array {
|
||||||
return $this->row;
|
return $this->row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function colsFunc(?array $row): ?array {
|
||||||
|
return $this->row !== null? array_keys($this->row): null;
|
||||||
|
}
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# Iterator
|
# Iterator
|
||||||
|
|
||||||
@ -212,26 +188,30 @@ class Cursor implements Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function valid(): bool {
|
function valid(): bool {
|
||||||
$cols = $this->colsFunc;
|
$colsFunc = $this->colsFunc;
|
||||||
$filter = $this->filterFunc;
|
$filterFunc = $this->filterFunc;
|
||||||
$map = $this->mapFunc;
|
$mapFunc = $this->mapFunc;
|
||||||
while ($valid = iter::valid($this->rows)) {
|
while ($valid = iter::valid($this->rows)) {
|
||||||
$this->raw = iter::current($this->rows, $this->key);
|
$this->raw = iter::current($this->rows, $this->key);
|
||||||
|
# conversion en enregistrement
|
||||||
$this->key ??= $this->origIndex;
|
$this->key ??= $this->origIndex;
|
||||||
$this->row = cl::withn($this->raw);
|
$this->row = $this->convertToRow($this->raw);
|
||||||
if ($filter === null) $filtered = $this->filter();
|
# filtrage
|
||||||
else $filtered = $filter->invoke([$this]);
|
if ($filterFunc === null) $filtered = $this->filterFunc($this->row);
|
||||||
if (!$filtered) {
|
else $filtered = $filterFunc->invoke([$this->row, $this]);
|
||||||
if ($map === null) $this->row = $this->map();
|
if ($filtered) {
|
||||||
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 {
|
|
||||||
iter::next($this->rows);
|
iter::next($this->rows);
|
||||||
$this->origIndex++;
|
$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) {
|
if (!$valid) {
|
||||||
|
@ -127,7 +127,6 @@ abstract class Schema implements ArrayAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
# type
|
# type
|
||||||
$types = [];
|
|
||||||
$deftype = $definition["type"];
|
$deftype = $definition["type"];
|
||||||
$nullable = $definition["nullable"] ?? false;
|
$nullable = $definition["nullable"] ?? false;
|
||||||
if ($deftype === null) {
|
if ($deftype === null) {
|
||||||
@ -138,7 +137,17 @@ abstract class Schema implements ArrayAccess {
|
|||||||
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
|
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
|
||||||
$deftype = explode("|", $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 = trim($type);
|
||||||
if ($type === null || $type === "null") {
|
if ($type === null || $type === "null") {
|
||||||
$nullable = true;
|
$nullable = true;
|
||||||
@ -151,10 +160,18 @@ abstract class Schema implements ArrayAccess {
|
|||||||
}
|
}
|
||||||
if ($type === "") throw SchemaException::invalid_type($type);
|
if ($type === "") throw SchemaException::invalid_type($type);
|
||||||
$type = cl::get(ref_types::ALIASES, $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["type"] = $types;
|
||||||
$definition["nullable"] = $nullable;
|
$definition["nullable"] = $nullable;
|
||||||
@ -224,7 +241,7 @@ abstract class Schema implements ArrayAccess {
|
|||||||
$types = $definition["type"];
|
$types = $definition["type"];
|
||||||
$nullable = $definition["nullable"];
|
$nullable = $definition["nullable"];
|
||||||
# s'il n'y a qu'une seul type, l'instancier tout de suite
|
# 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) {
|
foreach ($types as $key => $name) {
|
||||||
if ($key === 0) {
|
if ($key === 0) {
|
||||||
$args = null;
|
$args = null;
|
||||||
|
@ -5,19 +5,45 @@ use nulib\cl;
|
|||||||
use nulib\schema\_scalar\ScalarSchema;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
use nulib\schema\Result;
|
use nulib\schema\Result;
|
||||||
use nulib\schema\Schema;
|
use nulib\schema\Schema;
|
||||||
|
use nulib\str;
|
||||||
|
|
||||||
class tarray extends _tstring {
|
class tarray extends _tstring {
|
||||||
const NAME = "array";
|
const NAME = "array";
|
||||||
|
|
||||||
const SPLIT_PATTERN = '/\s+/';
|
const TRIM = true;
|
||||||
const FORMAT = " ";
|
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 {
|
public static function get_params_from_definition(?array $definition): ?array {
|
||||||
$params = parent::get_params_from_definition($definition);
|
$params = parent::get_params_from_definition($definition);
|
||||||
$splitPattern = $definition["split_pattern"] ?? null;
|
$sep = $definition["sep"] ?? null;
|
||||||
if ($splitPattern !== null) $params["split_pattern"] = $splitPattern;
|
if ($sep !== null) {
|
||||||
$format = $definition["format"] ?? null;
|
if (!is_array($sep)) {
|
||||||
if ($format !== null) $params["format"] = $format;
|
$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;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +69,9 @@ class tarray extends _tstring {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parse(string $value) {
|
function parse(string $value) {
|
||||||
$pattern = $this->params["split_pattern"] ?? static::SPLIT_PATTERN;
|
$sep = $this->params["parse_sep"] ?? static::DEFAULT_PARSE_SEP;
|
||||||
return preg_split($pattern, $value);
|
if ($sep !== false) $value = str::split($sep, $value);
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +90,7 @@ class tarray extends _tstring {
|
|||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
if ($value === null) return "";
|
if ($value === null) return "";
|
||||||
$format ??= $this->params["format"] ?? static::FORMAT;
|
$format ??= $this->params["format"] ?? static::DEFAULT_FORMAT_SEP;
|
||||||
return implode($format, $value);
|
return implode($format, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ class CursorTest extends TestCase {
|
|||||||
function test_map_row() {
|
function test_map_row() {
|
||||||
$cursor = new class extends Cursor {
|
$cursor = new class extends Cursor {
|
||||||
function mapRow(array $row, ?array $map): array {
|
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];
|
$row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
|
||||||
@ -32,7 +32,7 @@ class CursorTest extends TestCase {
|
|||||||
function test_filter_row() {
|
function test_filter_row() {
|
||||||
$cursor = new class extends Cursor {
|
$cursor = new class extends Cursor {
|
||||||
function filterRow(array $row, $filter): bool {
|
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];
|
$row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
|
||||||
|
56
tests/schema/types/arrayTest.php
Normal file
56
tests/schema/types/arrayTest.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
use nur\t\TestCase;
|
||||||
|
|
||||||
|
class arrayTest extends TestCase {
|
||||||
|
function testSchema() {
|
||||||
|
$value = " first second ";
|
||||||
|
Schema::nw($value, null, "array");
|
||||||
|
self::assertSame(["first", "second"], $value);
|
||||||
|
|
||||||
|
$value = " first second ";
|
||||||
|
Schema::nw($value, null, [
|
||||||
|
"array", "sep" => "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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user