(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
## 10/08/2022

View File

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

View File

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

View File

@ -1,5 +1,12 @@
<?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;
use DateTime;

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Cache;

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Backup\Backups;

View File

@ -3,7 +3,7 @@
/**
* @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.
*/

View File

@ -1,5 +1,12 @@
<?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);
namespace Grav\Plugin\Admin;

View File

@ -1,5 +1,12 @@
<?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);
namespace Grav\Plugin\Admin\Controllers;

View File

@ -3,7 +3,7 @@
/**
* @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.
*/

View File

@ -3,7 +3,7 @@
/**
* @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.
*/

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Cache;

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Config\Config;

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Grav;

View File

@ -3,7 +3,7 @@
/**
* @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.
*/

View File

@ -1,4 +1,12 @@
<?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;
use ScssPhp\ScssPhp\Compiler;

View File

@ -1,5 +1,12 @@
<?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;
class ScssList

View File

@ -1,5 +1,12 @@
<?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;
/**

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Data\Data;

View File

@ -1,5 +1,12 @@
<?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;
use Grav\Common\Grav;

View File

@ -1,6 +1,13 @@
<?php
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\Grav;
use Grav\Framework\File\File;

View File

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

View File

@ -3,8 +3,21 @@
// autoload.php @generated by Composer
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;
exit(1);
if (!headers_sent()) {
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';

View File

@ -42,6 +42,9 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */
private $vendorDir;
@ -106,6 +109,7 @@ class ClassLoader
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
@ -425,7 +429,8 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
@ -555,18 +560,26 @@ class ClassLoader
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* 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);
$includeFiles = \Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire98c98c1c3d67f21a128f935fe4a74897($fileIdentifier, $file);
$filesToLoad = \Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$files;
$requireFile = \Closure::bind(static function ($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;
}
}
/**
* @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",
"version": "v0.1.40",
"version_normalized": "0.1.40.0",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/aaronpk/picofeed.git",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c"
"url": "https://github.com/rhukster/picofeed.git",
"reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aaronpk/picofeed/zipball/356fd66d48779193b10ac28532cb4a4e11bb801c",
"reference": "356fd66d48779193b10ac28532cb4a4e11bb801c",
"url": "https://api.github.com/repos/rhukster/picofeed/zipball/8eacaa62f50a0935e26ca33f8d30d283344ca397",
"reference": "8eacaa62f50a0935e26ca33f8d30d283344ca397",
"shasum": ""
},
"require": {
@ -163,7 +163,7 @@
"suggest": {
"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": [
"picofeed"
],
@ -174,7 +174,6 @@
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -186,8 +185,7 @@
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/aaronpk/picoFeed",
"support": {
"issues": "https://github.com/aaronpk/picofeed/issues",
"source": "https://github.com/aaronpk/picofeed/tree/v0.1.40"
"source": "https://github.com/rhukster/picofeed/tree/1.0.0"
},
"install-path": "../p3k/picofeed"
},

View File

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

View File

@ -97,7 +97,7 @@ class DateParser extends Base
if ($date !== false) {
$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;
}
}

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
## 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 #}
{% set searchFields = searchFields|merge([key|replace({'.': '_'})]) %}
{% 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 %}

View File

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

View File

@ -3,7 +3,7 @@
{% set originalValue = value %}
{% set value = (value is null ? field.default : value) %}
{% 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 %}
{% 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 %}
{% block input %}
{% set savedOption = not isNew and savedOption == 'create-new' ? 'edit' : savedOption %}
{% for key, text in options %}
{% set id = field.id|default(field.name) ~ '-' ~ key %}

View File

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

View File

@ -907,6 +907,7 @@ class AdminController
// Set route to point to the current page.
if (!$this->redirect) {
$postAction = $request->getParsedBody()['_post_entries_save'] ?? 'edit';
$this->grav['session']->post_entries_save = $postAction;
if ($postAction === 'create-new') {
// Create another.
$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) {
/** @var Route $route */
$route = $this->grav['route'];
$id = $route->getGravParam('id');
$id = rawurldecode($route->getGravParam('id'));
if (null !== $id) {
$form['flex']['key'] = $id;
$event['form'] = $form;

View File

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

View File

@ -30,13 +30,13 @@ PLUGIN_FLEX_OBJECTS:
CREATE_NEW: Create New Item
EDIT_ITEM: Edit Item
LIST_ITEMS: "List Items"
LIST_ITEM: List Items
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?"
SEARCH_PLACEHOLDER: "Search…"
ADVANCED_OPTIONS: "Advanced Options"
APPLY_FILTERS: "Apply Filters"
RESET_FILTERS: "Reset to Defaults"
ACTIONS: "Actions"
FILTER:
PAGE_ATTRIBUTES: "Page Attributes"

View File

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

View File

@ -30,13 +30,13 @@ PLUGIN_FLEX_OBJECTS:
CREATE_NEW: 新しいデータを作成
EDIT_ITEM: "データの編集"
LIST_ITEMS: "データの一覧表示"
LIST_ITEM: データの一覧
DELETE_N: "削除" # In some languages 'delete OBJECT' may need a special declination
REALLY_DELETE: "%s を削除します。復元はできませんがよろしいですか?"
SEARCH_PLACEHOLDER: "検索…"
ADVANCED_OPTIONS: "詳細設定"
APPLY_FILTERS: "フィルターを適用"
RESET_FILTERS: "標準にリセットする"
ACTIONS: "アクション"
FILTER:
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
## 02/07/2022
1. [](#bugfix)
* 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)
* 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)
# v5.1.5
## 01/24/2022

