(Grav GitSync) Automatic Commit from RealStickman

This commit is contained in:
RealStickman 2022-06-23 14:34:01 +02:00 committed by GitSync
parent d83d133b81
commit 9096c10896
72 changed files with 4993 additions and 886 deletions

View File

@ -14,3 +14,4 @@ tests/_support/_generated/*
tests/cache/* tests/cache/*
tests/error.log tests/error.log
/crowdin.yaml /crowdin.yaml
.vscode

View File

@ -1,8 +1,16 @@
# v1.10.34
## 06/22/2022
1. [](#improved)
* Exposed `UriToMarkdown` util (`Grav.default.Utils.UriToMarkdown`) in admin, to convert links/images
1. [](#bugfix)
* Fixed `Latest Page Updates` permissions [#2294](https://github.com/getgrav/grav-plugin-admin/pull/2294)
# v1.10.33.1 # v1.10.33.1
## 04/25/2022 ## 04/25/2022
1. [](#bugfix) 1. [](#bugfix)
* Reverted [PR#2265](https://github.com/getgrav/grav-plugin-admin/pull/2265) as it broke sections output. * Reverted [PR#2265](https://github.com/getgrav/grav-plugin-admin/pull/2265) as it broke sections output
# v1.10.33 # v1.10.33
## 04/25/2022 ## 04/25/2022

View File

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

View File

@ -190,16 +190,16 @@
}, },
{ {
"name": "scssphp/scssphp", "name": "scssphp/scssphp",
"version": "v1.10.2", "version": "v1.10.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/scssphp/scssphp.git", "url": "https://github.com/scssphp/scssphp.git",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46" "reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/387f4f4abf5d99f16be16314c5ab856f81c82f46", "url": "https://api.github.com/repos/scssphp/scssphp/zipball/0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46", "reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -258,9 +258,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/scssphp/scssphp/issues", "issues": "https://github.com/scssphp/scssphp/issues",
"source": "https://github.com/scssphp/scssphp/tree/v1.10.2" "source": "https://github.com/scssphp/scssphp/tree/v1.10.3"
}, },
"time": "2022-03-02T21:15:09+00:00" "time": "2022-05-16T07:22:18+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -425,16 +425,16 @@
}, },
{ {
"name": "codeception/phpunit-wrapper", "name": "codeception/phpunit-wrapper",
"version": "7.8.2", "version": "7.8.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Codeception/phpunit-wrapper.git", "url": "https://github.com/Codeception/phpunit-wrapper.git",
"reference": "cafed18048826790c527843f9b85e8cc79b866f1" "reference": "dd44fc152433d27d3de03d59b4945449b3407af0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/cafed18048826790c527843f9b85e8cc79b866f1", "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/dd44fc152433d27d3de03d59b4945449b3407af0",
"reference": "cafed18048826790c527843f9b85e8cc79b866f1", "reference": "dd44fc152433d27d3de03d59b4945449b3407af0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -466,9 +466,9 @@
"description": "PHPUnit classes used by Codeception", "description": "PHPUnit classes used by Codeception",
"support": { "support": {
"issues": "https://github.com/Codeception/phpunit-wrapper/issues", "issues": "https://github.com/Codeception/phpunit-wrapper/issues",
"source": "https://github.com/Codeception/phpunit-wrapper/tree/7.8.2" "source": "https://github.com/Codeception/phpunit-wrapper/tree/7.8.4"
}, },
"time": "2020-12-28T14:00:26+00:00" "time": "2022-05-23T06:09:22+00:00"
}, },
{ {
"name": "codeception/stub", "name": "codeception/stub",
@ -697,16 +697,16 @@
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
"version": "6.5.5", "version": "6.5.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/guzzle.git", "url": "https://github.com/guzzle/guzzle.git",
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" "reference": "724562fa861e21a4071c652c8a159934e4f05592"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/724562fa861e21a4071c652c8a159934e4f05592",
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "reference": "724562fa861e21a4071c652c8a159934e4f05592",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -743,10 +743,40 @@
"MIT" "MIT"
], ],
"authors": [ "authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{ {
"name": "Michael Dowling", "name": "Michael Dowling",
"email": "mtdowling@gmail.com", "email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling" "homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
} }
], ],
"description": "Guzzle is a PHP HTTP client library", "description": "Guzzle is a PHP HTTP client library",
@ -762,9 +792,23 @@
], ],
"support": { "support": {
"issues": "https://github.com/guzzle/guzzle/issues", "issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/6.5" "source": "https://github.com/guzzle/guzzle/tree/6.5.7"
}, },
"time": "2020-06-16T21:01:06+00:00" "funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
],
"time": "2022-06-09T21:36:50+00:00"
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
@ -1241,16 +1285,16 @@
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
"version": "1.6.0", "version": "1.6.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git", "url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" "reference": "77a32518733312af16a44300404e945338981de3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", "reference": "77a32518733312af16a44300404e945338981de3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1285,9 +1329,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": { "support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues", "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
}, },
"time": "2022-01-04T19:58:01+00:00" "time": "2022-03-15T21:29:03+00:00"
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
@ -2618,16 +2662,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v4.4.38", "version": "v4.4.42",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "5a50085bf5460f0c0d60a50b58388c1249826b8a" "reference": "cce7a9f99e22937a71a16b23afa762558808d587"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5a50085bf5460f0c0d60a50b58388c1249826b8a", "url": "https://api.github.com/repos/symfony/console/zipball/cce7a9f99e22937a71a16b23afa762558808d587",
"reference": "5a50085bf5460f0c0d60a50b58388c1249826b8a", "reference": "cce7a9f99e22937a71a16b23afa762558808d587",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2688,7 +2732,7 @@
"description": "Eases the creation of beautiful and testable command line interfaces", "description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v4.4.38" "source": "https://github.com/symfony/console/tree/v4.4.42"
}, },
"funding": [ "funding": [
{ {
@ -2704,7 +2748,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-01-30T21:23:57+00:00" "time": "2022-05-14T12:35:33+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@ -2774,16 +2818,16 @@
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
"version": "v2.5.0", "version": "v2.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git", "url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2821,7 +2865,7 @@
"description": "A generic function and convention to trigger deprecation notices", "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
}, },
"funding": [ "funding": [
{ {
@ -2837,20 +2881,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-07-12T14:48:14+00:00" "time": "2022-01-02T09:53:40+00:00"
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
"version": "v4.4.39", "version": "v4.4.42",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dom-crawler.git", "url": "https://github.com/symfony/dom-crawler.git",
"reference": "4e9215a8b533802ba84a3cc5bd3c43103e7a6dc3" "reference": "be5a04618e5d44e71d013f177df80d3ec4b192a0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4e9215a8b533802ba84a3cc5bd3c43103e7a6dc3", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/be5a04618e5d44e71d013f177df80d3ec4b192a0",
"reference": "4e9215a8b533802ba84a3cc5bd3c43103e7a6dc3", "reference": "be5a04618e5d44e71d013f177df80d3ec4b192a0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2895,7 +2939,7 @@
"description": "Eases DOM navigation for HTML and XML documents", "description": "Eases DOM navigation for HTML and XML documents",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/dom-crawler/tree/v4.4.39" "source": "https://github.com/symfony/dom-crawler/tree/v4.4.42"
}, },
"funding": [ "funding": [
{ {
@ -2911,20 +2955,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-02-25T10:38:15+00:00" "time": "2022-04-30T18:34:00+00:00"
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v4.4.37", "version": "v4.4.42",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "3ccfcfb96ecce1217d7b0875a0736976bc6e63dc" "reference": "708e761740c16b02c86e3f0c932018a06b895d40"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3ccfcfb96ecce1217d7b0875a0736976bc6e63dc", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/708e761740c16b02c86e3f0c932018a06b895d40",
"reference": "3ccfcfb96ecce1217d7b0875a0736976bc6e63dc", "reference": "708e761740c16b02c86e3f0c932018a06b895d40",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2979,7 +3023,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v4.4.37" "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.42"
}, },
"funding": [ "funding": [
{ {
@ -2995,20 +3039,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-01-02T09:41:36+00:00" "time": "2022-05-05T15:33:49+00:00"
}, },
{ {
"name": "symfony/event-dispatcher-contracts", "name": "symfony/event-dispatcher-contracts",
"version": "v1.1.11", "version": "v1.1.12",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git", "url": "https://github.com/symfony/event-dispatcher-contracts.git",
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c" "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/01e9a4efac0ee33a05dfdf93b346f62e7d0e998c", "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/1d5cd762abaa6b2a4169d3e77610193a7157129e",
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c", "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3058,7 +3102,7 @@
"standards" "standards"
], ],
"support": { "support": {
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.11" "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.12"
}, },
"funding": [ "funding": [
{ {
@ -3074,20 +3118,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-03-23T15:25:38+00:00" "time": "2022-01-02T09:41:36+00:00"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v4.4.37", "version": "v4.4.41",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/finder.git", "url": "https://github.com/symfony/finder.git",
"reference": "b17d76d7ed179f017aad646e858c90a2771af15d" "reference": "40790bdf293b462798882ef6da72bb49a4a6633a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/b17d76d7ed179f017aad646e858c90a2771af15d", "url": "https://api.github.com/repos/symfony/finder/zipball/40790bdf293b462798882ef6da72bb49a4a6633a",
"reference": "b17d76d7ed179f017aad646e858c90a2771af15d", "reference": "40790bdf293b462798882ef6da72bb49a4a6633a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3120,7 +3164,7 @@
"description": "Finds files and directories via an intuitive fluent interface", "description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/finder/tree/v4.4.37" "source": "https://github.com/symfony/finder/tree/v4.4.41"
}, },
"funding": [ "funding": [
{ {
@ -3136,20 +3180,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-01-02T09:41:36+00:00" "time": "2022-04-14T15:36:10+00:00"
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab" "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"reference": "30885182c981ab175d4d034db0f6f469898070ab", "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3164,7 +3208,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3202,7 +3246,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -3218,20 +3262,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-10-20T20:35:02+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44" "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44", "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3245,7 +3289,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3289,7 +3333,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -3305,20 +3349,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-09-14T14:02:44+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3330,7 +3374,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3373,7 +3417,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -3389,20 +3433,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-19T12:13:01+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3417,7 +3461,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3456,7 +3500,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -3472,20 +3516,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-11-30T18:21:41+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3494,7 +3538,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3539,7 +3583,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -3555,20 +3599,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-04T08:16:47+00:00" "time": "2022-05-10T07:21:04+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v4.4.37", "version": "v4.4.41",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67" "reference": "9eedd60225506d56e42210a70c21bb80ca8456ce"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", "url": "https://api.github.com/repos/symfony/process/zipball/9eedd60225506d56e42210a70c21bb80ca8456ce",
"reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", "reference": "9eedd60225506d56e42210a70c21bb80ca8456ce",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3601,7 +3645,7 @@
"description": "Executes commands in sub-processes", "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/process/tree/v4.4.37" "source": "https://github.com/symfony/process/tree/v4.4.41"
}, },
"funding": [ "funding": [
{ {
@ -3617,26 +3661,26 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-01-27T17:14:04+00:00" "time": "2022-04-04T10:19:07+00:00"
}, },
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",
"version": "v2.5.0", "version": "v2.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/service-contracts.git", "url": "https://github.com/symfony/service-contracts.git",
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2.5", "php": ">=7.2.5",
"psr/container": "^1.1", "psr/container": "^1.1",
"symfony/deprecation-contracts": "^2.1" "symfony/deprecation-contracts": "^2.1|^3"
}, },
"conflict": { "conflict": {
"ext-psr": "<1.1|>=2" "ext-psr": "<1.1|>=2"
@ -3684,7 +3728,7 @@
"standards" "standards"
], ],
"support": { "support": {
"source": "https://github.com/symfony/service-contracts/tree/v2.5.0" "source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
}, },
"funding": [ "funding": [
{ {
@ -3700,7 +3744,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-11-04T16:48:04+00:00" "time": "2022-03-13T20:07:29+00:00"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
@ -3825,21 +3869,21 @@
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
"version": "1.10.0", "version": "1.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/webmozarts/assert.git", "url": "https://github.com/webmozarts/assert.git",
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "ext-ctype": "*",
"symfony/polyfill-ctype": "^1.8" "php": "^7.2 || ^8.0"
}, },
"conflict": { "conflict": {
"phpstan/phpstan": "<0.12.20", "phpstan/phpstan": "<0.12.20",
@ -3877,9 +3921,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/webmozarts/assert/issues", "issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.10.0" "source": "https://github.com/webmozarts/assert/tree/1.11.0"
}, },
"time": "2021-03-09T10:59:23+00:00" "time": "2022-06-03T18:03:27+00:00"
} }
], ],
"aliases": [], "aliases": [],

View File

@ -454,8 +454,11 @@ PLUGIN_ADMIN:
IMAGES_DEBUG_HELP: "Show an overlay over images indicating the pixel depth of the image when working with retina for example" IMAGES_DEBUG_HELP: "Show an overlay over images indicating the pixel depth of the image when working with retina for example"
IMAGES_LOADING: "Image loading behavior" IMAGES_LOADING: "Image loading behavior"
IMAGES_LOADING_HELP: "The loading attribute allows a browser to defer loading offscreen images and iframes until users scroll near them. loading supports three values: auto, lazy, eager" IMAGES_LOADING_HELP: "The loading attribute allows a browser to defer loading offscreen images and iframes until users scroll near them. loading supports three values: auto, lazy, eager"
# Removed in Grav 1.8
IMAGES_SEOFRIENDLY: "SEO-Friendly Image names" IMAGES_SEOFRIENDLY: "SEO-Friendly Image names"
IMAGES_SEOFRIENDLY_HELP: "When enabled, the image name is displayed first, then a smaller hash to reflect processed operations" IMAGES_SEOFRIENDLY_HELP: "When enabled, the image name is displayed first, then a smaller hash to reflect processed operations"
UPLOAD_LIMIT: "File upload limit" UPLOAD_LIMIT: "File upload limit"
UPLOAD_LIMIT_HELP: "Set maximum upload size in bytes (0 is unlimited)" UPLOAD_LIMIT_HELP: "Set maximum upload size in bytes (0 is unlimited)"
ENABLE_MEDIA_TIMESTAMP: "Enable timestamps on media" ENABLE_MEDIA_TIMESTAMP: "Enable timestamps on media"
@ -1142,3 +1145,7 @@ PLUGIN_ADMIN:
AVATAR: "Avatar Generator" AVATAR: "Avatar Generator"
AVATAR_HELP: "Multiavatar is a locally generated avatar. Gravatar is an external service that uses your email address to pull a preconfigured Avatar remotely" AVATAR_HELP: "Multiavatar is a locally generated avatar. Gravatar is an external service that uses your email address to pull a preconfigured Avatar remotely"
AVATAR_HASH: "NOTE: Optional Avatar custom 'hash' string" AVATAR_HASH: "NOTE: Optional Avatar custom 'hash' string"
IMAGES_TITLE: "Images"
LEGACY_MEDIA_MUTATION: "Legacy Media Manipulation Compatibility"
LEGACY_MEDIA_MUTATION_HELP: "Enable this setting only if image manipulation broke after Grav update."
BACKWARD_COMPATIBILITY: "Backward Compatibility"

View File

@ -4,6 +4,7 @@ import '@babel/polyfill';
import $ from 'jquery'; import $ from 'jquery';
import './utils/remodal'; import './utils/remodal';
import 'simplebar/dist/simplebar.min.js'; import 'simplebar/dist/simplebar.min.js';
import { UriToMarkdown } from './forms/fields/files.js';
import GPM, { Instance as gpm } from './utils/gpm'; import GPM, { Instance as gpm } from './utils/gpm';
import KeepAlive from './utils/keepalive'; import KeepAlive from './utils/keepalive';
import Updates, { Instance as updates, Notifications, Feed } from './updates'; import Updates, { Instance as updates, Notifications, Feed } from './updates';
@ -68,5 +69,5 @@ export default {
Instance: MediaFilterInstance Instance: MediaFilterInstance
}, },
Scrollbar: { Scrollbar: { deprecated: true }, Instance: { deprecated: true } }, Scrollbar: { Scrollbar: { deprecated: true }, Instance: { deprecated: true } },
Utils: { request, toastr, Cookies } Utils: { request, toastr, Cookies, UriToMarkdown }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
{% if authorize(['admin.pages', 'admin.super']) %} {% if authorize(['admin.pages.list', 'admin.pages', 'admin.super']) %}
<div id="latest"> <div id="latest">
<div class="button-bar"> <div class="button-bar">
<a class="button" href="{{ admin_route('/pages') }}"><i class="fa fa-fw fa-file-text-o"></i>{{ "PLUGIN_ADMIN.MANAGE_PAGES"|t }}</a> <a class="button" href="{{ admin_route('/pages') }}"><i class="fa fa-fw fa-file-text-o"></i>{{ "PLUGIN_ADMIN.MANAGE_PAGES"|t }}</a>
@ -15,6 +15,4 @@
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
{% else %}
<div class="padding">You don't have sufficient access to view the dashboard...</div>
{% endif %} {% endif %}

View File

@ -193,17 +193,17 @@
}, },
{ {
"name": "scssphp/scssphp", "name": "scssphp/scssphp",
"version": "v1.10.2", "version": "v1.10.3",
"version_normalized": "1.10.2.0", "version_normalized": "1.10.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/scssphp/scssphp.git", "url": "https://github.com/scssphp/scssphp.git",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46" "reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/387f4f4abf5d99f16be16314c5ab856f81c82f46", "url": "https://api.github.com/repos/scssphp/scssphp/zipball/0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46", "reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -226,7 +226,7 @@
"ext-iconv": "Can be used as fallback when ext-mbstring is not available", "ext-iconv": "Can be used as fallback when ext-mbstring is not available",
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv" "ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
}, },
"time": "2022-03-02T21:15:09+00:00", "time": "2022-05-16T07:22:18+00:00",
"bin": [ "bin": [
"bin/pscss" "bin/pscss"
], ],
@ -264,7 +264,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/scssphp/scssphp/issues", "issues": "https://github.com/scssphp/scssphp/issues",
"source": "https://github.com/scssphp/scssphp/tree/v1.10.2" "source": "https://github.com/scssphp/scssphp/tree/v1.10.3"
}, },
"install-path": "../scssphp/scssphp" "install-path": "../scssphp/scssphp"
} }

View File

@ -5,7 +5,7 @@
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '5c1e6d6d52f8fc8e0540b5599e736e25ea20c446', 'reference' => '0a5b51b9e2c2eb9626b17c8b164dcfc3296ccf3c',
'name' => 'getgrav/grav-plugin-admin', 'name' => 'getgrav/grav-plugin-admin',
'dev' => false, 'dev' => false,
), ),
@ -16,7 +16,7 @@
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '5c1e6d6d52f8fc8e0540b5599e736e25ea20c446', 'reference' => '0a5b51b9e2c2eb9626b17c8b164dcfc3296ccf3c',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'laminas/laminas-xml' => array( 'laminas/laminas-xml' => array(
@ -53,12 +53,12 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'scssphp/scssphp' => array( 'scssphp/scssphp' => array(
'pretty_version' => 'v1.10.2', 'pretty_version' => 'v1.10.3',
'version' => '1.10.2.0', 'version' => '1.10.3.0',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../scssphp/scssphp', 'install_path' => __DIR__ . '/../scssphp/scssphp',
'aliases' => array(), 'aliases' => array(),
'reference' => '387f4f4abf5d99f16be16314c5ab856f81c82f46', 'reference' => '0f1e1516ed2412ad43e42a6a319e77624ba1f713',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php72' => array( 'symfony/polyfill-php72' => array(

View File

@ -2350,9 +2350,9 @@ class Compiler
} }
/** /**
* Compile children and throw exception if unexpected `@return` * Compile children and throw exception if unexpected at-return
* *
* @param array $stms * @param array[] $stms
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out
* @param \ScssPhp\ScssPhp\Block $selfParent * @param \ScssPhp\ScssPhp\Block $selfParent
* @param string $traceName * @param string $traceName
@ -2367,13 +2367,13 @@ class Compiler
foreach ($stms as $stm) { foreach ($stms as $stm) {
if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) { if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) {
$oldSelfParent = $stm[1]->selfParent;
$stm[1]->selfParent = $selfParent; $stm[1]->selfParent = $selfParent;
$ret = $this->compileChild($stm, $out); $ret = $this->compileChild($stm, $out);
$stm[1]->selfParent = null; $stm[1]->selfParent = $oldSelfParent;
} elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) { } elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) {
$stm['selfParent'] = $selfParent; $stm['selfParent'] = $selfParent;
$ret = $this->compileChild($stm, $out); $ret = $this->compileChild($stm, $out);
unset($stm['selfParent']);
} else { } else {
$ret = $this->compileChild($stm, $out); $ret = $this->compileChild($stm, $out);
} }

View File

@ -14,6 +14,8 @@ namespace ScssPhp\ScssPhp\Logger;
/** /**
* A logger that silently ignores all messages. * A logger that silently ignores all messages.
*
* @final
*/ */
class QuietLogger implements LoggerInterface class QuietLogger implements LoggerInterface
{ {

View File

@ -14,6 +14,8 @@ namespace ScssPhp\ScssPhp\Logger;
/** /**
* A logger that prints to a PHP stream (for instance stderr) * A logger that prints to a PHP stream (for instance stderr)
*
* @final
*/ */
class StreamLogger implements LoggerInterface class StreamLogger implements LoggerInterface
{ {

View File

@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp;
*/ */
class Version class Version
{ {
const VERSION = '1.10.2'; const VERSION = '1.10.3';
} }

3
plugins/flex-objects/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
.DS_Store
node_modules

View File

@ -1,3 +1,16 @@
# v1.3.0
## 06/14/2022
1. [](#new)
* Added user object to `onFlexTask.*` and `onFlexAction.*` events
* Added tasks `MediaUploadMeta` and `MediaReorder` to support remote media fields
* Added support to remove media defined in a field
2. [](#improved)
* Refactored admin controller tasks and actions
* Added image preview support for 3rd party editors
1. [](#bugfix)
* Fixed broken error responses in object media tasks
# v1.2.0 # v1.2.0
## 03/28/2022 ## 03/28/2022

View File

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

View File

@ -12,6 +12,7 @@ use Grav\Common\Flex\Types\Pages\PageCollection;
use Grav\Common\Flex\Types\Pages\PageIndex; use Grav\Common\Flex\Types\Pages\PageIndex;
use Grav\Common\Flex\Types\Pages\PageObject; use Grav\Common\Flex\Types\Pages\PageObject;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Helpers\Excerpts;
use Grav\Common\Language\Language; use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Uri; use Grav\Common\Uri;
@ -120,9 +121,9 @@ class AdminController
/** /**
* Unknown task, call onFlexTask[NAME] event. * Unknown task, call onFlexTask[NAME] event.
* *
* @return bool * @return void
*/ */
public function taskDefault(): bool public function taskDefault(): void
{ {
$type = $this->target; $type = $this->target;
$directory = $this->getDirectory($type); $directory = $this->getDirectory($type);
@ -143,6 +144,7 @@ class AdminController
'directory' => $directory, 'directory' => $directory,
'object' => $object, 'object' => $object,
'data' => $this->data, 'data' => $this->data,
'user' => $this->user,
'redirect' => $this->redirect 'redirect' => $this->redirect
] ]
); );
@ -160,19 +162,15 @@ class AdminController
if ($redirect) { if ($redirect) {
$this->setRedirect($redirect); $this->setRedirect($redirect);
} }
return $event->isPropagationStopped();
} }
return false;
} }
/** /**
* Default action, onFlexAction[NAME] event. * Default action, onFlexAction[NAME] event.
* *
* @return bool * @return void
*/ */
public function actionDefault(): bool public function actionDefault(): void
{ {
$type = $this->target; $type = $this->target;
$directory = $this->getDirectory($type); $directory = $this->getDirectory($type);
@ -192,6 +190,7 @@ class AdminController
'flex' => $this->getFlex(), 'flex' => $this->getFlex(),
'directory' => $directory, 'directory' => $directory,
'object' => $object, 'object' => $object,
'user' => $this->user,
'redirect' => $this->redirect 'redirect' => $this->redirect
] ]
); );
@ -209,19 +208,15 @@ class AdminController
if ($redirect) { if ($redirect) {
$this->setRedirect($redirect); $this->setRedirect($redirect);
} }
return $event->isPropagationStopped();
} }
return false;
} }
/** /**
* Get datatable for list view. * Get datatable for list view.
* *
* @return void * @return ResponseInterface|null
*/ */
public function actionList(): void public function actionList(): ?ResponseInterface
{ {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
if (!$directory) { if (!$directory) {
@ -248,28 +243,28 @@ class AdminController
$table = $this->getFlex()->getDataTable($directory, $options); $table = $this->getFlex()->getDataTable($directory, $options);
$response = $this->createJsonResponse($table->jsonSerialize()); return $this->createJsonResponse($table->jsonSerialize());
$this->close($response);
} }
return null;
} }
/** /**
* Alias for Export action. * Alias for Export action.
* *
* @return void * @return ResponseInterface|null
*/ */
public function actionCsv(): void public function actionCsv(): ?ResponseInterface
{ {
$this->actionExport(); return $this->actionExport();
} }
/** /**
* Export action. Defaults to CVS export. * Export action. Defaults to CVS export.
* *
* @return void * @return ResponseInterface|null
*/ */
public function actionExport(): void public function actionExport(): ?ResponseInterface
{ {
$collection = $this->getCollection(); $collection = $this->getCollection();
if (!$collection) { if (!$collection) {
@ -341,15 +336,15 @@ class AdminController
$formatter->encode($list) $formatter->encode($list)
); );
$this->close($response); return $response;
} }
/** /**
* Delete object from directory. * Delete object from directory.
* *
* @return ObjectInterface|bool * @return void
*/ */
public function taskDelete() public function taskDelete(): void
{ {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
if (!$directory) { if (!$directory) {
@ -386,8 +381,6 @@ class AdminController
$this->setRedirect($this->referrerRoute->toString(true), 302); $this->setRedirect($this->referrerRoute->toString(true), 302);
} }
return $object !== null;
} }
/** /**
@ -482,6 +475,10 @@ class AdminController
} }
} }
/**
* @param FlexDirectoryInterface $directory
* @return void
*/
protected function continue(FlexDirectoryInterface $directory): void protected function continue(FlexDirectoryInterface $directory): void
{ {
$config = $directory->getConfig('admin'); $config = $directory->getConfig('admin');
@ -625,10 +622,10 @@ class AdminController
* *
* Route: /pages * Route: /pages
* *
* @return bool True if the action was performed. * @return void
* @throws RuntimeException * @throws RuntimeException
*/ */
protected function taskCopy(): bool protected function taskCopy(): void
{ {
try { try {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
@ -680,8 +677,6 @@ class AdminController
$this->admin->setMessage($this->admin::translate(['PLUGIN_FLEX_OBJECTS.CONTROLLER.TASK_COPY_FAILURE', $e->getMessage()]), 'error'); $this->admin->setMessage($this->admin::translate(['PLUGIN_FLEX_OBJECTS.CONTROLLER.TASK_COPY_FAILURE', $e->getMessage()]), 'error');
$this->setRedirect($this->referrerRoute->toString(true), 302); $this->setRedirect($this->referrerRoute->toString(true), 302);
} }
return true;
} }
/** /**
@ -821,17 +816,17 @@ class AdminController
} }
/** /**
* @return bool * @return void
*/ */
public function taskSaveas(): bool public function taskSaveas(): void
{ {
return $this->taskSave(); $this->taskSave();
} }
/** /**
* @return bool * @return void
*/ */
public function taskSave(): bool public function taskSave(): void
{ {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
if (!$directory) { if (!$directory) {
@ -985,14 +980,12 @@ class AdminController
// $this->setRedirect($this->referrerRoute->withQueryParam('uid', $flash->getUniqueId())->toString(true), 302); // $this->setRedirect($this->referrerRoute->withQueryParam('uid', $flash->getUniqueId())->toString(true), 302);
$this->setRedirect($this->referrerRoute->toString(true), 302); $this->setRedirect($this->referrerRoute->toString(true), 302);
} }
return true;
} }
/** /**
* @return bool * @return void
*/ */
public function taskConfigure(): bool public function taskConfigure(): void
{ {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
if (!$directory) { if (!$directory) {
@ -1040,161 +1033,152 @@ class AdminController
$this->admin->setMessage($this->admin::translate(['PLUGIN_FLEX_OBJECTS.CONTROLLER.TASK_CONFIGURE_FAILURE', $e->getMessage()]), 'error'); $this->admin->setMessage($this->admin::translate(['PLUGIN_FLEX_OBJECTS.CONTROLLER.TASK_CONFIGURE_FAILURE', $e->getMessage()]), 'error');
$this->setRedirect($this->referrerRoute->toString(true), 302); $this->setRedirect($this->referrerRoute->toString(true), 302);
} }
return true;
} }
/** /**
* @return bool * Used in 3rd party editors (e.g. next-gen).
*
* @return ResponseInterface
*/ */
public function taskMediaList(): bool public function taskConvertUrls(): ResponseInterface
{ {
$directory = $this->getDirectory(); $directory = $this->getDirectory();
if (!$directory) { if (!$directory) {
throw new RuntimeException('Not Found', 404); throw new RuntimeException('Not Found', 404);
} }
try { $key = $this->id;
$response = $this->forwardMediaTask('action', 'media.list');
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR); $object = $this->getObject($key);
} catch (Exception $e) { if (!$object instanceof PageInterface) {
die($e->getMessage());
}
return true;
}
/**
* @return bool
*/
public function taskMediaUpload(): bool
{
$directory = $this->getDirectory();
if (!$directory) {
throw new RuntimeException('Not Found', 404); throw new RuntimeException('Not Found', 404);
} }
try { $authorized = $object instanceof FlexAuthorizeInterface
$response = $this->forwardMediaTask('task', 'media.upload'); ? $object->isAuthorized('read', 'admin', $this->user)
: $directory->isAuthorized($object->exists() ? 'read' : 'create', 'admin', $this->user);
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR); if (!$authorized) {
} catch (Exception $e) { throw new RuntimeException($this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.',
die($e->getMessage()); 403);
} }
return true; $request = $this->getRequest();
$data = $request->getParsedBody();
$data['data'] = json_decode($data['data'] ?? '{}', true, 512, JSON_THROW_ON_ERROR);
if (!isset($data['data'])) {
throw new RequestException($request, 'Bad Request', 400);
}
$converted_links = [];
foreach ($data['data']['a'] ?? [] as $link) {
$converted_links[$link] = Excerpts::processLinkHtml($link, $object);
}
$converted_images = [];
foreach ($data['data']['img'] ?? [] as $image) {
$converted_images[$image] = Excerpts::processImageHtml($image, $object);
}
$json = [
'status' => 'success',
'message' => 'All links converted',
'data' => ['links' => $converted_links, 'images' => $converted_images]
];
return $this->createJsonResponse($json, 200);
} }
/** /**
* @return bool * @return ResponseInterface
*/ */
public function taskMediaDelete(): bool public function taskMediaList(): ResponseInterface
{ {
$directory = $this->getDirectory(); return $this->forwardMediaTask('action', 'media.list');
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('task', 'media.delete');
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
} catch (Exception $e) {
die($e->getMessage());
}
return true;
} }
/** /**
* @return bool * @return ResponseInterface
*/ */
public function taskListmedia(): bool public function taskMediaUpload(): ResponseInterface
{
return $this->forwardMediaTask('task', 'media.upload');
}
/**
* @return ResponseInterface
*/
public function taskMediaUploadMeta(): ResponseInterface
{
return $this->forwardMediaTask('task', 'media.upload.meta');
}
/**
* @return ResponseInterface
*/
public function taskMediaReorder(): ResponseInterface
{
return $this->forwardMediaTask('task', 'media.reorder');
}
/**
* @return ResponseInterface
*/
public function taskMediaDelete(): ResponseInterface
{
return $this->forwardMediaTask('task', 'media.delete');
}
/**
* @return ResponseInterface
*/
public function taskListmedia(): ResponseInterface
{ {
return $this->taskMediaList(); return $this->taskMediaList();
} }
/** /**
* @return bool * @return ResponseInterface
*/ */
public function taskAddmedia(): bool public function taskAddmedia(): ResponseInterface
{ {
$directory = $this->getDirectory(); return $this->forwardMediaTask('task', 'media.copy');
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('task', 'media.copy');
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
} catch (Exception $e) {
die($e->getMessage());
}
return true;
} }
/** /**
* @return bool * @return ResponseInterface
*/ */
public function taskDelmedia(): bool public function taskDelmedia(): ResponseInterface
{ {
$directory = $this->getDirectory(); return $this->forwardMediaTask('task', 'media.remove');
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('task', 'media.remove');
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
} catch (Exception $e) {
die($e->getMessage());
}
return true;
} }
/** /**
* @return bool * @return ResponseInterface
* @deprecated Do not use * @deprecated Do not use
*/ */
public function taskFilesUpload(): bool public function taskFilesUpload(): ResponseInterface
{ {
throw new RuntimeException('Task filesUpload should not be called!'); throw new RuntimeException('Task filesUpload should not be called!');
} }
/** /**
* @param string|null $filename * @param string|null $filename
* @return bool * @return ResponseInterface
* @deprecated Do not use * @deprecated Do not use
*/ */
public function taskRemoveMedia($filename = null): bool public function taskRemoveMedia($filename = null): ResponseInterface
{ {
throw new RuntimeException('Task removeMedia should not be called!'); throw new RuntimeException('Task removeMedia should not be called!');
} }
/** /**
* @return bool * @return ResponseInterface
*/ */
public function taskGetFilesInFolder(): bool public function taskGetFilesInFolder(): ResponseInterface
{ {
$directory = $this->getDirectory(); return $this->forwardMediaTask('action', 'media.picker');
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('action', 'media.picker');
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
} catch (Exception $e) {
$this->admin->json_response = ['success' => false, 'error' => $e->getMessage()];
}
return true;
} }
/** /**
@ -1204,12 +1188,18 @@ class AdminController
*/ */
protected function forwardMediaTask(string $type, string $name): ResponseInterface protected function forwardMediaTask(string $type, string $name): ResponseInterface
{ {
$route = Uri::getCurrentRoute()->withGravParam('task', null)->withGravParam($type, $name); $directory = $this->getDirectory();
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
$route = Uri::getCurrentRoute()->withGravParam('task', null);
$object = $this->getObject(); $object = $this->getObject();
/** @var ServerRequest $request */ /** @var ServerRequest $request */
$request = $this->grav['request']; $request = $this->grav['request'];
$request = $request $request = $request
->withAttribute($type, $name)
->withAttribute('type', $this->target) ->withAttribute('type', $this->target)
->withAttribute('key', $this->id) ->withAttribute('key', $this->id)
->withAttribute('storage_key', $object && $object->exists() ? $object->getStorageKey() : null) ->withAttribute('storage_key', $object && $object->exists() ? $object->getStorageKey() : null)
@ -1320,7 +1310,11 @@ class AdminController
// Post // Post
$post = $_POST; $post = $_POST;
if (isset($post['data'])) { if (isset($post['data'])) {
$this->data = $this->getPost($post['data']); $data = $post['data'];
if (is_string($data)) {
$data = json_decode($data, true);
}
$this->data = $this->getPost($data);
unset($post['data']); unset($post['data']);
} }

View File

@ -290,14 +290,16 @@ abstract class AbstractController implements RequestHandlerInterface
/** /**
* @param string $string * @param string $string
* @param array $args
* @return string * @return string
*/ */
public function translate(string $string): string public function translate(string $string, ...$args): string
{ {
/** @var Language $language */ /** @var Language $language */
$language = $this->grav['language']; $language = $this->grav['language'];
array_unshift($args, $string);
return $language->translate($string); return $language->translate($args);
} }
/** /**

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Grav\Plugin\FlexObjects\Controllers; namespace Grav\Plugin\FlexObjects\Controllers;
use Exception; use Exception;
use Grav\Common\Debugger;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Medium\Medium; use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Medium\MediumFactory; use Grav\Common\Page\Medium\MediumFactory;
@ -132,40 +133,165 @@ class MediaController extends AbstractController
return $this->createJsonResponse($response); return $this->createJsonResponse($response);
} }
/**
* @return ResponseInterface
*/
public function taskMediaUploadMeta(): ResponseInterface public function taskMediaUploadMeta(): ResponseInterface
{ {
$this->checkAuthorization('media.create'); try {
$this->checkAuthorization('media.create');
$object = $this->getObject(); $object = $this->getObject();
if (null === $object) { if (null === $object) {
throw new RuntimeException('Not Found', 404); throw new RuntimeException('Not Found', 404);
}
if (!method_exists($object, 'getMediaField')) {
throw new RuntimeException('Not Found', 404);
}
$object->refresh();
// Get updated object from Form Flash.
$flash = $this->getFormFlash($object);
if ($flash->exists()) {
$object = $flash->getObject() ?? $object;
$object->update([], $flash->getFilesByFields());
}
// Get field and data for the uploaded media.
$field = (string)$this->getPost('field');
$media = $object->getMediaField($field);
if (!$media) {
throw new RuntimeException('Media field not found: ' . $field, 404);
}
$data = $this->getPost('data');
if (is_string($data)) {
$data = json_decode($data, true);
}
$filename = Utils::basename($data['name'] ?? '');
// Update field.
$files = $object->getNestedProperty($field, []);
// FIXME: Do we want to save something into the field as well?
$files[$filename] = [];
$object->setNestedProperty($field, $files);
$info = [
'modified' => $data['modified'] ?? null,
'size' => $data['size'] ?? null,
'mime' => $data['mime'] ?? null,
'width' => $data['width'] ?? null,
'height' => $data['height'] ?? null,
'duration' => $data['duration'] ?? null,
'orientation' => $data['orientation'] ?? null,
'meta' => array_filter($data, static function ($val) { return $val !== null; })
];
$info = array_filter($info, static function ($val) { return $val !== null; });
// As the file may not be saved locally, we need to update the index.
$media->updateIndex([$filename => $info]);
$object->save();
$flash->save();
$response = [
'code' => 200,
'status' => 'success',
'message' => $this->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY'),
'field' => $field,
'filename' => $filename,
'metadata' => $data
];
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
return $this->createJsonErrorResponse($e);
} }
if (!method_exists($object, 'checkUploadedMediaFile')) { return $this->createJsonResponse($response);
throw new RuntimeException('Not Found', 404); }
/**
* @return ResponseInterface
*/
public function taskMediaReorder(): ResponseInterface
{
try {
$this->checkAuthorization('media.update');
$object = $this->getObject();
if (null === $object) {
throw new RuntimeException('Not Found', 404);
}
if (!method_exists($object, 'getMediaField')) {
throw new RuntimeException('Not Found', 404);
}
$object->refresh();
// Get updated object from Form Flash.
$flash = $this->getFormFlash($object);
if ($flash->exists()) {
$object = $flash->getObject() ?? $object;
$object->update([], $flash->getFilesByFields());
}
// Get field and data for the uploaded media.
$field = (string)$this->getPost('field');
$media = $object->getMediaField($field);
if (!$media) {
throw new RuntimeException('Media field not found: ' . $field, 404);
}
// Create id => filename map from all files in the media.
$map = [];
foreach ($media as $name => $medium) {
$id = $medium->get('meta.id');
if ($id) {
$map[$id] = $name;
}
}
// Get reorder list and reorder the map.
$data = $this->getPost('data');
if (is_string($data)) {
$data = json_decode($data, true);
}
$data = array_fill_keys($data, null);
$map = array_filter(array_merge($data, $map), static function($val) { return $val !== null; });
// Reorder the files.
$files = $object->getNestedProperty($field, []);
$map = array_fill_keys($map, null);
$files = array_filter(array_merge($map, $files), static function($val) { return $val !== null; });
// Update field.
$object->setNestedProperty($field, $files);
$object->save();
$flash->save();
$response = [
'code' => 200,
'status' => 'success',
'message' => $this->translate('PLUGIN_ADMIN.FIELD_REORDER_SUCCESSFUL'),
'field' => $field,
'ordering' => array_keys($files)
];
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$ex = new RuntimeException($this->translate('PLUGIN_ADMIN.FIELD_REORDER_FAILED', $field), $e->getCode(), $e);
return $this->createJsonErrorResponse($ex);
} }
// Get updated object from Form Flash.
$flash = $this->getFormFlash($object);
if ($flash->exists()) {
$object = $flash->getObject() ?? $object;
$object->update([], $flash->getFilesByFields());
}
// Get field and data for the uploaded media.
$field = $this->getPost('field');
$data = $this->getPost('data');
$filename = Utils::basename($data['name']);
$response = [
'code' => 200,
'status' => 'success',
'message' => $this->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY'),
'field' => $field,
'filename' => $filename,
'metadata' => $data
];
return $this->createJsonResponse($response); return $this->createJsonResponse($response);
} }
@ -288,6 +414,7 @@ class MediaController extends AbstractController
throw new RuntimeException('Not Found', 404); throw new RuntimeException('Not Found', 404);
} }
$field = $this->getPost('field');
$filename = $this->getPost('filename'); $filename = $this->getPost('filename');
// Handle bad filenames. // Handle bad filenames.
@ -295,7 +422,13 @@ class MediaController extends AbstractController
throw new RuntimeException($this->translate('PLUGIN_ADMIN.NO_FILE_FOUND'), 400); throw new RuntimeException($this->translate('PLUGIN_ADMIN.NO_FILE_FOUND'), 400);
} }
$object->deleteMediaFile($filename); $object->deleteMediaFile($filename, $field);
if ($field) {
$order = $object->getNestedProperty($field);
unset($order[$filename]);
$object->setNestedProperty($field, $order);
$object->save();
}
if ($object instanceof PageInterface) { if ($object instanceof PageInterface) {
// Backwards compatibility to existing plugins. // Backwards compatibility to existing plugins.
@ -526,6 +659,7 @@ class MediaController extends AbstractController
break; break;
case 'media.create': case 'media.create':
case 'media.update':
case 'media.delete': case 'media.delete':
$action = $object->exists() ? 'update' : 'create'; $action = $object->exists() ? 'update' : 'create';
break; break;

View File

@ -339,6 +339,34 @@ class ObjectController extends AbstractController
return $this->forwardMediaTask('task', 'media.upload'); return $this->forwardMediaTask('task', 'media.upload');
} }
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function taskMediaUploadMeta(ServerRequestInterface $request): ResponseInterface
{
$directory = $this->getDirectory();
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
return $this->forwardMediaTask('task', 'media.upload.meta');
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function taskMediaReorder(ServerRequestInterface $request): ResponseInterface
{
$directory = $this->getDirectory();
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
return $this->forwardMediaTask('task', 'media.reorder');
}
/** /**
* @param ServerRequestInterface $request * @param ServerRequestInterface $request
* @return ResponseInterface * @return ResponseInterface

View File

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

0
plugins/flex-objects/templates/flex-edit.html.twig Executable file → Normal file
View File

View File

@ -21,9 +21,15 @@
{% set route = admin ? admin.route : grav.uri.route() %} {% set route = admin ? admin.route : grav.uri.route() %}
<div class="form-field grid vertical {% if field.classes is defined %}{{ field.classes }}{% endif %}"> <div class="pagemedia-field form-field grid vertical {% if field.classes is defined %}{{ field.classes }}{% endif %}">
<div class="form-label"> <div class="form-label">
<label>{{ field.label|tu }}</label> <label class="media-collapser">
<i class="fa fa-fw small fa-chevron-{{ pageMediaStore.collapsed ? 'right' : 'down' }}"></i>
{{ field.label|t }} <span data-pagemedia-count>({{ admin.page.media|length }})</span>
</label>
<div class="{{ pageMediaStore.collapsed ? 'hidden' : '' }}">
<input type="range" min="70" step="10" max="200" value="{{ pageMediaStore.width }}" class="media-resizer">
</div>
</div> </div>
<div class="form-data form-uploads-wrapper"> <div class="form-data form-uploads-wrapper">
{% set uploadLimit = grav.config.system.media.upload_limit / 1024 / 1024 %} {% set uploadLimit = grav.config.system.media.upload_limit / 1024 / 1024 %}

View File

@ -149,7 +149,7 @@ class ClassLoader
/** /**
* @return string[] Array of classname => path * @return string[] Array of classname => path
* @psalm-var array<string, string> * @psalm-return array<string, string>
*/ */
public function getClassMap() public function getClassMap()
{ {

View File

@ -5,7 +5,7 @@
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => 'b6f98fd714995cd3097e8d93f1e08994579430c9', 'reference' => 'dfa9760987bbf2cdd61038bca5a7928b31a9dcff',
'name' => 'getgrav/grav-plugin-flex-objects', 'name' => 'getgrav/grav-plugin-flex-objects',
'dev' => false, 'dev' => false,
), ),
@ -16,7 +16,7 @@
'type' => 'grav-plugin', 'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => 'b6f98fd714995cd3097e8d93f1e08994579430c9', 'reference' => 'dfa9760987bbf2cdd61038bca5a7928b31a9dcff',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
), ),

