<pman>Intégration de la branche rel82-0.5.1
This commit is contained in:
		
						commit
						452182a3ef
					
				
							
								
								
									
										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> | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								CHANGES.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								CHANGES.md
									
									
									
									
									
								
							| @ -1,3 +1,15 @@ | |||||||
|  | ## 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.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 | ||||||
|  | |||||||
							
								
								
									
										48
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
| @ -1 +1 @@ | |||||||
| 0.5.0 | 0.5.1 | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @ -391,11 +391,11 @@ | |||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "nulib/php", |             "name": "nulib/php", | ||||||
|             "version": "0.5.0p82", |             "version": "0.5.1p82", | ||||||
|             "source": { |             "source": { | ||||||
|                 "type": "git", |                 "type": "git", | ||||||
|                 "url": "https://git.univ-reunion.fr/sda-php/nulib.git", |                 "url": "https://git.univ-reunion.fr/sda-php/nulib.git", | ||||||
|                 "reference": "9f8b6545e68079728bb7349487b99024bd7d5090" |                 "reference": "bd8cdcafbeee9a786200672a022493441582b177" | ||||||
|             }, |             }, | ||||||
|             "require": { |             "require": { | ||||||
|                 "ext-json": "*", |                 "ext-json": "*", | ||||||
| @ -435,7 +435,7 @@ | |||||||
|                 } |                 } | ||||||
|             ], |             ], | ||||||
|             "description": "fonctions et classes essentielles", |             "description": "fonctions et classes essentielles", | ||||||
|             "time": "2025-04-30T00:33:36+00:00" |             "time": "2025-05-12T11:31:32+00:00" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "nulib/phpss", |             "name": "nulib/phpss", | ||||||
| @ -1121,16 +1121,16 @@ | |||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "phpunit/phpunit", |             "name": "phpunit/phpunit", | ||||||
|             "version": "10.5.45", |             "version": "10.5.46", | ||||||
|             "source": { |             "source": { | ||||||
|                 "type": "git", |                 "type": "git", | ||||||
|                 "url": "https://github.com/sebastianbergmann/phpunit.git", |                 "url": "https://github.com/sebastianbergmann/phpunit.git", | ||||||
|                 "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" |                 "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" | ||||||
|             }, |             }, | ||||||
|             "dist": { |             "dist": { | ||||||
|                 "type": "zip", |                 "type": "zip", | ||||||
|                 "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", |                 "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", | ||||||
|                 "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", |                 "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", | ||||||
|                 "shasum": "" |                 "shasum": "" | ||||||
|             }, |             }, | ||||||
|             "require": { |             "require": { | ||||||
| @ -1140,7 +1140,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": ">=8.1", |                 "php": ">=8.1", | ||||||
| @ -1202,7 +1202,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/10.5.45" |                 "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" | ||||||
|             }, |             }, | ||||||
|             "funding": [ |             "funding": [ | ||||||
|                 { |                 { | ||||||
| @ -1213,12 +1213,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": "2025-02-06T16:08:12+00:00" |             "time": "2025-05-02T06:46:24+00:00" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "psr/http-client", |             "name": "psr/http-client", | ||||||
| @ -2416,7 +2424,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", | ||||||
| @ -2475,7 +2483,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": [ | ||||||
|                 { |                 { | ||||||
| @ -2495,16 +2503,16 @@ | |||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "symfony/yaml", |             "name": "symfony/yaml", | ||||||
|             "version": "v7.2.5", |             "version": "v7.2.6", | ||||||
|             "source": { |             "source": { | ||||||
|                 "type": "git", |                 "type": "git", | ||||||
|                 "url": "https://github.com/symfony/yaml.git", |                 "url": "https://github.com/symfony/yaml.git", | ||||||
|                 "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" |                 "reference": "0feafffb843860624ddfd13478f481f4c3cd8b23" | ||||||
|             }, |             }, | ||||||
|             "dist": { |             "dist": { | ||||||
|                 "type": "zip", |                 "type": "zip", | ||||||
|                 "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", |                 "url": "https://api.github.com/repos/symfony/yaml/zipball/0feafffb843860624ddfd13478f481f4c3cd8b23", | ||||||
|                 "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", |                 "reference": "0feafffb843860624ddfd13478f481f4c3cd8b23", | ||||||
|                 "shasum": "" |                 "shasum": "" | ||||||
|             }, |             }, | ||||||
|             "require": { |             "require": { | ||||||
| @ -2547,7 +2555,7 @@ | |||||||
|             "description": "Loads and dumps YAML files", |             "description": "Loads and dumps YAML files", | ||||||
|             "homepage": "https://symfony.com", |             "homepage": "https://symfony.com", | ||||||
|             "support": { |             "support": { | ||||||
|                 "source": "https://github.com/symfony/yaml/tree/v7.2.5" |                 "source": "https://github.com/symfony/yaml/tree/v7.2.6" | ||||||
|             }, |             }, | ||||||
|             "funding": [ |             "funding": [ | ||||||
|                 { |                 { | ||||||
| @ -2563,7 +2571,7 @@ | |||||||
|                     "type": "tidelift" |                     "type": "tidelift" | ||||||
|                 } |                 } | ||||||
|             ], |             ], | ||||||
|             "time": "2025-03-03T07:12:39+00:00" |             "time": "2025-04-04T10:10:11+00:00" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "name": "theseer/tokenizer", |             "name": "theseer/tokenizer", | ||||||
|  | |||||||
| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user