View File

@ -30,6 +30,8 @@ Note: when using email functionality in your forms, make sure you have configure
# 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 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
slug: form
type: plugin
version: 5.1.6
description: Enables the forms handling
version: 7.1.2
description: Enables forms handling and processing
icon: check-square
author:
name: Team Grav
@ -196,3 +196,150 @@ form:
label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY
help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP
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
if ($operator === '-') {
if ($first_num < $second_num) {
$result = "$second_num-$first_num";
$captcha_code = $second_num-$first_num;
$result = "$second_num - $first_num";
$captcha_code = $second_num - $first_num;
} else {
$result = "$first_num-$second_num";
$captcha_code = $first_num - $second_num;
}
} elseif ($operator === '*') {
$result = "{$first_num}x{$second_num}";
$captcha_code = $first_num - $second_num;
$result = "{$first_num} x {$second_num}";
$captcha_code = $first_num * $second_num;
} elseif ($operator === '/') {
$result = "$first_num/ second_num";
$result = "$first_num / second_num";
$captcha_code = $first_num / $second_num;
} elseif ($operator === '+') {
$result = "$first_num+$second_num";
$result = "$first_num + $second_num";
$captcha_code = $first_num + $second_num;
}
} else {

View File

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

View File

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

View File

@ -90,21 +90,28 @@ class TwigExtension extends AbstractExtension
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).
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.
$name = (string)($field['name'] ?? $name);
$name = $fieldName;
}
// Make sure that the field has a name.
$name = $name ?? $field['name'] ?? null;
if (!is_string($name) || $name === '') {
if ($name === '') {
return null;
}
// Prefix name with the parent name if needed.
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'])) {
$name = str_replace('*', $options['key'], $name);
}
@ -125,6 +132,7 @@ class TwigExtension extends AbstractExtension
// Always set field name.
$field['name'] = $name;
$field['prepared'] = true;
return $field;
}

View File

