(Grav GitSync) Automatic Commit from grav

This commit is contained in:
grav 2023-02-27 12:00:09 +01:00 committed by GitSync
parent a34430f25b
commit b38fa6e1ec
112 changed files with 2474 additions and 1898 deletions

View File

@ -1,3 +1,16 @@
# v1.10.39
## 02/19/2023
1. [](#bugfix)
* Forked and fixed PicoFeed library to support PHP 8.2
# v1.10.38
## 01/02/2023
1. [](#new)
* Update copyright dates
* Keep version number in sync with Grav version
# v1.10.37.1 # v1.10.37.1
## 10/08/2022 ## 10/08/2022

View File

@ -27,7 +27,7 @@ edit_mode: normal
frontend_preview_target: inline frontend_preview_target: inline
show_github_msg: true show_github_msg: true
admin_icons: line-awesome admin_icons: line-awesome
enable_auto_updates_check: false enable_auto_updates_check: true
notifications: notifications:
feed: true feed: true
dashboard: true dashboard: true

View File

@ -1,7 +1,7 @@
name: Admin Panel name: Admin Panel
slug: admin slug: admin
type: plugin type: plugin
version: 1.10.37.1 version: 1.10.39
description: Adds an advanced administration panel to manage your site description: Adds an advanced administration panel to manage your site
icon: empire icon: empire
author: author:

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use DateTime; use DateTime;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Cache; use Grav\Common\Cache;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Backup\Backups; use Grav\Common\Backup\Backups;

View File

@ -3,7 +3,7 @@
/** /**
* @package Grav\Plugin\Admin * @package Grav\Plugin\Admin
* *
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details. * @license MIT License; see LICENSE file for details.
*/ */

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
declare(strict_types=1); declare(strict_types=1);
namespace Grav\Plugin\Admin\Controllers; namespace Grav\Plugin\Admin\Controllers;

View File

@ -3,7 +3,7 @@
/** /**
* @package Grav\Plugin\Admin * @package Grav\Plugin\Admin
* *
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details. * @license MIT License; see LICENSE file for details.
*/ */

View File

@ -3,7 +3,7 @@
/** /**
* @package Grav\Plugin\Admin * @package Grav\Plugin\Admin
* *
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details. * @license MIT License; see LICENSE file for details.
*/ */

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Cache; use Grav\Common\Cache;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Grav; use Grav\Common\Grav;

View File

@ -3,7 +3,7 @@
/** /**
* @package Grav\Plugin\Admin * @package Grav\Plugin\Admin
* *
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details. * @license MIT License; see LICENSE file for details.
*/ */

View File

@ -1,4 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use ScssPhp\ScssPhp\Compiler; use ScssPhp\ScssPhp\Compiler;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
class ScssList class ScssList

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
/** /**

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin\Twig; namespace Grav\Plugin\Admin\Twig;
use Grav\Common\Data\Data; use Grav\Common\Data\Data;

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
use Grav\Common\Grav; use Grav\Common\Grav;

View File

@ -1,6 +1,13 @@
<?php <?php
namespace Grav\Plugin\Admin; namespace Grav\Plugin\Admin;
/**
* @package Grav\Plugin\Admin
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
use Grav\Common\Filesystem\Folder; use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Framework\File\File; use Grav\Framework\File\File;

View File

@ -22,9 +22,9 @@
"require": { "require": {
"php": "^7.3.6 || ^8.0", "php": "^7.3.6 || ^8.0",
"ext-json": "*", "ext-json": "*",
"p3k/picofeed": "@stable",
"scssphp/scssphp": "^1.7", "scssphp/scssphp": "^1.7",
"laminas/laminas-zendframework-bridge": "^1.4" "laminas/laminas-zendframework-bridge": "^1.4",
"p3k/picofeed": "@stable"
}, },
"require-dev": { "require-dev": {
"codeception/codeception": "^2.4", "codeception/codeception": "^2.4",
@ -54,5 +54,11 @@
"scripts": { "scripts": {
"test": "vendor/bin/codecept run unit", "test": "vendor/bin/codecept run unit",
"test-windows": "vendor\\bin\\codecept run unit" "test-windows": "vendor\\bin\\codecept run unit"
} },
"repositories": [
{
"type": "vcs",
"url": "https://github.com/rhukster/picoFeed"
}
]
} }

View File

@ -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": "e95b50cceb85b45792cbca45623f6a91", "content-hash": "1f6a6bec99c3a85ac753a1548c22f5d7",
"packages": [ "packages": [
{ {
"name": "laminas/laminas-xml", "name": "laminas/laminas-xml",
@ -130,16 +130,16 @@
}, },
{ {
"name": "p3k/picofeed", "name": "p3k/picofeed",
"version": "v0.1.40", "version": "1.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aaronpk/picofeed.git", "url": "https://github.com/rhukster/picofeed.git",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c" "reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aaronpk/picofeed/zipball/356fd66d48779193b10ac28532cb4a4e11bb801c", "url": "https://api.github.com/repos/rhukster/picofeed/zipball/8eacaa62f50a0935e26ca33f8d30d283344ca397",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c", "reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -171,7 +171,6 @@
"PicoFeed": "lib/" "PicoFeed": "lib/"
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -183,10 +182,9 @@
"description": "Modern library to handle RSS/Atom feeds", "description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/aaronpk/picoFeed", "homepage": "https://github.com/aaronpk/picoFeed",
"support": { "support": {
"issues": "https://github.com/aaronpk/picofeed/issues", "source": "https://github.com/rhukster/picofeed/tree/1.0.0"
"source": "https://github.com/aaronpk/picofeed/tree/v0.1.40"
}, },
"time": "2020-04-25T17:48:36+00:00" "time": "2023-02-19T19:58:09+00:00"
}, },
{ {
"name": "scssphp/scssphp", "name": "scssphp/scssphp",
@ -512,30 +510,30 @@
}, },
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
"version": "1.4.1", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/instantiator.git", "url": "https://github.com/doctrine/instantiator.git",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9", "doctrine/coding-standard": "^9 || ^11",
"ext-pdo": "*", "ext-pdo": "*",
"ext-phar": "*", "ext-phar": "*",
"phpbench/phpbench": "^0.16 || ^1", "phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4", "phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22" "vimeo/psalm": "^4.30 || ^5.4"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -562,7 +560,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/instantiator/issues", "issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1" "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
}, },
"funding": [ "funding": [
{ {
@ -578,7 +576,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-03T08:28:38+00:00" "time": "2022-12-30T00:15:36+00:00"
}, },
{ {
"name": "facebook/webdriver", "name": "facebook/webdriver",
@ -1341,27 +1339,28 @@
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
"version": "v1.15.0", "version": "v1.17.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/prophecy.git", "url": "https://github.com/phpspec/prophecy.git",
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2",
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "^1.2", "doctrine/instantiator": "^1.2 || ^2.0",
"php": "^7.2 || ~8.0, <8.2", "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*",
"phpdocumentor/reflection-docblock": "^5.2", "phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0", "sebastian/comparator": "^3.0 || ^4.0",
"sebastian/recursion-context": "^3.0 || ^4.0" "sebastian/recursion-context": "^3.0 || ^4.0"
}, },
"require-dev": { "require-dev": {
"phpspec/phpspec": "^6.0 || ^7.0", "phpspec/phpspec": "^6.0 || ^7.0",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^8.0 || ^9.0" "phpunit/phpunit": "^8.0 || ^9.0"
}, },
"type": "library", "type": "library",
@ -1402,9 +1401,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpspec/prophecy/issues", "issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/v1.15.0" "source": "https://github.com/phpspec/prophecy/tree/v1.17.0"
}, },
"time": "2021-12-08T12:19:24+00:00" "time": "2023-02-02T15:41:36+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -2668,16 +2667,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v4.4.45", "version": "v4.4.49",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "28b77970939500fb04180166a1f716e75a871ef8" "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/28b77970939500fb04180166a1f716e75a871ef8", "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"reference": "28b77970939500fb04180166a1f716e75a871ef8", "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2738,7 +2737,7 @@
"description": "Eases the creation of beautiful and testable command line interfaces", "description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v4.4.45" "source": "https://github.com/symfony/console/tree/v4.4.49"
}, },
"funding": [ "funding": [
{ {
@ -2754,7 +2753,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-08-17T14:50:19+00:00" "time": "2022-11-05T17:10:16+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@ -3190,16 +3189,16 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3214,7 +3213,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3252,7 +3251,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -3268,20 +3267,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8" "reference": "639084e360537a19f9ee352433b84ce831f3d2da"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
"reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8", "reference": "639084e360537a19f9ee352433b84ce831f3d2da",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3295,7 +3294,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3339,7 +3338,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -3355,20 +3354,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd" "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd", "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3380,7 +3379,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3423,7 +3422,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -3439,20 +3438,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3467,7 +3466,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3506,7 +3505,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -3522,20 +3521,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3544,7 +3543,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3589,7 +3588,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -3605,7 +3604,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-10T07:21:04+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",

View File

@ -3,8 +3,21 @@
// autoload.php @generated by Composer // autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) { if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; if (!headers_sent()) {
exit(1); header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
} }
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';

View File

@ -42,6 +42,9 @@ namespace Composer\Autoload;
*/ */
class ClassLoader class ClassLoader
{ {
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */ /** @var ?string */
private $vendorDir; private $vendorDir;
@ -106,6 +109,7 @@ class ClassLoader
public function __construct($vendorDir = null) public function __construct($vendorDir = null)
{ {
$this->vendorDir = $vendorDir; $this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
} }
/** /**
@ -425,7 +429,8 @@ class ClassLoader
public function loadClass($class) public function loadClass($class)
{ {
if ($file = $this->findFile($class)) { if ($file = $this->findFile($class)) {
includeFile($file); $includeFile = self::$includeFile;
$includeFile($file);
return true; return true;
} }
@ -555,18 +560,26 @@ class ClassLoader
return false; return false;
} }
}
/** /**
* Scope isolated include. * @return void
* */
* Prevents access to $this/self from included files. private static function initializeIncludeClosure()
* {
* @param string $file if (self::$includeFile !== null) {
* @return void return;
* @private }
*/
function includeFile($file) /**
{ * Scope isolated include.
include $file; *
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
} }

View File

@ -33,25 +33,18 @@ class ComposerAutoloaderInit98c98c1c3d67f21a128f935fe4a74897
$loader->register(true); $loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$files; $filesToLoad = \Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$files;
foreach ($includeFiles as $fileIdentifier => $file) { $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
composerRequire98c98c1c3d67f21a128f935fe4a74897($fileIdentifier, $file); if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
} }
return $loader; return $loader;
} }
} }
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire98c98c1c3d67f21a128f935fe4a74897($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@ -130,17 +130,17 @@
}, },
{ {
"name": "p3k/picofeed", "name": "p3k/picofeed",
"version": "v0.1.40", "version": "1.0.0",
"version_normalized": "0.1.40.0", "version_normalized": "1.0.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aaronpk/picofeed.git", "url": "https://github.com/rhukster/picofeed.git",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c" "reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aaronpk/picofeed/zipball/356fd66d48779193b10ac28532cb4a4e11bb801c", "url": "https://api.github.com/repos/rhukster/picofeed/zipball/8eacaa62f50a0935e26ca33f8d30d283344ca397",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c", "reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -163,7 +163,7 @@
"suggest": { "suggest": {
"ext-curl": "PicoFeed will use cURL if present" "ext-curl": "PicoFeed will use cURL if present"
}, },
"time": "2020-04-25T17:48:36+00:00", "time": "2023-02-19T19:58:09+00:00",
"bin": [ "bin": [
"picofeed" "picofeed"
], ],
@ -174,7 +174,6 @@
"PicoFeed": "lib/" "PicoFeed": "lib/"
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -186,8 +185,7 @@
"description": "Modern library to handle RSS/Atom feeds", "description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/aaronpk/picoFeed", "homepage": "https://github.com/aaronpk/picoFeed",
"support": { "support": {
"issues": "https://github.com/aaronpk/picofeed/issues", "source": "https://github.com/rhukster/picofeed/tree/1.0.0"
"source": "https://github.com/aaronpk/picofeed/tree/v0.1.40"
}, },
"install-path": "../p3k/picofeed" "install-path": "../p3k/picofeed"
}, },

View File

@ -3,7 +3,7 @@
'name' => 'getgrav/grav-plugin-admin', 'name' => 'getgrav/grav-plugin-admin',
'pretty_version' => 'dev-develop', 'pretty_version' => 'dev-develop',
'version' => 'dev-develop', 'version' => 'dev-develop',
'reference' => '97ab52df8179fad32d1190b530c32053b84d5979', 'reference' => '0d16602880413bb6cbc2c94b75f02dce3174d215',
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -13,7 +13,7 @@
'getgrav/grav-plugin-admin' => array( 'getgrav/grav-plugin-admin' => array(
'pretty_version' => 'dev-develop', 'pretty_version' => 'dev-develop',
'version' => 'dev-develop', 'version' => 'dev-develop',
'reference' => '97ab52df8179fad32d1190b530c32053b84d5979', 'reference' => '0d16602880413bb6cbc2c94b75f02dce3174d215',
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -44,9 +44,9 @@
), ),
), ),
'p3k/picofeed' => array( 'p3k/picofeed' => array(
'pretty_version' => 'v0.1.40', 'pretty_version' => '1.0.0',
'version' => '0.1.40.0', 'version' => '1.0.0.0',
'reference' => '356fd66d48779193b10ac28532cb4a4e11bb801c', 'reference' => '8eacaa62f50a0935e26ca33f8d30d283344ca397',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../p3k/picofeed', 'install_path' => __DIR__ . '/../p3k/picofeed',
'aliases' => array(), 'aliases' => array(),

View File

@ -97,7 +97,7 @@ class DateParser extends Base
if ($date !== false) { if ($date !== false) {
$errors = DateTime::getLastErrors(); $errors = DateTime::getLastErrors();
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { if ($errors === false || ($errors['error_count'] === 0 && $errors['warning_count'] === 0)) {
return $date; return $date;
} }
} }

View File

@ -1,3 +1,24 @@
# v1.3.4
## 02/19/2023
1. [](#improved)
* Support saving via admin in current language if not translated
# v1.3.3
## 01/04/2023
1. [](#improved)
* Save `post-save` action to session
* Set default `post-save` action to `edit` for create and edit
# v1.3.2
## 12/02/2022
1. [](#improved)
* Various translation enhancements
1. [](#bugfix)
* Fixed frontend editing objects with urlencoded ids
# v1.3.1 # v1.3.1
## 09/08/2022 ## 09/08/2022

View File

@ -54,7 +54,7 @@
{# FIXME: Search fields should be passed and individually customizable, right now defaulting to all fields selected #} {# FIXME: Search fields should be passed and individually customizable, right now defaulting to all fields selected #}
{% set searchFields = searchFields|merge([key|replace({'.': '_'})]) %} {% set searchFields = searchFields|merge([key|replace({'.': '_'})]) %}
{% endfor %} {% endfor %}
{% set tableFields = tableFields|merge([{ name: '_actions_', title: 'Actions', titleClass: 'right' }]) %} {% set tableFields = tableFields|merge([{ name: '_actions_', title: "PLUGIN_FLEX_OBJECTS.ACTION.ACTIONS"|tu, titleClass: 'right' }]) %}
{% set list = table.jsonSerialize %} {% set list = table.jsonSerialize %}

View File

@ -4,7 +4,7 @@
<i class="fa fa-check"></i> {{ "PLUGIN_ADMIN.SAVE"|tu }} <i class="fa fa-check"></i> {{ "PLUGIN_ADMIN.SAVE"|tu }}
</button> </button>
{% if can_translate %} {% if can_translate %}
{% set untranslated = admin_languages|array_diff(object_languages|merge([language])) %} {% set untranslated = admin_languages|array_diff(object_languages) %}
{% if count(untranslated) %} {% if count(untranslated) %}
<button id="titlebar-button-save" type="button" class="button success dropdown-toggle" data-toggle="dropdown"> <button id="titlebar-button-save" type="button" class="button success dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-caret-down"></i> <i class="fa fa-caret-down"></i>

View File

@ -3,7 +3,7 @@
{% set originalValue = value %} {% set originalValue = value %}
{% set value = (value is null ? field.default : value) %} {% set value = (value is null ? field.default : value) %}
{% set isNew = key ? false : true %} {% set isNew = key ? false : true %}
{% set savedOption = grav.session.post_entries_save|default('create-new') %} {% set savedOption = grav.session.post_entries_save|default('edit') %}
{% if isNew %} {% if isNew %}
{% set options = {'create-new':'PLUGIN_FLEX_OBJECTS.ACTION.CREATE_NEW', 'edit':'PLUGIN_FLEX_OBJECTS.ACTION.EDIT_ITEM', 'list':'PLUGIN_FLEX_OBJECTS.ACTION.LIST_ITEMS'} %} {% set options = {'create-new':'PLUGIN_FLEX_OBJECTS.ACTION.CREATE_NEW', 'edit':'PLUGIN_FLEX_OBJECTS.ACTION.EDIT_ITEM', 'list':'PLUGIN_FLEX_OBJECTS.ACTION.LIST_ITEMS'} %}
@ -12,7 +12,6 @@
{% endif %} {% endif %}
{% block input %} {% block input %}
{% set savedOption = not isNew and savedOption == 'create-new' ? 'edit' : savedOption %}
{% for key, text in options %} {% for key, text in options %}
{% set id = field.id|default(field.name) ~ '-' ~ key %} {% set id = field.id|default(field.name) ~ '-' ~ key %}

View File

@ -1,7 +1,7 @@
name: Flex Objects name: Flex Objects
slug: flex-objects slug: flex-objects
type: plugin type: plugin
version: 1.3.1 version: 1.3.4
description: Flex Objects plugin allows you to manage Flex Objects in Grav Admin. description: Flex Objects plugin allows you to manage Flex Objects in Grav Admin.
icon: list-alt icon: list-alt
author: author:

View File

@ -907,6 +907,7 @@ class AdminController
// Set route to point to the current page. // Set route to point to the current page.
if (!$this->redirect) { if (!$this->redirect) {
$postAction = $request->getParsedBody()['_post_entries_save'] ?? 'edit'; $postAction = $request->getParsedBody()['_post_entries_save'] ?? 'edit';
$this->grav['session']->post_entries_save = $postAction;
if ($postAction === 'create-new') { if ($postAction === 'create-new') {
// Create another. // Create another.
$route = $this->referrerRoute->withGravParam('action', null)->withGravParam('', 'add'); $route = $this->referrerRoute->withGravParam('action', null)->withGravParam('', 'add');

View File

@ -261,7 +261,7 @@ class FlexObjectsPlugin extends Plugin
if (!isset($form['flex']['key']) && $edit === true) { if (!isset($form['flex']['key']) && $edit === true) {
/** @var Route $route */ /** @var Route $route */
$route = $this->grav['route']; $route = $this->grav['route'];
$id = $route->getGravParam('id'); $id = rawurldecode($route->getGravParam('id'));
if (null !== $id) { if (null !== $id) {
$form['flex']['key'] = $id; $form['flex']['key'] = $id;
$event['form'] = $form; $event['form'] = $form;

View File

@ -13,6 +13,7 @@ PLUGIN_FLEX_OBJECTS:
DIRECTORIES: "Verzeichnisse" DIRECTORIES: "Verzeichnisse"
CSV: "CSV" CSV: "CSV"
PARENTS: "Eltern" PARENTS: "Eltern"
NEW: "Neu"
CONTROLLER: CONTROLLER:
TASK_DELETE_SUCCESS: 'Eintrag erfolgreich gelöscht' TASK_DELETE_SUCCESS: 'Eintrag erfolgreich gelöscht'
@ -35,6 +36,7 @@ PLUGIN_FLEX_OBJECTS:
ADVANCED_OPTIONS: "Erweiterte Optionen" ADVANCED_OPTIONS: "Erweiterte Optionen"
APPLY_FILTERS: "Filter anwenden" APPLY_FILTERS: "Filter anwenden"
RESET_FILTERS: "Filter zurücksetzen" RESET_FILTERS: "Filter zurücksetzen"
ACTIONS: "Aktionen"
FILTER: FILTER:
PAGE_ATTRIBUTES: "Seitenattribute" PAGE_ATTRIBUTES: "Seitenattribute"

View File

@ -30,13 +30,13 @@ PLUGIN_FLEX_OBJECTS:
CREATE_NEW: Create New Item CREATE_NEW: Create New Item
EDIT_ITEM: Edit Item EDIT_ITEM: Edit Item
LIST_ITEMS: "List Items" LIST_ITEMS: "List Items"
LIST_ITEM: List Items
DELETE_N: "Delete" # In some languages 'delete OBJECT' may need a special declination DELETE_N: "Delete" # In some languages 'delete OBJECT' may need a special declination
REALLY_DELETE: "Are you sure you want to permanently delete the %s?" REALLY_DELETE: "Are you sure you want to permanently delete the %s?"
SEARCH_PLACEHOLDER: "Search…" SEARCH_PLACEHOLDER: "Search…"
ADVANCED_OPTIONS: "Advanced Options" ADVANCED_OPTIONS: "Advanced Options"
APPLY_FILTERS: "Apply Filters" APPLY_FILTERS: "Apply Filters"
RESET_FILTERS: "Reset to Defaults" RESET_FILTERS: "Reset to Defaults"
ACTIONS: "Actions"
FILTER: FILTER:
PAGE_ATTRIBUTES: "Page Attributes" PAGE_ATTRIBUTES: "Page Attributes"

View File

@ -30,13 +30,13 @@ PLUGIN_FLEX_OBJECTS:
CREATE_NEW: "Crear nuevo ítem" CREATE_NEW: "Crear nuevo ítem"
EDIT_ITEM: "Editar ítem" EDIT_ITEM: "Editar ítem"
LIST_ITEMS: "Listar ítems" LIST_ITEMS: "Listar ítems"
LIST_ITEM: "Listar ítem"
DELETE_N: "Eliminar" DELETE_N: "Eliminar"
REALLY_DELETE: "¿Realmente quieres eliminar %s permanentemente?" REALLY_DELETE: "¿Realmente quieres eliminar %s permanentemente?"
SEARCH_PLACEHOLDER: "Buscar…" SEARCH_PLACEHOLDER: "Buscar…"
ADVANCED_OPTIONS: "Opciones avazadas" ADVANCED_OPTIONS: "Opciones avazadas"
APPLY_FILTERS: "Aplicar filtros" APPLY_FILTERS: "Aplicar filtros"
RESET_FILTERS: "Restablecer filtros" RESET_FILTERS: "Restablecer filtros"
ACTIONS: "Acciones"
FILTER: FILTER:
PAGE_ATTRIBUTES: "Atributos de página" PAGE_ATTRIBUTES: "Atributos de página"

View File

@ -30,13 +30,13 @@ PLUGIN_FLEX_OBJECTS:
CREATE_NEW: 新しいデータを作成 CREATE_NEW: 新しいデータを作成
EDIT_ITEM: "データの編集" EDIT_ITEM: "データの編集"
LIST_ITEMS: "データの一覧表示" LIST_ITEMS: "データの一覧表示"
LIST_ITEM: データの一覧
DELETE_N: "削除" # In some languages 'delete OBJECT' may need a special declination DELETE_N: "削除" # In some languages 'delete OBJECT' may need a special declination
REALLY_DELETE: "%s を削除します。復元はできませんがよろしいですか?" REALLY_DELETE: "%s を削除します。復元はできませんがよろしいですか?"
SEARCH_PLACEHOLDER: "検索…" SEARCH_PLACEHOLDER: "検索…"
ADVANCED_OPTIONS: "詳細設定" ADVANCED_OPTIONS: "詳細設定"
APPLY_FILTERS: "フィルターを適用" APPLY_FILTERS: "フィルターを適用"
RESET_FILTERS: "標準にリセットする" RESET_FILTERS: "標準にリセットする"
ACTIONS: "アクション"
FILTER: FILTER:
PAGE_ATTRIBUTES: "ページ属性" PAGE_ATTRIBUTES: "ページ属性"

View File

@ -0,0 +1,71 @@
PLUGIN_FLEX_OBJECTS:
PLUGIN_NAME: "Flex Objects"
PLUGIN_DESCRIPTION: "De Flex Objects plugin maakt het mogelijk om flexibele objecten te maken en te beheren."
TITLE: Flex Objects
TYPES_TITLE: Mappen
AFTER_SAVE: Na opslaan…
LIST_INFO: '{from} tot {to} van {total} items weergegeven'
EMPTY_RESULT: Deze zoekopdracht geeft geen resultaat
USE_BUILT_IN_CSS: "Gebruik ingebouwde CSS"
EXTRA_ADMIN_TWIG_PATH: "Extra Admin Twig pad"
DIRECTORIES: "Mappen"
CSV: "CSV"
PARENTS: "Ouders"
CONTROLLER:
TASK_DELETE_SUCCESS: 'Item succesvol verwijderd'
TASK_DELETE_FAILURE: 'Item verwijderen mislukt: %s'
TASK_NEW_FOLDER_SUCCESS: 'Map succesvol aangemaakt'
TASK_COPY_SUCCESS: 'Kopie succesvol aangemaakt'
TASK_COPY_FAILURE: 'Kopie aanmaken mislukt: %s'
TASK_SAVE_SUCCESS: 'Item succesvol opgeslagen'
TASK_SAVE_FAILURE: 'Item opslaan mislukt: %s'
TASK_CONFIGURE_SUCCESS: 'Configuratie succesvol opgeslagen'
TASK_CONFIGURE_FAILURE: 'Configuratie opslaan mislukt: %s'
ACTION:
CREATE_NEW: Nieuw item aanmaken
EDIT_ITEM: Item bewerken
LIST_ITEMS: Items weergeven
DELETE_N: "Verwijderen"
REALLY_DELETE: "Weet je zeker dat je %s permanent wilt verwijderen?"
SEARCH_PLACEHOLDER: "Zoeken…"
ADVANCED_OPTIONS: "Geavanceerde opties"
APPLY_FILTERS: "Filters toepassen"
RESET_FILTERS: "Filters resetten"
FILTER:
PAGE_ATTRIBUTES: "Pagina attributen"
PAGE_TYPES: "Pagina types"
MODULAR_TYPES: "Modulaire types"
LANGUAGE:
USING_DEFAULT: "<b>Standaard</b> taalbestand in gebruik."
UNUSED_DEFAULT: "Ongebruikt <b>standaard</b> taalbestand aanwezig."
USING_OVERRIDE: "<b>%s</b> taalbestand in gebruik."
NOT_TRANSLATED_YET: "Deze pagina is nog niet vertaald naar <i class=\"fa fa-flag-o\"></i> <b>%s</b>!"
NO_FALLBACK_FOUND: "Geen fallback taalbestand gevonden."
FALLING_BACK: "Terugvallen op <b>%s</b> taalbestand."
STATE:
LOADING: "Laden…"
CREATED_SUCCESSFULLY: "Succesvol aangemaakt"
UPDATED_SUCCESSFULLY: "Succesvol bijgewerkt"
DELETED_SUCCESSFULLY: "Succesvol verwijderd"
EDITING_DRAFT: "Je bewerkt een concept."
NOT_CREATED_YET: "Deze pagina bestaat niet, totdat je hem opslaat."
ERROR:
BAD_DIRECTORY: "Niet bestaande map"
PAGE_NOT_FOUND: "Pagina niet gevonden"
PAGE_NOT_EXIST: "Oeps! Deze pagina bestaat niet."
PAGE_FORBIDDEN: "Oeps! Je hebt geen toegang tot deze pagina."
LAYOUT_NOT_FOUND: "Object layout '%s' niet gevonden."
BLUEPRINT_NO_LIST: "De blueprint van <i>%s</i> bevat geen velddata of lijst pagina overschrijving."
BLUEPRINT_NO_LIST_ADVISE: "Voeg een <i>list</i> sectie toe aan de blueprint of overschrijf de <i>list</i> pagina in de configuratie."
BLUEPRINT_NO_LIST_TEMPLATE: "Maak een template bestand voor dit type aan in <b>flex-objects/types/%s/list.html.twig</b>"
LIST_EMPTY: "Er zijn geen items gevonden."
LIST_EMPTY_ADD: "Er zijn geen items op dit moment. Klik op de knop <a href=\"%s\">Toevoegen</a> om een nieuw item aan te maken."
NO_FLEX_DIRECTORIES: "Geen Flex Object mappen gedefinieerd."

View File

@ -1,165 +0,0 @@
{
"env": {
"browser": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"rules": {
"accessor-pairs": 2,
"array-bracket-spacing": 0,
"block-scoped-var": 0,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": 0,
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
"computed-property-spacing": 0,
"consistent-return": 0,
"consistent-this": 0,
"constructor-super": 2,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 0,
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"func-names": 0,
"func-style": 0,
"generator-star-spacing": [2, { "before": true, "after": true }],
"guard-for-in": 0,
"handle-callback-err": [2, "^(err|error)$" ],
"indent": [2, 4, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"linebreak-style": 0,
"lines-around-comment": 0,
"max-nested-callbacks": 0,
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"newline-after-var": 0,
"no-alert": 0,
"no-array-constructor": 2,
"no-caller": 2,
"no-catch-shadow": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 0,
"no-empty": 0,
"no-empty-character-class": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-lonely-if": 0,
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 0,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-path-concat": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 0,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 0,
"no-unexpected-multiline": 2,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-expressions": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-use-before-define": 0,
"no-var": 0,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"object-curly-spacing": 0,
"object-shorthand": 0,
"one-var": [2, { "initialized": "never" }],
"operator-assignment": 0,
"operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": 0,
"prefer-const": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"keyword-spacing": [2, {"after": true, "overrides": {"throw": { "after": true}, "return": { "before": true }}}],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }],
"strict": 0,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"wrap-regex": 0,
"yoda": [2, "never"]
}
}

View File

@ -1,5 +0,0 @@
# OS Generated
.DS_Store*
/.idea
node_modules
*.js.map

View File

@ -1,9 +1,94 @@
# v7.1.2
## 01/08/2023
1. [](#bugfix)
* Fixes issue with multiplication type BasicCaptcha Math Captcha [#587](https://github.com/getgrav/grav-plugin-form/issues/587)
# v7.1.1
## 11/29/2022
1. [](#bugfix)
* Fix missing blueprint entries for Turnstile configuration [#583](https://github.com/getgrav/grav-plugin-form/issues/583)
# v7.1.0
## 11/16/2022
1. [](#new)
* Added Cloudflare's `turnstile` captcha integration [documentation](https://learn.getgrav.org/17/forms/forms/fields-available#turnstile-field-cloudflare)
# v7.0.2
## 10/05/2022
1. [](#bugfix)
* Fix for modular form definitions at root-level (useful for storing shared forms)
# v7.0.1
## 09/20/2022
1. [](#improved)
* Provided some basic CSS styling for new captcha field
# v7.0.0
## 09/20/2022
1. [](#new)
* Added a new custom `basic-captcha` option with **character** and **math** puzzles. No 3rd-part service required.
# v6.0.4
## 08/08/2022
1. [](#improved)
* Added `attributes` to field [#573](https://github.com/getgrav/grav-plugin-form/pull/573)
# v6.0.3
## 05/05/2022
1. [](#bugfix)
* Regression: Fixed broken `addForm()` method
# v6.0.2
## 05/02/2022
1. [](#bugfix)
* Fixed `forms({ route: '/forms/_myform' })` not finding form from non-routable pages (second try)
# v6.0.1
## 04/25/2022
1. [](#improved)
* Improved `prepare_form_field()` twig method to include `plain_name`
1. [](#bugfix)
* Fixed `columns` and `column` fields with `.dotted` variables inside to ignore columns and column names
* Fixed `forms({ route: '/forms/_myform' })` not finding forms from non-routable pages
# v6.0.0
## 03/28/2022
1. [](#improved)
* Added log warning when trying to access form by non-unique name
* Optimized form caching by not initializing the forms in `onPageProcessed` event anymore
* **BACKWARD COMPATIBILITY**: As form initialization has been delayed, logic relaying on `onPageProcessed` with forms may not work anymore
1. [](#bugfix)
* Fixed select field where option is iterable (#558)
* Fixed `FormPlugin::getForm()` to properly search the current page first
* Fixed `FormPlugin::getForm()` to ignore fallback if the page was given as parameter
* Fixed dynamic forms to work with cache turned on
* Fix nested `toggleable`: originalValue now checks with `??` instead of `is defined`
# v5.1.7
## 03/14/2022
1. [](#new)
* Added `access` support for buttons
2. [](#bugfix)
* Fixed tabs in the frontend to ensure JS is loaded
# v5.1.6 # v5.1.6
## 02/07/2022 ## 02/07/2022
1. [](#bugfix) 1. [](#bugfix)
* Fixed Select field when using OptGroups, not allowing key/values [#541](https://github.com/getgrav/grav-plugin-form/issues/541) * Fixed Select field when using OptGroups, not allowing key/values [#541](https://github.com/getgrav/grav-plugin-form/issues/541)
* Support for translatable OptGroup labels in Select field [#540](https://github.com/getgrav/grav-plugin-form/issues/540) * Support for translatable OptGroup labels in Select field [#540](https://github.com/getgrav/grav-plugin-form/issues/540)
# v5.1.5 # v5.1.5
## 01/24/2022 ## 01/24/2022

View File

@ -30,6 +30,8 @@ Note: when using email functionality in your forms, make sure you have configure
# NOTES: # NOTES:
As of version **Form 6.0.0** forms are no longer initialized before caching, but when the form is requested. This has been done to make dynamic forms to work better with caching. There may be some backward compatibility issues for logic that modifies pages with forms as the modification doesn't happen without accessing the form first.
As of version **Form 5.0.0** Grav 1.7+ is required. As of version **Form 5.0.0** Grav 1.7+ is required.
As of version **Form 4.0.6**, form labels are now being output with the `|raw` filter. If you wish to show HTML in your form label, ie `Root Folder <root>`, then you need to escape that in your form definition: As of version **Form 4.0.6**, form labels are now being output with the `|raw` filter. If you wish to show HTML in your form label, ie `Root Folder <root>`, then you need to escape that in your form definition:

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
name: Form name: Form
slug: form slug: form
type: plugin type: plugin
version: 5.1.6 version: 7.1.2
description: Enables the forms handling description: Enables forms handling and processing
icon: check-square icon: check-square
author: author:
name: Team Grav name: Team Grav
@ -196,3 +196,150 @@ form:
label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY
help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP
default: '' default: ''
turnstile_captcha:
type: section
title: PLUGIN_FORM.TURNSTILE_CAPTCHA
fields:
turnstile.theme:
type: select
label: PLUGIN_FORM.RECAPTCHA_THEME
default: light
options:
light: PLUGIN_FORM.RECAPTCHA_THEME_LIGHT
dark: PLUGIN_FORM.RECAPTCHA_THEME_DARK
turnstile.site_key:
type: text
label: PLUGIN_FORM.RECAPTCHA_SITE_KEY
help: PLUGIN_FORM.RECAPTCHA_SITE_KEY_HELP
default: ''
turnstile.secret_key:
type: text
label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY
help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP
default: ''
basic_captcha:
type: section
title: PLUGIN_FORM.BASIC_CAPTCHA
fields:
basic_captcha.type:
type: elements
label: PLUGIN_FORM.BASIC_CAPTCHA_TYPE
default: 'characters'
size: medium
options:
characters: Random Characters
math: Math Puzzle
fields:
characters:
type: element
fields:
basic_captcha.chars.length:
type: range
label: PLUGIN_FORM.BASIC_CAPTCHA_LENGTH
default: 6
validate:
min: 4
max: 12
append: characters
basic_captcha.chars.font:
type: select
label: PLUGIN_FORM.BASIC_CAPTCHA_FONT
default: zxx-noise.ttf
options:
'zxx-noise.ttf': zxx-Noise
'zxx-xed.ttf': zxx-Xed
'zxx-camo.ttf': zxx-Camo
'zxx-sans.ttf': zxx-Sans
basic_captcha.chars.size:
type: range
label: PLUGIN_FORM.BASIC_CAPTCHA_SIZE
default: 24
append: px
validate:
min: 12
max: 32
step: 2
basic_captcha.chars.bg:
type: colorpicker
size: small
label: PLUGIN_FORM.BASIC_CAPTCHA_BG_COLOR
default: '#ffffff'
basic_captcha.chars.text:
type: colorpicker
size: small
label: PLUGIN_FORM.BASIC_CAPTCHA_TEXT_COLOR
default: '#000000'
basic_captcha.chars.start_x:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_START_X
default: 5
append: px
size: small
validate:
min: 0
type: number
basic_captcha.chars.start_y:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_START_Y
default: 30
append: px
size: small
validate:
min: 0
type: number
basic_captcha.chars.box_width:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_WIDTH
default: 135
append: px
size: small
validate:
min: 0
type: number
basic_captcha.chars.box_height:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_HEIGHT
default: 40
append: px
size: small
validate:
min: 0
type: number
math:
type: element
fields:
basic_captcha.math.min:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MIN
default: 1
size: small
validate:
min: 0
type: number
basic_captcha.math.max:
type: number
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MAX
default: 10
size: small
validate:
min: 1
type: number
basic_captcha.math.operators:
type: selectize
selectize:
options:
- value: '+'
text: '+ Addition'
- value: '-'
text: '- Subtraction'
- value: '*'
text: 'x Multiplication'
- value: '/'
text: '/ Division'
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_OPERATORS
validate:
type: commalist

View File

@ -32,20 +32,20 @@ class BasicCaptcha
// calculator // calculator
if ($operator === '-') { if ($operator === '-') {
if ($first_num < $second_num) { if ($first_num < $second_num) {
$result = "$second_num-$first_num"; $result = "$second_num - $first_num";
$captcha_code = $second_num-$first_num; $captcha_code = $second_num - $first_num;
} else { } else {
$result = "$first_num-$second_num"; $result = "$first_num-$second_num";
$captcha_code = $first_num - $second_num; $captcha_code = $first_num - $second_num;
} }
} elseif ($operator === '*') { } elseif ($operator === '*') {
$result = "{$first_num}x{$second_num}"; $result = "{$first_num} x {$second_num}";
$captcha_code = $first_num - $second_num; $captcha_code = $first_num * $second_num;
} elseif ($operator === '/') { } elseif ($operator === '/') {
$result = "$first_num/ second_num"; $result = "$first_num / second_num";
$captcha_code = $first_num / $second_num; $captcha_code = $first_num / $second_num;
} elseif ($operator === '+') { } elseif ($operator === '+') {
$result = "$first_num+$second_num"; $result = "$first_num + $second_num";
$captcha_code = $first_num + $second_num; $captcha_code = $first_num + $second_num;
} }
} else { } else {

View File

@ -119,7 +119,7 @@ class Form implements FormInterface, ArrayAccess
$this->items = $form; $this->items = $form;
} else { } else {
// Otherwise get all forms in the page. // Otherwise get all forms in the page.
$forms = $page->forms(); $forms = $page->getForms();
if ($name) { if ($name) {
// If form with given name was found, use that. // If form with given name was found, use that.
$this->items = $forms[$name] ?? []; $this->items = $forms[$name] ?? [];

View File

@ -113,7 +113,7 @@ class Forms
*/ */
protected function getPageParameters(PageInterface $page, ?string $name): array protected function getPageParameters(PageInterface $page, ?string $name): array
{ {
$forms = $page->forms(); $forms = $page->getForms();
if ($name) { if ($name) {
// If form with given name was found, use that. // If form with given name was found, use that.

View File

@ -90,21 +90,28 @@ class TwigExtension extends AbstractExtension
return null; return null;
} }
// If field has already been prepared, we do not need to do anything.
if (!empty($field['prepared'])) {
return $field;
}
// Check if we have just a list of fields (no name given). // Check if we have just a list of fields (no name given).
if (is_int($name)) { $fieldName = (string)($field['name'] ?? $name);
if (!is_string($name) || $name === '') {
// Look at the field.name and if not set, fall back to the key. // Look at the field.name and if not set, fall back to the key.
$name = (string)($field['name'] ?? $name); $name = $fieldName;
} }
// Make sure that the field has a name. // Make sure that the field has a name.
$name = $name ?? $field['name'] ?? null; if ($name === '') {
if (!is_string($name) || $name === '') {
return null; return null;
} }
// Prefix name with the parent name if needed. // Prefix name with the parent name if needed.
if (str_starts_with($name, '.')) { if (str_starts_with($name, '.')) {
$name = $parent ? $parent . $name : (string)substr($name, 1); $plainName = (string)substr($name, 1);
$field['plain_name'] = $plainName;
$name = $parent ? $parent . $name : $plainName;
} elseif (isset($options['key'])) { } elseif (isset($options['key'])) {
$name = str_replace('*', $options['key'], $name); $name = str_replace('*', $options['key'], $name);
} }
@ -125,6 +132,7 @@ class TwigExtension extends AbstractExtension
// Always set field name. // Always set field name.
$field['name'] = $name; $field['name'] = $name;
$field['prepared'] = true;
return $field; return $field;
} }

View File

@ -73,5 +73,5 @@
"platform-overrides": { "platform-overrides": {
"php": "7.3.6" "php": "7.3.6"
}, },
"plugin-api-version": "2.0.0" "plugin-api-version": "2.2.0"
} }

View File

@ -4,6 +4,7 @@ namespace Grav\Plugin;
use Composer\Autoload\ClassLoader; use Composer\Autoload\ClassLoader;
use DateTime; use DateTime;
use Doctrine\Common\Cache\Cache;
use Exception; use Exception;
use Grav\Common\Data\ValidationException; use Grav\Common\Data\ValidationException;
use Grav\Common\Debugger; use Grav\Common\Debugger;
@ -18,10 +19,13 @@ use Grav\Common\Utils;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\Yaml; use Grav\Common\Yaml;
use Grav\Framework\Form\Interfaces\FormInterface; use Grav\Framework\Form\Interfaces\FormInterface;
use Grav\Framework\Psr7\Response;
use Grav\Framework\Route\Route; use Grav\Framework\Route\Route;
use Grav\Plugin\Form\BasicCaptcha;
use Grav\Plugin\Form\Form; use Grav\Plugin\Form\Form;
use Grav\Plugin\Form\Forms; use Grav\Plugin\Form\Forms;
use Grav\Plugin\Form\TwigExtension; use Grav\Plugin\Form\TwigExtension;
use Grav\Common\HTTP\Client;
use ReCaptcha\ReCaptcha; use ReCaptcha\ReCaptcha;
use ReCaptcha\RequestMethod\CurlPost; use ReCaptcha\RequestMethod\CurlPost;
use RecursiveArrayIterator; use RecursiveArrayIterator;
@ -31,6 +35,7 @@ use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\Event\Event;
use RuntimeException; use RuntimeException;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Twig\Environment; use Twig\Environment;
use Twig\Extension\CoreExtension; use Twig\Extension\CoreExtension;
use Twig\Extension\EscaperExtension; use Twig\Extension\EscaperExtension;
@ -54,11 +59,9 @@ class FormPlugin extends Plugin
/** @var Form */ /** @var Form */
protected $form; protected $form;
/** @var array */ /** @var array[]|FormInterface[] */
protected $forms = []; protected $forms = [];
/** @var array */ /** @var FormInterface[] */
protected $flat_forms = [];
/** @var array */
protected $active_forms = []; protected $active_forms = [];
/** @var array */ /** @var array */
protected $json_response = []; protected $json_response = [];
@ -70,7 +73,7 @@ class FormPlugin extends Plugin
*/ */
public static function checkRequirements(): bool public static function checkRequirements(): bool
{ {
return version_compare(GRAV_VERSION, '1.6', '>'); return version_compare(GRAV_VERSION, '1.7', '>');
} }
/** /**
@ -83,18 +86,13 @@ class FormPlugin extends Plugin
} }
return [ return [
'onPluginsInitialized' => [ 'onPluginsInitialized' => ['onPluginsInitialized', 0],
['autoload', 100000],
['onPluginsInitialized', 0]
],
'onTwigExtensions' => ['onTwigExtensions', 0], 'onTwigExtensions' => ['onTwigExtensions', 0],
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0] 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0]
]; ];
} }
/** /**
* [onPluginsInitialized:100000] Composer autoload.
*
* @return ClassLoader * @return ClassLoader
*/ */
public function autoload() public function autoload()
@ -114,10 +112,9 @@ class FormPlugin extends Plugin
$this->grav['forms'] = function () { $this->grav['forms'] = function () {
$forms = new Forms(); $forms = new Forms();
$grav = Grav::instance();
$event = new Event(['forms' => $forms]); $event = new Event(['forms' => $forms]);
$grav->fireEvent('onFormRegisterTypes', $event);
$this->grav->fireEvent('onFormRegisterTypes', $event);
return $forms; return $forms;
}; };
@ -130,12 +127,19 @@ class FormPlugin extends Plugin
return; return;
} }
/** @var Uri $uri */
$uri = $this->grav['uri'];
// Mini Keep-Alive Logic // Mini Keep-Alive Logic
$task = $this->grav['uri']->param('task'); $task = $uri->param('task');
if ($task && $task === 'keep-alive') { if ($task === 'keep-alive') {
exit; $response = new Response(200);
$this->grav->close($response);
} }
$this->processBasicCaptchaImage($uri);
$this->enable([ $this->enable([
'onPageProcessed' => ['onPageProcessed', 0], 'onPageProcessed' => ['onPageProcessed', 0],
'onPagesInitialized' => ['onPagesInitialized', 0], 'onPagesInitialized' => ['onPagesInitialized', 0],
@ -161,16 +165,16 @@ class FormPlugin extends Plugin
/** /**
* Process forms after page header processing, but before caching * Process forms after page header processing, but before caching
* *
* @param Event $e * @param Event $event
* @return void * @return void
*/ */
public function onPageProcessed(Event $e): void public function onPageProcessed(Event $event): void
{ {
/** @var PageInterface $page */ /** @var PageInterface $page */
$page = $e['page']; $page = $event['page'];
$pageForms = $page->forms(); $forms = $page->getForms();
if (!$pageForms) { if (!$forms) {
return; return;
} }
@ -184,23 +188,18 @@ class FormPlugin extends Plugin
} }
$parent = $current && $current !== $page ? $current : null; $parent = $current && $current !== $page ? $current : null;
$page_route = $page->home() ? '/' : $page->route();
// If the form was in the modular page, we need to add the form into the parent page as well. // If the form was in the modular page, we need to add the form into the parent page as well.
if ($parent) { if ($parent) {
$parent->addForms($pageForms); $parent->addForms($forms);
$parent_route = $parent->home() ? '/' : $parent->route();
} }
/** @var Forms $forms */
$forms = $this->grav['forms'];
// Store the page forms in the forms instance // Store the page forms in the forms instance
foreach ($pageForms as $name => $form) { foreach ($forms as $name => $form) {
if (isset($parent, $parent_route)) { if ($parent) {
$this->addForm($parent_route, $forms->createPageForm($parent, $name, $form)); $this->addFormDefinition($parent, $name, $form);
} }
$this->addForm($page_route, $forms->createPageForm($page, $name, $form));
$this->addFormDefinition($page, $name, $form);
} }
} }
@ -257,7 +256,7 @@ class FormPlugin extends Plugin
if ($form instanceof Form) { if ($form instanceof Form) {
// Post the form // Post the form
$isJson = $uri->extension() === 'json'; $isJson = $uri->extension() === 'json';
$task = $uri->post('task') ?? $uri->param('task'); $task = (string)($uri->post('task') ?? $uri->param('task'));
if ($isJson) { if ($isJson) {
if ($task === 'store-state') { if ($task === 'store-state') {
@ -286,10 +285,13 @@ class FormPlugin extends Plugin
if ($this->json_response && $page->template() !== 'form') { if ($this->json_response && $page->template() !== 'form') {
$status = $this->json_response['status'] ?? null; $status = $this->json_response['status'] ?? null;
header('Content-Type: application/json'); $response = new Response(
http_response_code($status === 'error' ? 400 : 200); $status !== 'error' ? 200 : 400,
echo json_encode($this->json_response); ['Content-Type' => 'application/json'],
exit; json_encode($this->json_response, JSON_THROW_ON_ERROR)
);
$this->grav->close($response);
} }
} }
@ -313,11 +315,25 @@ class FormPlugin extends Plugin
// There is no active form to be posted. // There is no active form to be posted.
// Check all the forms for the current page; we are looking for forms with remember state turned on with random unique id. // Check all the forms for the current page; we are looking for forms with remember state turned on with random unique id.
/** @var Forms $forms */
$forms = $this->grav['forms'];
/** @var Route $route */ /** @var Route $route */
$route = $this->grav['route']; $route = $this->grav['route'];
$pageForms = $this->forms[$route->getRoute()] ?? []; $pageForms = $this->forms[$route->getRoute()] ?? [];
foreach ($pageForms as $formName => $form) { /**
* @var string $name
* @var array|FormInterface $form
*/
foreach ($pageForms as $name => $form) {
if (is_array($form)) {
$form = $this->createForm($page, $name, $form);
}
if (!$form instanceof FormInterface) {
continue;
}
if ($form->get('remember_redirect')) { if ($form->get('remember_redirect')) {
// Found one; we need to check if unique id is set. // Found one; we need to check if unique id is set.
$formParam = $form->get('uniqueid_param', 'fid'); $formParam = $form->get('uniqueid_param', 'fid');
@ -328,8 +344,6 @@ class FormPlugin extends Plugin
$form->setUniqueId($uniqueId); $form->setUniqueId($uniqueId);
$form->initialize(); $form->initialize();
/** @var Forms $forms */
$forms = $this->grav['forms'];
$forms->setActiveForm($form); $forms->setActiveForm($form);
break; break;
@ -337,7 +351,7 @@ class FormPlugin extends Plugin
// Append unique id to the URL and redirect. // Append unique id to the URL and redirect.
$route = $route->withGravParam($formParam, $form->getUniqueId()); $route = $route->withGravParam($formParam, $form->getUniqueId());
$page->redirect((string)$route->toString()); $page->redirect($route->toString());
// TODO: Do we want to add support for multiple forms with remembered state? // TODO: Do we want to add support for multiple forms with remembered state?
break; break;
@ -398,7 +412,7 @@ class FormPlugin extends Plugin
/** /**
* Make form accessible from twig. * Make form accessible from twig.
* *
* @param Event $event * @param Event|null $event
* @return void * @return void
*/ */
public function onTwigVariables(Event $event = null): void public function onTwigVariables(Event $event = null): void
@ -428,6 +442,7 @@ class FormPlugin extends Plugin
* @param Event $event * @param Event $event
* @return void * @return void
* @throws Exception * @throws Exception
* @throws TransportExceptionInterface
*/ */
public function onFormProcessed(Event $event): void public function onFormProcessed(Event $event): void
{ {
@ -499,6 +514,55 @@ class FormPlugin extends Plugin
return; return;
} }
break;
case 'basic-captcha':
$captcha = new BasicCaptcha();
$captcha_value = trim($form->value('basic-captcha'));
if (!$captcha->validateCaptcha($captcha_value)) {
$message = $params['message'] ?? $this->grav['language']->translate('PLUGIN_FORM.ERROR_BASIC_CAPTCHA');
$this->grav->fireEvent('onFormValidationError', new Event([
'form' => $form,
'message' => $message
]));
$event->stopPropagation();
return;
}
break;
case 'turnstile':
/** @var Uri $uri */
$uri = $this->grav['uri'];
$turnstile_config = $this->config->get('plugins.form.turnstile');
$secret = $turnstile_config['secret_key'] ?? null;
$token = $form->getValue('cf-turnstile-response') ?? null;
$ip = Uri::ip();
$client = Client::getClient();
$response = $client->request('POST', 'https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'body' => [
'secret' => $secret,
'response' => $token,
'remoteip' => $ip
]
]);
$content = $response->toArray();
if (!$content['success']) {
$message = $params['message'] ?? $this->grav['language']->translate('PLUGIN_FORM.ERROR_BASIC_CAPTCHA');
$this->grav->fireEvent('onFormValidationError', new Event([
'form' => $form,
'message' => $message
]));
$this->grav['log']->addWarning('Form Turnstile invalid: [' . $uri->route() . '] ' . json_encode($content));
$event->stopPropagation();
return;
}
break; break;
case 'timestamp': case 'timestamp':
$label = $params['label'] ?? 'Timestamp'; $label = $params['label'] ?? 'Timestamp';
@ -544,8 +608,7 @@ class FormPlugin extends Plugin
$this->grav['messages']->add($form->message, 'success'); $this->grav['messages']->add($form->message, 'success');
} }
$event['redirect'] = $url; $this->grav->redirect($url);
$event->stopPropagation();
break; break;
case 'reset': case 'reset':
if (Utils::isPositive($params)) { if (Utils::isPositive($params)) {
@ -736,6 +799,7 @@ class FormPlugin extends Plugin
*/ */
public function onFormValidationError(Event $event): void public function onFormValidationError(Event $event): void
{ {
/** @var FormInterface $form */
$form = $event['form']; $form = $event['form'];
if (isset($event['message'])) { if (isset($event['message'])) {
$form->status = 'error'; $form->status = 'error';
@ -743,6 +807,7 @@ class FormPlugin extends Plugin
$form->messages = $event['messages']; $form->messages = $event['messages'];
} }
/** @var Uri $uri */
$uri = $this->grav['uri']; $uri = $this->grav['uri'];
$route = $uri->route(); $route = $uri->route();
@ -752,8 +817,7 @@ class FormPlugin extends Plugin
/** @var Pages $pages */ /** @var Pages $pages */
$pages = $this->grav['pages']; $pages = $this->grav['pages'];
$page = $pages->dispatch($route, true); $page = $pages->find($route, true);
if ($page) { if ($page) {
unset($this->grav['page']); unset($this->grav['page']);
$this->grav['page'] = $page; $this->grav['page'] = $page;
@ -762,14 +826,32 @@ class FormPlugin extends Plugin
$event->stopPropagation(); $event->stopPropagation();
} }
/**
* Add a form definition to the forms plugin
*
* @param PageInterface $page
* @return void
*/
public function addFormDefinition(PageInterface $page, string $name, array $form): void
{
$route = ($page->home() ? '/' : $page->route()) ?? '/';
if (!isset($this->forms[$route][$name])) {
$form['_page_routable'] = !$page->isModule();
$this->forms[$route][$name] = $form;
$this->recache_forms = true;
}
}
/** /**
* Add a form to the forms plugin * Add a form to the forms plugin
* *
* @param string|null $page_route * @param string|null $route
* @param FormInterface|null $form * @param FormInterface|null $form
* @return void * @return void
*/ */
public function addForm(?string $page_route, ?FormInterface $form) public function addForm(?string $route, ?FormInterface $form): void
{ {
if (null === $form) { if (null === $form) {
return; return;
@ -777,10 +859,10 @@ class FormPlugin extends Plugin
$name = $form->getName(); $name = $form->getName();
if (!isset($this->forms[$page_route][$name])) { if (!isset($this->forms[$route][$name])) {
$this->forms[$page_route][$name] = $form; $form['_page_routable'] = true;
$this->flattenForms(); $this->forms[$route][$name] = $form;
$this->recache_forms = true; $this->recache_forms = true;
} }
} }
@ -788,45 +870,128 @@ class FormPlugin extends Plugin
/** /**
* function to get a specific form * function to get a specific form
* *
* @param null|array|string $data optional form `name` * @param string|array|null $data Optional form name or ['name' => $name, 'route' => $route]
* @return FormInterface|null * @return FormInterface|null
*/ */
public function getForm($data = null) public function getForm($data = null): ?FormInterface
{ {
/** @var Pages $pages */
$pages = $this->grav['pages'];
// Handle parameters.
if (is_array($data)) { if (is_array($data)) {
$form_name = $data['name'] ?? null; $name = (string)($data['name'] ?? '');
$page_route = $data['route'] ?? null; $route = (string)($data['route'] ?? '');
} elseif (is_string($data)) { } elseif (is_string($data)) {
$form_name = $data; $name = $data;
$page_route = null; $route = '';
} else { } else {
$form_name = null; $name = '';
$page_route = null; $route = '';
} }
// if no form name, use the first form found in the page // Return always the same form instance.
if (!$form_name) { $form = $this->active_forms[$route][$name] ?? null;
// If page route not provided, use the current page if ($form) {
if (!$page_route) { return $form;
// Get page route with a fallback using current URI if page not initialized yet
$page_route = $this->grav['page']->route() ?: $this->getCurrentPageRoute();
}
if (!empty($this->forms[$page_route])) {
$forms = $this->forms[$page_route];
$first_form = reset($forms) ?: null;
return $first_form;
}
// Try to get page by defined route first or get current if not found
$page = $this->grav['pages']->find($page_route) ?: $this->grav['page'];
// Try looking up in the defined page
return $this->grav['forms']->createPageForm($page);
} }
// return the form you are looking for if available $unnamed = $name === '';
return $this->getFormByName($form_name); $routed = $route !== '';
// Get the page.
if ($routed) {
// Use fixed route for the form.
$route_provided = true;
$page = $pages->find($route, true);
} else {
// Search form from the current page first.
$route_provided = false;
/** @var PageInterface|null $page */
$page = $this->grav['page'] ?? null;
if ($page) {
$route = $page->route();
} else {
// Get page route with a fallback using current URI if page is not yet initialized.
$route = $this->getCurrentPageRoute();
$page = $pages->find($route);
}
}
// Attempt to find the form from the page.
if ('' !== $route) {
$forms = $this->forms[$route] ?? [];
if (!$unnamed) {
// Get form by the name.
$form = $forms[$name] ?? null;
} else {
// Get the first form.
$form = reset($forms) ?: null;
$name = key($forms);
}
}
// Search the form from the other pages.
if (null === $form) {
// First check if we requested a specific form which didn't exist.
if ($route_provided || $unnamed) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addMessage(sprintf('Form %s not found in page %s', $name ?? 'unnamed', $route), 'warning');
return null;
}
// Attempt to find any form with given name.
$forms = $this->findFormByName($name);
$first = reset($forms);
if (!$first) {
return null;
}
// Check for naming conflicts.
if (count($forms) > 1) {
$debugger = $this->grav['debugger'];
$debugger->addMessage(sprintf('Fetching form by its name, but there are multiple pages with the same form name %s', $name), 'warning');
}
[$route, $name, $form] = $first;
$page = $pages->find($route);
}
// Form can be saved as an array or an object. If it's an array, we need to create object from it.
if (is_array($form)) {
// Form was cached as an array, try to create the object.
if (null === $page) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addMessage(sprintf('Form %s cannot be created as page %s does not exist', $name, $route), 'warning');
return null;
}
$form = $this->createForm($page, $name, $form);
}
// Register form to the active forms to get the same instance back next time.
$this->active_forms[$route][$name] = $form;
if ($unnamed) {
$this->active_forms[$route][''] = $form;
}
// Also make aliases if route was not provided to the method.
if (!$routed) {
$this->active_forms[''][$name] = $form;
if ($unnamed) {
$this->active_forms[''][''] = $form;
}
}
return $form;
} }
/** /**
@ -834,7 +999,7 @@ class FormPlugin extends Plugin
* *
* @return array * @return array
*/ */
public function getFormFieldTypes() public function getFormFieldTypes(): array
{ {
return [ return [
'avatar' => [ 'avatar' => [
@ -906,7 +1071,7 @@ class FormPlugin extends Plugin
* *
* - fillWithCurrentDateTime * - fillWithCurrentDateTime
* *
* @param Form $form * @param FormInterface $form
* @return void * @return void
*/ */
protected function process($form) protected function process($form)
@ -921,43 +1086,33 @@ class FormPlugin extends Plugin
/** /**
* Get current page's route * Get current page's route
* *
* @return mixed * @return string
*/ */
protected function getCurrentPageRoute() protected function getCurrentPageRoute()
{ {
$path = $this->grav['uri']->route(); $path = $this->grav['uri']->route();
$path = $path ?: '/';
return $path; return $path ?: '/';
} }
/** /**
* Retrieve a form based on the form name * Return all forms matching the given name.
* *
* @param string $form_name * @param string $name
* @param string $unique_id * @return array
* @return mixed
*/ */
protected function getFormByName($form_name, $unique_id = '') protected function findFormByName(string $name): array
{ {
$form = $this->active_forms[$form_name] ?? null; $list = [];
if (!$form) { foreach ($this->forms as $route => $forms) {
$form = $this->flat_forms[$form_name] ?? null; foreach ($forms as $key => $form) {
if ($name === $key && !empty($form['_page_routable'])) {
if (!$form) { $list[] = [$route, $key, $form];
return null; }
} }
if ('' === $unique_id) {
// Reset form to change the cached unique id and to fire onFormInitialized event.
$form->setUniqueId('');
$form->reset();
}
// Register form to the active forms to get the same instance back next time.
$this->active_forms[$form_name] = $form;
} }
return $form; return $list;
} }
/** /**
@ -965,12 +1120,11 @@ class FormPlugin extends Plugin
* *
* @return bool * @return bool
*/ */
protected function shouldProcessForm() protected function shouldProcessForm(): bool
{ {
/** @var Uri $uri */
$uri = $this->grav['uri']; $uri = $this->grav['uri'];
$nonce = $uri->post('form-nonce'); $status = (bool)$uri->post('form-nonce');
$status = $nonce ? true : false; // php72 quirk?
$refresh_prevention = null;
if ($status && $form = $this->form()) { if ($status && $form = $this->form()) {
// Make sure form is something we recognize. // Make sure form is something we recognize.
@ -1009,29 +1163,14 @@ class FormPlugin extends Plugin
return $status; return $status;
} }
/**
* Flatten the forms array into something that can be more easily searched
*
* @return void
*/
protected function flattenForms()
{
$this->flat_forms = Utils::arrayFlatten($this->forms);
}
/** /**
* Get the current form, should already be processed but can get it directly from the page if necessary * Get the current form, should already be processed but can get it directly from the page if necessary
* *
* @param PageInterface|null $page * @param PageInterface|null $page
* @return Form|null * @return FormInterface|null
*/ */
protected function form(PageInterface $page = null) protected function form(PageInterface $page = null)
{ {
// Regenerate list of flat_forms if not already populated
if (empty($this->flat_forms)) {
$this->flattenForms();
}
/** @var Forms $forms */ /** @var Forms $forms */
$forms = $this->grav['forms']; $forms = $this->grav['forms'];
@ -1050,11 +1189,16 @@ class FormPlugin extends Plugin
$form_name = $page ? $page->slug() : null; $form_name = $page ? $page->slug() : null;
} }
$form = $this->getFormByName($form_name, $unique_id); $form = $form_name ? $this->getForm($form_name) : null;
if ($form && '' === $unique_id) {
// Reset form to change the cached unique id and to fire onFormInitialized event.
$form->setUniqueId('');
$form->reset();
}
// last attempt using current page's form // last attempt using current page's form
if (!$form && $page) { if (!$form && $page) {
$form = $forms->createPageForm($page); $form = $this->createForm($page);
} }
if ($form) { if ($form) {
@ -1073,20 +1217,16 @@ class FormPlugin extends Plugin
/** /**
* @param PageInterface $page * @param PageInterface $page
* @param string|int|null $name * @param string|null $name
* @param array $form * @param array|null $form
* @return Form|null * @return FormInterface|null
* @deprecated
*/ */
protected function createForm(PageInterface $page, $name = null, $form = null) protected function createForm(PageInterface $page, string $name = null, array $form = null): ?FormInterface
{ {
/** @var Forms $forms */
$forms = $this->grav['forms'];
$header = $page->header(); return $forms->createPageForm($page, $name, $form);
if (isset($header->form) || isset($header->forms)) {
return new Form($page, $name, $form);
}
return null;
} }
/** /**
@ -1094,18 +1234,20 @@ class FormPlugin extends Plugin
* *
* @return void * @return void
*/ */
protected function loadCachedForms() protected function loadCachedForms(): void
{ {
// Get and set the cache of forms if it exists // Get and set the cache of forms if it exists
try { try {
[$forms] = $this->grav['cache']->fetch($this->getFormCacheId()); /** @var Cache $cache */
} catch (Exception $e) { $cache = $this->grav['cache'];
// Couldn't fetch cached forms.
$forms = null;
[$forms] = $cache->fetch($this->getFormCacheId());
} catch (Exception $e) {
/** @var Debugger $debugger */ /** @var Debugger $debugger */
$debugger = Grav::instance()['debugger']; $debugger = Grav::instance()['debugger'];
$debugger->addMessage(sprintf('Unserializing cached forms failed: %s', $e->getMessage()), 'error'); $debugger->addMessage(sprintf('Unserializing cached forms failed: %s', $e->getMessage()), 'error');
$forms = null;
} }
if (!is_array($forms)) { if (!is_array($forms)) {
@ -1113,9 +1255,8 @@ class FormPlugin extends Plugin
} }
// Only update the forms if it's not empty // Only update the forms if it's not empty
if (!empty($forms)) { if ($forms) {
$this->forms = array_merge($this->forms, $forms); $this->forms = array_merge($this->forms, $forms);
$this->flattenForms();
} }
} }
@ -1124,13 +1265,19 @@ class FormPlugin extends Plugin
* *
* @return void * @return void
*/ */
protected function saveCachedForms() protected function saveCachedForms(): void
{ {
// Save the current state of the forms to cache // Save the current state of the forms to cache
if ($this->recache_forms) { if (!$this->recache_forms) {
$this->recache_forms = false; return;
$this->grav['cache']->save($this->getFormCacheId(), [$this->forms]);
} }
$this->recache_forms = false;
/** @var Cache $cache */
$cache = $this->grav['cache'];
$cache->save($this->getFormCacheId(), [$this->forms]);
} }
/** /**
@ -1138,9 +1285,12 @@ class FormPlugin extends Plugin
* *
* @return string * @return string
*/ */
protected function getFormCacheId() protected function getFormCacheId(): string
{ {
return $this->grav['pages']->getPagesCacheId() . '-form-plugin'; /** @var Pages $pages */
$pages = $this->grav['pages'];
return $pages->getPagesCacheId() . '-form-plugin';
} }
/** /**
@ -1152,16 +1302,25 @@ class FormPlugin extends Plugin
*/ */
protected function udate($format = 'u', $raw = false) protected function udate($format = 'u', $raw = false)
{ {
$utimestamp = microtime(true);
if ($raw) { if ($raw) {
return date($format); return date($format);
} }
$utimestamp = microtime(true);
$timestamp = floor($utimestamp); $timestamp = floor($utimestamp);
$milliseconds = round(($utimestamp - $timestamp) * 1000000); $milliseconds = round(($utimestamp - $timestamp) * 1000000);
return date(preg_replace('`(?<!\\\\)u`', sprintf('%06d', $milliseconds), $format), $timestamp); return date(preg_replace('`(?<!\\\\)u`', sprintf('%06d', $milliseconds), $format), $timestamp);
} }
protected function processBasicCaptchaImage(Uri $uri)
{
if ($uri->path() === '/forms-basic-captcha-image.jpg') {
$captcha = new BasicCaptcha();
$code = $captcha->getCaptchaCode();
$image = $captcha->createCaptchaImage($code);
$captcha->renderCaptchaImage($image);
exit;
}
}
} }

View File

@ -17,4 +17,25 @@ recaptcha:
version: 2-checkbox version: 2-checkbox
theme: light theme: light
site_key: site_key:
secret_key: secret_key:
turnstile:
theme: light # options: [light | dark]
site_key:
secret_key:
basic_captcha:
type: characters # options: [characters | math]
chars:
length: 6 # number of chars to output
font: zxx-noise.ttf # options: [zxx-noise.ttf | zxx-camo.ttf | zxx-xed.ttf | zxx-sans.ttf]
bg: '#cccccc' # 6-char hex color
text: '#333333' # 6-char hex color
size: 24 # font size in px
start_x: 5 # start position in x direction in px
start_y: 30 # start position in y direction in px
box_width: 135 # box width in px
box_height: 40 # box height in px
math:
min: 1 # smallest digit
max: 12 # largest digit
operators: ['+','-','*'] # operators that can be used in math

View File

@ -10,6 +10,7 @@ en:
DESTINATION_HELP: "The location where the files should be uploaded to" DESTINATION_HELP: "The location where the files should be uploaded to"
ACCEPT: "Allowed MIME Types" ACCEPT: "Allowed MIME Types"
ACCEPT_HELP: "A list of MIME Types that are allowed for upload" ACCEPT_HELP: "A list of MIME Types that are allowed for upload"
ERROR_BASIC_CAPTCHA: "Captcha failed for this form, please try again"
ERROR_VALIDATING_CAPTCHA: "reCAPTCHA bot protection has identified this form submission is problematic" ERROR_VALIDATING_CAPTCHA: "reCAPTCHA bot protection has identified this form submission is problematic"
DATA_SUMMARY: "Here is the summary of what you wrote to us:" DATA_SUMMARY: "Here is the summary of what you wrote to us:"
NO_FORM_DATA: "No form data available" NO_FORM_DATA: "No form data available"
@ -72,6 +73,22 @@ en:
DESTINATION_NOT_SPECIFIED: "Destination not specified" DESTINATION_NOT_SPECIFIED: "Destination not specified"
INVALID_MIME_TYPE: "The MIME type %s for the file %s is not accepted." INVALID_MIME_TYPE: "The MIME type %s for the file %s is not accepted."
INVALID_FILE_EXTENSION: "The File Extension for the file %s is not accepted." INVALID_FILE_EXTENSION: "The File Extension for the file %s is not accepted."
BASIC_CAPTCHA: "Basic Captcha"
BASIC_CAPTCHA_TYPE: "Captcha challenge type"
BASIC_CAPTCHA_LENGTH: "Number of characters"
BASIC_CAPTCHA_FONT: "TTF Font"
BASIC_CAPTCHA_SIZE: "Font size"
BASIC_CAPTCHA_BG_COLOR: "Background color"
BASIC_CAPTCHA_TEXT_COLOR: "Text color"
BASIC_CAPTCHA_START_X: "Text start x-position"
BASIC_CAPTCHA_START_Y: "Text start y-position"
BASIC_CAPTCHA_BOX_WIDTH: "Image width"
BASIC_CAPTCHA_BOX_HEIGHT: "Image height"
BASIC_CAPTCHA_MATH_MIN: "Minimum number"
BASIC_CAPTCHA_MATH_MAX: "Maximum number"
BASIC_CAPTCHA_MATH_OPERATORS: "Mathematical operators (randomized)"
TURNSTILE_CAPTCHA: "Cloudflare Turnstile Captcha"
eu: eu:
PLUGIN_FORM: PLUGIN_FORM:
NOT_VALIDATED: "Formularioa ez da baliozkotu. Beharrezkoa den eremu bat edo gehiago falta dira." NOT_VALIDATED: "Formularioa ez da baliozkotu. Beharrezkoa den eremu bat edo gehiago falta dira."

View File

@ -1,4 +1,4 @@
$form-border-color: #eee; $form-border-color: #ccc;
$form-active-color: #000; $form-active-color: #000;
.form-group.has-errors { .form-group.has-errors {
@ -287,3 +287,29 @@ $form-active-color: #000;
display: inline-block; display: inline-block;
} }
.form-data.basic-captcha {
.form-input-wrapper {
border: 1px solid $form-border-color;
border-radius: 5px;
display: flex;
overflow: hidden;
}
.form-input-prepend {
display: flex;
color: #333;
background-color: #ccc;
flex-shrink: 0;
img {
margin: 0;
}
button > svg {
margin: 0 8px;
width: 18px;
height: 18px;
}
}
input.form-input {
border: 0;
}
}

View File

@ -10,7 +10,7 @@
{% set default = field.default %} {% set default = field.default %}
{% set toggleable = field.toggleable ?? false %} {% set toggleable = field.toggleable ?? false %}
{% if toggleable %} {% if toggleable %}
{% set originalValue = originalValue is defined ? originalValue : value %} {% set originalValue = originalValue ?? value %}
{% set toggleableChecked = originalValue is not null %} {% set toggleableChecked = originalValue is not null %}
{% elseif field.overridable %} {% elseif field.overridable %}
{% set toggleable = true %} {% set toggleable = true %}

View File

@ -1,10 +1,10 @@
{% set fields = prepare_form_fields(fields, name) %} {% set fields = prepare_form_fields(fields, name) %}
{% set originalValue = null %}
{% if fields|length %} {% if fields|length %}
{% block outer_markup_field_open %}{% endblock %} {% block outer_markup_field_open %}{% endblock %}
{% for field_name, field in fields %} {% for field_name, field in fields %}
{% set value = form ? form.value(field.name) : data.value(field.name) %} {% set value = form ? form.value(field.name) : data.value(field.name) %}
{% set field_templates = include_form_field(field.type, field_layout, fallback_field ?? 'text') %} {% set field_templates = include_form_field(field.type, field_layout, fallback_field ?? 'text') %}
{% block inner_markup_field_open %}{% endblock %} {% block inner_markup_field_open %}{% endblock %}
{% block field %} {% block field %}
{% include field_templates %} {% include field_templates %}

View File

@ -145,6 +145,7 @@
{% if form.isEnabled() ?? true %} {% if form.isEnabled() ?? true %}
{% for button in form.buttons %} {% for button in form.buttons %}
{% if not button.access or authorize(button.access) %}
{% if button.outerclasses is defined %}<div class="{{ button.outerclasses }}">{% endif %} {% if button.outerclasses is defined %}<div class="{{ button.outerclasses }}">{% endif %}
{% if button.url %} {% if button.url %}
@ -190,6 +191,7 @@
{% endembed %} {% endembed %}
{% if button.outerclasses is defined %}</div>{% endif %} {% if button.outerclasses is defined %}</div>{% endif %}
{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@ -5,7 +5,7 @@
{% block prepend %} {% block prepend %}
<div class="form-input-addon form-input-prepend"> <div class="form-input-addon form-input-prepend">
<img id="basic-captcha-reload" src="{{ url('/forms-basic-captcha-image.jpg') }}" alt="human test" /> <img id="basic-captcha-reload" src="{{ url('/forms-basic-captcha-image.jpg') }}" alt="human test" />
<button id="reload-captcha"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="#292D32"><path d="M14.74 22.39c4.68-1.24 8-5.49 8-10.4 0-5.95-4.79-10.75-10.75-10.75 -3.11 0-5.78 1.11-7.99 2.95 -.77.64-1.43 1.32-1.98 2.01 -.34.41-.57.75-.69.95 -.22.35-.1.81.25 1.02 .35.21.81.09 1.02-.26 .08-.15.27-.43.56-.79 .49-.62 1.08-1.23 1.76-1.81C6.87 3.67 9.21 2.7 11.94 2.7c5.13 0 9.25 4.12 9.25 9.25 0 4.22-2.86 7.88-6.9 8.94 -.41.1-.64.51-.54.91 .1.4.51.63.91.53Zm-12-14.84V2.99c-.001-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75v4.56c0 .41.33.75.75.75 .41 0 .75-.34.75-.75Zm-.75.75H4h2.43c.41 0 .75-.34.75-.75 0-.42-.34-.75-.75-.75H4 1.99c-.42 0-.75.33-.75.75 0 .41.33.75.75.75Z"/><path d="M1.25 12c0 1.09.16 2.16.48 3.18 .12.39.54.61.93.49 .39-.13.61-.55.49-.94 -.28-.89-.42-1.81-.42-2.75 0-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75Zm1.93 6.15c.61.88 1.36 1.67 2.22 2.33 .32.25.79.19 1.05-.14 .25-.33.19-.8-.14-1.06 -.74-.58-1.38-1.25-1.92-2.02 -.24-.34-.71-.43-1.05-.19 -.34.23-.43.7-.19 1.04Zm5.02 3.91c1 .37 2.06.6 3.15.66 .41.02.76-.3.79-.71 .02-.42-.3-.77-.71-.8 -.94-.06-1.85-.25-2.72-.58 -.39-.15-.83.04-.97.43 -.15.38.04.82.43.96Z"/></g></svg></button> <button id="reload-captcha"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="currentColor"><path d="M14.74 22.39c4.68-1.24 8-5.49 8-10.4 0-5.95-4.79-10.75-10.75-10.75 -3.11 0-5.78 1.11-7.99 2.95 -.77.64-1.43 1.32-1.98 2.01 -.34.41-.57.75-.69.95 -.22.35-.1.81.25 1.02 .35.21.81.09 1.02-.26 .08-.15.27-.43.56-.79 .49-.62 1.08-1.23 1.76-1.81C6.87 3.67 9.21 2.7 11.94 2.7c5.13 0 9.25 4.12 9.25 9.25 0 4.22-2.86 7.88-6.9 8.94 -.41.1-.64.51-.54.91 .1.4.51.63.91.53Zm-12-14.84V2.99c-.001-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75v4.56c0 .41.33.75.75.75 .41 0 .75-.34.75-.75Zm-.75.75H4h2.43c.41 0 .75-.34.75-.75 0-.42-.34-.75-.75-.75H4 1.99c-.42 0-.75.33-.75.75 0 .41.33.75.75.75Z"/><path d="M1.25 12c0 1.09.16 2.16.48 3.18 .12.39.54.61.93.49 .39-.13.61-.55.49-.94 -.28-.89-.42-1.81-.42-2.75 0-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75Zm1.93 6.15c.61.88 1.36 1.67 2.22 2.33 .32.25.79.19 1.05-.14 .25-.33.19-.8-.14-1.06 -.74-.58-1.38-1.25-1.92-2.02 -.24-.34-.71-.43-1.05-.19 -.34.23-.43.7-.19 1.04Zm5.02 3.91c1 .37 2.06.6 3.15.66 .41.02.76-.3.79-.71 .02-.42-.3-.77-.71-.8 -.94-.06-1.85-.25-2.72-.58 -.39-.15-.83.04-.97.43 -.15.38.04.82.43.96Z"/></g></svg></button>
<script> <script>
function stripQueryString(url) { function stripQueryString(url) {
return url.split("?")[0].split("#")[0]; return url.split("?")[0].split("#")[0];

View File

@ -1,11 +1,5 @@
{% extends "forms/field.html.twig" %} {% extends "forms/field.html.twig" %}
{% set originalValue = value %}
{% set value = (value is null ? field.default : value) %}
{% if field.use == 'keys' and field.default %}
{% set value = field.default|merge(value) %}
{% endif %}
{% block global_attributes %} {% block global_attributes %}
{{ parent() }} {{ parent() }}
data-grav-keys="{{ field.use == 'keys' ? 'true' : 'false' }}" data-grav-keys="{{ field.use == 'keys' ? 'true' : 'false' }}"
@ -13,6 +7,11 @@
{% endblock %} {% endblock %}
{% block input %} {% block input %}
{% set value = (value is null ? field.default : value) %}
{% if field.use == 'keys' and field.default %}
{% set value = field.default|merge(value) %}
{% endif %}
{% for key, text in field.options %} {% for key, text in field.options %}
{% set id = field.id|default(field.name)|hyphenize ~ '-' ~ key %} {% set id = field.id|default(field.name)|hyphenize ~ '-' ~ key %}

View File

@ -1,7 +1,7 @@
{% extends "forms/field.html.twig" %} {% extends "forms/field.html.twig" %}
{% block field %} {% block field %}
{% embed 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %} {% embed 'forms/default/fields.html.twig' with {name: name, fields: field.fields} %}
{% block outer_markup_field_open %}<div class="form-column {{ field.classes }}">{% endblock %} {% block outer_markup_field_open %}<div class="form-column {{ field.classes }}">{% endblock %}
{% block outer_markup_field_close %}</div>{% endblock %} {% block outer_markup_field_close %}</div>{% endblock %}
{% endembed %} {% endembed %}

View File

@ -2,6 +2,6 @@
{% block field %} {% block field %}
<div class="form-columns {{ field.classes }}"> <div class="form-columns {{ field.classes }}">
{% include 'forms/default/fields.html.twig' with {name: field.name, fields: field.fields} %} {% include 'forms/default/fields.html.twig' with {name: field.name|parent_field, fields: field.fields} %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,5 @@
{% extends "forms/field.html.twig" %} {% extends "forms/field.html.twig" %}
{% set originalValue = value %}
{% set value = (value is null ? field.default : value) %}
{% block input %} {% block input %}
{% for key, text in field.options %} {% for key, text in field.options %}
{% set id = field.id|default(field.name) ~ '-' ~ key %} {% set id = field.id|default(field.name) ~ '-' ~ key %}

View File

@ -28,6 +28,11 @@
data-{{ datakey }}="{{ datavalue|e('html_attr') }}" data-{{ datakey }}="{{ datavalue|e('html_attr') }}"
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if field.attributes %}
{% for key, value in field.attributes %}
{{ key }}="{{ value|e('html_attr') }}"
{% endfor %}
{% endif %}
> >
{% if field.placeholder %}<option value="" disabled selected>{{ field.placeholder|t|raw }}</option>{% endif %} {% if field.placeholder %}<option value="" disabled selected>{{ field.placeholder|t|raw }}</option>{% endif %}
@ -43,9 +48,9 @@
{% set akey = field.selectize and field.multiple ? item_value : key %} {% set akey = field.selectize and field.multiple ? item_value : key %}
{% set avalue = item_value.value|t %} {% set avalue = item_value.value|t %}
<option {{ item_value.disabled ? 'disabled="disabled"' : '' }} <option {{ item_value.disabled ? 'disabled="disabled"' : '' }}
{{ item_value.selected ? 'selected="selected"' : '' }} {{ item_value.selected or key == value ? 'selected="selected"' : '' }}
{{ item_value.label ? 'label=' ~ item_value.label : '' }} {{ item_value.label ? 'label=' ~ item_value.label : '' }}
value="{{ item_value.akey }}" value="{{ akey }}"
> >
{{ avalue|raw }} {{ avalue|raw }}
</option> </option>

View File

@ -1,6 +1,6 @@
{% extends "forms/field.html.twig" %} {% extends "forms/field.html.twig" %}
{% if grav.admin is not defined %} {% if not grav.admin %}
{% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %} {% do assets.addJs('plugin://form/assets/form.vendor.js', { 'group': 'bottom', 'loading': 'defer' }) %}
{% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %} {% do assets.addJs('plugin://form/assets/form.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
{% endif %} {% endif %}
@ -29,7 +29,7 @@
{% endfor %} {% endfor %}
{% else %} {% else %}
{% set tabsKey = form.name ~ '-' ~ fields|keys|join(':')|md5 %} {% set tabsKey = form.name ~ '-' ~ fields|keys|join(':')|md5 %}
{% set storedValue = grav.admin is defined ? get_cookie('grav-tabs-state')|default('{}')|json_decode : [] %} {% set storedValue = grav.admin ? get_cookie('grav-tabs-state')|default('{}')|json_decode : [] %}
{% set storedTab = attribute(storedValue, 'tab-' ~ tabsKey) %} {% set storedTab = attribute(storedValue, 'tab-' ~ tabsKey) %}
{% if storedTab is empty %} {% if storedTab is empty %}
{% set active = uri.params.tab ?? field.active ?? 1 %} {% set active = uri.params.tab ?? field.active ?? 1 %}

View File

@ -0,0 +1,15 @@
{% extends "forms/field.html.twig" %}
{% set config = grav.config %}
{% set site_key = field.turnstile_site_key ?? config.plugins.form.turnstile.site_key %}
{% set theme = field.theme ?? config.plugins.form.turnstile.theme ?? 'light' %}
{% block label %}{% endblock %}
{% block input %}
{% do assets.addJs('https://challenges.cloudflare.com/turnstile/v0/api.js', { defer: '', async: '' }) %}
<div class="turnstile">
<div class="cf-turnstile" data-sitekey="{{ site_key }}" data-theme="{{ theme }}"></div>
</div>
{% endblock %}

View File

@ -42,21 +42,75 @@ namespace Composer\Autoload;
*/ */
class ClassLoader class ClassLoader
{ {
/** @var ?string */
private $vendorDir;
// PSR-4 // PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array(); private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array(); private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array(); private $fallbackDirsPsr4 = array();
// PSR-0 // PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array(); private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array(); private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false; private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array(); private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false; private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array(); private $missingClasses = array();
/** @var ?string */
private $apcuPrefix; private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes() public function getPrefixes()
{ {
if (!empty($this->prefixesPsr0)) { if (!empty($this->prefixesPsr0)) {
@ -66,28 +120,47 @@ class ClassLoader
return array(); return array();
} }
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4() public function getPrefixesPsr4()
{ {
return $this->prefixDirsPsr4; return $this->prefixDirsPsr4;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs() public function getFallbackDirs()
{ {
return $this->fallbackDirsPsr0; return $this->fallbackDirsPsr0;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4() public function getFallbackDirsPsr4()
{ {
return $this->fallbackDirsPsr4; return $this->fallbackDirsPsr4;
} }
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap() public function getClassMap()
{ {
return $this->classMap; return $this->classMap;
} }
/** /**
* @param array $classMap Class to filename map * @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/ */
public function addClassMap(array $classMap) public function addClassMap(array $classMap)
{ {
@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either * Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix. * appending or prepending to the ones previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories * @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
*
* @return void
*/ */
public function add($prefix, $paths, $prepend = false) public function add($prefix, $paths, $prepend = false)
{ {
@ -147,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either * Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace. * appending or prepending to the ones previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function addPsr4($prefix, $paths, $prepend = false) public function addPsr4($prefix, $paths, $prepend = false)
{ {
@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, * Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix. * replacing any others previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories * @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/ */
public function set($prefix, $paths) public function set($prefix, $paths)
{ {
@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, * Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace. * replacing any others previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function setPsr4($prefix, $paths) public function setPsr4($prefix, $paths)
{ {
@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files. * Turns on searching the include path for class files.
* *
* @param bool $useIncludePath * @param bool $useIncludePath
*
* @return void
*/ */
public function setUseIncludePath($useIncludePath) public function setUseIncludePath($useIncludePath)
{ {
@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map. * that have not been registered with the class map.
* *
* @param bool $classMapAuthoritative * @param bool $classMapAuthoritative
*
* @return void
*/ */
public function setClassMapAuthoritative($classMapAuthoritative) public function setClassMapAuthoritative($classMapAuthoritative)
{ {
@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled. * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
* *
* @param string|null $apcuPrefix * @param string|null $apcuPrefix
*
* @return void
*/ */
public function setApcuPrefix($apcuPrefix) public function setApcuPrefix($apcuPrefix)
{ {
@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader. * Registers this instance as an autoloader.
* *
* @param bool $prepend Whether to prepend the autoloader or not * @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/ */
public function register($prepend = false) public function register($prepend = false)
{ {
spl_autoload_register(array($this, 'loadClass'), true, $prepend); spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
} }
/** /**
* Unregisters this instance as an autoloader. * Unregisters this instance as an autoloader.
*
* @return void
*/ */
public function unregister() public function unregister()
{ {
spl_autoload_unregister(array($this, 'loadClass')); spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
} }
/** /**
* Loads the given class or interface. * Loads the given class or interface.
* *
* @param string $class The name of the class * @param string $class The name of the class
* @return bool|null True if loaded, null otherwise * @return true|null True if loaded, null otherwise
*/ */
public function loadClass($class) public function loadClass($class)
{ {
@ -323,6 +429,8 @@ class ClassLoader
return true; return true;
} }
return null;
} }
/** /**
@ -367,6 +475,21 @@ class ClassLoader
return $file; return $file;
} }
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext) private function findFileWithExtension($class, $ext)
{ {
// PSR-4 lookup // PSR-4 lookup
@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include. * Scope isolated include.
* *
* Prevents access to $this/self from included files. * Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/ */
function includeFile($file) function includeFile($file)
{ {

View File

@ -1,228 +1,350 @@
<?php <?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer; namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser; use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions class InstalledVersions
{ {
private static $installed = array ( /**
'root' => * @var mixed[]|null
array ( * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
'pretty_version' => 'dev-develop', */
'version' => 'dev-develop', private static $installed;
'aliases' =>
array (
),
'reference' => 'd53d3124481c8a72f223c49a03e6fd9b1f5bd7ee',
'name' => 'getgrav/grav-plugin-form',
),
'versions' =>
array (
'getgrav/grav-plugin-form' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'aliases' =>
array (
),
'reference' => 'd53d3124481c8a72f223c49a03e6fd9b1f5bd7ee',
),
'google/recaptcha' =>
array (
'pretty_version' => '1.2.4',
'version' => '1.2.4.0',
'aliases' =>
array (
),
'reference' => '614f25a9038be4f3f2da7cbfd778dc5b357d2419',
),
),
);
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
public static function getInstalledPackages() foreach (self::getInstalled() as $installed) {
{ foreach ($installed['versions'] as $name => $package) {
return array_keys(self::$installed['versions']); if (isset($package['type']) && $package['type'] === $type) {
} $packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
public static function isInstalled($packageName) *
{ * This also returns true if the package name is provided or replaced by another package
return isset(self::$installed['versions'][$packageName]); *
} * @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
public static function satisfies(VersionParser $parser, $packageName, $constraint) /**
{ * Checks whether the given package satisfies a version constraint
$constraint = $parser->parseConstraints($constraint); *
$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
return $provided->matches($constraint); * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
} *
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
public static function getVersionRanges($packageName) return $provided->matches($constraint);
{ }
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); /**
} * Returns a version constraint representing all the range(s) which are installed for a given package
*
$ranges = array(); * It is easier to use this via isInstalled() with the $constraint argument if you need to check
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { * whether a given version of a package is installed, and not just whether it exists
$ranges[] = self::$installed['versions'][$packageName]['pretty_version']; *
} * @param string $packageName
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { * @return string Version constraint usable with composer/semver
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); */
} public static function getVersionRanges($packageName)
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); foreach (self::getInstalled() as $installed) {
} if (!isset($installed['versions'][$packageName])) {
if (array_key_exists('provided', self::$installed['versions'][$packageName])) { continue;
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); }
}
$ranges = array();
return implode(' || ', $ranges); if (isset($installed['versions'][$packageName]['pretty_version'])) {
} $ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
public static function getVersion($packageName) $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
{ }
if (!isset(self::$installed['versions'][$packageName])) { if (array_key_exists('provided', $installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
} }
if (!isset(self::$installed['versions'][$packageName]['version'])) { return implode(' || ', $ranges);
return null; }
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
return self::$installed['versions'][$packageName]['version']; }
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
public static function getPrettyVersion($packageName) {
{ foreach (self::getInstalled() as $installed) {
if (!isset(self::$installed['versions'][$packageName])) { if (!isset($installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); continue;
} }
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { if (!isset($installed['versions'][$packageName]['version'])) {
return null; return null;
} }
return self::$installed['versions'][$packageName]['pretty_version']; return $installed['versions'][$packageName]['version'];
} }
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
public static function getReference($packageName) * @param string $packageName
{ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
if (!isset(self::$installed['versions'][$packageName])) { */
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); public static function getPrettyVersion($packageName)
} {
foreach (self::getInstalled() as $installed) {
if (!isset(self::$installed['versions'][$packageName]['reference'])) { if (!isset($installed['versions'][$packageName])) {
return null; continue;
} }
return self::$installed['versions'][$packageName]['reference']; if (!isset($installed['versions'][$packageName]['pretty_version'])) {
} return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
public static function getRootPackage() throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
{ }
return self::$installed['root'];
} /**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
public static function getRawData() continue;
{ }
return self::$installed;
} if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
public static function reload($data)
{ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
self::$installed = $data; }
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
} }

View File

@ -25,7 +25,7 @@ class ComposerAutoloaderInitd9f2f96e3ad6fd86ce688af0527a1d7b
require __DIR__ . '/platform_check.php'; require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitd9f2f96e3ad6fd86ce688af0527a1d7b', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInitd9f2f96e3ad6fd86ce688af0527a1d7b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitd9f2f96e3ad6fd86ce688af0527a1d7b', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInitd9f2f96e3ad6fd86ce688af0527a1d7b', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());

View File

@ -56,6 +56,6 @@
"install-path": "../google/recaptcha" "install-path": "../google/recaptcha"
} }
], ],
"dev": false, "dev": true,
"dev-package-names": [] "dev-package-names": []
} }

View File

@ -1,33 +1,32 @@
<?php return array ( <?php return array(
'root' => 'root' => array(
array ( 'pretty_version' => 'dev-develop',
'pretty_version' => 'dev-develop', 'version' => 'dev-develop',
'version' => 'dev-develop', 'type' => 'grav-plugin',
'aliases' => 'install_path' => __DIR__ . '/../../',
array ( 'aliases' => array(),
'reference' => '2e6d16dbc801d605f56d2d2942cd99a8bf63550c',
'name' => 'getgrav/grav-plugin-form',
'dev' => true,
), ),
'reference' => 'd53d3124481c8a72f223c49a03e6fd9b1f5bd7ee', 'versions' => array(
'name' => 'getgrav/grav-plugin-form', 'getgrav/grav-plugin-form' => array(
), 'pretty_version' => 'dev-develop',
'versions' => 'version' => 'dev-develop',
array ( 'type' => 'grav-plugin',
'getgrav/grav-plugin-form' => 'install_path' => __DIR__ . '/../../',
array ( 'aliases' => array(),
'pretty_version' => 'dev-develop', 'reference' => '2e6d16dbc801d605f56d2d2942cd99a8bf63550c',
'version' => 'dev-develop', 'dev_requirement' => false,
'aliases' => ),
array ( 'google/recaptcha' => array(
), 'pretty_version' => '1.2.4',
'reference' => 'd53d3124481c8a72f223c49a03e6fd9b1f5bd7ee', 'version' => '1.2.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../google/recaptcha',
'aliases' => array(),
'reference' => '614f25a9038be4f3f2da7cbfd778dc5b357d2419',
'dev_requirement' => false,
),
), ),
'google/recaptcha' =>
array (
'pretty_version' => '1.2.4',
'version' => '1.2.4.0',
'aliases' =>
array (
),
'reference' => '614f25a9038be4f3f2da7cbfd778dc5b357d2419',
),
),
); );

View File

@ -1,7 +0,0 @@
/.php_cs.cache
/.phpunit.result.cache
/build
/composer.lock
/examples/config.php
/nbproject/private/
/vendor/

View File

@ -1,33 +0,0 @@
dist: trusty
language: php
sudo: false
php:
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- '7.2'
- '7.3'
before_script:
- composer install
- phpenv version-name | grep ^5.[34] && echo "extension=apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true
- phpenv version-name | grep ^5.[34] && echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true
script:
- mkdir -p build/logs
- composer run-script lint
- composer run-script test
after_success:
- travis_retry php vendor/bin/php-coveralls
cache:
directories:
- "$HOME/.composer/cache/files"
git:
depth: 5

View File

@ -1,2 +0,0 @@
.idea
.DS_Store

View File

@ -1,3 +1,39 @@
# v3.7.3
## 02/17/2023
1. [](#bugfix)
* Fixed an issue where user was not being redirected to the correct page after login
# v3.7.2
## 01/02/2023
1. [](#new)
* Added new `onBeforeSessionStart()` event to store redirect + messages when session is regenerated during login
* Requires Grav `1.7.38` for new event availability
# v3.7.1
## 06/14/2022
1. [](#bugfix)
* PHP 8.1 fixes in QR code library
# v3.7.0
## 03/28/2022
1. [](#new)
* Require **Grav 1.7.32**, **Form 6.0.0** and **Email 3.1.6**
* Added support for fully customizable email templates
* Added `Grav\PluginsLogin\Email` class to simplify sending emails
* Added `PageAuthorizeEvent` event for customizing page access
1. [](#bugfix)
* Removed ACL checks for page modules as they caused unexpected behavior
# v3.6.3
## 03/14/2022
1. [](#improved)
* Improved multi-site support in user emails
# v3.6.2 # v3.6.2
## 01/12/2022 ## 01/12/2022
@ -10,7 +46,7 @@
1. [](#bugfix) 1. [](#bugfix)
* Fixed issue with forgot password error message translation [#285](https://github.com/getgrav/grav-plugin-login/pull/285) * Fixed issue with forgot password error message translation [#285](https://github.com/getgrav/grav-plugin-login/pull/285)
*
# v3.6.0 # v3.6.0
## 10/26/2021 ## 10/26/2021

View File

@ -1,7 +1,7 @@
name: Login name: Login
slug: login slug: login
type: plugin type: plugin
version: 3.6.2 version: 3.7.3
testing: false testing: false
description: Enables user authentication and login screen. description: Enables user authentication and login screen.
icon: sign-in icon: sign-in
@ -15,9 +15,9 @@ bugs: https://github.com/getgrav/grav-plugin-login/issues
license: MIT license: MIT
dependencies: dependencies:
- { name: grav, version: '>=1.7.27' } - { name: grav, version: '>=1.7.38' }
- { name: form, version: '>=5.1.0' } - { name: form, version: '>=6.0.0' }
- { name: email, version: '>=3.1.0' } - { name: email, version: '>=3.1.6' }
form: form:
validation: loose validation: loose

View File

@ -12,12 +12,12 @@ namespace Grav\Plugin\Login;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Language\Language; use Grav\Common\Language\Language;
use Grav\Common\Page\Pages;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\User\Interfaces\UserCollectionInterface; use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Plugin\Email\Utils as EmailUtils; use Grav\Plugin\Email\Utils as EmailUtils;
use Grav\Plugin\Form\Form;
use Grav\Plugin\Form\Forms; use Grav\Plugin\Form\Forms;
use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Invitations\Invitation; use Grav\Plugin\Login\Invitations\Invitation;
@ -343,7 +343,8 @@ class Controller
*/ */
protected function taskForgot() protected function taskForgot()
{ {
$param_sep = $this->grav['config']->get('system.param_sep', ':'); /** @var Config $config */
$config = $this->grav['config'];
$data = $this->post; $data = $this->post;
/** @var UserCollectionInterface $users */ /** @var UserCollectionInterface $users */
@ -386,7 +387,7 @@ class Controller
return true; return true;
} }
$from = $this->grav['config']->get('plugins.email.from'); $from = $config->get('plugins.email.from');
if (empty($from)) { if (empty($from)) {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_EMAIL_NOT_CONFIGURED'), 'error'); $messages->add($language->translate('PLUGIN_LOGIN.FORGOT_EMAIL_NOT_CONFIGURED'), 'error');
@ -406,37 +407,18 @@ class Controller
return true; return true;
} }
$token = md5(uniqid(mt_rand(), true)); $token = md5(uniqid((string)mt_rand(), true));
$expire = time() + 604800; // next week $expire = time() + 604800; // next week
$user->reset = $token . '::' . $expire; $user->reset = $token . '::' . $expire;
$user->save(); $user->save();
$author = $this->grav['config']->get('site.author.name', ''); try {
$fullname = $user->fullname ?: $user->username; Email::sendResetPasswordEmail($user);
if ($this->grav['language']->getDefault() != $this->grav['language']->getLanguage()) {
$lang = '/'.$this->grav['language']->getLanguage();
} else {
$lang = '';
}
$resetRoute = $this->login->getRoute('reset');
$reset_link = $this->grav['base_url_absolute'] . $lang . $resetRoute . '/task' . $param_sep . 'login.reset/token' . $param_sep . $token . '/user' . $param_sep . $user->username . '/nonce' . $param_sep . Utils::getNonce('reset-form');
$sitename = $this->grav['config']->get('site.title', 'Website');
$to = $user->email;
$subject = $language->translate(['PLUGIN_LOGIN.FORGOT_EMAIL_SUBJECT', $sitename]);
$content = $language->translate(['PLUGIN_LOGIN.FORGOT_EMAIL_BODY', $fullname, $reset_link, $author, $sitename]);
$sent = EmailUtils::sendEmail($subject, $content, $to);
if ($sent < 1) {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_FAILED_TO_EMAIL'), 'error');
} else {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'), 'info'); $messages->add($language->translate('PLUGIN_LOGIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'), 'info');
} catch (\Exception $e) {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_FAILED_TO_EMAIL'), 'error');
} }
$this->setRedirect($this->login->getRoute('login') ?? '/'); $this->setRedirect($this->login->getRoute('login') ?? '/');

View File

@ -3,7 +3,7 @@
/** /**
* @package Grav\Plugin\Login * @package Grav\Plugin\Login
* *
* @copyright Copyright (C) 2014 - 2021 RocketTheme, LLC. All rights reserved. * @copyright Copyright (C) 2014 - 2022 RocketTheme, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details. * @license MIT License; see LICENSE file for details.
*/ */
@ -19,7 +19,7 @@ use RocketTheme\Toolbox\Event\Event;
/** /**
* Class UserLoginEvent * Class UserLoginEvent
* @package Grav\Common\User\Events * @package Grav\Plugin\Login\Events
* *
* @property int $status * @property int $status
* @property array $credentials * @property array $credentials

View File

@ -24,7 +24,7 @@ use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Plugin\Email\Utils as EmailUtils; use Grav\Plugin\Login\Events\PageAuthorizeEvent;
use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Invitations\Invitation; use Grav\Plugin\Login\Invitations\Invitation;
use Grav\Plugin\Login\RememberMe\RememberMe; use Grav\Plugin\Login\RememberMe\RememberMe;
@ -445,25 +445,9 @@ class Login
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD'));
} }
$site_name = $this->config->get('site.title', 'Website'); try {
Email::sendNotificationEmail($user);
$subject = $this->language->translate(['PLUGIN_LOGIN.NOTIFICATION_EMAIL_SUBJECT', $site_name]); } catch (\Exception $e) {
$content = $this->language->translate([
'PLUGIN_LOGIN.NOTIFICATION_EMAIL_BODY',
$site_name,
$user->username,
$user->email,
$this->grav['base_url_absolute'],
]);
$to = $this->config->get('plugins.email.to');
if (empty($to)) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_NOT_CONFIGURED'));
}
$sent = EmailUtils::sendEmail($subject, $content, $to);
if ($sent < 1) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
} }
@ -484,22 +468,9 @@ class Login
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD'));
} }
$site_name = $this->config->get('site.title', 'Website'); try {
$author = $this->grav['config']->get('site.author.name', ''); Email::sendWelcomeEmail($user);
$fullname = $user->fullname ?: $user->username; } catch (\Exception $e) {
$subject = $this->language->translate(['PLUGIN_LOGIN.WELCOME_EMAIL_SUBJECT', $site_name]);
$content = $this->language->translate(['PLUGIN_LOGIN.WELCOME_EMAIL_BODY',
$fullname,
$this->grav['base_url_absolute'],
$site_name,
$author
]);
$to = $user->email;
$sent = EmailUtils::sendEmail($subject, $content, $to);
if ($sent < 1) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
} }
@ -525,25 +496,9 @@ class Login
$user->activation_token = $token . '::' . $expire; $user->activation_token = $token . '::' . $expire;
$user->save(); $user->save();
$param_sep = $this->config->get('system.param_sep', ':'); try {
$activationRoute = $this->getRoute('activate'); Email::sendActivationEmail($user);
$activation_link = $this->grav['base_url_absolute'] . $activationRoute . '/token' . $param_sep . $token . '/username' . $param_sep . $user->username; } catch (\Exception $e) {
$site_name = $this->config->get('site.title', 'Website');
$author = $this->grav['config']->get('site.author.name', '');
$fullname = $user->fullname ?: $user->username;
$subject = $this->language->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_SUBJECT', $site_name]);
$content = $this->language->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_BODY',
$fullname,
$activation_link,
$site_name,
$author
]);
$to = $user->email;
$sent = EmailUtils::sendEmail($subject, $content, $to);
if ($sent < 1) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
} }
@ -561,27 +516,9 @@ class Login
*/ */
public function sendInviteEmail(Invitation $invitation, string $message = null, UserInterface $user = null) public function sendInviteEmail(Invitation $invitation, string $message = null, UserInterface $user = null)
{ {
/** @var UserInterface $user */ try {
$user = $user ?? $this->grav['user']; Email::sendInvitationEmail($invitation, $message, $user);
} catch (\Exception $e) {
$param_sep = $this->config->get('system.param_sep', ':');
$inviteRoute = $this->getRoute('register', true);
$invitationLink = $this->grav['base_url_absolute'] . "{$inviteRoute}/{$param_sep}{$invitation->token}";
$siteName = $this->config->get('site.title', 'Website');
$subject = $this->language->translate(['PLUGIN_LOGIN.INVITATION_EMAIL_SUBJECT', $siteName]);
$message = $message ?? $this->language->translate(['PLUGIN_LOGIN.INVITATION_EMAIL_MESSAGE']);
$content = $this->language->translate(['PLUGIN_LOGIN.INVITATION_EMAIL_BODY',
$siteName,
$message,
$invitationLink,
$user->fullname
]);
$to = $invitation->email;
$sent = EmailUtils::sendEmail($subject, $content, $to);
if ($sent < 1) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
} }
@ -789,27 +726,15 @@ class Login
* @param Data|null $config * @param Data|null $config
* @return bool * @return bool
*/ */
public function isUserAuthorizedForPage(UserInterface $user, PageInterface $page, $config = null) public function isUserAuthorizedForPage(UserInterface $user, PageInterface $page, Data $config = null): bool
{ {
$header = $page->header(); /** @var PageAuthorizeEvent $event */
$rules = (array)($header->access ?? []); $event = $this->grav->dispatchEvent(new PageAuthorizeEvent($page, $user, $config));
if (!$event->hasProtectedAccess()) {
if (!$rules && $config !== null && $config->get('parent_acl')) {
// If page has no ACL rules, use its parent's rules
$parent = $page->parent();
while (!$rules and $parent) {
$header = $parent->header();
$rules = (array)($header->access ?? []);
$parent = $parent->parent();
}
}
// Continue to the page if it has no ACL rules.
if (!$rules) {
return true; return true;
} }
// All protected pages have a private cache-control. This includes pages which are for guests only. // All access protected pages have a private cache-control. This includes pages which are for guests only.
$cacheControl = $page->cacheControl(); $cacheControl = $page->cacheControl();
if (!$cacheControl) { if (!$cacheControl) {
$cacheControl = 'private, no-cache, must-revalidate'; $cacheControl = 'private, no-cache, must-revalidate';
@ -831,28 +756,12 @@ class Login
$page->cacheControl($cacheControl); $page->cacheControl($cacheControl);
// Deny access if user has not completed 2FA challenge. // Deny access if user has not completed 2FA challenge.
$user = $event->user;
if ($user->authenticated && !$user->authorized) { if ($user->authenticated && !$user->authorized) {
return false; $event->deny();
} }
// Continue to the page if user is authorized to access the page. return $event->isAllowed();
foreach ($rules as $rule => $value) {
if (is_int($rule)) {
if ($user->authorize($value) === true) {
return true;
}
} elseif (\is_array($value)) {
foreach ($value as $nested_rule => $nested_value) {
if ($user->authorize($rule . '.' . $nested_rule) === Utils::isPositive($nested_value)) {
return true;
}
}
} elseif ($user->authorize($rule) === Utils::isPositive($value)) {
return true;
}
}
return false;
} }
/** /**

View File

@ -26,10 +26,10 @@
"docs": "https://github.com/getgrav/grav-plugin-login/blob/master/README.md" "docs": "https://github.com/getgrav/grav-plugin-login/blob/master/README.md"
}, },
"require": { "require": {
"php": ">=7.1.3", "php": ">=7.3.6",
"ext-json": "*", "ext-json": "*",
"birke/rememberme": "1.*", "birke/rememberme": "^1.0",
"robthree/twofactorauth": "^1.6", "robthree/twofactorauth": "^1.8",
"bacon/bacon-qr-code": "^1.0" "bacon/bacon-qr-code": "^1.0"
}, },
"autoload": { "autoload": {
@ -43,7 +43,7 @@
}, },
"config": { "config": {
"platform": { "platform": {
"php": "7.1.3" "php": "7.3.6"
} }
} }
} }

View File

@ -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": "bbcc2f78d1043e3e053994cf79736169", "content-hash": "a76edd1409f49153b8c55e0f6013ae17",
"packages": [ "packages": [
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -160,16 +160,16 @@
}, },
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"version": "1.8.0", "version": "1.8.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git", "url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c" "reference": "65681de5a324eae05140ac58b08648a60212afc0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/30a38627ae1e7c9399dae67e265063cd6ec5276c", "url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c", "reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -226,7 +226,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-03-09T18:24:05+00:00" "time": "2022-03-22T16:11:07+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
@ -236,12 +236,12 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.1.3", "php": ">=7.3.6",
"ext-json": "*" "ext-json": "*"
}, },
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {
"php": "7.1.3" "php": "7.3.6"
}, },
"plugin-api-version": "2.0.0" "plugin-api-version": "2.2.0"
} }

View File

@ -158,4 +158,5 @@ PLUGIN_LOGIN:
INVITATION_EMAIL_BODY: "<h1>Account Invitation</h1><p>Hi, </p><p>You have been invited to join <b>%1$s</b>.</p><p>%2$s</p><p><br/><a href=\"%3$s\" class=\"btn-primary\">Create Your Account Now</a><br/><br/></p><p>Alternatively, copy the following URL into your browser's address bar:</p><p class=\"word-break\"><a href=\"%3$s\">%3$s</a></p><p><br/>Kind regards,<br/><br/>%4$s</p>" INVITATION_EMAIL_BODY: "<h1>Account Invitation</h1><p>Hi, </p><p>You have been invited to join <b>%1$s</b>.</p><p>%2$s</p><p><br/><a href=\"%3$s\" class=\"btn-primary\">Create Your Account Now</a><br/><br/></p><p>Alternatively, copy the following URL into your browser's address bar:</p><p class=\"word-break\"><a href=\"%3$s\">%3$s</a></p><p><br/>Kind regards,<br/><br/>%4$s</p>"
INVITATION_EMAIL_MESSAGE: "We welcome you to register an account to on site." INVITATION_EMAIL_MESSAGE: "We welcome you to register an account to on site."
INVALID_INVITE_EMAILS: "<strong>Error:</strong> An invalid list of emails was provided" INVALID_INVITE_EMAILS: "<strong>Error:</strong> An invalid list of emails was provided"
INVALID_FORM: "<strong>Error:</strong> Invalid form" INVALID_FORM: "<strong>Error:</strong> Invalid form"
FAILED_TO_SEND_EMAILS: "Failed to send emails to: %s"

View File

@ -23,13 +23,16 @@ use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Events\BeforeSessionStartEvent;
use Grav\Events\PluginsLoadedEvent; use Grav\Events\PluginsLoadedEvent;
use Grav\Events\SessionStartEvent; use Grav\Events\SessionStartEvent;
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Form\Interfaces\FormInterface; use Grav\Framework\Form\Interfaces\FormInterface;
use Grav\Framework\Psr7\Response;
use Grav\Framework\Session\SessionInterface; use Grav\Framework\Session\SessionInterface;
use Grav\Plugin\Form\Form; use Grav\Plugin\Form\Form;
use Grav\Plugin\Login\Events\PageAuthorizeEvent;
use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Invitations\Invitation; use Grav\Plugin\Login\Invitations\Invitation;
use Grav\Plugin\Login\Invitations\Invitations; use Grav\Plugin\Login\Invitations\Invitations;
@ -38,6 +41,7 @@ use Grav\Plugin\Login\Controller;
use Grav\Plugin\Login\RememberMe\RememberMe; use Grav\Plugin\Login\RememberMe\RememberMe;
use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Session\Message; use RocketTheme\Toolbox\Session\Message;
use function is_array;
/** /**
* Class LoginPlugin * Class LoginPlugin
@ -59,6 +63,9 @@ class LoginPlugin extends Plugin
/** @var Invitation|null */ /** @var Invitation|null */
protected $invitation; protected $invitation;
protected $temp_redirect;
protected $temp_messages;
/** /**
* @return array * @return array
*/ */
@ -67,6 +74,8 @@ class LoginPlugin extends Plugin
return [ return [
PluginsLoadedEvent::class => [['onPluginsLoaded', 10]], PluginsLoadedEvent::class => [['onPluginsLoaded', 10]],
SessionStartEvent::class => ['onSessionStart', 0], SessionStartEvent::class => ['onSessionStart', 0],
BeforeSessionStartEvent::class => ['onBeforeSessionStart', 0],
PageAuthorizeEvent::class => ['onPageAuthorizeEvent', -10000],
'onPluginsInitialized' => [['initializeSession', 10000], ['initializeLogin', 1000]], 'onPluginsInitialized' => [['initializeSession', 10000], ['initializeLogin', 1000]],
'onTask.login.login' => ['loginController', 0], 'onTask.login.login' => ['loginController', 0],
'onTask.login.twofa' => ['loginController', 0], 'onTask.login.twofa' => ['loginController', 0],
@ -120,10 +129,35 @@ class LoginPlugin extends Plugin
}; };
} }
/**
* @param BeforeSessionStartEvent $event
* @return void
*/
public function onBeforeSessionStart(BeforeSessionStartEvent $event): void
{
$session = $event->session;
$this->temp_redirect = $session->redirect_after_login ?? null;
$this->temp_messages = $session->messages;
}
/**
* @param SessionStartEvent $event
* @return void
*/
public function onSessionStart(SessionStartEvent $event): void public function onSessionStart(SessionStartEvent $event): void
{ {
$session = $event->session; $session = $event->session;
if (isset($this->temp_redirect)) {
$session->redirect_after_login = $this->temp_redirect;
unset($this->temp_redirect);
}
if (isset($this->temp_messages)) {
$session->messages = $this->temp_messages;
unset($this->temp_messages);
}
$user = $session->user ?? null; $user = $session->user ?? null;
if ($user && $user->exists() && ($this->config()['session_user_sync'] ?? false)) { if ($user && $user->exists() && ($this->config()['session_user_sync'] ?? false)) {
// User is stored into the filesystem. // User is stored into the filesystem.
@ -303,7 +337,7 @@ class LoginPlugin extends Plugin
if ($page) { if ($page) {
$header = $page->header(); $header = $page->header();
$allowed = ($header->login_redirect_here ?? true) === false; $allowed = ($header->login_redirect_here ?? true) === true;
if ($allowed && $page->routable()) { if ($allowed && $page->routable()) {
$redirect = $page->route(); $redirect = $page->route();
foreach ($uri->params(null, true) as $key => $value) { foreach ($uri->params(null, true) as $key => $value) {
@ -577,6 +611,67 @@ class LoginPlugin extends Plugin
} }
} }
/**
* @param PageAuthorizeEvent $event
* @return void
*/
public function onPageAuthorizeEvent(PageAuthorizeEvent $event): void
{
if ($event->isDenied()) {
// Deny access always wins.
return;
}
$page = $event->page;
$header = $page->header();
$rules = (array)($header->access ?? []);
if (!$rules && $event->config->get('parent_acl')) {
// If page has no ACL rules, use its parent's rules
$parent = $page->parent();
while (!$rules and $parent) {
$header = $parent->header();
$rules = (array)($header->access ?? []);
$parent = $parent->parent();
}
}
// Continue to the page if it has no access rules.
if (!$rules) {
return;
}
// Mark the page to be protected by access rules.
$event->setProtectedAccess();
// Continue to the page if user is authorized to access the page.
$user = $event->user;
foreach ($rules as $rule => $value) {
if (is_int($rule)) {
if ($user->authorize($value) === true) {
$event->allow();
return;
}
} elseif (is_array($value)) {
foreach ($value as $nested_rule => $nested_value) {
if ($user->authorize($rule . '.' . $nested_rule) === Utils::isPositive($nested_value)) {
$event->allow();
return;
}
}
} elseif ($user->authorize($rule) === Utils::isPositive($value)) {
$event->allow();
return;
}
}
// No match, deny access.
$event->deny();
}
/** /**
* [onPageInitialized] Authorize Page * [onPageInitialized] Authorize Page
*/ */
@ -590,7 +685,7 @@ class LoginPlugin extends Plugin
$user = $this->grav['user']; $user = $this->grav['user'];
$page = $this->grav['page'] ?? null; $page = $this->grav['page'] ?? null;
if (!$page instanceof PageInterface) { if (!$page instanceof PageInterface || $page->isModule()) {
return; return;
} }
@ -603,8 +698,9 @@ class LoginPlugin extends Plugin
$uri_extension = $this->grav['uri']->extension('html'); $uri_extension = $this->grav['uri']->extension('html');
$supported_types = $this->config->get('media.types'); $supported_types = $this->config->get('media.types');
if ($uri_extension !== 'html' && array_key_exists($uri_extension, $supported_types)) { if ($uri_extension !== 'html' && array_key_exists($uri_extension, $supported_types)) {
header('HTTP/1.0 403 Forbidden'); $response = new Response(403);
exit;
$this->grav->close($response);
} }
// User is not logged in; redirect to login page. // User is not logged in; redirect to login page.
@ -783,7 +879,7 @@ class LoginPlugin extends Plugin
foreach ($default_values as $key => $param) { foreach ($default_values as $key => $param) {
if ($key === $field) { if ($key === $field) {
if (\is_array($param)) { if (is_array($param)) {
$values = explode(',', $param); $values = explode(',', $param);
} else { } else {
$values = $param; $values = $param;

View File

@ -1,9 +0,0 @@
composer.lock
vendor
nbproject
.idea
.buildpath
.project
.DS_Store
.*.sw*
.*.un~

View File

@ -1,14 +0,0 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
install:
- travis_retry composer install --no-interaction
- composer info -i
script: vendor/bin/phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml tests

View File

@ -1,4 +0,0 @@
.idea
nbproject
example/tokens
/vendor/

View File

@ -42,21 +42,75 @@ namespace Composer\Autoload;
*/ */
class ClassLoader class ClassLoader
{ {
/** @var ?string */
private $vendorDir;
// PSR-4 // PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array(); private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array(); private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array(); private $fallbackDirsPsr4 = array();
// PSR-0 // PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array(); private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array(); private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false; private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array(); private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false; private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array(); private $missingClasses = array();
/** @var ?string */
private $apcuPrefix; private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes() public function getPrefixes()
{ {
if (!empty($this->prefixesPsr0)) { if (!empty($this->prefixesPsr0)) {
@ -66,28 +120,47 @@ class ClassLoader
return array(); return array();
} }
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4() public function getPrefixesPsr4()
{ {
return $this->prefixDirsPsr4; return $this->prefixDirsPsr4;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs() public function getFallbackDirs()
{ {
return $this->fallbackDirsPsr0; return $this->fallbackDirsPsr0;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4() public function getFallbackDirsPsr4()
{ {
return $this->fallbackDirsPsr4; return $this->fallbackDirsPsr4;
} }
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap() public function getClassMap()
{ {
return $this->classMap; return $this->classMap;
} }
/** /**
* @param array $classMap Class to filename map * @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/ */
public function addClassMap(array $classMap) public function addClassMap(array $classMap)
{ {
@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either * Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix. * appending or prepending to the ones previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories * @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
*
* @return void
*/ */
public function add($prefix, $paths, $prepend = false) public function add($prefix, $paths, $prepend = false)
{ {
@ -147,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either * Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace. * appending or prepending to the ones previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function addPsr4($prefix, $paths, $prepend = false) public function addPsr4($prefix, $paths, $prepend = false)
{ {
@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, * Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix. * replacing any others previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories * @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/ */
public function set($prefix, $paths) public function set($prefix, $paths)
{ {
@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, * Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace. * replacing any others previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function setPsr4($prefix, $paths) public function setPsr4($prefix, $paths)
{ {
@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files. * Turns on searching the include path for class files.
* *
* @param bool $useIncludePath * @param bool $useIncludePath
*
* @return void
*/ */
public function setUseIncludePath($useIncludePath) public function setUseIncludePath($useIncludePath)
{ {
@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map. * that have not been registered with the class map.
* *
* @param bool $classMapAuthoritative * @param bool $classMapAuthoritative
*
* @return void
*/ */
public function setClassMapAuthoritative($classMapAuthoritative) public function setClassMapAuthoritative($classMapAuthoritative)
{ {
@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled. * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
* *
* @param string|null $apcuPrefix * @param string|null $apcuPrefix
*
* @return void
*/ */
public function setApcuPrefix($apcuPrefix) public function setApcuPrefix($apcuPrefix)
{ {
@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader. * Registers this instance as an autoloader.
* *
* @param bool $prepend Whether to prepend the autoloader or not * @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/ */
public function register($prepend = false) public function register($prepend = false)
{ {
spl_autoload_register(array($this, 'loadClass'), true, $prepend); spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
} }
/** /**
* Unregisters this instance as an autoloader. * Unregisters this instance as an autoloader.
*
* @return void
*/ */
public function unregister() public function unregister()
{ {
spl_autoload_unregister(array($this, 'loadClass')); spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
} }
/** /**
* Loads the given class or interface. * Loads the given class or interface.
* *
* @param string $class The name of the class * @param string $class The name of the class
* @return bool|null True if loaded, null otherwise * @return true|null True if loaded, null otherwise
*/ */
public function loadClass($class) public function loadClass($class)
{ {
@ -323,6 +429,8 @@ class ClassLoader
return true; return true;
} }
return null;
} }
/** /**
@ -367,6 +475,21 @@ class ClassLoader
return $file; return $file;
} }
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext) private function findFileWithExtension($class, $ext)
{ {
// PSR-4 lookup // PSR-4 lookup
@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include. * Scope isolated include.
* *
* Prevents access to $this/self from included files. * Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/ */
function includeFile($file) function includeFile($file)
{ {

View File

@ -1,255 +1,350 @@
<?php <?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer; namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser; use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions class InstalledVersions
{ {
private static $installed = array ( /**
'root' => * @var mixed[]|null
array ( * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
'pretty_version' => 'dev-develop', */
'version' => 'dev-develop', private static $installed;
'aliases' =>
array (
),
'reference' => 'b7996fda7a3b56432ef9e249584616d86d977517',
'name' => 'getgrav/grav-plugin-login',
),
'versions' =>
array (
'bacon/bacon-qr-code' =>
array (
'pretty_version' => '1.0.3',
'version' => '1.0.3.0',
'aliases' =>
array (
),
'reference' => '5a91b62b9d37cee635bbf8d553f4546057250bee',
),
'birke/rememberme' =>
array (
'pretty_version' => '1.0.5',
'version' => '1.0.5.0',
'aliases' =>
array (
),
'reference' => '810473852eb4823098e47e23376a19b77ba0c165',
),
'getgrav/grav-plugin-login' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'aliases' =>
array (
),
'reference' => 'b7996fda7a3b56432ef9e249584616d86d977517',
),
'paragonie/random_compat' =>
array (
'pretty_version' => 'v1.4.3',
'version' => '1.4.3.0',
'aliases' =>
array (
),
'reference' => '9b3899e3c3ddde89016f576edb8c489708ad64cd',
),
'robthree/twofactorauth' =>
array (
'pretty_version' => '1.8.0',
'version' => '1.8.0.0',
'aliases' =>
array (
),
'reference' => '30a38627ae1e7c9399dae67e265063cd6ec5276c',
),
),
);
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
public static function getInstalledPackages() foreach (self::getInstalled() as $installed) {
{ foreach ($installed['versions'] as $name => $package) {
return array_keys(self::$installed['versions']); if (isset($package['type']) && $package['type'] === $type) {
} $packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
public static function isInstalled($packageName) *
{ * This also returns true if the package name is provided or replaced by another package
return isset(self::$installed['versions'][$packageName]); *
} * @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
public static function satisfies(VersionParser $parser, $packageName, $constraint) /**
{ * Checks whether the given package satisfies a version constraint
$constraint = $parser->parseConstraints($constraint); *
$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
return $provided->matches($constraint); * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
} *
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
public static function getVersionRanges($packageName) return $provided->matches($constraint);
{ }
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); /**
} * Returns a version constraint representing all the range(s) which are installed for a given package
*
$ranges = array(); * It is easier to use this via isInstalled() with the $constraint argument if you need to check
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { * whether a given version of a package is installed, and not just whether it exists
$ranges[] = self::$installed['versions'][$packageName]['pretty_version']; *
} * @param string $packageName
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { * @return string Version constraint usable with composer/semver
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); */
} public static function getVersionRanges($packageName)
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); foreach (self::getInstalled() as $installed) {
} if (!isset($installed['versions'][$packageName])) {
if (array_key_exists('provided', self::$installed['versions'][$packageName])) { continue;
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); }
}
$ranges = array();
return implode(' || ', $ranges); if (isset($installed['versions'][$packageName]['pretty_version'])) {
} $ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
public static function getVersion($packageName) $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
{ }
if (!isset(self::$installed['versions'][$packageName])) { if (array_key_exists('provided', $installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
} }
if (!isset(self::$installed['versions'][$packageName]['version'])) { return implode(' || ', $ranges);
return null; }
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
return self::$installed['versions'][$packageName]['version']; }
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
public static function getPrettyVersion($packageName) {
{ foreach (self::getInstalled() as $installed) {
if (!isset(self::$installed['versions'][$packageName])) { if (!isset($installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); continue;
} }
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { if (!isset($installed['versions'][$packageName]['version'])) {
return null; return null;
} }
return self::$installed['versions'][$packageName]['pretty_version']; return $installed['versions'][$packageName]['version'];
} }
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
public static function getReference($packageName) * @param string $packageName
{ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
if (!isset(self::$installed['versions'][$packageName])) { */
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); public static function getPrettyVersion($packageName)
} {
foreach (self::getInstalled() as $installed) {
if (!isset(self::$installed['versions'][$packageName]['reference'])) { if (!isset($installed['versions'][$packageName])) {
return null; continue;
} }
return self::$installed['versions'][$packageName]['reference']; if (!isset($installed['versions'][$packageName]['pretty_version'])) {
} return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
public static function getRootPackage() throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
{ }
return self::$installed['root'];
} /**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
public static function getRawData() continue;
{ }
return self::$installed;
} if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
public static function reload($data)
{ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
self::$installed = $data; }
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
} }

View File

@ -25,7 +25,7 @@ class ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366
require __DIR__ . '/platform_check.php'; require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
@ -65,11 +65,16 @@ class ComposerAutoloaderIniteed5e5cf0aa1e2139f2db7445511e366
} }
} }
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequireeed5e5cf0aa1e2139f2db7445511e366($fileIdentifier, $file) function composerRequireeed5e5cf0aa1e2139f2db7445511e366($fileIdentifier, $file)
{ {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
} }
} }

View File

@ -150,17 +150,17 @@
}, },
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"version": "1.8.0", "version": "1.8.2",
"version_normalized": "1.8.0.0", "version_normalized": "1.8.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git", "url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c" "reference": "65681de5a324eae05140ac58b08648a60212afc0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/30a38627ae1e7c9399dae67e265063cd6ec5276c", "url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c", "reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -174,7 +174,7 @@
"bacon/bacon-qr-code": "Needed for BaconQrCodeProvider provider", "bacon/bacon-qr-code": "Needed for BaconQrCodeProvider provider",
"endroid/qr-code": "Needed for EndroidQrCodeProvider" "endroid/qr-code": "Needed for EndroidQrCodeProvider"
}, },
"time": "2021-03-09T18:24:05+00:00", "time": "2022-03-22T16:11:07+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {

View File

@ -1,60 +1,59 @@
<?php return array ( <?php return array(
'root' => 'root' => array(
array ( 'pretty_version' => 'dev-develop',
'pretty_version' => 'dev-develop', 'version' => 'dev-develop',
'version' => 'dev-develop', 'type' => 'library',
'aliases' => 'install_path' => __DIR__ . '/../../',
array ( 'aliases' => array(),
'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'name' => 'getgrav/grav-plugin-login',
'dev' => false,
), ),
'reference' => 'b7996fda7a3b56432ef9e249584616d86d977517', 'versions' => array(
'name' => 'getgrav/grav-plugin-login', 'bacon/bacon-qr-code' => array(
), 'pretty_version' => '1.0.3',
'versions' => 'version' => '1.0.3.0',
array ( 'type' => 'library',
'bacon/bacon-qr-code' => 'install_path' => __DIR__ . '/../bacon/bacon-qr-code',
array ( 'aliases' => array(),
'pretty_version' => '1.0.3', 'reference' => '5a91b62b9d37cee635bbf8d553f4546057250bee',
'version' => '1.0.3.0', 'dev_requirement' => false,
'aliases' => ),
array ( 'birke/rememberme' => array(
), 'pretty_version' => '1.0.5',
'reference' => '5a91b62b9d37cee635bbf8d553f4546057250bee', 'version' => '1.0.5.0',
'type' => 'library',
'install_path' => __DIR__ . '/../birke/rememberme',
'aliases' => array(),
'reference' => '810473852eb4823098e47e23376a19b77ba0c165',
'dev_requirement' => false,
),
'getgrav/grav-plugin-login' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v1.4.3',
'version' => '1.4.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'reference' => '9b3899e3c3ddde89016f576edb8c489708ad64cd',
'dev_requirement' => false,
),
'robthree/twofactorauth' => array(
'pretty_version' => '1.8.2',
'version' => '1.8.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../robthree/twofactorauth',
'aliases' => array(),
'reference' => '65681de5a324eae05140ac58b08648a60212afc0',
'dev_requirement' => false,
),
), ),
'birke/rememberme' =>
array (
'pretty_version' => '1.0.5',
'version' => '1.0.5.0',
'aliases' =>
array (
),
'reference' => '810473852eb4823098e47e23376a19b77ba0c165',
),
'getgrav/grav-plugin-login' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'aliases' =>
array (
),
'reference' => 'b7996fda7a3b56432ef9e249584616d86d977517',
),
'paragonie/random_compat' =>
array (
'pretty_version' => 'v1.4.3',
'version' => '1.4.3.0',
'aliases' =>
array (
),
'reference' => '9b3899e3c3ddde89016f576edb8c489708ad64cd',
),
'robthree/twofactorauth' =>
array (
'pretty_version' => '1.8.0',
'version' => '1.8.0.0',
'aliases' =>
array (
),
'reference' => '30a38627ae1e7c9399dae67e265063cd6ec5276c',
),
),
); );

View File

@ -4,8 +4,8 @@
$issues = array(); $issues = array();
if (!(PHP_VERSION_ID >= 70103)) { if (!(PHP_VERSION_ID >= 70306)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.3". You are running ' . PHP_VERSION . '.'; $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.6". You are running ' . PHP_VERSION . '.';
} }
if ($issues) { if ($issues) {

View File

@ -1,2 +0,0 @@
coverage_clover: clover.xml
json_path: coveralls-upload.json

View File

@ -1,3 +0,0 @@
/composer.lock
/phpunit.xml
/vendor/

View File

@ -1,41 +0,0 @@
sudo: false
language: php
cache:
directories:
- $HOME/.composer/cache
- $HOME/.local
- vendor
matrix:
fast_finish: true
include:
- php: 7.1
env:
- EXECUTE_CS_CHECK=true
- EXECUTE_TEST_COVERALLS=true
- PATH="$HOME/.local/bin:$PATH"
- php: nightly
allow_failures:
- php: nightly
before_install:
- if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
- composer self-update
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer require --dev --no-update satooshi/php-coveralls:dev-master ; fi
install:
- travis_retry composer install --no-interaction
- composer info -i
script:
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then vendor/bin/phpunit --coverage-clover clover.xml ; fi
- if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then vendor/bin/phpunit ; fi
- if [[ $EXECUTE_CS_CHECK == 'true' ]]; then vendor/bin/phpcs ; fi
after_script:
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then vendor/bin/coveralls ; fi
notifications:
email: true

View File

@ -0,0 +1,30 @@
name: Test Bacon QR Code Provider
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer
coverage: xdebug
ini-values: error_reporting=E_ALL
- uses: ramsey/composer-install@v1
- run: composer require bacon/bacon-qr-code
- run: composer lint
- run: composer test testsDependency/BaconQRCodeTest.php

View File

@ -0,0 +1,46 @@
name: Test Endroid QR Code Provider
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.0', '8.1']
endroid-version: ["^4"]
include:
- php-version: 5.6
# earliest supported version
endroid-version: 2.2.1
- php-version: 7.0
endroid-version: 2.5.1
- php-version: 7.1
# this version is 7.1+
endroid-version: 3.0.0
- php-version: 7.2
# all later versions are 7.3+
endroid-version: 3.5.9
- php-version: 7.3
endroid-version: 3.9.7
- php-version: 7.4
endroid-version: 4.0.0
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer
coverage: xdebug
ini-values: error_reporting=E_ALL
- uses: ramsey/composer-install@v1
- run: composer require endroid/qrcode:${{ matrix.endroid-version }}
- run: composer test testsDependency/EndroidQRCodeTest.php

View File

@ -10,7 +10,7 @@ jobs:
strategy: strategy:
matrix: matrix:
php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -20,6 +20,7 @@ jobs:
php-version: ${{ matrix.php-version }} php-version: ${{ matrix.php-version }}
tools: composer tools: composer
coverage: xdebug coverage: xdebug
ini-values: error_reporting=E_ALL
- uses: ramsey/composer-install@v1 - uses: ramsey/composer-install@v1

View File

@ -1,192 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# If using the old MSBuild-Integrated Package Restore, uncomment this:
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Composer
/vendor
composer.lock
# .vs
.vs/
.phpunit.result.cache

Some files were not shown because too many files have changed in this diff Show More