Compare commits

..

1 Commits

Author SHA1 Message Date
Renovate d5d829d3a6 Update dependency prettier to v3 2024-02-04 08:00:46 +00:00
45 changed files with 329 additions and 1060 deletions

View File

@ -1,6 +1,3 @@
{
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"],
"rules": {
"scss/at-extend-no-missing-placeholder": null
}
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"]
}

View File

@ -15,7 +15,7 @@ FROM python:3.12-slim as production
ENV VIRTUAL_ENV=/venv
# renovate: datasource=github-tags depName=just-containers/s6-overlay
# renovate: datasource=github-tags depName=gchq/cyberchef
ENV S6_OVERLAY_VERSION=3.1.6.2
RUN useradd website --create-home -u 1000 && mkdir /app $VIRTUAL_ENV && chown -R website /app $VIRTUAL_ENV

View File

@ -14,9 +14,6 @@ server {
gzip_static on;
gzip on;
gzip_vary on;
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
@ -42,7 +39,6 @@ server {
location / {
proxy_pass http://localhost:8080;
gzip_types *;
}
location /static {

View File

@ -52,9 +52,5 @@ lint_python:
docker-compose -f {{ DEV_COMPOSE }} up -d
docker-compose -f {{ DEV_COMPOSE }} exec web bash
@sh-root:
docker-compose -f {{ DEV_COMPOSE }} up -d
docker-compose -f {{ DEV_COMPOSE }} exec --user=root web bash
@down:
docker-compose -f {{ DEV_COMPOSE }} down

449
package-lock.json generated
View File

@ -9,25 +9,25 @@
"version": "0.0.0",
"dependencies": {
"@fontsource/fira-code": "5.0.2",
"@fortawesome/fontawesome-free": "6.5.2",
"@fortawesome/fontawesome-free": "6.4.0",
"@ledge/is-ie-11": "7.0.0",
"bulma": "0.9.4",
"elevator.js": "1.0.1",
"esbuild": "0.20.2",
"glightbox": "3.3.0",
"esbuild": "0.19.2",
"glightbox": "3.2.0",
"htmx.org": "1.9.2",
"lite-youtube-embed": "0.3.0",
"lodash.clamp": "4.0.3",
"lodash.debounce": "4.0.8",
"lodash.throttle": "4.1.1",
"npm-run-all": "4.1.5",
"sass": "1.75.0",
"sass": "1.70.0",
"shareon": "2.4.0"
},
"devDependencies": {
"eslint": "8.55.0",
"eslint-plugin-unicorn": "49.0.0",
"prettier": "2.7.1",
"prettier": "3.2.5",
"stylelint": "14.16.1",
"stylelint-config-prettier-scss": "0.0.1",
"stylelint-config-standard-scss": "6.1.0"
@ -94,25 +94,10 @@
"postcss-selector-parser": "^6.0.10"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
"integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
"cpu": [
"arm"
],
@ -125,9 +110,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
"integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
"cpu": [
"arm64"
],
@ -140,9 +125,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
"integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
"cpu": [
"x64"
],
@ -155,9 +140,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
"integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
"cpu": [
"arm64"
],
@ -170,9 +155,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
"integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
"cpu": [
"x64"
],
@ -185,9 +170,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
"integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
"cpu": [
"arm64"
],
@ -200,9 +185,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
"integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
"cpu": [
"x64"
],
@ -215,9 +200,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
"integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
"cpu": [
"arm"
],
@ -230,9 +215,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
"integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
"cpu": [
"arm64"
],
@ -245,9 +230,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
"integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
"cpu": [
"ia32"
],
@ -260,9 +245,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
"integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
"cpu": [
"loong64"
],
@ -275,9 +260,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
"integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
"cpu": [
"mips64el"
],
@ -290,9 +275,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
"integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
"cpu": [
"ppc64"
],
@ -305,9 +290,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
"integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
"cpu": [
"riscv64"
],
@ -320,9 +305,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
"integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
"cpu": [
"s390x"
],
@ -335,9 +320,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
"integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
"cpu": [
"x64"
],
@ -350,9 +335,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
"integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
"cpu": [
"x64"
],
@ -365,9 +350,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
"integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
"cpu": [
"x64"
],
@ -380,9 +365,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
"integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
"cpu": [
"x64"
],
@ -395,9 +380,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
"integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
"cpu": [
"arm64"
],
@ -410,9 +395,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
"integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
"cpu": [
"ia32"
],
@ -425,9 +410,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
"integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
"cpu": [
"x64"
],
@ -501,9 +486,9 @@
"integrity": "sha512-T3yzwKP/JFRYdBUHjDXQfRGp9EOI7+V0uCf9ky1rZXDzMUECxuUqTfBcj60CE3oRLzzSm9fgiEGYLSvzo/S/Fw=="
},
"node_modules/@fortawesome/fontawesome-free": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
@ -1148,9 +1133,9 @@
}
},
"node_modules/esbuild": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
"integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@ -1159,29 +1144,28 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
"@esbuild/android-arm": "0.19.2",
"@esbuild/android-arm64": "0.19.2",
"@esbuild/android-x64": "0.19.2",
"@esbuild/darwin-arm64": "0.19.2",
"@esbuild/darwin-x64": "0.19.2",
"@esbuild/freebsd-arm64": "0.19.2",
"@esbuild/freebsd-x64": "0.19.2",
"@esbuild/linux-arm": "0.19.2",
"@esbuild/linux-arm64": "0.19.2",
"@esbuild/linux-ia32": "0.19.2",
"@esbuild/linux-loong64": "0.19.2",
"@esbuild/linux-mips64el": "0.19.2",
"@esbuild/linux-ppc64": "0.19.2",
"@esbuild/linux-riscv64": "0.19.2",
"@esbuild/linux-s390x": "0.19.2",
"@esbuild/linux-x64": "0.19.2",
"@esbuild/netbsd-x64": "0.19.2",
"@esbuild/openbsd-x64": "0.19.2",
"@esbuild/sunos-x64": "0.19.2",
"@esbuild/win32-arm64": "0.19.2",
"@esbuild/win32-ia32": "0.19.2",
"@esbuild/win32-x64": "0.19.2"
}
},
"node_modules/escape-string-regexp": {
@ -1721,9 +1705,9 @@
}
},
"node_modules/glightbox": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.3.0.tgz",
"integrity": "sha512-SJukatHBZZ/POMOpLUQ6/dhXf/wJTDx1wZ/FwApjseXw2WrRj3Ze9DzNCFYzca0oU7RjXQhi9o02aIZ9SuCz1A=="
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.2.0.tgz",
"integrity": "sha512-iit1xYixqL4YVL+I2YJLfMeyJwvLi6FE6kY3qNKeZHEJgRIz80QU8Rm7YCyw1wOTgXvmNDnXGVhHOHRCwnDltQ=="
},
"node_modules/glob": {
"version": "7.2.3",
@ -2981,15 +2965,15 @@
}
},
"node_modules/prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=10.13.0"
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
@ -3319,9 +3303,9 @@
}
},
"node_modules/sass": {
"version": "1.75.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
"version": "1.70.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@ -4094,142 +4078,136 @@
"dev": true,
"requires": {}
},
"@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"optional": true
},
"@esbuild/android-arm": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
"integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
"optional": true
},
"@esbuild/android-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
"integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
"optional": true
},
"@esbuild/android-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
"integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
"optional": true
},
"@esbuild/darwin-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
"integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
"optional": true
},
"@esbuild/darwin-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
"integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
"optional": true
},
"@esbuild/freebsd-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
"integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
"optional": true
},
"@esbuild/freebsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
"integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
"optional": true
},
"@esbuild/linux-arm": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
"integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
"optional": true
},
"@esbuild/linux-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
"integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
"optional": true
},
"@esbuild/linux-ia32": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
"integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
"integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
"optional": true
},
"@esbuild/linux-mips64el": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
"integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
"optional": true
},
"@esbuild/linux-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
"integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
"optional": true
},
"@esbuild/linux-riscv64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
"integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
"optional": true
},
"@esbuild/linux-s390x": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
"integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
"optional": true
},
"@esbuild/linux-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
"integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
"optional": true
},
"@esbuild/netbsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
"integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
"optional": true
},
"@esbuild/openbsd-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
"integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
"optional": true
},
"@esbuild/sunos-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
"integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
"optional": true
},
"@esbuild/win32-arm64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
"integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
"optional": true
},
"@esbuild/win32-ia32": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
"integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
"optional": true
},
"@esbuild/win32-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
"integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
"optional": true
},
"@eslint-community/eslint-utils": {
@ -4276,9 +4254,9 @@
"integrity": "sha512-T3yzwKP/JFRYdBUHjDXQfRGp9EOI7+V0uCf9ky1rZXDzMUECxuUqTfBcj60CE3oRLzzSm9fgiEGYLSvzo/S/Fw=="
},
"@fortawesome/fontawesome-free": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz",
"integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q=="
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ=="
},
"@humanwhocodes/config-array": {
"version": "0.11.13",
@ -4755,33 +4733,32 @@
}
},
"esbuild": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
"integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
"requires": {
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
"@esbuild/android-arm": "0.19.2",
"@esbuild/android-arm64": "0.19.2",
"@esbuild/android-x64": "0.19.2",
"@esbuild/darwin-arm64": "0.19.2",
"@esbuild/darwin-x64": "0.19.2",
"@esbuild/freebsd-arm64": "0.19.2",
"@esbuild/freebsd-x64": "0.19.2",
"@esbuild/linux-arm": "0.19.2",
"@esbuild/linux-arm64": "0.19.2",
"@esbuild/linux-ia32": "0.19.2",
"@esbuild/linux-loong64": "0.19.2",
"@esbuild/linux-mips64el": "0.19.2",
"@esbuild/linux-ppc64": "0.19.2",
"@esbuild/linux-riscv64": "0.19.2",
"@esbuild/linux-s390x": "0.19.2",
"@esbuild/linux-x64": "0.19.2",
"@esbuild/netbsd-x64": "0.19.2",
"@esbuild/openbsd-x64": "0.19.2",
"@esbuild/sunos-x64": "0.19.2",
"@esbuild/win32-arm64": "0.19.2",
"@esbuild/win32-ia32": "0.19.2",
"@esbuild/win32-x64": "0.19.2"
}
},
"escape-string-regexp": {
@ -5174,9 +5151,9 @@
}
},
"glightbox": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.3.0.tgz",
"integrity": "sha512-SJukatHBZZ/POMOpLUQ6/dhXf/wJTDx1wZ/FwApjseXw2WrRj3Ze9DzNCFYzca0oU7RjXQhi9o02aIZ9SuCz1A=="
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.2.0.tgz",
"integrity": "sha512-iit1xYixqL4YVL+I2YJLfMeyJwvLi6FE6kY3qNKeZHEJgRIz80QU8Rm7YCyw1wOTgXvmNDnXGVhHOHRCwnDltQ=="
},
"glob": {
"version": "7.2.3",
@ -6066,9 +6043,9 @@
"dev": true
},
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true
},
"punycode": {
@ -6285,9 +6262,9 @@
}
},
"sass": {
"version": "1.75.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
"version": "1.70.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",

View File

@ -22,26 +22,26 @@
"devDependencies": {
"eslint": "8.55.0",
"eslint-plugin-unicorn": "49.0.0",
"prettier": "2.7.1",
"prettier": "3.2.5",
"stylelint": "14.16.1",
"stylelint-config-prettier-scss": "0.0.1",
"stylelint-config-standard-scss": "6.1.0"
},
"dependencies": {
"@fontsource/fira-code": "5.0.2",
"@fortawesome/fontawesome-free": "6.5.2",
"@fortawesome/fontawesome-free": "6.4.0",
"@ledge/is-ie-11": "7.0.0",
"bulma": "0.9.4",
"elevator.js": "1.0.1",
"esbuild": "0.20.2",
"glightbox": "3.3.0",
"esbuild": "0.19.2",
"glightbox": "3.2.0",
"htmx.org": "1.9.2",
"lite-youtube-embed": "0.3.0",
"lodash.clamp": "4.0.3",
"lodash.debounce": "4.0.8",
"lodash.throttle": "4.1.1",
"npm-run-all": "4.1.5",
"sass": "1.75.0",
"sass": "1.70.0",
"shareon": "2.4.0"
}
}