0
plugins/flex-objects/watch.sh Executable file → Normal file
View File

File diff suppressed because it is too large Load Diff

2
plugins/login/.gitignore vendored Normal file
View File

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

View File

@ -1,3 +1,9 @@
# v3.7.1
## 06/14/2022
1. [](#bugfix)
* PHP 8.1 fixes in QR code library
# v3.7.0 # v3.7.0
## 03/28/2022 ## 03/28/2022

View File

@ -1,7 +1,7 @@
name: Login name: Login
slug: login slug: login
type: plugin type: plugin
version: 3.7.0 version: 3.7.1
testing: false testing: false
description: Enables user authentication and login screen. description: Enables user authentication and login screen.
icon: sign-in icon: sign-in

View File

@ -407,7 +407,7 @@ class Controller
return true; return true;
} }
$token = md5(uniqid(mt_rand(), true)); $token = md5(uniqid((string)mt_rand(), true));
$expire = time() + 604800; // next week $expire = time() + 604800; // next week
$user->reset = $token . '::' . $expire; $user->reset = $token . '::' . $expire;

0
plugins/login/classes/Invitations/Invitation.php Executable file → Normal file
View File

0
plugins/login/classes/Invitations/Invitations.php Executable file → Normal file
View File

0
plugins/login/classes/Login.php Executable file → Normal file
View File

