diff --git a/composer.json b/composer.json
index 614c0597..b8af5bf6 100644
--- a/composer.json
+++ b/composer.json
@@ -23,6 +23,7 @@
"kigkonsult/icalcreator": "^2.29",
"league/csv": "^9.2",
"league/period": "^4.9",
+ "lochmueller/staticfilecache": "^10.2",
"pixelant/pxa-social-feed": "^2.0",
"symfony/http-client": "^4.3",
"symfony/options-resolver": "^4.2",
diff --git a/composer.lock b/composer.lock
index d2dd13ac..ccbe03e4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "3c9082df3930a24daad45b684ce6c58a",
+ "content-hash": "8d24d6aa5cf407f4ae9811bc6a781623",
"packages": [
{
"name": "algo26-matthias/idna-convert",
@@ -1475,6 +1475,83 @@
],
"time": "2019-09-02T06:27:10+00:00"
},
+ {
+ "name": "lochmueller/staticfilecache",
+ "version": "10.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/lochmueller/staticfilecache.git",
+ "reference": "63dc21dabc1a7dc226045c460ffa77134506a43e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/lochmueller/staticfilecache/zipball/63dc21dabc1a7dc226045c460ffa77134506a43e",
+ "reference": "63dc21dabc1a7dc226045c460ffa77134506a43e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pdo": "*",
+ "ext-zlib": "*",
+ "php": "^7.2||^7.3",
+ "typo3/cms-core": "^9.5||^10.2",
+ "typo3/cms-extbase": "^9.5||^10.2",
+ "typo3/cms-frontend": "^9.5||^10.2"
+ },
+ "replace": {
+ "typo3-ter/staticfilecache": "self.version"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.14",
+ "namelesscoder/typo3-repository-client": "^2.0",
+ "phpmd/phpmd": "^2.6",
+ "phpmetrics/phpmetrics": "^2.4",
+ "rector/rector": "^0.4",
+ "scrutinizer/ocular": "^1.5",
+ "squizlabs/php_codesniffer": "^3.4",
+ "typo3/testing-framework": "^4.2"
+ },
+ "suggest": {
+ "friendsofsymfony/http-cache": "For HTTP proxy cache services like Varnish",
+ "lochmueller/seocli": "Scan websites against SEO criteria and/or trigger the indexing process and cache warming in deployment scripts"
+ },
+ "type": "typo3-cms-extension",
+ "extra": {
+ "typo3/cms": {
+ "extension-key": "staticfilecache",
+ "cms-package-dir": "{$vendor-dir}/typo3/cms",
+ "web-dir": ".Build/Web"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SFC\\Staticfilecache\\": "Classes"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Tim Lochmüller",
+ "email": "tim@fruit-lab.de",
+ "homepage": "https://github.com/lochmueller",
+ "role": "Developer"
+ }
+ ],
+ "description": "Transparent StaticFileCache solution using mod_rewrite and mod_expires. Increase performance for static pages by a factor of 230!!",
+ "homepage": "https://github.com/lochmueller/staticfilecache",
+ "keywords": [
+ "StaticFileCache",
+ "TYPO3 CMS",
+ "cache",
+ "file",
+ "magic",
+ "speed",
+ "static"
+ ],
+ "time": "2019-12-09T08:17:21+00:00"
+ },
{
"name": "nikic/php-parser",
"version": "v4.3.0",
@@ -1975,16 +2052,16 @@
},
{
"name": "symfony/cache",
- "version": "v5.0.1",
+ "version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
- "reference": "d8df2c5a3ba88430c559145bc8b944cb578f9d65"
+ "reference": "6e8d978878ae5de705ec9fabbb6011cc18776bc9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/d8df2c5a3ba88430c559145bc8b944cb578f9d65",
- "reference": "d8df2c5a3ba88430c559145bc8b944cb578f9d65",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/6e8d978878ae5de705ec9fabbb6011cc18776bc9",
+ "reference": "6e8d978878ae5de705ec9fabbb6011cc18776bc9",
"shasum": ""
},
"require": {
@@ -2050,7 +2127,7 @@
"caching",
"psr6"
],
- "time": "2019-11-28T14:20:16+00:00"
+ "time": "2019-12-12T13:03:32+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -2112,16 +2189,16 @@
},
{
"name": "symfony/console",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "f0aea3df20d15635b3cb9730ca5eea1c65b7f201"
+ "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/f0aea3df20d15635b3cb9730ca5eea1c65b7f201",
- "reference": "f0aea3df20d15635b3cb9730ca5eea1c65b7f201",
+ "url": "https://api.github.com/repos/symfony/console/zipball/82437719dab1e6bdd28726af14cb345c2ec816d0",
+ "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0",
"shasum": ""
},
"require": {
@@ -2184,20 +2261,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2019-12-01T10:06:17+00:00"
+ "time": "2019-12-17T10:32:23+00:00"
},
{
"name": "symfony/expression-language",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
- "reference": "8a1028a65623a8f7bba582b60e4fd308e6f43001"
+ "reference": "539e7ff0b635c8b90d8127bc929da781a96eab2d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/expression-language/zipball/8a1028a65623a8f7bba582b60e4fd308e6f43001",
- "reference": "8a1028a65623a8f7bba582b60e4fd308e6f43001",
+ "url": "https://api.github.com/repos/symfony/expression-language/zipball/539e7ff0b635c8b90d8127bc929da781a96eab2d",
+ "reference": "539e7ff0b635c8b90d8127bc929da781a96eab2d",
"shasum": ""
},
"require": {
@@ -2235,11 +2312,11 @@
],
"description": "Symfony ExpressionLanguage Component",
"homepage": "https://symfony.com",
- "time": "2019-11-12T14:53:53+00:00"
+ "time": "2019-12-10T10:33:21+00:00"
},
{
"name": "symfony/finder",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
@@ -2288,16 +2365,16 @@
},
{
"name": "symfony/http-client",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "6c235cf15d8b516b41204a28d555685191409729"
+ "reference": "9ebfc77b5018a05226b38642def82746f3e2ce0f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/6c235cf15d8b516b41204a28d555685191409729",
- "reference": "6c235cf15d8b516b41204a28d555685191409729",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/9ebfc77b5018a05226b38642def82746f3e2ce0f",
+ "reference": "9ebfc77b5018a05226b38642def82746f3e2ce0f",
"shasum": ""
},
"require": {
@@ -2352,7 +2429,7 @@
],
"description": "Symfony HttpClient component",
"homepage": "https://symfony.com",
- "time": "2019-11-29T16:55:58+00:00"
+ "time": "2019-12-19T15:57:49+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -2413,7 +2490,7 @@
},
{
"name": "symfony/inflector",
- "version": "v5.0.1",
+ "version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/inflector.git",
@@ -2471,7 +2548,7 @@
},
{
"name": "symfony/intl",
- "version": "v5.0.1",
+ "version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
@@ -2546,7 +2623,7 @@
},
{
"name": "symfony/options-resolver",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
@@ -2950,16 +3027,16 @@
},
{
"name": "symfony/process",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "51c0135ef3f44c5803b33dc60e96bf4f77752726"
+ "reference": "b84501ad50adb72a94fb460a5b5c91f693e99c9b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/51c0135ef3f44c5803b33dc60e96bf4f77752726",
- "reference": "51c0135ef3f44c5803b33dc60e96bf4f77752726",
+ "url": "https://api.github.com/repos/symfony/process/zipball/b84501ad50adb72a94fb460a5b5c91f693e99c9b",
+ "reference": "b84501ad50adb72a94fb460a5b5c91f693e99c9b",
"shasum": ""
},
"require": {
@@ -2995,20 +3072,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2019-11-28T13:33:56+00:00"
+ "time": "2019-12-06T10:06:46+00:00"
},
{
"name": "symfony/property-access",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
- "reference": "bafdc8c3a9d2671af4a81baec0fcc4687c0c17bc"
+ "reference": "055fe3134f8f301ff44af314d83463b858ea6413"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/property-access/zipball/bafdc8c3a9d2671af4a81baec0fcc4687c0c17bc",
- "reference": "bafdc8c3a9d2671af4a81baec0fcc4687c0c17bc",
+ "url": "https://api.github.com/repos/symfony/property-access/zipball/055fe3134f8f301ff44af314d83463b858ea6413",
+ "reference": "055fe3134f8f301ff44af314d83463b858ea6413",
"shasum": ""
},
"require": {
@@ -3062,20 +3139,20 @@
"property path",
"reflection"
],
- "time": "2019-12-01T10:50:45+00:00"
+ "time": "2019-12-10T10:33:21+00:00"
},
{
"name": "symfony/routing",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "51f3f20ad29329a0bdf5c0e2f722d3764b065273"
+ "reference": "628bcafae1b2043969378dcfbf9c196539a38722"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/51f3f20ad29329a0bdf5c0e2f722d3764b065273",
- "reference": "51f3f20ad29329a0bdf5c0e2f722d3764b065273",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/628bcafae1b2043969378dcfbf9c196539a38722",
+ "reference": "628bcafae1b2043969378dcfbf9c196539a38722",
"shasum": ""
},
"require": {
@@ -3138,20 +3215,20 @@
"uri",
"url"
],
- "time": "2019-12-01T08:39:58+00:00"
+ "time": "2019-12-12T12:53:52+00:00"
},
{
"name": "symfony/serializer",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
- "reference": "842637bdcbed31c7dc62b3c30dfc8949f6457968"
+ "reference": "e5bc3f6dee44dc06e7e640cce4baa741b73ecb6e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/serializer/zipball/842637bdcbed31c7dc62b3c30dfc8949f6457968",
- "reference": "842637bdcbed31c7dc62b3c30dfc8949f6457968",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/e5bc3f6dee44dc06e7e640cce4baa741b73ecb6e",
+ "reference": "e5bc3f6dee44dc06e7e640cce4baa741b73ecb6e",
"shasum": ""
},
"require": {
@@ -3219,7 +3296,7 @@
],
"description": "Symfony Serializer Component",
"homepage": "https://symfony.com",
- "time": "2019-11-28T13:33:56+00:00"
+ "time": "2019-12-16T11:07:37+00:00"
},
{
"name": "symfony/service-contracts",
@@ -3281,7 +3358,7 @@
},
{
"name": "symfony/var-exporter",
- "version": "v5.0.1",
+ "version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
@@ -3341,16 +3418,16 @@
},
{
"name": "symfony/yaml",
- "version": "v4.4.1",
+ "version": "v4.4.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "76de473358fe802578a415d5bb43c296cf09d211"
+ "reference": "a08832b974dd5fafe3085a66d41fe4c84bb2628c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/76de473358fe802578a415d5bb43c296cf09d211",
- "reference": "76de473358fe802578a415d5bb43c296cf09d211",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/a08832b974dd5fafe3085a66d41fe4c84bb2628c",
+ "reference": "a08832b974dd5fafe3085a66d41fe4c84bb2628c",
"shasum": ""
},
"require": {
@@ -3396,7 +3473,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2019-11-12T14:51:11+00:00"
+ "time": "2019-12-10T10:33:21+00:00"
},
{
"name": "typo3/class-alias-loader",
@@ -3714,21 +3791,21 @@
},
{
"name": "typo3/cms-composer-installers",
- "version": "v2.2.3",
+ "version": "v2.2.4",
"source": {
"type": "git",
"url": "https://github.com/TYPO3/CmsComposerInstallers.git",
- "reference": "7ecc2253e2d97a52824fcdd4e2f632bae9936810"
+ "reference": "17243f398bf027ba56be6507c11ab957b15bbe3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/TYPO3/CmsComposerInstallers/zipball/7ecc2253e2d97a52824fcdd4e2f632bae9936810",
- "reference": "7ecc2253e2d97a52824fcdd4e2f632bae9936810",
+ "url": "https://api.github.com/repos/TYPO3/CmsComposerInstallers/zipball/17243f398bf027ba56be6507c11ab957b15bbe3b",
+ "reference": "17243f398bf027ba56be6507c11ab957b15bbe3b",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0.0",
- "php": ">=7.0.0 <=7.4"
+ "php": ">=7.0.0 <7.5"
},
"conflict": {
"composer/installers": "<1.0.24 || >1.0.24"
@@ -3777,7 +3854,7 @@
"installer",
"typo3"
],
- "time": "2019-09-24T22:09:42+00:00"
+ "time": "2019-12-18T20:02:06+00:00"
},
{
"name": "typo3/cms-core",
@@ -5393,16 +5470,16 @@
"packages-dev": [
{
"name": "deployer/deployer",
- "version": "v6.7.2",
+ "version": "v6.7.3",
"source": {
"type": "git",
"url": "https://github.com/deployphp/deployer.git",
- "reference": "97c63e91cce93fb46b921de9908f69c7bcbebd32"
+ "reference": "5095b5de166498aedb6ef7ffff98d7243fa036ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/deployphp/deployer/zipball/97c63e91cce93fb46b921de9908f69c7bcbebd32",
- "reference": "97c63e91cce93fb46b921de9908f69c7bcbebd32",
+ "url": "https://api.github.com/repos/deployphp/deployer/zipball/5095b5de166498aedb6ef7ffff98d7243fa036ea",
+ "reference": "5095b5de166498aedb6ef7ffff98d7243fa036ea",
"shasum": ""
},
"require": {
@@ -5441,7 +5518,7 @@
],
"description": "Deployment Tool",
"homepage": "https://deployer.org",
- "time": "2019-12-16T11:41:57+00:00"
+ "time": "2019-12-18T17:30:49+00:00"
},
{
"name": "deployer/phar-update",
diff --git a/public/.htaccess b/public/.htaccess
index 71fc4732..c78f5b06 100644
--- a/public/.htaccess
+++ b/public/.htaccess
@@ -292,6 +292,162 @@ AddDefaultCharset utf-8
# NOTE: Add your additional local storages here
RewriteRule ^(?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]
+ ### Begin: StaticFileCache (preparation) ####
+
+ # Document root configuration
+ RewriteRule .* - [E=SFC_ROOT:%{DOCUMENT_ROOT}]
+
+ # Cleanup URI
+ RewriteCond %{REQUEST_URI} ^.*$
+ RewriteRule .* - [E=SFC_URI:/%{REQUEST_URI}]
+ RewriteCond %{REQUEST_URI} ^/.*$
+ RewriteRule .* - [E=SFC_URI:%{REQUEST_URI}]
+ RewriteCond %{REQUEST_URI} ^/?$
+ RewriteRule .* - [E=SFC_URI:/]
+
+ # Cleanup HOST
+ RewriteCond %{HTTP_HOST} ^([^:]+)(:[0-9]+)?$
+ RewriteRule .* - [E=SFC_HOST:%1]
+
+ # Disable cache for EXT:solr indexing requests
+ RewriteCond %{HTTP:X-Tx-Solr-Iq} .+
+ RewriteRule .* - [E=SFC_HOST:invalid-host]
+
+ # Get scheme
+ RewriteRule .* - [E=SFC_PROTOCOL:http]
+ RewriteCond %{SERVER_PORT} ^443$ [OR]
+ RewriteCond %{HTTP:X-Forwarded-Proto} https
+ RewriteRule .* - [E=SFC_PROTOCOL:https]
+
+ # Get port
+ RewriteRule .* - [E=SFC_PORT:80]
+ RewriteCond %{ENV:SFC_PROTOCOL} ^https$ [NC]
+ RewriteRule .* - [E=SFC_PORT:443]
+ RewriteCond %{SERVER_PORT} ^[0-9]+$
+ RewriteRule .* - [E=SFC_PORT:%{SERVER_PORT}]
+ RewriteCond %{HTTP:X-Forwarded-Port} ^[0-9]+$
+ RewriteRule .* - [E=SFC_PORT:%{HTTP:X-Forwarded-Port}]
+
+ # Full path for redirect
+ RewriteRule .* - [E=SFC_FULLPATH:typo3temp/tx_staticfilecache/%{ENV:SFC_PROTOCOL}_%{ENV:SFC_HOST}_%{ENV:SFC_PORT}%{ENV:SFC_URI}/index]
+
+ # Extension (Order: br, gzip, default)
+ RewriteRule .* - [E=SFC_EXT:]
+ RewriteCond %{HTTP:Accept-Encoding} br [NC]
+ RewriteRule .* - [E=SFC_EXT:.br]
+ RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT} !-f
+ RewriteRule .* - [E=SFC_EXT:]
+ RewriteCond %{ENV:SFC_EXT} ^$
+ RewriteCond %{HTTP:Accept-Encoding} gzip [NC]
+ RewriteRule .* - [E=SFC_EXT:.gz]
+ RewriteCond %{ENV:SFC_EXT} ^\.gz$
+ RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT} !-f
+ RewriteRule .* - [E=SFC_EXT:]
+
+ # Write Extension to SFC_FULLPATH
+ RewriteRule .* - [E=SFC_FULLPATH:%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT}]
+
+ ### Begin: StaticFileCache (main) ####
+
+ # We only redirect URI's without query strings
+ RewriteCond %{QUERY_STRING} ^$
+
+ # It only makes sense to do the other checks if a static file actually exists.
+ RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH} -f
+
+ # NO frontend or backend user is logged in. Logged in users may see different
+ # information than anonymous users. But the anonymous version is cached. So
+ # don't show the anonymous version to logged in users.
+ RewriteCond %{HTTP_COOKIE} !staticfilecache [NC]
+
+ # We only redirect GET requests
+ RewriteCond %{REQUEST_METHOD} GET
+
+ # Rewrite the request to the static file.
+ RewriteRule .* %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH} [L]
+
+ # Do not allow direct call the cache entries
+ RewriteCond %{ENV:SFC_URI} ^/typo3temp/tx_staticfilecache/.*
+ RewriteCond %{ENV:REDIRECT_STATUS} ^$
+ RewriteRule .* - [F,L]
+
+ # Handle application cache
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-l
+ RewriteRule ^.*\.sfc$ %{ENV:CWD}index.php?eID=sfc_manifest [QSA,L]
+
+ ### Begin: StaticFileCache (options) ####
+
+ # Set proper content type and encoding for gzipped html.
+
+ SetEnv no-gzip 1
+ SetEnv no-brotli 1
+
+ Header set Content-Encoding gzip
+
+
+
+ SetEnv no-gzip 1
+ SetEnv no-brotli 1
+
+ Header set Content-Encoding gzip
+
+
+
+ SetEnv no-gzip 1
+ SetEnv no-brotli 1
+
+ Header set Content-Encoding br
+
+
+
+ # if there are same problems with ForceType, please try the AddType alternative
+ # Set proper content type gzipped html
+
+ ForceType text/html
+ # AddType "text/html" .gzip
+
+
+ ForceType text/xml
+ # AddType "text/xml" .gzip
+
+
+ ForceType text/xml
+ # AddType "text/xml" .gzip
+
+
+ ForceType text/html
+ # AddType "text/html" .gz
+
+
+ ForceType text/xml
+ # AddType "text/xml" .gz
+
+
+ ForceType text/xml
+ # AddType "text/xml" .gz
+
+
+ ForceType text/html
+ # AddType "text/html" .br
+
+
+ ForceType text/xml
+ # AddType "text/xml" .br
+
+
+ ForceType text/xml
+ # AddType "text/xml" .br
+
+
+ # Avoid .br files being delivered with Content-Language: br headers
+
+ RemoveLanguage .br
+
+
+ ### End: StaticFileCache ###
+
# If the file/symlink/directory does not exist => Redirect to index.php.
# For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'.
RewriteCond %{REQUEST_FILENAME} !-f