@ -73,5 +73,5 @@
"platform-overrides": {
"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 DateTime;
use Doctrine\Common\Cache\Cache;
use Exception;
use Grav\Common\Data\ValidationException;
use Grav\Common\Debugger;
@ -18,10 +19,13 @@ use Grav\Common\Utils;
use Grav\Common\Uri;
use Grav\Common\Yaml;
use Grav\Framework\Form\Interfaces\FormInterface;
use Grav\Framework\Psr7\Response;
use Grav\Framework\Route\Route;
use Grav\Plugin\Form\BasicCaptcha;
use Grav\Plugin\Form\Form;
use Grav\Plugin\Form\Forms;
use Grav\Plugin\Form\TwigExtension;
use Grav\Common\HTTP\Client;
use ReCaptcha\ReCaptcha;
use ReCaptcha\RequestMethod\CurlPost;
use RecursiveArrayIterator;
@ -31,6 +35,7 @@ use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\Event\Event;
use RuntimeException;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Twig\Environment;
use Twig\Extension\CoreExtension;
use Twig\Extension\EscaperExtension;
@ -54,11 +59,9 @@ class FormPlugin extends Plugin
/** @var Form */
protected $form;
/** @var array */
/** @var array[]|FormInterface[] */
protected $forms = [];
/** @var array */
protected $flat_forms = [];
/** @var array */
/** @var FormInterface[] */
protected $active_forms = [];
/** @var array */
protected $json_response = [];
@ -70,7 +73,7 @@ class FormPlugin extends Plugin
*/
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 [
'onPluginsInitialized' => [
['autoload', 100000],
['onPluginsInitialized', 0]
],
'onPluginsInitialized' => ['onPluginsInitialized', 0],
'onTwigExtensions' => ['onTwigExtensions', 0],
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0]
];
}
/**
* [onPluginsInitialized:100000] Composer autoload.
*
* @return ClassLoader
*/
public function autoload()
@ -114,10 +112,9 @@ class FormPlugin extends Plugin
$this->grav['forms'] = function () {
$forms = new Forms();
$grav = Grav::instance();
$event = new Event(['forms' => $forms]);
$grav->fireEvent('onFormRegisterTypes', $event);
$this->grav->fireEvent('onFormRegisterTypes', $event);
return $forms;
};
@ -130,12 +127,19 @@ class FormPlugin extends Plugin
return;
}
/** @var Uri $uri */
$uri = $this->grav['uri'];
// Mini Keep-Alive Logic
$task = $this->grav['uri']->param('task');
if ($task && $task === 'keep-alive') {
exit;
$task = $uri->param('task');
if ($task === 'keep-alive') {
$response = new Response(200);
$this->grav->close($response);
}
$this->processBasicCaptchaImage($uri);
$this->enable([
'onPageProcessed' => ['onPageProcessed', 0],
'onPagesInitialized' => ['onPagesInitialized', 0],
@ -161,16 +165,16 @@ class FormPlugin extends Plugin
/**
* Process forms after page header processing, but before caching
*
* @param Event $e
* @param Event $event
* @return void
*/
public function onPageProcessed(Event $e): void
public function onPageProcessed(Event $event): void
{
/** @var PageInterface $page */
$page = $e['page'];
$page = $event['page'];
$pageForms = $page->forms();
if (!$pageForms) {
$forms = $page->getForms();
if (!$forms) {
return;
}
@ -184,23 +188,18 @@ class FormPlugin extends Plugin
}
$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 ($parent) {
$parent->addForms($pageForms);
$parent_route = $parent->home() ? '/' : $parent->route();
$parent->addForms($forms);
}
/** @var Forms $forms */
$forms = $this->grav['forms'];
// Store the page forms in the forms instance
foreach ($pageForms as $name => $form) {
if (isset($parent, $parent_route)) {
$this->addForm($parent_route, $forms->createPageForm($parent, $name, $form));
foreach ($forms as $name => $form) {
if ($parent) {
$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) {
// Post the form
$isJson = $uri->extension() === 'json';
$task = $uri->post('task') ?? $uri->param('task');
$task = (string)($uri->post('task') ?? $uri->param('task'));
if ($isJson) {
if ($task === 'store-state') {
@ -286,10 +285,13 @@ class FormPlugin extends Plugin
if ($this->json_response && $page->template() !== 'form') {
$status = $this->json_response['status'] ?? null;
header('Content-Type: application/json');
http_response_code($status === 'error' ? 400 : 200);
echo json_encode($this->json_response);
exit;
$response = new Response(
$status !== 'error' ? 200 : 400,
['Content-Type' => 'application/json'],
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.
// 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 */
$route = $this->grav['route'];
$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')) {
// Found one; we need to check if unique id is set.
$formParam = $form->get('uniqueid_param', 'fid');
@ -328,8 +344,6 @@ class FormPlugin extends Plugin
$form->setUniqueId($uniqueId);
$form->initialize();
/** @var Forms $forms */
$forms = $this->grav['forms'];
$forms->setActiveForm($form);
break;
@ -337,7 +351,7 @@ class FormPlugin extends Plugin
// Append unique id to the URL and redirect.
$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?
break;
@ -398,7 +412,7 @@ class FormPlugin extends Plugin
/**
* Make form accessible from twig.
*
* @param Event $event
* @param Event|null $event
* @return void
*/
public function onTwigVariables(Event $event = null): void
@ -428,6 +442,7 @@ class FormPlugin extends Plugin
* @param Event $event
* @return void
* @throws Exception
* @throws TransportExceptionInterface
*/
public function onFormProcessed(Event $event): void
{
@ -499,6 +514,55 @@ class FormPlugin extends Plugin
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;
case 'timestamp':
$label = $params['label'] ?? 'Timestamp';
@ -544,8 +608,7 @@ class FormPlugin extends Plugin
$this->grav['messages']->add($form->message, 'success');
}
$event['redirect'] = $url;
$event->stopPropagation();
$this->grav->redirect($url);
break;
case 'reset':
if (Utils::isPositive($params)) {
@ -736,6 +799,7 @@ class FormPlugin extends Plugin
*/
public function onFormValidationError(Event $event): void
{
/** @var FormInterface $form */
$form = $event['form'];
if (isset($event['message'])) {
$form->status = 'error';
@ -743,6 +807,7 @@ class FormPlugin extends Plugin
$form->messages = $event['messages'];
}
/** @var Uri $uri */
$uri = $this->grav['uri'];
$route = $uri->route();
@ -752,8 +817,7 @@ class FormPlugin extends Plugin
/** @var Pages $pages */
$pages = $this->grav['pages'];
$page = $pages->dispatch($route, true);
$page = $pages->find($route, true);
if ($page) {
unset($this->grav['page']);
$this->grav['page'] = $page;
@ -762,14 +826,32 @@ class FormPlugin extends Plugin
$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
*
* @param string|null $page_route
* @param string|null $route
* @param FormInterface|null $form
* @return void
*/
public function addForm(?string $page_route, ?FormInterface $form)
public function addForm(?string $route, ?FormInterface $form): void
{
if (null === $form) {
return;
@ -777,10 +859,10 @@ class FormPlugin extends Plugin
$name = $form->getName();
if (!isset($this->forms[$page_route][$name])) {
$this->forms[$page_route][$name] = $form;
if (!isset($this->forms[$route][$name])) {
$form['_page_routable'] = true;
$this->flattenForms();
$this->forms[$route][$name] = $form;
$this->recache_forms = true;
}
}
@ -788,45 +870,128 @@ class FormPlugin extends Plugin
/**
* 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
*/
public function getForm($data = null)
public function getForm($data = null): ?FormInterface
{
/** @var Pages $pages */
$pages = $this->grav['pages'];
// Handle parameters.
if (is_array($data)) {
$form_name = $data['name'] ?? null;
$page_route = $data['route'] ?? null;
$name = (string)($data['name'] ?? '');
$route = (string)($data['route'] ?? '');
} elseif (is_string($data)) {
$form_name = $data;
$page_route = null;
$name = $data;
$route = '';
} else {
$form_name = null;
$page_route = null;
$name = '';
$route = '';
}
// if no form name, use the first form found in the page
if (!$form_name) {
// If page route not provided, use the current page
if (!$page_route) {
// 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 always the same form instance.
$form = $this->active_forms[$route][$name] ?? null;
if ($form) {
return $form;
}
// return the form you are looking for if available
return $this->getFormByName($form_name);
$unnamed = $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
*/
public function getFormFieldTypes()
public function getFormFieldTypes(): array
{
return [
'avatar' => [
@ -906,7 +1071,7 @@ class FormPlugin extends Plugin
*
* - fillWithCurrentDateTime
*
* @param Form $form
* @param FormInterface $form
* @return void
*/
protected function process($form)
@ -921,43 +1086,33 @@ class FormPlugin extends Plugin
/**
* Get current page's route
*
* @return mixed
* @return string
*/
protected function getCurrentPageRoute()
{
$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 $unique_id
* @return mixed
* @param string $name
* @return array
*/
protected function getFormByName($form_name, $unique_id = '')
protected function findFormByName(string $name): array
{
$form = $this->active_forms[$form_name] ?? null;
if (!$form) {
$form = $this->flat_forms[$form_name] ?? null;
if (!$form) {
return null;
$list = [];
foreach ($this->forms as $route => $forms) {
foreach ($forms as $key => $form) {
if ($name === $key && !empty($form['_page_routable'])) {
$list[] = [$route, $key, $form];
}
}
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
*/
protected function shouldProcessForm()
protected function shouldProcessForm(): bool
{
/** @var Uri $uri */
$uri = $this->grav['uri'];
$nonce = $uri->post('form-nonce');
$status = $nonce ? true : false; // php72 quirk?
$refresh_prevention = null;
$status = (bool)$uri->post('form-nonce');
if ($status && $form = $this->form()) {
// Make sure form is something we recognize.
@ -1009,29 +1163,14 @@ class FormPlugin extends Plugin
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
*
* @param PageInterface|null $page
* @return Form|null
* @return FormInterface|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 */
$forms = $this->grav['forms'];
@ -1050,11 +1189,16 @@ class FormPlugin extends Plugin
$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
if (!$form && $page) {
$form = $forms->createPageForm($page);
$form = $this->createForm($page);
}
if ($form) {
@ -1073,20 +1217,16 @@ class FormPlugin extends Plugin
/**
* @param PageInterface $page
* @param string|int|null $name
* @param array $form
* @return Form|null
* @deprecated
* @param string|null $name
* @param array|null $form
* @return FormInterface|null
*/
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();
if (isset($header->form) || isset($header->forms)) {
return new Form($page, $name, $form);
}
return null;
return $forms->createPageForm($page, $name, $form);
}
/**
@ -1094,18 +1234,20 @@ class FormPlugin extends Plugin
*
* @return void
*/
protected function loadCachedForms()
protected function loadCachedForms(): void
{
// Get and set the cache of forms if it exists
try {
[$forms] = $this->grav['cache']->fetch($this->getFormCacheId());
} catch (Exception $e) {
// Couldn't fetch cached forms.
$forms = null;
/** @var Cache $cache */
$cache = $this->grav['cache'];
[$forms] = $cache->fetch($this->getFormCacheId());
} catch (Exception $e) {
/** @var Debugger $debugger */
$debugger = Grav::instance()['debugger'];
$debugger->addMessage(sprintf('Unserializing cached forms failed: %s', $e->getMessage()), 'error');
$forms = null;
}
if (!is_array($forms)) {
@ -1113,9 +1255,8 @@ class FormPlugin extends Plugin
}
// Only update the forms if it's not empty
if (!empty($forms)) {
if ($forms) {
$this->forms = array_merge($this->forms, $forms);
$this->flattenForms();
}
}
@ -1124,13 +1265,19 @@ class FormPlugin extends Plugin
*
* @return void
*/
protected function saveCachedForms()
protected function saveCachedForms(): void
{
// Save the current state of the forms to cache
if ($this->recache_forms) {
$this->recache_forms = false;
$this->grav['cache']->save($this->getFormCacheId(), [$this->forms]);
if (!$this->recache_forms) {
return;
}
$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
*/
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)
{
$utimestamp = microtime(true);
if ($raw) {
return date($format);
}
$utimestamp = microtime(true);
$timestamp = floor($utimestamp);
$milliseconds = round(($utimestamp - $timestamp) * 1000000);
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
theme: light
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"
ACCEPT: "Allowed MIME Types"
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"
DATA_SUMMARY: "Here is the summary of what you wrote to us:"
NO_FORM_DATA: "No form data available"
@ -72,6 +73,22 @@ en:
DESTINATION_NOT_SPECIFIED: "Destination not specified"
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."
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:
PLUGIN_FORM:
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-group.has-errors {
@ -287,3 +287,29 @@ $form-active-color: #000;
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 toggleable = field.toggleable ?? false %}
{% if toggleable %}
{% set originalValue = originalValue is defined ? originalValue : value %}
{% set originalValue = originalValue ?? value %}
{% set toggleableChecked = originalValue is not null %}
{% elseif field.overridable %}
{% set toggleable = true %}

View File

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

View File

@ -145,6 +145,7 @@
{% if form.isEnabled() ?? true %}
{% 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.url %}
@ -190,6 +191,7 @@
{% endembed %}
{% if button.outerclasses is defined %}</div>{% endif %}
{% endif %}
{% endfor %}
{% endif %}

View File

@ -5,7 +5,7 @@
{% block prepend %}
<div class="form-input-addon form-input-prepend">
<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>
function stripQueryString(url) {
return url.split("?")[0].split("#")[0];

View File

@ -1,11 +1,5 @@
{% 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 %}
{{ parent() }}
data-grav-keys="{{ field.use == 'keys' ? 'true' : 'false' }}"
@ -13,6 +7,11 @@
{% endblock %}
{% 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 %}
{% set id = field.id|default(field.name)|hyphenize ~ '-' ~ key %}

View File

@ -1,7 +1,7 @@
{% extends "forms/field.html.twig" %}
{% 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_close %}</div>{% endblock %}
{% endembed %}

View File

@ -2,6 +2,6 @@
{% block field %}
<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>
{% endblock %}

View File

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

View File

@ -28,6 +28,11 @@
data-{{ datakey }}="{{ datavalue|e('html_attr') }}"
{% endfor %}
{% 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 %}
@ -43,9 +48,9 @@
{% set akey = field.selectize and field.multiple ? item_value : key %}
{% set avalue = item_value.value|t %}
<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 : '' }}
value="{{ item_value.akey }}"
value="{{ akey }}"
>
{{ avalue|raw }}
</option>

View File

@ -1,6 +1,6 @@
{% 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.min.js', { 'group': 'bottom', 'loading': 'defer' }) %}
{% endif %}
@ -29,7 +29,7 @@
{% endfor %}
{% else %}
{% 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) %}
{% if storedTab is empty %}
{% 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
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -66,28 +120,47 @@ class ClassLoader
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
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)
{
@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
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
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
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.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
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.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @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)
{
@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
@ -367,6 +475,21 @@ class ClassLoader
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)
{
// PSR-4 lookup
@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{

View File

@ -1,228 +1,350 @@
<?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;
use Composer\Autoload\ClassLoader;
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
{
private static $installed = array (
'root' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'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 mixed[]|null
* @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
*/
private static $installed;
/**
* @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()
{
return array_keys(self::$installed['versions']);
}
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
$ranges = array();
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['version'])) {
return null;
}
return self::$installed['versions'][$packageName]['version'];
}
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return self::$installed['versions'][$packageName]['pretty_version'];
}
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
return null;
}
return self::$installed['versions'][$packageName]['reference'];
}
public static function getRootPackage()
{
return self::$installed['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
}
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @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;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* 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));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
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])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
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 version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
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 version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
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 reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
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;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
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';
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'));
$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"
}
],
"dev": false,
"dev": true,
"dev-package-names": []
}

View File

@ -1,33 +1,32 @@
<?php return array (
'root' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'aliases' =>
array (
<?php return array(
'root' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '2e6d16dbc801d605f56d2d2942cd99a8bf63550c',
'name' => 'getgrav/grav-plugin-form',
'dev' => true,
),
'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',
'versions' => array(
'getgrav/grav-plugin-form' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '2e6d16dbc801d605f56d2d2942cd99a8bf63550c',
'dev_requirement' => false,
),
'google/recaptcha' => array(
'pretty_version' => '1.2.4',
'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
## 01/12/2022
@ -10,7 +46,7 @@
1. [](#bugfix)
* Fixed issue with forgot password error message translation [#285](https://github.com/getgrav/grav-plugin-login/pull/285)
*
# v3.6.0
## 10/26/2021

View File

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

View File

@ -12,12 +12,12 @@ namespace Grav\Plugin\Login;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Page\Pages;
use Grav\Common\Uri;
use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Utils;
use Grav\Plugin\Email\Utils as EmailUtils;
use Grav\Plugin\Form\Form;
use Grav\Plugin\Form\Forms;
use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Invitations\Invitation;
@ -343,7 +343,8 @@ class Controller
*/
protected function taskForgot()
{
$param_sep = $this->grav['config']->get('system.param_sep', ':');
/** @var Config $config */
$config = $this->grav['config'];
$data = $this->post;
/** @var UserCollectionInterface $users */
@ -386,7 +387,7 @@ class Controller
return true;
}
$from = $this->grav['config']->get('plugins.email.from');
$from = $config->get('plugins.email.from');
if (empty($from)) {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_EMAIL_NOT_CONFIGURED'), 'error');
@ -406,37 +407,18 @@ class Controller
return true;
}
$token = md5(uniqid(mt_rand(), true));
$token = md5(uniqid((string)mt_rand(), true));
$expire = time() + 604800; // next week
$user->reset = $token . '::' . $expire;
$user->save();
$author = $this->grav['config']->get('site.author.name', '');
$fullname = $user->fullname ?: $user->username;
try {
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');
} catch (\Exception $e) {
$messages->add($language->translate('PLUGIN_LOGIN.FORGOT_FAILED_TO_EMAIL'), 'error');
}
$this->setRedirect($this->login->getRoute('login') ?? '/');

View File

@ -3,7 +3,7 @@
/**
* @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.
*/
@ -19,7 +19,7 @@ use RocketTheme\Toolbox\Event\Event;
/**
* Class UserLoginEvent
* @package Grav\Common\User\Events
* @package Grav\Plugin\Login\Events
*
* @property int $status
* @property array $credentials

View File

@ -24,7 +24,7 @@ use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Uri;
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\Invitations\Invitation;
use Grav\Plugin\Login\RememberMe\RememberMe;
@ -445,25 +445,9 @@ class Login
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD'));
}
$site_name = $this->config->get('site.title', 'Website');
$subject = $this->language->translate(['PLUGIN_LOGIN.NOTIFICATION_EMAIL_SUBJECT', $site_name]);
$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) {
try {
Email::sendNotificationEmail($user);
} catch (\Exception $e) {
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'));
}
$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.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) {
try {
Email::sendWelcomeEmail($user);
} catch (\Exception $e) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
}
@ -525,25 +496,9 @@ class Login
$user->activation_token = $token . '::' . $expire;
$user->save();
$param_sep = $this->config->get('system.param_sep', ':');
$activationRoute = $this->getRoute('activate');
$activation_link = $this->grav['base_url_absolute'] . $activationRoute . '/token' . $param_sep . $token . '/username' . $param_sep . $user->username;
$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) {
try {
Email::sendActivationEmail($user);
} catch (\Exception $e) {
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)
{
/** @var UserInterface $user */
$user = $user ?? $this->grav['user'];
$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) {
try {
Email::sendInvitationEmail($invitation, $message, $user);
} catch (\Exception $e) {
throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE'));
}
@ -789,27 +726,15 @@ class Login
* @param Data|null $config
* @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();
$rules = (array)($header->access ?? []);
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) {
/** @var PageAuthorizeEvent $event */
$event = $this->grav->dispatchEvent(new PageAuthorizeEvent($page, $user, $config));
if (!$event->hasProtectedAccess()) {
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();
if (!$cacheControl) {
$cacheControl = 'private, no-cache, must-revalidate';
@ -831,28 +756,12 @@ class Login
$page->cacheControl($cacheControl);
// Deny access if user has not completed 2FA challenge.
$user = $event->user;
if ($user->authenticated && !$user->authorized) {
return false;
$event->deny();
}
// Continue to the page if user is authorized to access the page.
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;
return $event->isAllowed();
}
/**

View File

@ -26,10 +26,10 @@
"docs": "https://github.com/getgrav/grav-plugin-login/blob/master/README.md"
},
"require": {
"php": ">=7.1.3",
"php": ">=7.3.6",
"ext-json": "*",
"birke/rememberme": "1.*",
"robthree/twofactorauth": "^1.6",
"birke/rememberme": "^1.0",
"robthree/twofactorauth": "^1.8",
"bacon/bacon-qr-code": "^1.0"
},
"autoload": {
@ -43,7 +43,7 @@
},
"config": {
"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",
"This file is @generated automatically"
],
"content-hash": "bbcc2f78d1043e3e053994cf79736169",
"content-hash": "a76edd1409f49153b8c55e0f6013ae17",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -160,16 +160,16 @@
},
{
"name": "robthree/twofactorauth",
"version": "1.8.0",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c"
"reference": "65681de5a324eae05140ac58b08648a60212afc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/30a38627ae1e7c9399dae67e265063cd6ec5276c",
"reference": "30a38627ae1e7c9399dae67e265063cd6ec5276c",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": ""
},
"require": {
@ -226,7 +226,7 @@
"type": "github"
}
],
"time": "2021-03-09T18:24:05+00:00"
"time": "2022-03-22T16:11:07+00:00"
}
],
"packages-dev": [],
@ -236,12 +236,12 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.1.3",
"php": ">=7.3.6",
"ext-json": "*"
},
"platform-dev": [],
"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_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_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\Utils;
use Grav\Common\Uri;
use Grav\Events\BeforeSessionStartEvent;
use Grav\Events\PluginsLoadedEvent;
use Grav\Events\SessionStartEvent;
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Form\Interfaces\FormInterface;
use Grav\Framework\Psr7\Response;
use Grav\Framework\Session\SessionInterface;
use Grav\Plugin\Form\Form;
use Grav\Plugin\Login\Events\PageAuthorizeEvent;
use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Invitations\Invitation;
use Grav\Plugin\Login\Invitations\Invitations;
@ -38,6 +41,7 @@ use Grav\Plugin\Login\Controller;
use Grav\Plugin\Login\RememberMe\RememberMe;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Session\Message;
use function is_array;
/**
* Class LoginPlugin
@ -59,6 +63,9 @@ class LoginPlugin extends Plugin
/** @var Invitation|null */
protected $invitation;
protected $temp_redirect;
protected $temp_messages;
/**
* @return array
*/
@ -67,6 +74,8 @@ class LoginPlugin extends Plugin
return [
PluginsLoadedEvent::class => [['onPluginsLoaded', 10]],
SessionStartEvent::class => ['onSessionStart', 0],
BeforeSessionStartEvent::class => ['onBeforeSessionStart', 0],
PageAuthorizeEvent::class => ['onPageAuthorizeEvent', -10000],
'onPluginsInitialized' => [['initializeSession', 10000], ['initializeLogin', 1000]],
'onTask.login.login' => ['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
{
$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;
if ($user && $user->exists() && ($this->config()['session_user_sync'] ?? false)) {
// User is stored into the filesystem.
@ -303,7 +337,7 @@ class LoginPlugin extends Plugin
if ($page) {
$header = $page->header();
$allowed = ($header->login_redirect_here ?? true) === false;
$allowed = ($header->login_redirect_here ?? true) === true;
if ($allowed && $page->routable()) {
$redirect = $page->route();
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
*/
@ -590,7 +685,7 @@ class LoginPlugin extends Plugin
$user = $this->grav['user'];
$page = $this->grav['page'] ?? null;
if (!$page instanceof PageInterface) {
if (!$page instanceof PageInterface || $page->isModule()) {
return;
}
@ -603,8 +698,9 @@ class LoginPlugin extends Plugin
$uri_extension = $this->grav['uri']->extension('html');
$supported_types = $this->config->get('media.types');
if ($uri_extension !== 'html' && array_key_exists($uri_extension, $supported_types)) {
header('HTTP/1.0 403 Forbidden');
exit;
$response = new Response(403);
$this->grav->close($response);
}
// User is not logged in; redirect to login page.
@ -783,7 +879,7 @@ class LoginPlugin extends Plugin
foreach ($default_values as $key => $param) {
if ($key === $field) {
if (\is_array($param)) {
if (is_array($param)) {
$values = explode(',', $param);
} else {
$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
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -66,28 +120,47 @@ class ClassLoader
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
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)
{
@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
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
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
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.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
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.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @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)
{
@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
@ -367,6 +475,21 @@ class ClassLoader
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)
{
// PSR-4 lookup
@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{

View File

@ -1,255 +1,350 @@
<?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;
use Composer\Autoload\ClassLoader;
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
{
private static $installed = array (
'root' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'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 mixed[]|null
* @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
*/
private static $installed;
/**
* @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()
{
return array_keys(self::$installed['versions']);
}
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
$ranges = array();
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['version'])) {
return null;
}
return self::$installed['versions'][$packageName]['version'];
}
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return self::$installed['versions'][$packageName]['pretty_version'];
}
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
return null;
}
return self::$installed['versions'][$packageName]['reference'];
}
public static function getRootPackage()
{
return self::$installed['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
}
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @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;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* 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));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
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])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
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 version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
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 version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
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 reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
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;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
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';
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'));
$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)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

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

View File

@ -1,60 +1,59 @@
<?php return array (
'root' =>
array (
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'aliases' =>
array (
<?php return array(
'root' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'name' => 'getgrav/grav-plugin-login',
'dev' => false,
),
'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',
'versions' => array(
'bacon/bacon-qr-code' => array(
'pretty_version' => '1.0.3',
'version' => '1.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../bacon/bacon-qr-code',
'aliases' => array(),
'reference' => '5a91b62b9d37cee635bbf8d553f4546057250bee',
'dev_requirement' => false,
),
'birke/rememberme' => array(
'pretty_version' => '1.0.5',
'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();
if (!(PHP_VERSION_ID >= 70103)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.3". You are running ' . PHP_VERSION . '.';
if (!(PHP_VERSION_ID >= 70306)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.6". You are running ' . PHP_VERSION . '.';
}
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:
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:
- uses: actions/checkout@v2
@ -20,6 +20,7 @@ jobs:
php-version: ${{ matrix.php-version }}
tools: composer
coverage: xdebug
ini-values: error_reporting=E_ALL
- 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