(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/error.log
/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
## 04/25/2022
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
## 04/25/2022

View File

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

View File

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

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_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"
# Removed in Grav 1.8
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"
UPLOAD_LIMIT: "File upload limit"
UPLOAD_LIMIT_HELP: "Set maximum upload size in bytes (0 is unlimited)"
ENABLE_MEDIA_TIMESTAMP: "Enable timestamps on media"
@ -1142,3 +1145,7 @@ PLUGIN_ADMIN:
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_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 './utils/remodal';
import 'simplebar/dist/simplebar.min.js';
import { UriToMarkdown } from './forms/fields/files.js';
import GPM, { Instance as gpm } from './utils/gpm';
import KeepAlive from './utils/keepalive';
import Updates, { Instance as updates, Notifications, Feed } from './updates';
@ -68,5 +69,5 @@ export default {
Instance: MediaFilterInstance
},
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 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>
@ -15,6 +15,4 @@
{% endfor %}
</table>
</div>
{% else %}
<div class="padding">You don't have sufficient access to view the dashboard...</div>
{% endif %}

View File

@ -193,17 +193,17 @@
},
{
"name": "scssphp/scssphp",
"version": "v1.10.2",
"version_normalized": "1.10.2.0",
"version": "v1.10.3",
"version_normalized": "1.10.3.0",
"source": {
"type": "git",
"url": "https://github.com/scssphp/scssphp.git",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46"
"reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/387f4f4abf5d99f16be16314c5ab856f81c82f46",
"reference": "387f4f4abf5d99f16be16314c5ab856f81c82f46",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713",
"shasum": ""
},
"require": {
@ -226,7 +226,7 @@
"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"
},
"time": "2022-03-02T21:15:09+00:00",
"time": "2022-05-16T07:22:18+00:00",
"bin": [
"bin/pscss"
],
@ -264,7 +264,7 @@
],
"support": {
"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"
}

View File

@ -5,7 +5,7 @@
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5c1e6d6d52f8fc8e0540b5599e736e25ea20c446',
'reference' => '0a5b51b9e2c2eb9626b17c8b164dcfc3296ccf3c',
'name' => 'getgrav/grav-plugin-admin',
'dev' => false,
),
@ -16,7 +16,7 @@
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5c1e6d6d52f8fc8e0540b5599e736e25ea20c446',
'reference' => '0a5b51b9e2c2eb9626b17c8b164dcfc3296ccf3c',
'dev_requirement' => false,
),
'laminas/laminas-xml' => array(
@ -53,12 +53,12 @@
'dev_requirement' => false,
),
'scssphp/scssphp' => array(
'pretty_version' => 'v1.10.2',
'version' => '1.10.2.0',
'pretty_version' => 'v1.10.3',
'version' => '1.10.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../scssphp/scssphp',
'aliases' => array(),
'reference' => '387f4f4abf5d99f16be16314c5ab856f81c82f46',
'reference' => '0f1e1516ed2412ad43e42a6a319e77624ba1f713',
'dev_requirement' => false,
),
'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\Block $selfParent
* @param string $traceName
@ -2367,13 +2367,13 @@ class Compiler
foreach ($stms as $stm) {
if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) {
$oldSelfParent = $stm[1]->selfParent;
$stm[1]->selfParent = $selfParent;
$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])) {
$stm['selfParent'] = $selfParent;
$ret = $this->compileChild($stm, $out);
unset($stm['selfParent']);
} else {
$ret = $this->compileChild($stm, $out);
}

View File