View File

@ -160,16 +160,16 @@
}, },
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"version": "1.8.1", "version": "1.8.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git", "url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b" "reference": "65681de5a324eae05140ac58b08648a60212afc0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5afcb45282f1c75562a48d479ecd1732c9bdb11b", "url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b", "reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -226,7 +226,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-10-20T12:19:55+00:00" "time": "2022-03-22T16:11:07+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

15
plugins/login/hebe.json Normal file
View File

@ -0,0 +1,15 @@
{
"project":"grav-plugin-login",
"platforms":{
"grav":{
"nodes":{
"plugin":[
{
"source":"/",
"destination":"/user/plugins/login"
}
]
}
}
}
}

0
plugins/login/login.php Executable file → Normal file
View File

View File

@ -0,0 +1,46 @@
{% extends 'email/base.html.twig' %}
{# Magic login link email (login by email link) #}
{%- set subject = 'PLUGIN_LOGIN.MAGIC_LOGIN_EMAIL_SUBJECT'|t(site_name) %}
{%- set message = message ?? 'PLUGIN_LOGIN.MAGIC_LOGIN_EMAIL_MESSAGE'|t %}
{%- do email.message.setSubject(subject) %}
{%- block content -%}
{{ 'PLUGIN_LOGIN.MAGIC_LOGIN_EMAIL_BODY'|t(site_name, message, login_link, actor.fullname)|raw }}
{%- endblock content -%}
{# https://workos.com/blog/a-guide-to-magic-links
<h1>Account Invitation</h1>
<p>
Hi,
</p>
<p>
You have been invited to join <b>{{ site_name }}</b>.
</p>
<p>
{{ message }}
</p>
<p>
<br/>
<a href="{{ login_link }}" 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="{{ login_link }}">{{ login_link }}</a>
</p>
<p>
<br/>
Kind regards,
<br/>
<br/>
{{ actor.fullname }}
</p>
#}

0
plugins/login/templates/partials/login-form.html.twig Executable file → Normal file
View File

View File

View File

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

View File

@ -0,0 +1,14 @@
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

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

View File

0
plugins/login/vendor/birke/rememberme/src/Rememberme/Cookie.php vendored Executable file → Normal file
View File

0
plugins/login/vendor/birke/rememberme/src/Rememberme/Storage/DB.php vendored Executable file → Normal file
View File

View File

View File

0
plugins/login/vendor/birke/rememberme/test/RemembermeTest.php vendored Executable file → Normal file
View File

0
plugins/login/vendor/birke/rememberme/test/Storage/PDO.php vendored Executable file → Normal file
View File

0
plugins/login/vendor/birke/rememberme/test/Storage/tokens.xml vendored Executable file → Normal file
View File

0
plugins/login/vendor/birke/rememberme/test/bootstrap.php vendored Executable file → Normal file
View File

View File

@ -150,17 +150,17 @@
}, },
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"version": "1.8.1", "version": "1.8.2",
"version_normalized": "1.8.1.0", "version_normalized": "1.8.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git", "url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b" "reference": "65681de5a324eae05140ac58b08648a60212afc0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5afcb45282f1c75562a48d479ecd1732c9bdb11b", "url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b", "reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -174,7 +174,7 @@
"bacon/bacon-qr-code": "Needed for BaconQrCodeProvider provider", "bacon/bacon-qr-code": "Needed for BaconQrCodeProvider provider",
"endroid/qr-code": "Needed for EndroidQrCodeProvider" "endroid/qr-code": "Needed for EndroidQrCodeProvider"
}, },
"time": "2021-10-20T12:19:55+00:00", "time": "2022-03-22T16:11:07+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {

View File

@ -5,7 +5,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '5bff0ca6fbc27f5d7795fdb1b4a86b62ce233af3', 'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'name' => 'getgrav/grav-plugin-login', 'name' => 'getgrav/grav-plugin-login',
'dev' => false, 'dev' => false,
), ),
@ -34,7 +34,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '5bff0ca6fbc27f5d7795fdb1b4a86b62ce233af3', 'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'paragonie/random_compat' => array( 'paragonie/random_compat' => array(
@ -47,12 +47,12 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'robthree/twofactorauth' => array( 'robthree/twofactorauth' => array(
'pretty_version' => '1.8.1', 'pretty_version' => '1.8.2',
'version' => '1.8.1.0', 'version' => '1.8.2.0',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../robthree/twofactorauth', 'install_path' => __DIR__ . '/../robthree/twofactorauth',
'aliases' => array(), 'aliases' => array(),
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b', 'reference' => '65681de5a324eae05140ac58b08648a60212afc0',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
), ),

View File

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

View File

@ -0,0 +1,41 @@
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

@ -0,0 +1,28 @@
name: Test
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 lint
- run: composer test

View File

@ -0,0 +1,192 @@
## 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

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2015 Rob Janssen Copyright (c) 2014-2021 Rob Janssen and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,7 +1,7 @@
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"description": "Two Factor Authentication", "description": "Two Factor Authentication",
"version": "1.8.1", "version": "1.8.2",
"type": "library", "type": "library",
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ], "keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
"homepage": "https://github.com/RobThree/TwoFactorAuth", "homepage": "https://github.com/RobThree/TwoFactorAuth",

