(Grav GitSync) Automatic Commit from RealStickman
This commit is contained in:
parent
0643fa0f2d
commit
6d1184df78
@ -1,3 +1,16 @@
|
||||
# v1.10.37.1
|
||||
## 10/08/2022
|
||||
|
||||
1. [](#bugfix)
|
||||
* Removed new GumRoad cart icon + new button styling [getgrav/grav#3631](https://github.com/getgrav/grav/issues/3631)
|
||||
|
||||
# v1.10.37
|
||||
## 10/05/2022
|
||||
|
||||
1. [](#improved)
|
||||
* Updated vendor libraries to latest versions
|
||||
* Removed a reference to `SwiftMailer` library to support new **Email** plugin v4.0
|
||||
|
||||
# v1.10.36
|
||||
## 09/08/2022
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: Admin Panel
|
||||
slug: admin
|
||||
type: plugin
|
||||
version: 1.10.36
|
||||
version: 1.10.37.1
|
||||
description: Adds an advanced administration panel to manage your site
|
||||
icon: empire
|
||||
author:
|
||||
|
@ -487,11 +487,8 @@ class LoginController extends AdminController
|
||||
throw new \RuntimeException('Sending email failed');
|
||||
}
|
||||
|
||||
// For testing only!
|
||||
//Admin::DEBUG && Admin::addDebugMessage(sprintf('Email sent to %s', $to), $body);
|
||||
|
||||
$this->setMessage($this->translate('PLUGIN_ADMIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'));
|
||||
} catch (\RuntimeException|\Swift_SwiftException $e) {
|
||||
} catch (\Exception $e) {
|
||||
$rateLimiter->resetRateLimit($username);
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
|
144
plugins/admin/composer.lock
generated
144
plugins/admin/composer.lock
generated
@ -190,16 +190,16 @@
|
||||
},
|
||||
{
|
||||
"name": "scssphp/scssphp",
|
||||
"version": "v1.10.4",
|
||||
"version": "v1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/scssphp/scssphp.git",
|
||||
"reference": "8ed20753db2d3d82629e6f5d35535bbbd3893b0c"
|
||||
"reference": "33749d12c2569bb24071f94e9af828662dabb068"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/8ed20753db2d3d82629e6f5d35535bbbd3893b0c",
|
||||
"reference": "8ed20753db2d3d82629e6f5d35535bbbd3893b0c",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/33749d12c2569bb24071f94e9af828662dabb068",
|
||||
"reference": "33749d12c2569bb24071f94e9af828662dabb068",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -264,9 +264,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/scssphp/scssphp/issues",
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.10.4"
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.11.0"
|
||||
},
|
||||
"time": "2022-07-26T16:28:33+00:00"
|
||||
"time": "2022-09-02T21:24:55+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@ -818,16 +818,16 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "1.5.1",
|
||||
"version": "1.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
|
||||
"reference": "b94b2807d85443f9719887892882d0329d1e2598"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
|
||||
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
|
||||
"reference": "b94b2807d85443f9719887892882d0329d1e2598",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -882,7 +882,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/1.5.1"
|
||||
"source": "https://github.com/guzzle/promises/tree/1.5.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -898,7 +898,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-22T20:56:57+00:00"
|
||||
"time": "2022-08-28T14:55:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
@ -1987,16 +1987,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "1071dfcef776a57013124ff35e1fc41ccd294758"
|
||||
"reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758",
|
||||
"reference": "1071dfcef776a57013124ff35e1fc41ccd294758",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770",
|
||||
"reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2049,7 +2049,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2057,7 +2057,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T08:04:30+00:00"
|
||||
"time": "2022-09-14T12:31:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
@ -2190,16 +2190,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db"
|
||||
"reference": "73a9676f2833b9a7c36968f9d882589cd75511e6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6",
|
||||
"reference": "73a9676f2833b9a7c36968f9d882589cd75511e6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2255,7 +2255,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4"
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2263,7 +2263,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-11T13:51:24+00:00"
|
||||
"time": "2022-09-14T06:00:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
@ -2596,16 +2596,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/browser-kit",
|
||||
"version": "v4.4.37",
|
||||
"version": "v4.4.44",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/browser-kit.git",
|
||||
"reference": "6e81008cac62369871cb6b8de64576ed138e3998"
|
||||
"reference": "2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/6e81008cac62369871cb6b8de64576ed138e3998",
|
||||
"reference": "6e81008cac62369871cb6b8de64576ed138e3998",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb",
|
||||
"reference": "2a1ff40723ef6b29c8229a860a9c8f815ad7dbbb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2648,7 +2648,7 @@
|
||||
"description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/browser-kit/tree/v4.4.37"
|
||||
"source": "https://github.com/symfony/browser-kit/tree/v4.4.44"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2664,20 +2664,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:41:36+00:00"
|
||||
"time": "2022-07-25T12:56:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v4.4.43",
|
||||
"version": "v4.4.45",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46"
|
||||
"reference": "28b77970939500fb04180166a1f716e75a871ef8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
|
||||
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/28b77970939500fb04180166a1f716e75a871ef8",
|
||||
"reference": "28b77970939500fb04180166a1f716e75a871ef8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2738,7 +2738,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.43"
|
||||
"source": "https://github.com/symfony/console/tree/v4.4.45"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2754,20 +2754,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-23T12:22:25+00:00"
|
||||
"time": "2022-08-17T14:50:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v4.4.37",
|
||||
"version": "v4.4.44",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "0628e6c6d7c92f1a7bae543959bdc17347be2436"
|
||||
"reference": "bd0a6737e48de45b4b0b7b6fc98c78404ddceaed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/0628e6c6d7c92f1a7bae543959bdc17347be2436",
|
||||
"reference": "0628e6c6d7c92f1a7bae543959bdc17347be2436",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/bd0a6737e48de45b4b0b7b6fc98c78404ddceaed",
|
||||
"reference": "bd0a6737e48de45b4b0b7b6fc98c78404ddceaed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2804,7 +2804,7 @@
|
||||
"description": "Converts CSS selectors to XPath expressions",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/css-selector/tree/v4.4.37"
|
||||
"source": "https://github.com/symfony/css-selector/tree/v4.4.44"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2820,7 +2820,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:41:36+00:00"
|
||||
"time": "2022-06-27T13:16:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@ -2891,16 +2891,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v4.4.42",
|
||||
"version": "v4.4.45",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "be5a04618e5d44e71d013f177df80d3ec4b192a0"
|
||||
"reference": "4b8daf6c56801e6d664224261cb100b73edc78a5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/be5a04618e5d44e71d013f177df80d3ec4b192a0",
|
||||
"reference": "be5a04618e5d44e71d013f177df80d3ec4b192a0",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b8daf6c56801e6d664224261cb100b73edc78a5",
|
||||
"reference": "4b8daf6c56801e6d664224261cb100b73edc78a5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2945,7 +2945,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.42"
|
||||
"source": "https://github.com/symfony/dom-crawler/tree/v4.4.45"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2961,20 +2961,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-30T18:34:00+00:00"
|
||||
"time": "2022-08-03T12:57:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v4.4.42",
|
||||
"version": "v4.4.44",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "708e761740c16b02c86e3f0c932018a06b895d40"
|
||||
"reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/708e761740c16b02c86e3f0c932018a06b895d40",
|
||||
"reference": "708e761740c16b02c86e3f0c932018a06b895d40",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1e866e9e5c1b22168e0ce5f0b467f19bba61266a",
|
||||
"reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3029,7 +3029,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.42"
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v4.4.44"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -3045,7 +3045,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-05T15:33:49+00:00"
|
||||
"time": "2022-07-20T09:59:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
@ -3128,16 +3128,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v4.4.41",
|
||||
"version": "v4.4.44",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "40790bdf293b462798882ef6da72bb49a4a6633a"
|
||||
"reference": "66bd787edb5e42ff59d3523f623895af05043e4f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/40790bdf293b462798882ef6da72bb49a4a6633a",
|
||||
"reference": "40790bdf293b462798882ef6da72bb49a4a6633a",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/66bd787edb5e42ff59d3523f623895af05043e4f",
|
||||
"reference": "66bd787edb5e42ff59d3523f623895af05043e4f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3170,7 +3170,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.41"
|
||||
"source": "https://github.com/symfony/finder/tree/v4.4.44"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -3186,7 +3186,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-14T15:36:10+00:00"
|
||||
"time": "2022-07-29T07:35:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -3609,16 +3609,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v4.4.41",
|
||||
"version": "v4.4.44",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "9eedd60225506d56e42210a70c21bb80ca8456ce"
|
||||
"reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/9eedd60225506d56e42210a70c21bb80ca8456ce",
|
||||
"reference": "9eedd60225506d56e42210a70c21bb80ca8456ce",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/5cee9cdc4f7805e2699d9fd66991a0e6df8252a2",
|
||||
"reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3651,7 +3651,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v4.4.41"
|
||||
"source": "https://github.com/symfony/process/tree/v4.4.44"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -3667,7 +3667,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-04T10:19:07+00:00"
|
||||
"time": "2022-06-27T13:16:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
@ -3754,16 +3754,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v4.4.43",
|
||||
"version": "v4.4.45",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "07e392f0ef78376d080d5353c081a5e5704835bd"
|
||||
"reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/07e392f0ef78376d080d5353c081a5e5704835bd",
|
||||
"reference": "07e392f0ef78376d080d5353c081a5e5704835bd",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d",
|
||||
"reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3805,7 +3805,7 @@
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v4.4.43"
|
||||
"source": "https://github.com/symfony/yaml/tree/v4.4.45"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -3821,7 +3821,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-20T08:31:17+00:00"
|
||||
"time": "2022-08-02T15:47:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
@ -1857,10 +1857,15 @@ table.noflex {
|
||||
line-height: inherit !important;
|
||||
font-family: inherit !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: inherit !important; }
|
||||
box-shadow: inherit !important;
|
||||
border-color: transparent !important; }
|
||||
#admin-main .admin-block a.gumroad-button:hover {
|
||||
transform: inherit; }
|
||||
#admin-main .admin-block a.gumroad-button .gumroad-button-logo {
|
||||
display: none !important;
|
||||
background-image: none !important; }
|
||||
#admin-main .admin-block a.gumroad-button .logo-full {
|
||||
display: none; }
|
||||
|
||||
#error {
|
||||
text-align: center;
|
||||
@ -2815,6 +2820,9 @@ table.noflex {
|
||||
right: 0;
|
||||
left: 0; }
|
||||
|
||||
.gumroad .cart-button {
|
||||
display: none !important; }
|
||||
|
||||
@media only all and (max-width: 47.938em) {
|
||||
#admin-main .config-wrapper-system .form-tabs.side-tabs > .tabs-nav {
|
||||
display: none;
|
||||
|
File diff suppressed because one or more lines are too long
@ -1220,6 +1220,13 @@ body.sidebar-quickopen #admin-main {
|
||||
}
|
||||
}
|
||||
|
||||
// Gumroad
|
||||
.gumroad {
|
||||
.cart-button {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Config Side Tabs
|
||||
@include breakpoint(mobile-only) {
|
||||
#admin-main .config-wrapper-system .form-tabs.side-tabs {
|
||||
|
@ -124,8 +124,17 @@
|
||||
font-family: inherit !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: inherit !important;
|
||||
border-color: transparent !important;
|
||||
|
||||
&:hover {
|
||||
transform: inherit;
|
||||
}
|
||||
.gumroad-button-logo {
|
||||
display: none !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
.logo-full {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
2
plugins/admin/vendor/autoload.php
vendored
2
plugins/admin/vendor/autoload.php
vendored
@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit38a448a612c5797456d245c809d4a914::getLoader();
|
||||
return ComposerAutoloaderInit98c98c1c3d67f21a128f935fe4a74897::getLoader();
|
||||
|
15
plugins/admin/vendor/bin/picofeed
vendored
15
plugins/admin/vendor/bin/picofeed
vendored
@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
@ -98,7 +108,10 @@ if (PHP_VERSION_ID < 80000) {
|
||||
}
|
||||
}
|
||||
|
||||
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
include("phpvfscomposer://" . __DIR__ . '/..'.'/p3k/picofeed/picofeed');
|
||||
exit(0);
|
||||
}
|
||||
|
14
plugins/admin/vendor/composer/autoload_real.php
vendored
14
plugins/admin/vendor/composer/autoload_real.php
vendored
@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit38a448a612c5797456d245c809d4a914
|
||||
class ComposerAutoloaderInit98c98c1c3d67f21a128f935fe4a74897
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
@ -24,18 +24,18 @@ class ComposerAutoloaderInit38a448a612c5797456d245c809d4a914
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit38a448a612c5797456d245c809d4a914', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInit98c98c1c3d67f21a128f935fe4a74897', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit38a448a612c5797456d245c809d4a914', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit98c98c1c3d67f21a128f935fe4a74897', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit38a448a612c5797456d245c809d4a914::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit38a448a612c5797456d245c809d4a914::$files;
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire38a448a612c5797456d245c809d4a914($fileIdentifier, $file);
|
||||
composerRequire98c98c1c3d67f21a128f935fe4a74897($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
@ -47,7 +47,7 @@ class ComposerAutoloaderInit38a448a612c5797456d245c809d4a914
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire38a448a612c5797456d245c809d4a914($fileIdentifier, $file)
|
||||
function composerRequire98c98c1c3d67f21a128f935fe4a74897($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit38a448a612c5797456d245c809d4a914
|
||||
class ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897
|
||||
{
|
||||
public static $files = array (
|
||||
'7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
||||
@ -63,10 +63,10 @@ class ComposerStaticInit38a448a612c5797456d245c809d4a914
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit38a448a612c5797456d245c809d4a914::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit38a448a612c5797456d245c809d4a914::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit38a448a612c5797456d245c809d4a914::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInit38a448a612c5797456d245c809d4a914::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInit98c98c1c3d67f21a128f935fe4a74897::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
14
plugins/admin/vendor/composer/installed.json
vendored
14
plugins/admin/vendor/composer/installed.json
vendored
@ -193,17 +193,17 @@
|
||||
},
|
||||
{
|
||||
"name": "scssphp/scssphp",
|
||||
"version": "v1.10.4",
|
||||
"version_normalized": "1.10.4.0",
|
||||
"version": "v1.11.0",
|
||||
"version_normalized": "1.11.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/scssphp/scssphp.git",
|
||||
"reference": "8ed20753db2d3d82629e6f5d35535bbbd3893b0c"
|
||||
"reference": "33749d12c2569bb24071f94e9af828662dabb068"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/8ed20753db2d3d82629e6f5d35535bbbd3893b0c",
|
||||
"reference": "8ed20753db2d3d82629e6f5d35535bbbd3893b0c",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/33749d12c2569bb24071f94e9af828662dabb068",
|
||||
"reference": "33749d12c2569bb24071f94e9af828662dabb068",
|
||||
"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-07-26T16:28:33+00:00",
|
||||
"time": "2022-09-02T21:24:55+00:00",
|
||||
"bin": [
|
||||
"bin/pscss"
|
||||
],
|
||||
@ -270,7 +270,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/scssphp/scssphp/issues",
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.10.4"
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.11.0"
|
||||
},
|
||||
"install-path": "../scssphp/scssphp"
|
||||
}
|
||||
|
10
plugins/admin/vendor/composer/installed.php
vendored
10
plugins/admin/vendor/composer/installed.php
vendored
@ -3,7 +3,7 @@
|
||||
'name' => 'getgrav/grav-plugin-admin',
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => '713002e2b83b9660bfcefe5bb0d76954eedf2b90',
|
||||
'reference' => '97ab52df8179fad32d1190b530c32053b84d5979',
|
||||
'type' => 'grav-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -13,7 +13,7 @@
|
||||
'getgrav/grav-plugin-admin' => array(
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => '713002e2b83b9660bfcefe5bb0d76954eedf2b90',
|
||||
'reference' => '97ab52df8179fad32d1190b530c32053b84d5979',
|
||||
'type' => 'grav-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -53,9 +53,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'scssphp/scssphp' => array(
|
||||
'pretty_version' => 'v1.10.4',
|
||||
'version' => '1.10.4.0',
|
||||
'reference' => '8ed20753db2d3d82629e6f5d35535bbbd3893b0c',
|
||||
'pretty_version' => 'v1.11.0',
|
||||
'version' => '1.11.0.0',
|
||||
'reference' => '33749d12c2569bb24071f94e9af828662dabb068',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../scssphp/scssphp',
|
||||
'aliases' => array(),
|
||||
|
@ -50,16 +50,16 @@
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "sass/sass-spec",
|
||||
"version": "2022.02.24",
|
||||
"version": "2022.08.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sass/sass-spec.git",
|
||||
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba"
|
||||
"reference": "2bdc199723a3445d5badac3ac774105698f08861"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sass/sass-spec/zipball/f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
||||
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
||||
"url": "https://api.github.com/repos/sass/sass-spec/zipball/2bdc199723a3445d5badac3ac774105698f08861",
|
||||
"reference": "2bdc199723a3445d5badac3ac774105698f08861",
|
||||
"shasum": ""
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="PSR12 (adapted for PHP 5.6+)">
|
||||
<rule ref="PSR12">
|
||||
<!-- Ignore this PHP 7.1+ sniff as long as we support PHP 5.6+ -->
|
||||
<exclude name="PSR12.Properties.ConstantVisibility.NotFound"/>
|
||||
|
||||
<!-- This sniff doesn't ignore comment blocks -->
|
||||
<!--
|
||||
<exclude name="Generic.Files.LineLength"/>
|
||||
-->
|
||||
</rule>
|
||||
</ruleset>
|
@ -7947,7 +7947,11 @@ EOL;
|
||||
$max = 100;
|
||||
}
|
||||
|
||||
return $number->valueInRange($change ? 0 : -$max, $max, $name);
|
||||
if ($scale || $assertPercent) {
|
||||
return $number->valueInRange($change ? 0 : -$max, $max, $name);
|
||||
}
|
||||
|
||||
return $number->valueInRangeWithUnit($change ? 0 : -$max, $max, $name, $checkPercent ? '%' : '');
|
||||
};
|
||||
|
||||
$alpha = $getParam('alpha', 1);
|
||||
@ -8582,7 +8586,7 @@ EOL;
|
||||
$color = $this->assertColor($args[0], 'color');
|
||||
$amount = $this->assertNumber($args[1], 'amount');
|
||||
|
||||
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRange(0, 1, 'amount');
|
||||
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRangeWithUnit(0, 1, 'amount', '');
|
||||
$color[4] = min(1, max(0, $color[4]));
|
||||
|
||||
return $color;
|
||||
@ -8601,7 +8605,7 @@ EOL;
|
||||
$color = $this->assertColor($args[0], 'color');
|
||||
$amount = $this->assertNumber($args[1], 'amount');
|
||||
|
||||
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRange(0, 1, 'amount');
|
||||
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRangeWithUnit(0, 1, 'amount', '');
|
||||
$color[4] = min(1, max(0, $color[4]));
|
||||
|
||||
return $color;
|
||||
@ -8769,7 +8773,7 @@ will be an error in future versions of Sass.\n on line $line of $fname";
|
||||
protected function libNth($args)
|
||||
{
|
||||
$list = $this->coerceList($args[0], ',', false);
|
||||
$n = $this->assertNumber($args[1])->getDimension();
|
||||
$n = $this->assertInteger($args[1]);
|
||||
|
||||
if ($n > 0) {
|
||||
$n--;
|
||||
@ -8784,7 +8788,7 @@ will be an error in future versions of Sass.\n on line $line of $fname";
|
||||
protected function libSetNth($args)
|
||||
{
|
||||
$list = $this->coerceList($args[0]);
|
||||
$n = $this->assertNumber($args[1])->getDimension();
|
||||
$n = $this->assertInteger($args[1]);
|
||||
|
||||
if ($n > 0) {
|
||||
$n--;
|
||||
@ -9589,7 +9593,25 @@ will be an error in future versions of Sass.\n on line $line of $fname";
|
||||
protected function libRandom($args)
|
||||
{
|
||||
if (isset($args[0]) && $args[0] !== static::$null) {
|
||||
$n = $this->assertInteger($args[0], 'limit');
|
||||
$limit = $this->assertNumber($args[0], 'limit');
|
||||
|
||||
if ($limit->hasUnits()) {
|
||||
$unitString = $limit->unitStr();
|
||||
$message = <<<TXT
|
||||
random() will no longer ignore \$limit units ($limit) in a future release.
|
||||
|
||||
Recommendation: random(\$limit / 1$unitString) * 1$unitString
|
||||
|
||||
To preserve current behavior: random(\$limit / 1$unitString)
|
||||
|
||||
More info: https://sass-lang.com/d/random-with-units
|
||||
|
||||
TXT;
|
||||
|
||||
Warn::deprecation($this->addLocationToMessage($message));
|
||||
}
|
||||
|
||||
$n = $this->assertInteger($limit, 'limit');
|
||||
|
||||
if ($n < 1) {
|
||||
throw new SassScriptException("\$limit: Must be greater than 0, was $n.");
|
||||
|
@ -227,6 +227,16 @@ class Number extends Node implements \ArrayAccess
|
||||
return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number has any units
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUnits()
|
||||
{
|
||||
return !$this->unitless();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the number has exactly this unit
|
||||
*
|
||||
@ -266,7 +276,27 @@ class Number extends Node implements \ArrayAccess
|
||||
try {
|
||||
return Util::checkRange('', new Range($min, $max), $this);
|
||||
} catch (RangeException $e) {
|
||||
throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s', $this, $min, $this->unitStr(), $max), $name);
|
||||
throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $this->unitStr(), $max), $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|int $min
|
||||
* @param float|int $max
|
||||
* @param string $name
|
||||
* @param string $unit
|
||||
*
|
||||
* @return float|int
|
||||
* @throws SassScriptException
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function valueInRangeWithUnit($min, $max, $name, $unit)
|
||||
{
|
||||
try {
|
||||
return Util::checkRange('', new Range($min, $max), $this);
|
||||
} catch (RangeException $e) {
|
||||
throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $unit, $max), $name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2461,7 +2461,7 @@ class Parser
|
||||
$whiteBefore = isset($this->buffer[$this->count - 1]) &&
|
||||
ctype_space($this->buffer[$this->count - 1]);
|
||||
|
||||
while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) {
|
||||
while ($this->match($operators, $m, false) && static::$precedence[strtolower($m[1])] >= $minP) {
|
||||
$whiteAfter = isset($this->buffer[$this->count]) &&
|
||||
ctype_space($this->buffer[$this->count]);
|
||||
$varAfter = isset($this->buffer[$this->count]) &&
|
||||
@ -2485,7 +2485,7 @@ class Parser
|
||||
}
|
||||
|
||||
// consume higher-precedence operators on the right-hand side
|
||||
$rhs = $this->expHelper($rhs, static::$precedence[$op] + 1);
|
||||
$rhs = $this->expHelper($rhs, static::$precedence[strtolower($op)] + 1);
|
||||
|
||||
$lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
|
||||
|
||||
@ -2804,6 +2804,10 @@ class Parser
|
||||
$this->argValues($args) &&
|
||||
$this->matchChar(')')
|
||||
) {
|
||||
if (strtolower($name) === 'var' && \count($args) === 2 && $args[1][0] === Type::T_NULL) {
|
||||
$args[1] = [null, [Type::T_STRING, '', [' ']], false];
|
||||
}
|
||||
|
||||
$func = [Type::T_FUNCTION_CALL, $name, $args];
|
||||
|
||||
return true;
|
||||
|
@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp;
|
||||
*/
|
||||
class Version
|
||||
{
|
||||
const VERSION = '1.10.4';
|
||||
const VERSION = '1.11.0';
|
||||
}
|
||||
|
@ -1,3 +1,9 @@
|
||||
# v1.9.0
|
||||
## 10/05/2022
|
||||
|
||||
1. [](#new)
|
||||
* Utilize the new `onPageHeaders()` event in Grav `1.7.37` to force UTF-8 encoding
|
||||
|
||||
# v1.8.5
|
||||
## 06/09/2021
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: Feed
|
||||
type: plugin
|
||||
slug: feed
|
||||
version: 1.8.5
|
||||
version: 1.9.0
|
||||
description: The **Feed** plugin is a simple yet powerful add-on that lets you view a Grav Collection as **JSON**, **RSS** or **Atom** news feed.
|
||||
icon: rss
|
||||
author:
|
||||
|
@ -41,7 +41,8 @@ class FeedPlugin extends Plugin
|
||||
['autoload', 100000],
|
||||
['onPluginsInitialized', 0],
|
||||
],
|
||||
'onBlueprintCreated' => ['onBlueprintCreated', 0]
|
||||
'onBlueprintCreated' => ['onBlueprintCreated', 0],
|
||||
'onPageHeaders' => ['onPageHeaders', 0]
|
||||
];
|
||||
}
|
||||
|
||||
@ -159,4 +160,19 @@ class FeedPlugin extends Plugin
|
||||
$inEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force UTF-8 char-set encoding via `Content-Type` header
|
||||
*
|
||||
* @param Event $e
|
||||
* @return void
|
||||
*/
|
||||
public function onPageHeaders(Event $e)
|
||||
{
|
||||
$headers = $e['headers'];
|
||||
$content_type = $headers->{'Content-Type'} ?? null;
|
||||
if ($content_type) {
|
||||
$headers->{'Content-Type'} = "$content_type; charset=utf-8";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,21 @@
|
||||
# v7.0.2
|
||||
## 10/05/2022
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for modular form definitions at root-level (useful for storing shared forms)
|
||||
|
||||
# v7.0.1
|
||||
## 09/20/2022
|
||||
|
||||
1. [](#improved)
|
||||
* Provided some basic CSS styling for new captcha field
|
||||
|
||||
# v7.0.0
|
||||
## 09/20/2022
|
||||
|
||||
1. [](#new)
|
||||
* Added a new custom `basic-captcha` option with **character** and **math** puzzles. No 3rd-part service required.
|
||||
|
||||
# v6.0.4
|
||||
## 08/08/2022
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
1
plugins/form/assets/form-styles.css.map
Normal file
1
plugins/form/assets/form-styles.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../scss/form-styles.scss"],"names":[],"mappings":"CAGA,uBACI,6BACA,kCACA,kBACA,cACA,cAGJ,aACI,cAGJ,gBACI,kBACA,6BACA,WACA,UACA,gBACA,8BAGJ,eACI,SAKA,uBACI,aAGJ,wCACI,kBACA,kBACA,OACA,QACA,QACA,2BACA,SAGJ,0BACI,kBACA,gBACA,kBACA,qBACA,uBACA,WACA,cAEA,sCACI,aAEA,4CACI,UAGJ,wDACI,gBACA,WAGJ,gHAEI,kBACA,UAWZ,qBACI,aACA,gBAEA,mBAEA,uBACI,OACA,8CACA,eACA,kBACA,aACA,aACA,mBACA,uBACA,6BACA,0BAEA,8BACI,sBACA,sCACA,cAEA,mCACI,MAtGA,KA2GZ,0BACI,qBACA,gBAKR,4BACI,0BAKA,uCACI,aACA,iBAEA,8CACI,cAOhB,YACI,qBAEA,kBACI,eACA,eACA,kBACA,mBACA,kBAGJ,yBACI,WACA,qBACA,WACA,YACA,OACA,aACA,kBACA,kBACA,kBAEA,yBAGJ,iCACI,aAEJ,sDACI,YACA,eACA,cACA,kBAGJ,6BACI,eAMJ,8CACI,iBACA,sBAEJ,yCACI,qBAKR,eACI,oBACA,gBACA,kBACA,iBACA,sBAEA,iCACI,kBACA,kBACA,aAGJ,qBACI,qBACA,eACA,eACA,SACA,mBACA,cACA,qCAGJ,6CACI,gBACA,WAGJ,mCACI,WACA,gBAOR,eACI,kBACA,oBACA,oBACA,aACA,4BACA,6BACA,0BACA,sBACA,eACA,WACA,YACA,gBACA,iBACA,yBACA,sBACA,aAGJ,qBACI,kBACA,mBACA,WACA,OACA,yBACA,iBAGJ,4BACI,kBACA,OACA,MACA,WACA,YACA,kBACA,yCAGJ,uBACI,cACA,kBACA,gBAGJ,wBACI,oBACA,oBACA,aACA,yBACA,sBACA,8BACA,eAGJ,kCACI,aACA,mBACA,oBAGJ,mFAGI,eACA,qBAIA,6CACI,sBACA,kBACA,aACA,gBAEJ,6CACI,aACA,WACA,sBACA,cACA,iDACI,SAEJ,wDACI,aACA,WACA,YAGR,0CACI","file":"form-styles.css"}
|
@ -1,7 +1,7 @@
|
||||
name: Form
|
||||
slug: form
|
||||
type: plugin
|
||||
version: 6.0.4
|
||||
version: 7.0.2
|
||||
description: Enables forms handling and processing
|
||||
icon: check-square
|
||||
author:
|
||||
@ -196,3 +196,127 @@ form:
|
||||
label: PLUGIN_FORM.RECAPTCHA_SECRET_KEY
|
||||
help: PLUGIN_FORM.RECAPTCHA_SECRET_KEY_HELP
|
||||
default: ''
|
||||
|
||||
basic_captcha:
|
||||
type: section
|
||||
title: PLUGIN_FORM.BASIC_CAPTCHA
|
||||
|
||||
fields:
|
||||
basic_captcha.type:
|
||||
type: elements
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_TYPE
|
||||
default: 'characters'
|
||||
size: medium
|
||||
options:
|
||||
characters: Random Characters
|
||||
math: Math Puzzle
|
||||
fields:
|
||||
characters:
|
||||
type: element
|
||||
fields:
|
||||
basic_captcha.chars.length:
|
||||
type: range
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_LENGTH
|
||||
default: 6
|
||||
validate:
|
||||
min: 4
|
||||
max: 12
|
||||
append: characters
|
||||
basic_captcha.chars.font:
|
||||
type: select
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_FONT
|
||||
default: zxx-noise.ttf
|
||||
options:
|
||||
'zxx-noise.ttf': zxx-Noise
|
||||
'zxx-xed.ttf': zxx-Xed
|
||||
'zxx-camo.ttf': zxx-Camo
|
||||
'zxx-sans.ttf': zxx-Sans
|
||||
basic_captcha.chars.size:
|
||||
type: range
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_SIZE
|
||||
default: 24
|
||||
append: px
|
||||
validate:
|
||||
min: 12
|
||||
max: 32
|
||||
step: 2
|
||||
basic_captcha.chars.bg:
|
||||
type: colorpicker
|
||||
size: small
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BG_COLOR
|
||||
default: '#ffffff'
|
||||
basic_captcha.chars.text:
|
||||
type: colorpicker
|
||||
size: small
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_TEXT_COLOR
|
||||
default: '#000000'
|
||||
basic_captcha.chars.start_x:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_START_X
|
||||
default: 5
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.start_y:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_START_Y
|
||||
default: 30
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.box_width:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_WIDTH
|
||||
default: 135
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.chars.box_height:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_BOX_HEIGHT
|
||||
default: 40
|
||||
append: px
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
math:
|
||||
type: element
|
||||
fields:
|
||||
basic_captcha.math.min:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MIN
|
||||
default: 1
|
||||
size: small
|
||||
validate:
|
||||
min: 0
|
||||
type: number
|
||||
basic_captcha.math.max:
|
||||
type: number
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_MAX
|
||||
default: 10
|
||||
size: small
|
||||
validate:
|
||||
min: 1
|
||||
type: number
|
||||
basic_captcha.math.operators:
|
||||
type: selectize
|
||||
selectize:
|
||||
options:
|
||||
- value: '+'
|
||||
text: '+ Addition'
|
||||
- value: '-'
|
||||
text: '- Subtraction'
|
||||
- value: '*'
|
||||
text: 'x Multiplication'
|
||||
- value: '/'
|
||||
text: '/ Division'
|
||||
label: PLUGIN_FORM.BASIC_CAPTCHA_MATH_OPERATORS
|
||||
validate:
|
||||
type: commalist
|
||||
|
122
plugins/form/classes/BasicCaptcha.php
Normal file
122
plugins/form/classes/BasicCaptcha.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\Form;
|
||||
|
||||
use GdImage;
|
||||
use Grav\Common\Grav;
|
||||
|
||||
class BasicCaptcha
|
||||
{
|
||||
protected $session = null;
|
||||
protected $key = 'basic_captcha_code';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->session = Grav::instance()['session'];
|
||||
}
|
||||
|
||||
public function getCaptchaCode($length = null): string
|
||||
{
|
||||
$config = Grav::instance()['config']->get('plugins.form.basic_captcha');
|
||||
$type = $config['type'] ?? 'characters';
|
||||
|
||||
if ($type == 'math') {
|
||||
$min = $config['math']['min'] ?? 1;
|
||||
$max = $config['math']['max'] ?? 12;
|
||||
$operators = $config['math']['operators'] ?? ['+','-','*'];
|
||||
|
||||
$first_num = random_int($min, $max);
|
||||
$second_num = random_int($min, $max);
|
||||
$operator = $operators[array_rand($operators)];
|
||||
|
||||
// calculator
|
||||
if ($operator === '-') {
|
||||
if ($first_num < $second_num) {
|
||||
$result = "$second_num-$first_num";
|
||||
$captcha_code = $second_num-$first_num;
|
||||
} else {
|
||||
$result = "$first_num-$second_num";
|
||||
$captcha_code = $first_num - $second_num;
|
||||
}
|
||||
} elseif ($operator === '*') {
|
||||
$result = "{$first_num}x{$second_num}";
|
||||
$captcha_code = $first_num - $second_num;
|
||||
} elseif ($operator === '/') {
|
||||
$result = "$first_num/ second_num";
|
||||
$captcha_code = $first_num / $second_num;
|
||||
} elseif ($operator === '+') {
|
||||
$result = "$first_num+$second_num";
|
||||
$captcha_code = $first_num + $second_num;
|
||||
}
|
||||
} else {
|
||||
if ($length === null) {
|
||||
$length = $config['chars']['length'] ?? 6;
|
||||
}
|
||||
$random_alpha = md5(random_bytes(64));
|
||||
$captcha_code = substr($random_alpha, 0, $length);
|
||||
$result = $captcha_code;
|
||||
}
|
||||
|
||||
|
||||
$this->setSession($this->key, $captcha_code);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setSession($key, $value): void
|
||||
{
|
||||
$this->session->$key = $value;
|
||||
}
|
||||
|
||||
public function getSession($key = null): ?string
|
||||
{
|
||||
if ($key === null) {
|
||||
$key = $this->key;
|
||||
}
|
||||
return $this->session->$key ?? null;
|
||||
}
|
||||
|
||||
public function createCaptchaImage($captcha_code)
|
||||
{
|
||||
$config = Grav::instance()['config']->get('plugins.form.basic_captcha');
|
||||
$font = $config['chars']['font'] ?? 'zxx-xed.ttf';
|
||||
|
||||
$target_layer = imagecreatetruecolor($config['chars']['box_width'], $config['chars']['box_height']);
|
||||
|
||||
$bg = $this->hexToRgb($config['chars']['bg'] ?? '#ffffff');
|
||||
$text = $this->hexToRgb($config['chars']['text'] ?? '#000000');
|
||||
|
||||
$captcha_background = imagecolorallocate($target_layer, $bg[0], $bg[1], $bg[2]);
|
||||
$captcha_text_color = imagecolorallocate($target_layer, $text[0], $text[1], $text[2]);
|
||||
|
||||
$font_path = __DIR__ . '/../fonts/' . $font;
|
||||
|
||||
imagefill($target_layer, 0, 0, $captcha_background);
|
||||
|
||||
imagefttext($target_layer, $config['chars']['size'], 0, $config['chars']['start_x'], $config['chars']['start_y'], $captcha_text_color, $font_path, $captcha_code);
|
||||
return $target_layer;
|
||||
}
|
||||
|
||||
public function renderCaptchaImage($imageData): void
|
||||
{
|
||||
header("Content-type: image/jpeg");
|
||||
imagejpeg($imageData);
|
||||
}
|
||||
|
||||
public function validateCaptcha($formData): bool
|
||||
{
|
||||
$isValid = false;
|
||||
$capchaSessionData = $this->getSession();
|
||||
|
||||
if ($capchaSessionData == $formData) {
|
||||
$isValid = true;
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
private function hexToRgb($hex): array
|
||||
{
|
||||
return sscanf($hex, "#%02x%02x%02x");
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
plugins/form/fonts/zxx-camo.ttf
Normal file
BIN
plugins/form/fonts/zxx-camo.ttf
Normal file
Binary file not shown.
BIN
plugins/form/fonts/zxx-noise.ttf
Normal file
BIN
plugins/form/fonts/zxx-noise.ttf
Normal file
Binary file not shown.
BIN
plugins/form/fonts/zxx-sans.ttf
Normal file
BIN
plugins/form/fonts/zxx-sans.ttf
Normal file
Binary file not shown.
BIN
plugins/form/fonts/zxx-xed.ttf
Normal file
BIN
plugins/form/fonts/zxx-xed.ttf
Normal file
Binary file not shown.
@ -21,6 +21,7 @@ use Grav\Common\Yaml;
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Grav\Framework\Route\Route;
|
||||
use Grav\Plugin\Form\BasicCaptcha;
|
||||
use Grav\Plugin\Form\Form;
|
||||
use Grav\Plugin\Form\Forms;
|
||||
use Grav\Plugin\Form\TwigExtension;
|
||||
@ -135,6 +136,8 @@ class FormPlugin extends Plugin
|
||||
$this->grav->close($response);
|
||||
}
|
||||
|
||||
$this->processBasicCaptchaImage($uri);
|
||||
|
||||
$this->enable([
|
||||
'onPageProcessed' => ['onPageProcessed', 0],
|
||||
'onPagesInitialized' => ['onPagesInitialized', 0],
|
||||
@ -509,6 +512,21 @@ class FormPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'basic-captcha':
|
||||
$captcha = new BasicCaptcha();
|
||||
$captcha_value = trim($form->value('basic-captcha'));
|
||||
if (!$captcha->validateCaptcha($captcha_value)) {
|
||||
$message = $params['message'] ?? $this->grav['language']->translate('PLUGIN_FORM.ERROR_BASIC_CAPTCHA');
|
||||
|
||||
$this->grav->fireEvent('onFormValidationError', new Event([
|
||||
'form' => $form,
|
||||
'message' => $message
|
||||
]));
|
||||
|
||||
$event->stopPropagation();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'timestamp':
|
||||
$label = $params['label'] ?? 'Timestamp';
|
||||
$format = $params['format'] ?? 'Y-m-d H:i:s';
|
||||
@ -553,8 +571,7 @@ class FormPlugin extends Plugin
|
||||
$this->grav['messages']->add($form->message, 'success');
|
||||
}
|
||||
|
||||
$event['redirect'] = $url;
|
||||
$event->stopPropagation();
|
||||
$this->grav->redirect($url);
|
||||
break;
|
||||
case 'reset':
|
||||
if (Utils::isPositive($params)) {
|
||||
@ -780,7 +797,7 @@ class FormPlugin extends Plugin
|
||||
*/
|
||||
public function addFormDefinition(PageInterface $page, string $name, array $form): void
|
||||
{
|
||||
$route = $page->home() ? '/' : $page->route();
|
||||
$route = ($page->home() ? '/' : $page->route()) ?? '/';
|
||||
|
||||
if (!isset($this->forms[$route][$name])) {
|
||||
$form['_page_routable'] = !$page->isModule();
|
||||
@ -1258,4 +1275,15 @@ class FormPlugin extends Plugin
|
||||
|
||||
return date(preg_replace('`(?<!\\\\)u`', sprintf('%06d', $milliseconds), $format), $timestamp);
|
||||
}
|
||||
|
||||
protected function processBasicCaptchaImage(Uri $uri)
|
||||
{
|
||||
if ($uri->path() === '/forms-basic-captcha-image.jpg') {
|
||||
$captcha = new BasicCaptcha();
|
||||
$code = $captcha->getCaptchaCode();
|
||||
$image = $captcha->createCaptchaImage($code);
|
||||
$captcha->renderCaptchaImage($image);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,4 +17,20 @@ recaptcha:
|
||||
version: 2-checkbox
|
||||
theme: light
|
||||
site_key:
|
||||
secret_key:
|
||||
secret_key:
|
||||
basic_captcha:
|
||||
type: characters # options: [characters | math]
|
||||
chars:
|
||||
length: 6 # number of chars to output
|
||||
font: zxx-noise.ttf # options: [zxx-noise.ttf | zxx-camo.ttf | zxx-xed.ttf | zxx-sans.ttf]
|
||||
bg: '#cccccc' # 6-char hex color
|
||||
text: '#333333' # 6-char hex color
|
||||
size: 24 # font size in px
|
||||
start_x: 5 # start position in x direction in px
|
||||
start_y: 30 # start position in y direction in px
|
||||
box_width: 135 # box width in px
|
||||
box_height: 40 # box height in px
|
||||
math:
|
||||
min: 1 # smallest digit
|
||||
max: 12 # largest digit
|
||||
operators: ['+','-','*'] # operators that can be used in math
|
@ -10,6 +10,7 @@ en:
|
||||
DESTINATION_HELP: "The location where the files should be uploaded to"
|
||||
ACCEPT: "Allowed MIME Types"
|
||||
ACCEPT_HELP: "A list of MIME Types that are allowed for upload"
|
||||
ERROR_BASIC_CAPTCHA: "Captcha failed for this form, please try again"
|
||||
ERROR_VALIDATING_CAPTCHA: "reCAPTCHA bot protection has identified this form submission is problematic"
|
||||
DATA_SUMMARY: "Here is the summary of what you wrote to us:"
|
||||
NO_FORM_DATA: "No form data available"
|
||||
@ -72,6 +73,21 @@ en:
|
||||
DESTINATION_NOT_SPECIFIED: "Destination not specified"
|
||||
INVALID_MIME_TYPE: "The MIME type %s for the file %s is not accepted."
|
||||
INVALID_FILE_EXTENSION: "The File Extension for the file %s is not accepted."
|
||||
BASIC_CAPTCHA: "Basic Captcha"
|
||||
BASIC_CAPTCHA_TYPE: "Captcha challenge type"
|
||||
BASIC_CAPTCHA_LENGTH: "Number of characters"
|
||||
BASIC_CAPTCHA_FONT: "TTF Font"
|
||||
BASIC_CAPTCHA_SIZE: "Font size"
|
||||
BASIC_CAPTCHA_BG_COLOR: "Background color"
|
||||
BASIC_CAPTCHA_TEXT_COLOR: "Text color"
|
||||
BASIC_CAPTCHA_START_X: "Text start x-position"
|
||||
BASIC_CAPTCHA_START_Y: "Text start y-position"
|
||||
BASIC_CAPTCHA_BOX_WIDTH: "Image width"
|
||||
BASIC_CAPTCHA_BOX_HEIGHT: "Image height"
|
||||
BASIC_CAPTCHA_MATH_MIN: "Minimum number"
|
||||
BASIC_CAPTCHA_MATH_MAX: "Maximum number"
|
||||
BASIC_CAPTCHA_MATH_OPERATORS: "Mathematical operators (randomized)"
|
||||
|
||||
eu:
|
||||
PLUGIN_FORM:
|
||||
NOT_VALIDATED: "Formularioa ez da baliozkotu. Beharrezkoa den eremu bat edo gehiago falta dira."
|
||||
|
2
plugins/form/scss.sh
Normal file
2
plugins/form/scss.sh
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
sass --watch -s compressed scss:assets
|
@ -1,4 +1,4 @@
|
||||
$form-border-color: #eee;
|
||||
$form-border-color: #ccc;
|
||||
$form-active-color: #000;
|
||||
|
||||
.form-group.has-errors {
|
||||
@ -287,3 +287,29 @@ $form-active-color: #000;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-data.basic-captcha {
|
||||
.form-input-wrapper {
|
||||
border: 1px solid $form-border-color;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.form-input-prepend {
|
||||
display: flex;
|
||||
color: #333;
|
||||
background-color: #ccc;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
margin: 0;
|
||||
}
|
||||
button > svg {
|
||||
margin: 0 8px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
input.form-input {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
{% set form_field_outer_data_classes = 'form-data basic-captcha' %}
|
||||
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block prepend %}
|
||||
<div class="form-input-addon form-input-prepend">
|
||||
<img id="basic-captcha-reload" src="{{ url('/forms-basic-captcha-image.jpg') }}" alt="human test" />
|
||||
<button id="reload-captcha"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="#292D32"><path d="M14.74 22.39c4.68-1.24 8-5.49 8-10.4 0-5.95-4.79-10.75-10.75-10.75 -3.11 0-5.78 1.11-7.99 2.95 -.77.64-1.43 1.32-1.98 2.01 -.34.41-.57.75-.69.95 -.22.35-.1.81.25 1.02 .35.21.81.09 1.02-.26 .08-.15.27-.43.56-.79 .49-.62 1.08-1.23 1.76-1.81C6.87 3.67 9.21 2.7 11.94 2.7c5.13 0 9.25 4.12 9.25 9.25 0 4.22-2.86 7.88-6.9 8.94 -.41.1-.64.51-.54.91 .1.4.51.63.91.53Zm-12-14.84V2.99c-.001-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75v4.56c0 .41.33.75.75.75 .41 0 .75-.34.75-.75Zm-.75.75H4h2.43c.41 0 .75-.34.75-.75 0-.42-.34-.75-.75-.75H4 1.99c-.42 0-.75.33-.75.75 0 .41.33.75.75.75Z"/><path d="M1.25 12c0 1.09.16 2.16.48 3.18 .12.39.54.61.93.49 .39-.13.61-.55.49-.94 -.28-.89-.42-1.81-.42-2.75 0-.42-.34-.75-.75-.75 -.42 0-.75.33-.75.75Zm1.93 6.15c.61.88 1.36 1.67 2.22 2.33 .32.25.79.19 1.05-.14 .25-.33.19-.8-.14-1.06 -.74-.58-1.38-1.25-1.92-2.02 -.24-.34-.71-.43-1.05-.19 -.34.23-.43.7-.19 1.04Zm5.02 3.91c1 .37 2.06.6 3.15.66 .41.02.76-.3.79-.71 .02-.42-.3-.77-.71-.8 -.94-.06-1.85-.25-2.72-.58 -.39-.15-.83.04-.97.43 -.15.38.04.82.43.96Z"/></g></svg></button>
|
||||
<script>
|
||||
function stripQueryString(url) {
|
||||
return url.split("?")[0].split("#")[0];
|
||||
}
|
||||
document.getElementById("reload-captcha").onclick = function(event) {
|
||||
event.preventDefault();
|
||||
const src = stripQueryString(document.getElementById("basic-captcha-reload").src);
|
||||
document.getElementById("basic-captcha-reload").src = src + `?v=${new Date().getTime()}`;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block input_attributes %}
|
||||
type="text"
|
||||
{% if field.size %}size="{{ field.size }}"{% endif %}
|
||||
{% if field.minlength is defined or field.validate.min is defined %}minlength="{{ field.minlength | default(field.validate.min) }}"{% endif %}
|
||||
{% if field.maxlength is defined or field.validate.max is defined %}maxlength="{{ field.maxlength | default(field.validate.max) }}"{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user