@ -14,6 +14,8 @@ namespace ScssPhp\ScssPhp\Logger;
/**
* A logger that silently ignores all messages.
*
* @final
*/
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)
*
* @final
*/
class StreamLogger implements LoggerInterface
{

View File

@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp;
*/
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
## 03/28/2022

View File

@ -1,7 +1,7 @@
name: Flex Objects
slug: flex-objects
type: plugin
version: 1.2.0
version: 1.3.0
description: Flex Objects plugin allows you to manage Flex Objects in Grav Admin.
icon: list-alt
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\PageObject;
use Grav\Common\Grav;
use Grav\Common\Helpers\Excerpts;
use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Uri;
@ -120,9 +121,9 @@ class AdminController
/**
* Unknown task, call onFlexTask[NAME] event.
*
* @return bool
* @return void
*/
public function taskDefault(): bool
public function taskDefault(): void
{
$type = $this->target;
$directory = $this->getDirectory($type);
@ -143,6 +144,7 @@ class AdminController
'directory' => $directory,
'object' => $object,
'data' => $this->data,
'user' => $this->user,
'redirect' => $this->redirect
]
);
@ -160,19 +162,15 @@ class AdminController
if ($redirect) {
$this->setRedirect($redirect);
}
return $event->isPropagationStopped();
}
return false;
}
/**
* Default action, onFlexAction[NAME] event.
*
* @return bool
* @return void
*/
public function actionDefault(): bool
public function actionDefault(): void
{
$type = $this->target;
$directory = $this->getDirectory($type);
@ -192,6 +190,7 @@ class AdminController
'flex' => $this->getFlex(),
'directory' => $directory,
'object' => $object,
'user' => $this->user,
'redirect' => $this->redirect
]
);
@ -209,19 +208,15 @@ class AdminController
if ($redirect) {
$this->setRedirect($redirect);
}
return $event->isPropagationStopped();
}
return false;
}
/**
* Get datatable for list view.
*
* @return void
* @return ResponseInterface|null
*/
public function actionList(): void
public function actionList(): ?ResponseInterface
{
$directory = $this->getDirectory();
if (!$directory) {
@ -248,28 +243,28 @@ class AdminController
$table = $this->getFlex()->getDataTable($directory, $options);
$response = $this->createJsonResponse($table->jsonSerialize());
$this->close($response);
return $this->createJsonResponse($table->jsonSerialize());
}
return null;
}
/**
* 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.
*
* @return void
* @return ResponseInterface|null
*/
public function actionExport(): void
public function actionExport(): ?ResponseInterface
{
$collection = $this->getCollection();
if (!$collection) {
@ -341,15 +336,15 @@ class AdminController
$formatter->encode($list)
);
$this->close($response);
return $response;
}
/**
* Delete object from directory.
*
* @return ObjectInterface|bool
* @return void
*/
public function taskDelete()
public function taskDelete(): void
{
$directory = $this->getDirectory();
if (!$directory) {
@ -386,8 +381,6 @@ class AdminController
$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
{
$config = $directory->getConfig('admin');
@ -625,10 +622,10 @@ class AdminController
*
* Route: /pages
*
* @return bool True if the action was performed.
* @return void
* @throws RuntimeException
*/
protected function taskCopy(): bool
protected function taskCopy(): void
{
try {
$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->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();
if (!$directory) {
@ -985,14 +980,12 @@ class AdminController
// $this->setRedirect($this->referrerRoute->withQueryParam('uid', $flash->getUniqueId())->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();
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->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();
if (!$directory) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('action', 'media.list');
$key = $this->id;
$this->admin->json_response = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
} catch (Exception $e) {
die($e->getMessage());
}
return true;
}
/**
* @return bool
*/
public function taskMediaUpload(): bool
{
$directory = $this->getDirectory();
if (!$directory) {
$object = $this->getObject($key);
if (!$object instanceof PageInterface) {
throw new RuntimeException('Not Found', 404);
}
try {
$response = $this->forwardMediaTask('task', 'media.upload');
$authorized = $object instanceof FlexAuthorizeInterface
? $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);
} catch (Exception $e) {
die($e->getMessage());
if (!$authorized) {
throw new RuntimeException($this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.',
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();
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 $this->forwardMediaTask('action', 'media.list');
}
/**
* @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 bool
* @return ResponseInterface
*/
public function taskAddmedia(): bool
public function taskAddmedia(): ResponseInterface
{
$directory = $this->getDirectory();
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 $this->forwardMediaTask('task', 'media.copy');
}
/**
* @return bool
* @return ResponseInterface
*/
public function taskDelmedia(): bool
public function taskDelmedia(): ResponseInterface
{
$directory = $this->getDirectory();
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 $this->forwardMediaTask('task', 'media.remove');
}
/**
* @return bool
* @return ResponseInterface
* @deprecated Do not use
*/
public function taskFilesUpload(): bool
public function taskFilesUpload(): ResponseInterface
{
throw new RuntimeException('Task filesUpload should not be called!');
}
/**
* @param string|null $filename
* @return bool
* @return ResponseInterface
* @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!');
}
/**
* @return bool
* @return ResponseInterface
*/
public function taskGetFilesInFolder(): bool
public function taskGetFilesInFolder(): ResponseInterface
{
$directory = $this->getDirectory();
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;
return $this->forwardMediaTask('action', 'media.picker');
}
/**
@ -1204,12 +1188,18 @@ class AdminController
*/
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();
/** @var ServerRequest $request */
$request = $this->grav['request'];
$request = $request
->withAttribute($type, $name)
->withAttribute('type', $this->target)
->withAttribute('key', $this->id)
->withAttribute('storage_key', $object && $object->exists() ? $object->getStorageKey() : null)
@ -1320,7 +1310,11 @@ class AdminController
// Post
$post = $_POST;
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']);
}

View File

@ -290,14 +290,16 @@ abstract class AbstractController implements RequestHandlerInterface
/**
* @param string $string
* @param array $args
* @return string
*/
public function translate(string $string): string
public function translate(string $string, ...$args): string
{
/** @var Language $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;
use Exception;
use Grav\Common\Debugger;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Medium\MediumFactory;
@ -132,40 +133,165 @@ class MediaController extends AbstractController
return $this->createJsonResponse($response);
}
/**
* @return ResponseInterface
*/
public function taskMediaUploadMeta(): ResponseInterface
{
$this->checkAuthorization('media.create');
try {
$this->checkAuthorization('media.create');
$object = $this->getObject();
if (null === $object) {
throw new RuntimeException('Not Found', 404);
$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);
}
$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')) {
throw new RuntimeException('Not Found', 404);
return $this->createJsonResponse($response);
}
/**
* @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);
}
@ -288,6 +414,7 @@ class MediaController extends AbstractController
throw new RuntimeException('Not Found', 404);
}
$field = $this->getPost('field');
$filename = $this->getPost('filename');
// Handle bad filenames.
@ -295,7 +422,13 @@ class MediaController extends AbstractController
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) {
// Backwards compatibility to existing plugins.
@ -526,6 +659,7 @@ class MediaController extends AbstractController
break;
case 'media.create':
case 'media.update':
case 'media.delete':
$action = $object->exists() ? 'update' : 'create';
break;

View File

@ -339,6 +339,34 @@ class ObjectController extends AbstractController
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
* @return ResponseInterface

View File

@ -21,5 +21,5 @@
"platform-overrides": {
"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() %}
<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">
<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 class="form-data form-uploads-wrapper">
{% set uploadLimit = grav.config.system.media.upload_limit / 1024 / 1024 %}

View File

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

View File

@ -5,7 +5,7 @@
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'b6f98fd714995cd3097e8d93f1e08994579430c9',
'reference' => 'dfa9760987bbf2cdd61038bca5a7928b31a9dcff',
'name' => 'getgrav/grav-plugin-flex-objects',
'dev' => false,
),
@ -16,7 +16,7 @@
'type' => 'grav-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'b6f98fd714995cd3097e8d93f1e08994579430c9',
'reference' => 'dfa9760987bbf2cdd61038bca5a7928b31a9dcff',
'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
## 03/28/2022

View File

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

View File

@ -407,7 +407,7 @@ 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;

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",
"version": "1.8.1",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b"
"reference": "65681de5a324eae05140ac58b08648a60212afc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5afcb45282f1c75562a48d479ecd1732c9bdb11b",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/65681de5a324eae05140ac58b08648a60212afc0",
"reference": "65681de5a324eae05140ac58b08648a60212afc0",
"shasum": ""
},
"require": {
@ -226,7 +226,7 @@
"type": "github"
}
],
"time": "2021-10-20T12:19:55+00:00"
"time": "2022-03-22T16:11:07+00:00"
}
],
"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",
"version": "1.8.1",
"version_normalized": "1.8.1.0",
"version": "1.8.2",
"version_normalized": "1.8.2.0",
"source": {
"type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b"
"reference": "65681de5a324eae05140ac58b08648a60212afc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5afcb45282f1c75562a48d479ecd1732c9bdb11b",
"reference": "5afcb45282f1c75562a48d479ecd1732c9bdb11b",
"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-10-20T12:19:55+00:00",
"time": "2022-03-22T16:11:07+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

View File

@ -5,7 +5,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5bff0ca6fbc27f5d7795fdb1b4a86b62ce233af3',
'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'name' => 'getgrav/grav-plugin-login',
'dev' => false,
),
@ -34,7 +34,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5bff0ca6fbc27f5d7795fdb1b4a86b62ce233af3',
'reference' => 'b873dd50c66d15151e44fde56a79cfc16ba38bd3',
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
@ -47,12 +47,12 @@
'dev_requirement' => false,
),
'robthree/twofactorauth' => array(
'pretty_version' => '1.8.1',
'version' => '1.8.1.0',
'pretty_version' => '1.8.2',
'version' => '1.8.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../robthree/twofactorauth',
'aliases' => array(),
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
'reference' => '65681de5a324eae05140ac58b08648a60212afc0',
'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)
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
of this software and associated documentation files (the "Software"), to deal

View File

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

View File

@ -125,12 +125,19 @@ class BaconQrCodeProvider implements IQRCodeProvider
{
if (is_string($colour) && $colour[0] == '#') {
$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 = str_split(trim($input, '#'), strlen($input) / 3);
$split = str_split($input, strlen($input) / 3);
// cope with three character hex reference
// three characters plus a # = 4
if (strlen($input) == 4) {
if (strlen($input) == 3) {
array_walk($split, function (&$character) {
$character = str_repeat($character, 2);
});

View File

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

View File

@ -1,8 +1,8 @@
<?php
namespace RobThree\Auth\Providers\Qr;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Logo\Logo;
use Endroid\QrCode\Writer\PngWriter;
class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider
{
@ -20,13 +20,33 @@ class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider
$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) {
$qrCode = parent::qrCodeInstance($qrtext, $size);
if ($this->logoPath) {
if (!$this->endroid4 && $this->logoPath) {
$qrCode->setLogoPath($this->logoPath);
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)
. '?secret=' . rawurlencode($secret)
. '&issuer=' . rawurlencode($this->issuer)
. '&issuer=' . rawurlencode((string)$this->issuer)
. '&period=' . intval($this->period)
. '&algorithm=' . rawurlencode(strtoupper($this->algorithm))
. '&digits=' . intval($this->digits);

View File

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

View File

@ -5,26 +5,11 @@ namespace Tests\Providers\Qr;
use PHPUnit\Framework\TestCase;
use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\TwoFactorAuthException;
use RobThree\Auth\Providers\Qr\HandlesDataUri;
class IQRCodeProviderTest extends TestCase
{
/**
* @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;
}
use HandlesDataUri;
/**
* @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']);
}
/**
* @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
*/

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']);
}
}