View File

@ -1,16 +1,16 @@
Django==5.0.4
wagtail==5.2.5
Django==5.0.1
wagtail==5.2.2
django-environ==0.11.2
whitenoise[brotli]==6.6.0
Pygments==2.17.2
beautifulsoup4
lxml==5.2.1
lxml==5.1.0
requests
wagtail-generic-chooser==0.6
django-rq==2.10.1
django-redis==5.4.0
gunicorn==22.0.0
psycopg==3.1.18
gunicorn==21.2.0
psycopg2==2.9.9
djangorestframework
django-htmx==1.17.2
wagtail-metadata==5.0.0
@ -28,12 +28,10 @@ django-permissions-policy==4.18.0
django-enforce-host==1.1.0
django-proxy==1.2.2
wagtail-lite-youtube-embed==0.1.0
django-minify-html==1.7.1
# DRF OpenAPI dependencies
uritemplate
PyYAML
inflection
# Use custom `wagtail-favicon` with performance improvements
git+https://github.com/RealOrangeOne/wagtail-favicon@b892165e047b35c46d7244109b9ad9226d32a213

View File

@ -56,9 +56,6 @@ section.content {
.gslide-image img {
object-fit: contain !important;
// Manually set sizes, as mermaid images are very small
width: 80vw !important;
&[src*="mermaid.ink"] {
@include dark-mode {
filter: invert(100%);

View File

@ -1,27 +1,17 @@
body.page-homepage {
min-height: 100vh;
height: 100vh;
main {
background-repeat: no-repeat;
background-size: cover;
background-position: center;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: $white;
margin-bottom: 0;
padding-bottom: 2rem;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.top-section {
min-width: 90%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 2rem;
margin-bottom: 1rem;
}
h1 {
@ -33,7 +23,7 @@ body.page-homepage {
min-width: 45%;
}
.box {
.latest {
padding: 0.25rem 0.5rem;
margin-top: 2rem;
background-color: color.adjust($dark, $alpha: -0.2);
@ -69,60 +59,4 @@ body.page-homepage {
#to-top {
display: none;
}
.content-list {
width: 90%;
.card-image {
overflow: hidden;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
transition: filter 0.25s;
filter: brightness(0.85);
}
p {
position: absolute;
bottom: 0;
color: $white;
padding: 0.5rem;
transition: background-color 0.25s;
width: 100%;
background-color: rgb(0 0 0 / 55%);
}
figure {
margin: 0;
text-align: initial;
}
a:hover {
img {
filter: unset;
}
p {
background-color: rgb(0 0 0 / 75%);
}
}
}
.recent-posts {
display: flex;
align-items: center;
flex-direction: column;
width: 100%;
flex-grow: unset;
margin-top: 2rem;
.box {
margin: 0 auto;
display: inline-block;
}
}
}

View File

@ -48,7 +48,7 @@
}
}
.container.listing {
.page-blogpostlistpage {
.date-header {
font-size: $size-2;
font-weight: $weight-bold;

View File

@ -3,5 +3,4 @@ from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 25

View File

@ -7,7 +7,6 @@ from django.utils import timezone
from django.utils.functional import cached_property
from modelcluster.fields import ParentalManyToManyField
from wagtail.admin.panels import FieldPanel
from wagtail.models import PageQuerySet
from wagtail.search import index
from wagtailautocomplete.edit_handlers import AutocompletePanel
@ -62,19 +61,6 @@ class BlogPostPage(BaseContentPage):
def tag_list_page_url(self) -> Optional[str]:
return SingletonPageCache.get_url(BlogPostTagListPage)
@cached_property
def tags_list(self) -> models.QuerySet:
"""
Use this to get a page's tags.
"""
tags = self.tags.order_by("slug")
# In drafts, `django-modelcluster` doesn't support these filters
if isinstance(tags, PageQuerySet):
return tags.public().live()
return tags
@cached_property
def blog_post_list_page_url(self) -> Optional[str]:
return SingletonPageCache.get_url(BlogPostListPage)

View File

@ -1,12 +1,14 @@
{% extends "common/listing_page.html" %}
{% load wagtailroutablepage_tags %}
{% block hero_buttons %}
<a class="button is-radiusless" href="{{ page.tag_list_page_url }}" title="View tags"><i class="fas fa-tags" aria-hidden="true"></i></a>
{{ block.super }}
{% endblock %}
{% block post_content %}
<section class="container listing">
<section class="container">
{% for page in listing_pages %}
{% ifchanged %}
<h2 id="date-{{ page.date|date:'Y-m' }}" class="date-header">

View File

@ -1,11 +1,11 @@
{% extends "common/content_page.html" %}
{% load wagtail_cache navbar_tags %}
{% load wagtail_cache %}
{% block post_content %}
{% if not request.is_preview %}
{% include "common/shareon.html" %}
{{ block.super }}
{% if not request.is_preview %}
{% wagtailpagecache FRAGMENT_CACHE_TTL "similar-content" %}
<section class="container similar-content" id="similar-content">
<h2 class="subtitle is-size-2">Similar content</h2>
@ -16,17 +16,11 @@
{% for page in page.get_similar_posts %}
{% block listing_item %}
{% include "common/listing-item.html" with show_listing_images=True %}
{% include "common/listing-item.html" %}
{% endblock %}
{% endfor %}
</section>
{% endwagtailpagecache %}
{% include "common/comments.html" %}
{% if not request.user.is_authenticated %}
{% support_pill %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -1,9 +0,0 @@
from django_minify_html.middleware import MinifyHtmlMiddleware
class CustomMinifyHtmlMiddleware(MinifyHtmlMiddleware):
minify_args = {
"do_not_minify_doctype": True,
"ensure_spec_compliant_unquoted_attribute_values": True,
"keep_spaces_between_attributes": True,
}

View File

@ -11,7 +11,7 @@ from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect
from django.template.defaultfilters import pluralize
from django.utils.functional import cached_property, classproperty
from django.utils.text import Truncator, slugify
from django.utils.text import slugify
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.contrib.settings.models import BaseGenericSetting, register_setting
@ -31,10 +31,12 @@ from .serializers import PaginationSerializer
from .streamfield import add_heading_anchors, get_blocks, get_content_html
from .utils import (
TocEntry,
count_words,
extract_text,
get_site_title,
get_table_of_contents,
get_url_mime_type,
truncate_string,
)
@ -139,11 +141,16 @@ class BaseContentPage(BasePage, MetadataMixin):
@cached_property
def word_count(self) -> int:
return len(self.plain_text.split())
return count_words(self.plain_text)
@cached_property
def summary(self) -> str:
return Truncator(self.plain_text).words(50)
summary = truncate_string(self.plain_text, 50)
if summary and summary != self.plain_text and not summary.endswith("."):
summary += ""
return summary
@cached_property
def body_html(self) -> str:
@ -256,12 +263,7 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
def get_context(self, request: HttpRequest) -> dict:
context = super().get_context(request)
listing_pages = self.get_paginator_page()
context["listing_pages"] = listing_pages
# Show listing images if at least 1 page has an image
context["show_listing_images"] = any(p.list_image_url for p in listing_pages)
context["listing_pages"] = self.get_paginator_page()
return context
@cached_property
@ -295,13 +297,6 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
def feed(self, request: HttpRequest) -> HttpResponse:
return redirect("feed", permanent=True)
@route(r"^random/$")
def random(self, request: HttpRequest) -> HttpResponse:
page = self.get_listing_pages().order_by("?").first()
if page is None:
return redirect(self.get_url(request=request), permanent=False)
return redirect(page.get_url(request=request), permanent=False)
class ListingPage(BaseListingPage):
pass

View File

@ -2,7 +2,7 @@
<figure>
<div class="image">
<a href="{% image_url value.image 'original' %}" class="glightbox" data-gallery="content" data-height="70vh" data-alt="{{ value.caption|richtext|extract_text }}" data-title="{{ value.caption|richtext|extract_text }}">
<a href="{% image_url value.image 'original' %}" class="glightbox" data-gallery="content" data-height="70vh" data-width="95vw" data-alt="{{ value.caption|richtext|extract_text }}">
<img src="{% image_url value.image 'width-1500' %}" alt="{{ value.caption|richtext|extract_text }}" loading="lazy" decoding="async" />
</a>
</div>

View File

@ -20,50 +20,17 @@
</div>
{% endif %}
{% if page.tags_list %}
{% if page.tags.public.live.all %}
<div class="icon-text is-family-code">
<span class="icon">
<a href="{{ page.tag_list_page_url }}" title="View all tags">
<i class="fas fa-lg fa-tags"></i>
</a>
</span>
{% for tag in page.tags_list %}
{% for tag in page.tags.public.live.all|dictsort:"slug" %}
<span><a title="{{ tag.name }}" href="{% pageurl tag %}">#{{ tag.slug }}</a></span>
{% endfor %}
</div>
{% endif %}
{% if page.slides_url %}
<span class="icon-text">
<a href="{{ page.slides_url }}">
<span class="icon">
<i class="fas fa-lg fa-images"></i>
</span>
<span>Slides</span>
</a>
</span>
{% endif %}
{% if page.video_url %}
<span class="icon-text">
<a href="{{ page.video_url }}">
<span class="icon">
<i class="fas fa-lg fa-film"></i>
</span>
<span>Video</span>
</a>
</span>
{% endif %}
{% if page.location_name and page.location_url %}
<span class="icon-text">
<a href="{{ page.location_url }}">
<span class="icon">
<i class="fas fa-lg fa-map-marker-alt"></i>
</span>
<span>{{ page.location_name }}</span>
</a>
</span>
{% endif %}
</div>
{% endwagtailpagecache %}

View File

@ -1,9 +1,9 @@
{% load wagtailcore_tags wagtail_cache util_tags %}
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs show_listing_images %}
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs %}
<article class="media listing-item">
<div class="columns">
<figure class="media-left column is-{{ show_listing_images|yesno:'3,1' }} image-column">
<figure class="media-left column is-3 image-column">
{% if page.list_image_url %}
<a href="{% pageurl page %}" class="image" title="{{ page.title }}">
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />

View File

@ -1,10 +1,8 @@
{% extends "common/content_page.html" %}
{% load wagtailadmin_tags wagtailroutablepage_tags %}
{% load wagtailadmin_tags %}
{% block hero_buttons %}
<a class="button is-radiusless" href="{% routablepageurl page 'random' %}" title="View random"><i class="fas fa-dice" aria-hidden="true"></i></a>
{% if listing_pages.has_previous %}
<a class="button is-radiusless" href="{% querystring page=listing_pages.previous_page_number %}" title="Previous page"><i class="fas fa-arrow-left" aria-hidden="true"></i></a>
{% endif %}

View File

@ -1,9 +0,0 @@
{% load wagtailcore_tags %}
{% spaceless %}
{{ obj.content_html | truncatewords_html:100 | safe }}
<p>
<a href="{% fullpageurl obj %}">Continue Reading&hellip;</a>
</p>
{% endspaceless %}

View File

@ -77,12 +77,3 @@ class ListingPageTestCase(TestCase):
self.assertEqual(
response.context["page"].get_meta_url(), self.page.full_url + "?page=2"
)
def test_random(self) -> None:
url = self.page.url + self.page.reverse_subpage("random")
with self.assertNumQueries(10):
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertIn(
response.url, [page.get_url() for page in self.page.get_listing_pages()]
)

View File

@ -3,6 +3,7 @@ from django.test import SimpleTestCase
from wagtail.rich_text import features as richtext_feature_registry
from website.common.utils import (
count_words,
extract_text,
get_table_of_contents,
heading_id,
@ -96,6 +97,13 @@ class ExtractTextTestCase(SimpleTestCase):
self.assertEqual(extract_text("Hello there!"), "Hello there!")
class CountWordsTestCase(SimpleTestCase):
def test_counts_words(self) -> None:
self.assertEqual(count_words("a b c"), 3)
self.assertEqual(count_words("Correct Horse Battery Staple"), 4)
self.assertEqual(count_words("Hello there! How are you?"), 5)
class RichTextFeaturesTestCase(SimpleTestCase):
def test_features_exist(self) -> None:
for editor, editor_config in settings.WAGTAILADMIN_RICH_TEXT_EDITORS.items():

View File

@ -1,13 +1,13 @@
from dataclasses import dataclass
from itertools import pairwise
from typing import Optional, Type
from itertools import islice, pairwise
from typing import Iterable, Optional, Type
import requests
from bs4 import BeautifulSoup, SoupStrainer
from django.conf import settings
from django.db import models
from django.http.request import HttpRequest
from django.utils.text import slugify
from django.utils.text import re_words, slugify
from django_cache_decorator import django_cache_decorator
from wagtail.models import Page, Site
from wagtail.models import get_page_models as get_wagtail_page_models
@ -69,6 +69,19 @@ def show_toolbar_callback(request: HttpRequest) -> bool:
return settings.DEBUG
def split_words(text: str) -> Iterable[str]:
for word in re_words.split(text):
if word and word.strip():
yield word.strip()
def count_words(text: str) -> int:
"""
Count the number of words in the text, without duplicating the item in memory
"""
return len(list(split_words(text)))
def extract_text(html: str) -> str:
"""
Get the plain text of some HTML.
@ -78,6 +91,10 @@ def extract_text(html: str) -> str:
)
def truncate_string(text: str, words: int) -> str:
return " ".join(islice(split_words(text), words))
def heading_id(heading: str) -> str:
"""
Convert a heading into an identifier which is valid for a HTML id attribute

View File

@ -15,7 +15,6 @@ from wagtail.query import PageQuerySet
from wagtail_favicon.models import FaviconSettings
from wagtail_favicon.utils import get_rendition_url
from website.blog.models import BlogPostPage
from website.common.utils import get_site_title
from website.contrib.singleton_page.utils import SingletonPageCache
from website.home.models import HomePage
@ -64,7 +63,6 @@ class KeybaseView(TemplateView):
class AllPagesFeed(Feed):
feed_type = CustomFeed
link = "/"
description_template = "feed-description.html"
def __init__(self) -> None:
self.style_tag = f'<?xml-stylesheet href="{static("contrib/pretty-feed-v3.xsl")}" type="text/xsl"?>'.encode()
@ -124,9 +122,12 @@ class AllPagesFeed(Feed):
def item_updateddate(self, item: BasePage) -> datetime:
return item.last_published_at
def item_description(self, item: BasePage) -> str:
return getattr(item, "summary", None) or item.title
def item_categories(self, item: BasePage) -> Optional[list[str]]:
if isinstance(item, BlogPostPage):
return item.tags_list.values_list("slug", flat=True)
if tags := getattr(item, "tags", None):
return tags.public().live().order_by("slug").values_list("slug", flat=True)
return None
def item_enclosure_url(self, item: BasePage) -> Optional[str]:

View File

@ -9,7 +9,6 @@ class GitHubLinguistHealthCheckBackend(BaseHealthCheckBackend):
colours = _get_linguist_colours()
except Exception as e:
self.add_error(str(e))
return
if colours is None:
self.add_error("No colours provided")

View File

@ -2,7 +2,7 @@
<figure>
<div class="image">
<a href="https://mermaid.ink/svg/{{ value.pako }}" data-gallery="content" class="glightbox" data-type="image" data-height="70vh" data-alt="{{ value.caption|richtext|extract_text }}" data-title="{{ value.caption|richtext|extract_text }}">
<a href="https://mermaid.ink/svg/{{ value.pako }}" data-gallery="content" class="glightbox" data-type="image" data-height="60vh" data-width="95vw" data-alt="{{ value.caption|richtext|extract_text }}">
<img src="https://mermaid.ink/svg/{{ value.pako }}" referrerpolicy="no-referrer" alt="{{ value.caption|richtext|extract_text }}" loading="lazy" decoding="async" />
</a>
</div>

View File

@ -1,6 +1,5 @@
from django.core.cache import cache
from wagtail import hooks
from wagtail.models import Page
from website.common.utils import get_page_models
@ -8,7 +7,7 @@ from .utils import SingletonPageCache
@hooks.register("after_move_page")
def clear_singleton_url_cache(page_to_move: Page) -> None:
def clear_singleton_url_cache(**kwargs: dict) -> None:
"""
Clear all page caches, in case a parent has moved
"""

View File

@ -1,5 +1,8 @@
from typing import Optional, Tuple
from django.db import models
from django.http.request import HttpRequest
from django_cache_decorator import django_cache_decorator
from wagtail.admin.panels import FieldPanel
from wagtail.images import get_image_model_string
from wagtail.images.models import Image
@ -9,6 +12,20 @@ from website.common.models import BasePage
from website.contrib.singleton_page.utils import SingletonPageCache
@django_cache_decorator(time=600)
def get_latest_blog_post() -> Optional[Tuple[str, str]]:
from website.blog.models import BlogPostPage
try:
latest_blog_post = (
BlogPostPage.objects.live().public().defer_streamfields().latest("date")
)
except BlogPostPage.DoesNotExist:
return None
return latest_blog_post.title, latest_blog_post.get_url()
class HomePage(BasePage, WagtailImageMetadataMixin):
max_count = 1
@ -38,22 +55,9 @@ class HomePage(BasePage, WagtailImageMetadataMixin):
return self.html_title
def get_context(self, request: HttpRequest) -> dict:
from website.blog.models import BlogPostListPage, BlogPostPage
from website.search.models import SearchPage
context = super().get_context(request)
context["recent_posts"] = list(
BlogPostPage.objects.live()
.public()
.defer_streamfields()
.order_by("-date")[:7]
)
context["latest_blog_post"] = (
context["recent_posts"].pop(0) if context["recent_posts"] else None
)
context["latest_blog_post"] = get_latest_blog_post()
context["search_page_url"] = SingletonPageCache.get_url(SearchPage, request)
context["blog_post_list_url"] = SingletonPageCache.get_url(
BlogPostListPage, request
)
return context

View File

@ -4,38 +4,21 @@
{% block main %}
<main {% if page.image %}style="background-image: url({% image_url page.image 'width-1200' %})"{% endif %}>
<div class="top-section">
<div class="heading-wrapper">
<h1>{{ page.heading }}</h1>
{% if search_page_url %}
<form action="{{ search_page_url }}">
<input id="search-input" class="input" type="text" placeholder="Search" name="q" />
</form>
{% endif %}
</div>
{% if latest_blog_post %}
<div class="box latest is-size-5">
<strong>Latest Post</strong>:
<a href="{% pageurl latest_blog_post %}">{{ latest_blog_post.title }}</a>
&rarr;
</div>
<div class="heading-wrapper">
<h1>{{ page.heading }}</h1>
{% if search_page_url %}
<form action="{{ search_page_url }}">
<input id="search-input" class="input" type="text" placeholder="Search" name="q" />
</form>
{% endif %}
</div>
<section class="container content recent-posts">
<h2 class="has-text-centered has-text-white is-size-3">Recent Posts</h2>
<div class="columns content-list is-multiline">
{% for page in recent_posts %}
{% include "home/home_page_card.html" %}
{% endfor %}
{% if latest_blog_post %}
<div class="box latest">
<strong>Latest Post</strong>:
<a href="{{ latest_blog_post.1 }}">{{ latest_blog_post.0 }}</a>
&rarr;
</div>
{% if blog_post_list_url %}
<div class="box">
<a href="{{ blog_post_list_url }}">View more &rarr;</a>
</div>
{% endif %}
</section>
{% endif %}
</main>
{% endblock %}

View File

@ -1,16 +0,0 @@
{% load wagtail_cache wagtailcore_tags %}
{% wagtailpagecache FRAGMENT_CACHE_TTL "homepage-card" %}
<div class="column is-one-third-widescreen is-half">
<div class="card">
<div class="card-image">
<a href="{% pageurl page %}">
<figure class="image is-16by9">
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />
<p>{{ page.title }}</p>
</figure>
</a>
</div>
</div>
</div>
{% endwagtailpagecache %}

View File

@ -10,5 +10,4 @@ urlpatterns = [
path("tags/<slug:slug>/", views.TagView.as_view()),
path("tags/", views.TagsView.as_view()),
path("categories/", views.TagsView.as_view()),
path("index.json", views.PageLinksView.as_view()),
]

View File

@ -12,12 +12,6 @@ class AllPagesFeedView(RedirectView):
permanent = True
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
class PageLinksView(RedirectView):
pattern_name = "api:page-links"
permanent = True
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
class TagView(RedirectView):
permanent = True

View File

@ -1,7 +1,7 @@
{% load wagtailadmin_tags %}
{% for page in results %}
{% include "common/listing-item.html" with breadcrumbs=True show_listing_images=True %}
{% include "common/listing-item.html" with breadcrumbs=True %}
{% endfor %}
{% if not results and page_num == 1 %}

View File

@ -38,7 +38,7 @@ class SearchPageTestCase(TestCase):
self.assertEqual(search_input.attrs["name"], "q")
self.assertEqual(search_input.attrs["hx-get"], "results/")
self.assertNotIn("value", search_input.attrs) # Because of minify-html
self.assertEqual(search_input.attrs["value"], "")
self.assertEqual(len(soup.select(search_input.attrs["hx-target"])), 1)
self.assertEqual(len(soup.select(search_input.attrs["hx-indicator"])), 2)

View File

@ -42,7 +42,6 @@ INSTALLED_APPS = [
"website.utils",
"website.well_known",
"website.legacy",
"website.talks",
"website.contrib.code_block",
"website.contrib.mermaid_block",
"website.contrib.unsplash",
@ -79,7 +78,6 @@ INSTALLED_APPS = [
"wagtail_2fa",
"django_otp",
"django_otp.plugins.otp_totp",
"django_minify_html",
"health_check",
"health_check.db",
"health_check.cache",
@ -94,12 +92,12 @@ INSTALLED_APPS = [
]
MIDDLEWARE = [
"django.middleware.gzip.GZipMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"corsheaders.middleware.CorsMiddleware",
"enforce_host.EnforceHostMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"website.common.middleware.CustomMinifyHtmlMiddleware",
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
@ -376,11 +374,6 @@ LOGGING = {
"level": "WARNING",
"propagate": False,
},
"wagtail.images": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": False,
},
"django.request": {
"handlers": ["console"],
"level": "ERROR",

View File

@ -1,17 +0,0 @@
from datetime import timedelta
from website.common.factories import BaseContentFactory, BaseListingFactory
from . import models
class TalksListPageFactory(BaseListingFactory):
class Meta:
model = models.TalksListPage
class TalkPageFactory(BaseContentFactory):
duration = timedelta(minutes=30)
class Meta:
model = models.TalkPage

View File

@ -1,354 +0,0 @@
# Generated by Django 5.0.1 on 2024-03-01 17:44
import django.db.models.deletion
import django.utils.timezone
import wagtail.blocks
import wagtail.contrib.routable_page.models
import wagtail.contrib.typed_table_block.blocks
import wagtail.embeds.blocks
import wagtail.fields
import wagtail.images.blocks
import wagtailmetadata.models
from django.db import migrations, models
import website.contrib.code_block.blocks
class Migration(migrations.Migration):
initial = True
dependencies = [
("images", "0002_alter_customimage_file_alter_customrendition_file"),
("unsplash", "0001_initial"),
("wagtailcore", "0089_log_entry_data_json_null_to_object"),
]
operations = [
migrations.CreateModel(
name="TalkPage",
fields=[
(
"page_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="wagtailcore.page",
),
),
("subtitle", wagtail.fields.RichTextField(blank=True)),
(
"body",
wagtail.fields.StreamField(
[
("embed", wagtail.embeds.blocks.EmbedBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
(
"lorem",
wagtail.blocks.StructBlock(
[
(
"paragraphs",
wagtail.blocks.IntegerBlock(min_value=1),
)
]
),
),
("html", wagtail.blocks.RawHTMLBlock()),
(
"image",
wagtail.blocks.StructBlock(
[
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
(
"code",
wagtail.blocks.StructBlock(
[
(
"filename",
wagtail.blocks.CharBlock(
max_length=128, required=False
),
),
(
"language",
wagtail.blocks.ChoiceBlock(
choices=website.contrib.code_block.blocks.get_language_choices
),
),
("source", wagtail.blocks.TextBlock()),
]
),
),
(
"tangent",
wagtail.blocks.StructBlock(
[
(
"name",
wagtail.blocks.CharBlock(max_length=64),
),
(
"content",
wagtail.blocks.RichTextBlock(
editor="simple"
),
),
]
),
),
(
"mermaid",
wagtail.blocks.StructBlock(
[
("source", wagtail.blocks.TextBlock()),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"rich_text",
wagtail.blocks.RichTextBlock(
editor="plain"
),
),
("numeric", wagtail.blocks.FloatBlock()),
("text", wagtail.blocks.CharBlock()),
]
),
),
(
"iframe",
wagtail.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock()),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
],
blank=True,
use_json_field=True,
),
),
("date", models.DateField(default=django.utils.timezone.now)),
("duration", models.DurationField()),
("slides_url", models.URLField(blank=True)),
("video_url", models.URLField(blank=True)),
("location_name", models.CharField(blank=True, max_length=64)),
("location_url", models.URLField(blank=True)),
(
"hero_image",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="images.customimage",
),
),
(
"hero_unsplash_photo",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="unsplash.unsplashphoto",
),
),
],
options={
"abstract": False,
},
bases=("wagtailcore.page", wagtailmetadata.models.MetadataMixin),
),
migrations.CreateModel(
name="TalksListPage",
fields=[
(
"page_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="wagtailcore.page",
),
),
(
"body",
wagtail.fields.StreamField(
[
("embed", wagtail.embeds.blocks.EmbedBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
(
"lorem",
wagtail.blocks.StructBlock(
[
(
"paragraphs",
wagtail.blocks.IntegerBlock(min_value=1),
)
]
),
),
("html", wagtail.blocks.RawHTMLBlock()),
(
"image",
wagtail.blocks.StructBlock(
[
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
(
"code",
wagtail.blocks.StructBlock(
[
(
"filename",
wagtail.blocks.CharBlock(
max_length=128, required=False
),
),
(
"language",
wagtail.blocks.ChoiceBlock(
choices=website.contrib.code_block.blocks.get_language_choices
),
),
("source", wagtail.blocks.TextBlock()),
]
),
),
(
"tangent",
wagtail.blocks.StructBlock(
[
(
"name",
wagtail.blocks.CharBlock(max_length=64),
),
(
"content",
wagtail.blocks.RichTextBlock(
editor="simple"
),
),
]
),
),
(
"mermaid",
wagtail.blocks.StructBlock(
[
("source", wagtail.blocks.TextBlock()),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"rich_text",
wagtail.blocks.RichTextBlock(
editor="plain"
),
),
("numeric", wagtail.blocks.FloatBlock()),
("text", wagtail.blocks.CharBlock()),
]
),
),
(
"iframe",
wagtail.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock()),
(
"caption",
wagtail.blocks.RichTextBlock(
editor="plain", required=False
),
),
]
),
),
],
blank=True,
use_json_field=True,
),
),
(
"hero_image",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="images.customimage",
),
),
(
"hero_unsplash_photo",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="unsplash.unsplashphoto",
),
),
],
options={
"abstract": False,
},
bases=(
wagtail.contrib.routable_page.models.RoutablePageMixin,
"wagtailcore.page",
wagtailmetadata.models.MetadataMixin,
),
),
]

View File

@ -1,62 +0,0 @@
from datetime import timedelta
from typing import Any
from django.db import models
from django.utils import timezone
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from website.common.models import BaseContentPage, BaseListingPage
class TalksListPage(BaseListingPage):
max_count = 1
subpage_types = ["talks.TalkPage"]
class TalkPage(BaseContentPage):
subpage_types: list[Any] = []
parent_page_types = [TalksListPage]
date = models.DateField(default=timezone.now)
duration = models.DurationField()
slides_url = models.URLField(blank=True)
video_url = models.URLField(blank=True)
location_name = models.CharField(max_length=64, blank=True)
location_url = models.URLField(blank=True)
content_panels = BaseContentPage.content_panels + [
MultiFieldPanel(
[
FieldPanel("slides_url"),
FieldPanel("video_url"),
],
heading="Media",
),
MultiFieldPanel(
[
FieldPanel("location_name"),
FieldPanel("location_url"),
],
heading="Location",
),
FieldPanel("duration"),
]
promote_panels = BaseContentPage.promote_panels + [
FieldPanel("date"),
]
@property
def show_table_of_contents(self) -> bool:
return False
@property
def reading_time(self) -> timedelta:
return self.duration
@property
def word_count(self) -> int:
return 0

View File

@ -1,11 +0,0 @@
{% extends "common/content_page.html" %}
{% load wagtailembeds_tags %}
{% block pre_content %}
{% if page.video_url %}
<section class="container mb-5 content">
<div class="block-embed">{% embed page.video_url %}</div>
</section>
{% endif %}
{% endblock %}

View File

@ -1,26 +0,0 @@
{% extends "common/listing_page.html" %}
{% load wagtailroutablepage_tags %}
{% block post_content %}
<section class="container listing">
{% for page in listing_pages %}
{% ifchanged %}
<h2 id="date-{{ page.date.year }}" class="date-header">
<time datetime="{{ page.date.year }}" title="{{ page.date.year }}">
{{ page.date.year }}
</time>
</h2>
{% endifchanged %}
{% include "common/listing-item.html" %}
{% endfor %}
</section>
{% if listing_pages.has_other_pages %}
<section class="container">
<hr class="my-5" />
{% include "common/pagination.html" with page=listing_pages %}
</section>
{% endif %}
{% endblock %}

View File

@ -1,47 +0,0 @@
from django.test import TestCase
from django.urls import reverse
from website.home.models import HomePage
from .factories import TalkPageFactory, TalksListPageFactory
class TalkPageTestCase(TestCase):
@classmethod
def setUpTestData(cls) -> None:
cls.home_page = HomePage.objects.get()
cls.list_page = TalksListPageFactory(parent=cls.home_page)
cls.page = TalkPageFactory(parent=cls.list_page)
def test_accessible(self) -> None:
response = self.client.get(self.page.url)
self.assertEqual(response.status_code, 200)
def test_queries(self) -> None:
with self.assertNumQueries(34):
self.client.get(self.page.url)
class TalksListPageTestCase(TestCase):
@classmethod
def setUpTestData(cls) -> None:
cls.home_page = HomePage.objects.get()
cls.page = TalksListPageFactory(parent=cls.home_page)
TalkPageFactory(parent=cls.page)
TalkPageFactory(parent=cls.page)
def test_accessible(self) -> None:
response = self.client.get(self.page.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context["listing_pages"]), 2)
def test_queries(self) -> None:
with self.assertNumQueries(35):
self.client.get(self.page.url)
def test_feed_accessible(self) -> None:
response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
self.assertRedirects(
response, reverse("feed"), status_code=301, fetch_redirect_response=True
)

View File

@ -1,5 +1,4 @@
from datetime import timedelta
from urllib.parse import urljoin
from django.conf import settings
from django.http.request import HttpRequest
@ -57,13 +56,10 @@ def activitypub_proxy(request: HttpRequest) -> HttpResponse:
if not settings.ACTIVITYPUB_HOST:
raise Http404
activitypub_url = urljoin(
"https://" + settings.ACTIVITYPUB_HOST,
request.path,
allow_fragments=True,
)
try:
return proxy_view(request, activitypub_url)
return proxy_view(
request,
f"https://{settings.ACTIVITYPUB_HOST}{request.path}",
)
except RequestException:
return HttpResponse(status=502)
return HttpResponse(status_code=502)