View File

@ -125,12 +125,19 @@ class BaconQrCodeProvider implements IQRCodeProvider
{ {
if (is_string($colour) && $colour[0] == '#') { if (is_string($colour) && $colour[0] == '#') {
$hexToRGB = function ($input) { $hexToRGB = function ($input) {
// ensure input no longer has a # for more predictable division
// PHP 8.1 does not like implicitly casting a float to an int
$input = trim($input, '#');
if (strlen($input) != 3 && strlen($input) != 6) {
throw new \RuntimeException('Colour should be a 3 or 6 character value after the #');
}
// split the array into three chunks // split the array into three chunks
$split = str_split(trim($input, '#'), strlen($input) / 3); $split = str_split($input, strlen($input) / 3);
// cope with three character hex reference // cope with three character hex reference
// three characters plus a # = 4 if (strlen($input) == 3) {
if (strlen($input) == 4) {
array_walk($split, function (&$character) { array_walk($split, function (&$character) {
$character = str_repeat($character, 2); $character = str_repeat($character, 2);
}); });

View File

@ -1,8 +1,14 @@
<?php <?php
namespace RobThree\Auth\Providers\Qr; namespace RobThree\Auth\Providers\Qr;
use Endroid\QrCode\Color\Color;
use Endroid\QrCode\ErrorCorrectionLevel; use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile;
use Endroid\QrCode\QrCode; use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
class EndroidQrCodeProvider implements IQRCodeProvider class EndroidQrCodeProvider implements IQRCodeProvider
{ {
@ -11,8 +17,12 @@ class EndroidQrCodeProvider implements IQRCodeProvider
public $margin; public $margin;
public $errorcorrectionlevel; public $errorcorrectionlevel;
protected $endroid4 = false;
public function __construct($bgcolor = 'ffffff', $color = '000000', $margin = 0, $errorcorrectionlevel = 'H') public function __construct($bgcolor = 'ffffff', $color = '000000', $margin = 0, $errorcorrectionlevel = 'H')
{ {
$this->endroid4 = method_exists(QrCode::class, 'create');
$this->bgcolor = $this->handleColor($bgcolor); $this->bgcolor = $this->handleColor($bgcolor);
$this->color = $this->handleColor($color); $this->color = $this->handleColor($color);
$this->margin = $margin; $this->margin = $margin;
@ -26,7 +36,12 @@ class EndroidQrCodeProvider implements IQRCodeProvider
public function getQRCodeImage($qrtext, $size) public function getQRCodeImage($qrtext, $size)
{ {
return $this->qrCodeInstance($qrtext, $size)->writeString(); if (!$this->endroid4) {
return $this->qrCodeInstance($qrtext, $size)->writeString();
}
$writer = new PngWriter();
return $writer->write($this->qrCodeInstance($qrtext, $size))->getString();
} }
protected function qrCodeInstance($qrtext, $size) protected function qrCodeInstance($qrtext, $size)
@ -49,22 +64,21 @@ class EndroidQrCodeProvider implements IQRCodeProvider
$g = hexdec($split[1]); $g = hexdec($split[1]);
$b = hexdec($split[2]); $b = hexdec($split[2]);
return ['r' => $r, 'g' => $g, 'b' => $b, 'a' => 0]; return $this->endroid4 ? new Color($r, $g, $b, 0) : ['r' => $r, 'g' => $g, 'b' => $b, 'a' => 0];
} }
private function handleErrorCorrectionLevel($level) private function handleErrorCorrectionLevel($level)
{ {
switch ($level) { switch ($level) {
case 'L': case 'L':
return ErrorCorrectionLevel::LOW(); return $this->endroid4 ? new ErrorCorrectionLevelLow() : ErrorCorrectionLevel::LOW();
case 'M': case 'M':
return ErrorCorrectionLevel::MEDIUM(); return $this->endroid4 ? new ErrorCorrectionLevelMedium() : ErrorCorrectionLevel::MEDIUM();
case 'Q': case 'Q':
return ErrorCorrectionLevel::QUARTILE(); return $this->endroid4 ? new ErrorCorrectionLevelQuartile() : ErrorCorrectionLevel::QUARTILE();
case 'H': case 'H':
return ErrorCorrectionLevel::HIGH();
default: default:
return ErrorCorrectionLevel::HIGH(); return $this->endroid4 ? new ErrorCorrectionLevelHigh() : ErrorCorrectionLevel::HIGH();
} }
} }
} }

View File

@ -1,8 +1,8 @@
<?php <?php
namespace RobThree\Auth\Providers\Qr; namespace RobThree\Auth\Providers\Qr;
use Endroid\QrCode\ErrorCorrectionLevel; use Endroid\QrCode\Logo\Logo;
use Endroid\QrCode\QrCode; use Endroid\QrCode\Writer\PngWriter;
class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider
{ {
@ -20,13 +20,33 @@ class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider
$this->logoSize = (array)$size; $this->logoSize = (array)$size;
} }
public function getQRCodeImage($qrtext, $size)
{
if (!$this->endroid4) {
return $this->qrCodeInstance($qrtext, $size)->writeString();
}
$logo = null;
if ($this->logoPath) {
$logo = Logo::create($this->logoPath);
if ($this->logoSize) {
$logo->setResizeToWidth($this->logoSize[0]);
if (isset($this->logoSize[1])) {
$logo->setResizeToHeight($this->logoSize[1]);
}
}
}
$writer = new PngWriter();
return $writer->write($this->qrCodeInstance($qrtext, $size), $logo)->getString();
}
protected function qrCodeInstance($qrtext, $size) { protected function qrCodeInstance($qrtext, $size) {
$qrCode = parent::qrCodeInstance($qrtext, $size); $qrCode = parent::qrCodeInstance($qrtext, $size);
if ($this->logoPath) { if (!$this->endroid4 && $this->logoPath) {
$qrCode->setLogoPath($this->logoPath); $qrCode->setLogoPath($this->logoPath);
if ($this->logoSize) { if ($this->logoSize) {
$qrCode->setLogoSize($this->logoSize[0], $this->logoSize[1]); $qrCode->setLogoSize($this->logoSize[0], isset($this->logoSize[1]) ? $this->logoSize[1] : null);
} }
} }

View File

@ -0,0 +1,24 @@
<?php
namespace RobThree\Auth\Providers\Qr;
trait HandlesDataUri
{
/**
* @param string $datauri
*
* @return null|array
*/
private function DecodeDataUri($datauri)
{
if (preg_match('/data:(?P<mimetype>[\w\.\-\+\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
return array(
'mimetype' => $m['mimetype'],
'encoding' => $m['encoding'],
'data' => base64_decode($m['data'])
);
}
return null;
}
}

View File

@ -272,7 +272,7 @@ class TwoFactorAuth
{ {
return 'otpauth://totp/' . rawurlencode($label) return 'otpauth://totp/' . rawurlencode($label)
. '?secret=' . rawurlencode($secret) . '?secret=' . rawurlencode($secret)
. '&issuer=' . rawurlencode($this->issuer) . '&issuer=' . rawurlencode((string)$this->issuer)
. '&period=' . intval($this->period) . '&period=' . intval($this->period)
. '&algorithm=' . rawurlencode(strtoupper($this->algorithm)) . '&algorithm=' . rawurlencode(strtoupper($this->algorithm))
. '&digits=' . intval($this->digits); . '&digits=' . intval($this->digits);

View File

@ -2,6 +2,7 @@
<phpunit backupGlobals="false" <phpunit backupGlobals="false"
backupStaticAttributes="false" backupStaticAttributes="false"
colors="true" colors="true"
convertDeprecationsToExceptions="true"
convertErrorsToExceptions="true" convertErrorsToExceptions="true"
convertNoticesToExceptions="true" convertNoticesToExceptions="true"
convertWarningsToExceptions="true" convertWarningsToExceptions="true"

View File

@ -5,26 +5,11 @@ namespace Tests\Providers\Qr;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RobThree\Auth\TwoFactorAuth; use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\TwoFactorAuthException; use RobThree\Auth\TwoFactorAuthException;
use RobThree\Auth\Providers\Qr\HandlesDataUri;
class IQRCodeProviderTest extends TestCase class IQRCodeProviderTest extends TestCase
{ {
/** use HandlesDataUri;
* @param string $datauri
*
* @return null|array
*/
private function DecodeDataUri($datauri)
{
if (preg_match('/data:(?P<mimetype>[\w\.\-\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
return array(
'mimetype' => $m['mimetype'],
'encoding' => $m['encoding'],
'data' => base64_decode($m['data'])
);
}
return null;
}
/** /**
* @return void * @return void
@ -40,6 +25,25 @@ class IQRCodeProviderTest extends TestCase
$this->assertEquals('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=Test%26Issuer&period=30&algorithm=SHA1&digits=6@200', $data['data']); $this->assertEquals('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=Test%26Issuer&period=30&algorithm=SHA1&digits=6@200', $data['data']);
} }
/**
* @return void
*/
public function testTotpUriIsCorrectNoIssuer()
{
$qr = new TestQrProvider();
/**
* The library specifies the issuer is null by default however in PHP 8.1
* there is a deprecation warning for passing null as a string argument to rawurlencode
*/
$tfa = new TwoFactorAuth(null, 6, 30, 'sha1', $qr);
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
$this->assertEquals('test/test', $data['mimetype']);
$this->assertEquals('base64', $data['encoding']);
$this->assertEquals('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=&period=30&algorithm=SHA1&digits=6@200', $data['data']);
}
/** /**
* @return void * @return void
*/ */

View File

@ -0,0 +1,61 @@
<?php
namespace TestsDependency;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use PHPUnit\Framework\TestCase;
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider;
use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\Providers\Qr\HandlesDataUri;
class BaconQRCodeTest extends TestCase
{
use HandlesDataUri;
public function testDependency()
{
// php < 7.1 will install an older Bacon QR Code
if (! class_exists(ImagickImageBackEnd::class)) {
$this->expectException(\RuntimeException::class);
$qr = new BaconQrCodeProvider(1, '#000', '#FFF', 'svg');
} else {
$qr = new BaconQrCodeProvider(1, '#000', '#FFF', 'svg');
$tfa = new TwoFactorAuth('Test&Issuer', 6, 30, 'sha1', $qr);
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
$this->assertEquals('image/svg+xml', $data['mimetype']);
}
}
public function testBadTextColour()
{
$this->expectException(\RuntimeException::class);
new BaconQrCodeProvider(1, 'not-a-colour', '#FFF');
}
public function testBadBackgroundColour()
{
$this->expectException(\RuntimeException::class);
new BaconQrCodeProvider(1, '#000', 'not-a-colour');
}
public function testBadTextColourHexRef()
{
$this->expectException(\RuntimeException::class);
new BaconQrCodeProvider(1, '#AAAA', '#FFF');
}
public function testBadBackgroundColourHexRef()
{
$this->expectException(\RuntimeException::class);
new BaconQrCodeProvider(1, '#000', '#AAAA');
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace TestsDependency;
use PHPUnit\Framework\TestCase;
use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\Providers\Qr\EndroidQrCodeProvider;
use RobThree\Auth\Providers\Qr\HandlesDataUri;
class EndroidQRCodeTest extends TestCase
{
use HandlesDataUri;
public function testDependency()
{
$qr = new EndroidQrCodeProvider();
$tfa = new TwoFactorAuth('Test&Issuer', 6, 30, 'sha1', $qr);
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
$this->assertEquals('image/png', $data['mimetype']);
$this->assertEquals('base64', $data['encoding']);
$this->assertNotEmpty($data['data']);
}
}